1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-07-04 11:13:36 +00:00

merge upstream v7.9.0 & v7.9.1

This commit is contained in:
luvletter2333 2021-08-01 09:31:28 -04:00
commit 1cc03ad060
174 changed files with 4967 additions and 1520 deletions

View File

@ -3,15 +3,15 @@ import cn.hutool.core.util.RuntimeUtil
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
def verName = "7.8.2-preview01"
def verCode = 350
def verName = "7.9.1-preview01"
def verCode = 355
if (System.getenv("DEBUG_BUILD") == "true") {
verName += "-" + RuntimeUtil.execForStr("git log --pretty=format:'%h' -n 1")
}
def officialVer = "7.8.2"
def officialCode = 2376
def officialVer = "7.9.1"
def officialCode = 2387
def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json")

View File

@ -448,7 +448,7 @@ target_compile_definitions(sqlite PUBLIC
#voip
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
set(NATIVE_LIB "tmessages.39")
set(NATIVE_LIB "tmessages.40")
#tmessages
add_library(${NATIVE_LIB} SHARED

View File

@ -213,8 +213,8 @@ void setUserId(JNIEnv *env, jclass c, jint instanceNum, int32_t id) {
ConnectionsManager::getInstance(instanceNum).setUserId(id);
}
void switchBackend(JNIEnv *env, jclass c, jint instanceNum) {
ConnectionsManager::getInstance(instanceNum).switchBackend();
void switchBackend(JNIEnv *env, jclass c, jint instanceNum, jboolean restart) {
ConnectionsManager::getInstance(instanceNum).switchBackend(restart);
}
void pauseNetwork(JNIEnv *env, jclass c, jint instanceNum) {
@ -528,7 +528,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
{"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode},
{"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId},
{"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode},
{"native_switchBackend", "(I)V", (void *) switchBackend},
{"native_switchBackend", "(IZ)V", (void *) switchBackend},
{"native_pauseNetwork", "(I)V", (void *) pauseNetwork},
{"native_resumeNetwork", "(IZ)V", (void *) resumeNetwork},
{"native_updateDcSettings", "(I)V", (void *) updateDcSettings},

View File

@ -1889,14 +1889,16 @@ void ConnectionsManager::setUserId(int32_t userId) {
});
}
void ConnectionsManager::switchBackend() {
//scheduleTask([&] {
currentDatacenterId = 1;
testBackend = !testBackend;
datacenters.clear();
initDatacenters();
saveConfig();
//exit(1);
void ConnectionsManager::switchBackend(bool restart) {
//scheduleTask([&, restart] {
currentDatacenterId = 1;
testBackend = !testBackend;
datacenters.clear();
initDatacenters();
saveConfig();
if (restart) {
exit(1);
}
//});
}

View File

@ -58,7 +58,7 @@ public:
void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate);
ConnectionState getConnectionState();
void setUserId(int32_t userId);
void switchBackend();
void switchBackend(bool restart);
void resumeNetwork(bool partial);
void pauseNetwork();
void setNetworkAvailable(bool value, int32_t type, bool slow);

View File

@ -2218,6 +2218,7 @@ add_library(voipandroid STATIC
voip/webrtc/modules/audio_device/android/audio_manager.cc
voip/webrtc/modules/audio_device/android/audio_record_jni.cc
voip/webrtc/modules/audio_device/android/audio_screen_record_jni.cc
voip/webrtc/modules/audio_device/android/audio_merged_screen_record_jni.cc
voip/webrtc/modules/audio_device/android/audio_track_jni.cc
voip/webrtc/modules/audio_device/android/build_info.cc
voip/webrtc/modules/audio_device/android/opensles_common.cc

View File

@ -169,8 +169,10 @@ struct InstanceHolder {
std::unique_ptr<Instance> nativeInstance;
std::unique_ptr<GroupInstanceCustomImpl> groupNativeInstance;
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
std::shared_ptr<tgcalls::VideoCaptureInterface> _screenVideoCapture;
std::shared_ptr<PlatformContext> _platformContext;
std::map<std::string, SetVideoSink> remoteGroupSinks;
bool useScreencast = false;
};
jlong getInstanceHolderId(JNIEnv *env, jobject obj) {
@ -392,38 +394,40 @@ JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeInstance_makeGrou
});
},
.videoCapture = videoCapture,
.requestBroadcastPart = [](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);
((AndroidContext *) platformContext.get())->streamTask = task;
tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task](JNIEnv *env) {
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJ)V"), timestamp, duration);
});
return task;
},
.videoContentType = screencast ? VideoContentType::Screencast : VideoContentType::Generic,
.initialEnableNoiseSuppression = (bool) noiseSupression,
.requestMediaChannelDescriptions = [platformContext](std::vector<uint32_t> const &ssrcs, std::function<void(std::vector<MediaChannelDescription> &&)> callback) -> std::shared_ptr<RequestMediaChannelDescriptionTask> {
std::shared_ptr<RequestMediaChannelDescriptionTaskJava> task = std::make_shared<RequestMediaChannelDescriptionTaskJava>(platformContext, callback);
((AndroidContext *) platformContext.get())->descriptionTasks.push_back(task);
tgvoip::jni::DoWithJNI([platformContext, ssrcs, task](JNIEnv *env) {
unsigned int size = ssrcs.size();
jintArray intArray = env->NewIntArray(size);
jint intFill[size];
for (int a = 0; a < size; a++) {
intFill[a] = ssrcs[a];
}
env->SetIntArrayRegion(intArray, 0, size, intFill);
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onParticipantDescriptionsRequired", "(J[I)V"), (jlong) task.get(), intArray);
env->DeleteLocalRef(intArray);
});
return task;
},
.platformContext = platformContext
};
if (!screencast) {
descriptor.requestBroadcastPart = [](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);
((AndroidContext *) platformContext.get())->streamTask = task;
tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task](JNIEnv *env) {
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJ)V"), timestamp, duration);
});
return task;
};
descriptor.requestMediaChannelDescriptions = [platformContext](std::vector<uint32_t> const &ssrcs, std::function<void(std::vector<MediaChannelDescription> &&)> callback) -> std::shared_ptr<RequestMediaChannelDescriptionTask> {
std::shared_ptr<RequestMediaChannelDescriptionTaskJava> task = std::make_shared<RequestMediaChannelDescriptionTaskJava>(platformContext, callback);
((AndroidContext *) platformContext.get())->descriptionTasks.push_back(task);
tgvoip::jni::DoWithJNI([platformContext, ssrcs, task](JNIEnv *env) {
unsigned int size = ssrcs.size();
jintArray intArray = env->NewIntArray(size);
jint intFill[size];
for (int a = 0; a < size; a++) {
intFill[a] = ssrcs[a];
}
env->SetIntArrayRegion(intArray, 0, size, intFill);
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onParticipantDescriptionsRequired", "(J[I)V"), (jlong) task.get(), intArray);
env->DeleteLocalRef(intArray);
});
return task;
};
}
auto *holder = new InstanceHolder;
holder->groupNativeInstance = std::make_unique<GroupInstanceCustomImpl>(std::move(descriptor));
@ -846,9 +850,9 @@ JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeInstance_createVi
initWebRTC(env);
std::unique_ptr<VideoCaptureInterface> capture;
if (type == 0 || type == 1) {
capture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), type == 1 ? "front" : "back", std::make_shared<AndroidContext>(env, nullptr, false));
capture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), type == 1 ? "front" : "back", false, std::make_shared<AndroidContext>(env, nullptr, false));
} else {
capture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), "screen", std::make_shared<AndroidContext>(env, nullptr, true));
capture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), "screen", true, std::make_shared<AndroidContext>(env, nullptr, true));
}
capture->setOutput(webrtc::JavaToNativeVideoSink(env, localSink));
capture->setState(VideoState::Active);
@ -866,6 +870,15 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_activateV
capturer->setState(VideoState::Active);
}
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_clearVideoCapturer(JNIEnv *env, jobject obj) {
InstanceHolder *instance = getInstanceHolder(env, obj);
if (instance->nativeInstance) {
instance->nativeInstance->setVideoCapture(nullptr);
} else if (instance->groupNativeInstance) {
instance->groupNativeInstance->setVideoSource(nullptr);
}
}
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_destroyVideoCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer) {
auto capturer = reinterpret_cast<VideoCaptureInterface *>(videoCapturer);
delete capturer;
@ -899,24 +912,35 @@ JNIEXPORT jboolean JNICALL Java_org_telegram_messenger_voip_NativeInstance_hasVi
JNIEXPORT void Java_org_telegram_messenger_voip_NativeInstance_setVideoState(JNIEnv *env, jobject obj, jint state) {
InstanceHolder *instance = getInstanceHolder(env, obj);
if (instance->_videoCapture == nullptr) {
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer = instance->useScreencast ? instance->_screenVideoCapture : instance->_videoCapture;
if (capturer == nullptr) {
return;
}
instance->_videoCapture->setState(static_cast<VideoState>(state));
capturer->setState(static_cast<VideoState>(state));
}
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setupOutgoingVideo(JNIEnv *env, jobject obj, jobject localSink, jboolean front) {
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setupOutgoingVideo(JNIEnv *env, jobject obj, jobject localSink, jint type) {
InstanceHolder *instance = getInstanceHolder(env, obj);
if (instance->_videoCapture) {
return;
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer;
if (type == 0 || type == 1) {
if (instance->_videoCapture == nullptr) {
instance->_videoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), type == 1 ? "front" : "back", false, instance->_platformContext);
}
capturer = instance->_videoCapture;
instance->useScreencast = false;
} else {
if (instance->_screenVideoCapture == nullptr) {
instance->_screenVideoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), "screen", true, instance->_platformContext);
}
capturer = instance->_screenVideoCapture;
instance->useScreencast = true;
}
instance->_videoCapture = tgcalls::VideoCaptureInterface::Create(StaticThreads::getThreads(), front ? "front" : "back", instance->_platformContext);
instance->_videoCapture->setOutput(webrtc::JavaToNativeVideoSink(env, localSink));
instance->_videoCapture->setState(VideoState::Active);
capturer->setOutput(webrtc::JavaToNativeVideoSink(env, localSink));
capturer->setState(VideoState::Active);
if (instance->nativeInstance) {
instance->nativeInstance->setVideoCapture(instance->_videoCapture);
instance->nativeInstance->setVideoCapture(capturer);
} else if (instance->groupNativeInstance) {
instance->groupNativeInstance->setVideoCapture(instance->_videoCapture);
instance->groupNativeInstance->setVideoCapture(capturer);
}
}
@ -931,6 +955,7 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setupOutg
instance->_videoCapture->setState(VideoState::Active);
if (instance->nativeInstance) {
instance->nativeInstance->setVideoCapture(instance->_videoCapture);
instance->useScreencast = false;
} else if (instance->groupNativeInstance) {
instance->groupNativeInstance->setVideoCapture(instance->_videoCapture);
}

View File

@ -22,7 +22,7 @@ bool SkipDefaultDevice(const char *name) {
} // namespace
void SetAudioInputDeviceById(webrtc::AudioDeviceModule *adm, const std::string &id) {
const auto recording = adm->Recording();
const auto recording = adm->Recording() || adm->RecordingIsInitialized();
if (recording) {
adm->StopRecording();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <memory>
namespace webrtc {
class VideoTrackSourceInterface;

View File

@ -191,6 +191,8 @@ public:
virtual void setInputVolume(float level) = 0;
virtual void setOutputVolume(float level) = 0;
virtual void setAudioOutputDuckingEnabled(bool enabled) = 0;
virtual void addExternalAudioSamples(std::vector<uint8_t> &&samples) {
}
virtual void setIsLowBatteryLevel(bool isLowBatteryLevel) = 0;

View File

@ -126,6 +126,12 @@ void InstanceImpl::setAudioOutputDuckingEnabled(bool enabled) {
// TODO: not implemented
}
void InstanceImpl::addExternalAudioSamples(std::vector<uint8_t> &&samples) {
_manager->perform(RTC_FROM_HERE, [samples = std::move(samples)](Manager *manager) mutable {
manager->addExternalAudioSamples(std::move(samples));
});
}
void InstanceImpl::setIsLowBatteryLevel(bool isLowBatteryLevel) {
_manager->perform(RTC_FROM_HERE, [isLowBatteryLevel](Manager *manager) {
manager->setIsLowBatteryLevel(isLowBatteryLevel);

View File

@ -35,6 +35,7 @@ public:
void setInputVolume(float level) override;
void setOutputVolume(float level) override;
void setAudioOutputDuckingEnabled(bool enabled) override;
void addExternalAudioSamples(std::vector<uint8_t> &&samples) override;
void setIsLowBatteryLevel(bool isLowBatteryLevel) override;
std::string getLastError() override;
std::string getDebugInfo() override;

View File

@ -453,4 +453,10 @@ void Manager::setOutputVolume(float level) {
});
}
void Manager::addExternalAudioSamples(std::vector<uint8_t> &&samples) {
_mediaManager->perform(RTC_FROM_HERE, [samples = std::move(samples)](MediaManager *mediaManager) mutable {
mediaManager->addExternalAudioSamples(std::move(samples));
});
}
} // namespace tgcalls

View File

@ -42,6 +42,8 @@ public:
void setInputVolume(float level);
void setOutputVolume(float level);
void addExternalAudioSamples(std::vector<uint8_t> &&samples);
private:
void sendSignalingAsync(int delayMs, int cause);
void receiveMessage(DecryptedMessage &&message);

View File

@ -66,6 +66,90 @@ public:
virtual ~AudioCaptureAnalyzer() = default;
};
class AudioCapturePostProcessor : public webrtc::CustomProcessing {
public:
AudioCapturePostProcessor(std::function<void(float)> updated, std::vector<float> *externalAudioSamples, webrtc::Mutex *externalAudioSamplesMutex) :
_updated(updated),
_externalAudioSamples(externalAudioSamples),
_externalAudioSamplesMutex(externalAudioSamplesMutex) {
}
virtual ~AudioCapturePostProcessor() {
}
private:
virtual void Initialize(int sample_rate_hz, int num_channels) override {
}
virtual void Process(webrtc::AudioBuffer *buffer) override {
if (!buffer) {
return;
}
if (buffer->num_channels() != 1) {
return;
}
float peak = 0;
int peakCount = 0;
const float *samples = buffer->channels_const()[0];
for (int i = 0; i < buffer->num_frames(); i++) {
float sample = samples[i];
if (sample < 0) {
sample = -sample;
}
if (peak < sample) {
peak = sample;
}
peakCount += 1;
}
_peakCount += peakCount;
if (_peak < peak) {
_peak = peak;
}
if (_peakCount >= 1200) {
float level = _peak / 8000.0f;
_peak = 0;
_peakCount = 0;
_updated(level);
}
_externalAudioSamplesMutex->Lock();
if (!_externalAudioSamples->empty()) {
float *bufferData = buffer->channels()[0];
int takenSamples = 0;
for (int i = 0; i < _externalAudioSamples->size() && i < buffer->num_frames(); i++) {
float sample = (*_externalAudioSamples)[i];
sample += bufferData[i];
sample = std::min(sample, 32768.f);
sample = std::max(sample, -32768.f);
bufferData[i] = sample;
takenSamples++;
}
if (takenSamples != 0) {
_externalAudioSamples->erase(_externalAudioSamples->begin(), _externalAudioSamples->begin() + takenSamples);
}
}
_externalAudioSamplesMutex->Unlock();
}
virtual std::string ToString() const override {
return "CustomPostProcessing";
}
virtual void SetRuntimeSetting(webrtc::AudioProcessing::RuntimeSetting setting) override {
}
private:
std::function<void(float)> _updated;
int32_t _peakCount = 0;
float _peak = 0;
std::vector<float> *_externalAudioSamples = nullptr;
webrtc::Mutex *_externalAudioSamplesMutex = nullptr;
};
} // namespace
@ -226,55 +310,21 @@ _platformContext(platformContext) {
preferredCodecs,
_platformContext);
// [this] should outlive the analyzer
auto analyzer = new AudioCaptureAnalyzer([this](const webrtc::AudioBuffer* buffer) {
if (!buffer) {
return;
}
if (buffer->num_channels() != 1) {
return;
}
float peak = 0;
int peakCount = 0;
const float *samples = buffer->channels_const()[0];
for (int i = 0; i < buffer->num_frames(); i++) {
float sample = samples[i];
if (sample < 0) {
sample = -sample;
}
if (peak < sample) {
peak = sample;
}
peakCount += 1;
}
this->_thread->PostTask(RTC_FROM_HERE, [this, peak, peakCount](){
auto strong = this;
strong->_myAudioLevelPeakCount += peakCount;
if (strong->_myAudioLevelPeak < peak) {
strong->_myAudioLevelPeak = peak;
}
if (strong->_myAudioLevelPeakCount >= 1200) {
float level = strong->_myAudioLevelPeak / 4000.0f;
strong->_myAudioLevelPeak = 0;
strong->_myAudioLevelPeakCount = 0;
strong->_currentMyAudioLevel = level;
}
});
});
webrtc::AudioProcessingBuilder builder;
builder.SetCaptureAnalyzer(std::unique_ptr<AudioCaptureAnalyzer>(analyzer));
std::unique_ptr<AudioCapturePostProcessor> audioProcessor = std::make_unique<AudioCapturePostProcessor>([this](float level) {
this->_thread->PostTask(RTC_FROM_HERE, [this, level](){
auto strong = this;
strong->_currentMyAudioLevel = level;
});
}, &_externalAudioSamples, &_externalAudioSamplesMutex);
builder.SetCapturePostProcessing(std::move(audioProcessor));
mediaDeps.audio_processing = builder.Create();
/*_audioDeviceModule = this->createAudioDeviceModule();
_audioDeviceModule = this->createAudioDeviceModule();
if (!_audioDeviceModule) {
return;
}
mediaDeps.adm = _audioDeviceModule;*/
mediaDeps.adm = _audioDeviceModule;
_mediaEngine = cricket::CreateMediaEngine(std::move(mediaDeps));
_mediaEngine->Init();
@ -367,7 +417,11 @@ rtc::scoped_refptr<webrtc::AudioDeviceModule> MediaManager::createAudioDeviceMod
return result;
}
}
#ifdef ANDROID
return check(create(webrtc::AudioDeviceModule::kAndroidMergedScreenAudio));
#else
return check(create(webrtc::AudioDeviceModule::kPlatformDefaultAudio));
#endif
}
void MediaManager::start() {
@ -587,6 +641,7 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
_videoCapture = videoCapture;
if (_videoCapture) {
_videoCapture->setPreferredAspectRatio(_preferredAspectRatio);
_isScreenCapture = _videoCapture->isScreenCapture();
const auto thread = _thread;
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
@ -599,7 +654,10 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
});
setOutgoingVideoState(VideoState::Active);
} else {
_isScreenCapture = false;
setOutgoingVideoState(VideoState::Inactive);
resetSendingVideo();
}
checkIsSendingVideoChanged(wasSending);
@ -669,6 +727,30 @@ void MediaManager::configureSendingVideoIfNeeded() {
adjustBitratePreferences(true);
}
void MediaManager::resetSendingVideo() {
if (!_didConfigureVideo) {
return;
}
if (_enableFlexfec) {
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
_videoChannel->RemoveSendStream(_ssrcVideo.fecOutgoing);
} else {
_videoChannel->RemoveSendStream(_ssrcVideo.outgoing);
}
if (_enableFlexfec) {
cricket::StreamParams videoSendStreamParams;
cricket::SsrcGroup videoSendSsrcGroup(cricket::kFecFrSsrcGroupSemantics, {_ssrcVideo.outgoing, _ssrcVideo.fecOutgoing});
videoSendStreamParams.ssrcs = {_ssrcVideo.outgoing};
videoSendStreamParams.ssrc_groups.push_back(videoSendSsrcGroup);
videoSendStreamParams.cname = "cname";
_videoChannel->AddSendStream(videoSendStreamParams);
} else {
_videoChannel->AddSendStream(cricket::StreamParams::CreateLegacy(_ssrcVideo.outgoing));
}
}
void MediaManager::checkIsSendingVideoChanged(bool wasSending) {
const auto sending = computeIsSendingVideo();
if (sending == wasSending) {
@ -708,9 +790,16 @@ int MediaManager::getMaxAudioBitrate() const {
void MediaManager::adjustBitratePreferences(bool resetStartBitrate) {
if (computeIsSendingVideo()) {
webrtc::BitrateConstraints preferences;
preferences.min_bitrate_bps = 64000;
if (resetStartBitrate) {
preferences.start_bitrate_bps = 400000;
if (_isScreenCapture) {
preferences.min_bitrate_bps = 700000;
if (resetStartBitrate) {
preferences.start_bitrate_bps = 700000;
}
} else {
preferences.min_bitrate_bps = 64000;
if (resetStartBitrate) {
preferences.start_bitrate_bps = 400000;
}
}
preferences.max_bitrate_bps = getMaxVideoBitrate();
@ -927,6 +1016,23 @@ void MediaManager::setOutputVolume(float level) {
// }
}
void MediaManager::addExternalAudioSamples(std::vector<uint8_t> &&samples) {
if (samples.size() % 2 != 0) {
return;
}
_externalAudioSamplesMutex.Lock();
size_t previousSize = _externalAudioSamples.size();
_externalAudioSamples.resize(_externalAudioSamples.size() + samples.size() / 2);
webrtc::S16ToFloatS16((const int16_t *)samples.data(), samples.size() / 2, _externalAudioSamples.data() + previousSize);
if (_externalAudioSamples.size() > 2 * 48000) {
_externalAudioSamples.erase(_externalAudioSamples.begin(), _externalAudioSamples.begin() + (_externalAudioSamples.size() - 2 * 48000));
}
_externalAudioSamplesMutex.Unlock();
}
MediaManager::NetworkInterfaceImpl::NetworkInterfaceImpl(MediaManager *mediaManager, bool isVideo) :
_mediaManager(mediaManager),
_isVideo(isVideo) {

View File

@ -71,6 +71,8 @@ public:
void setInputVolume(float level);
void setOutputVolume(float level);
void addExternalAudioSamples(std::vector<uint8_t> &&samples);
private:
struct SSRC {
uint32_t incoming = 0;
@ -100,6 +102,7 @@ private:
bool computeIsSendingVideo() const;
void configureSendingVideoIfNeeded();
void resetSendingVideo();
void checkIsSendingVideoChanged(bool wasSending);
bool videoCodecsNegotiated() const;
@ -156,6 +159,7 @@ private:
std::unique_ptr<cricket::VideoMediaChannel> _videoChannel;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory> _videoBitrateAllocatorFactory;
std::shared_ptr<VideoCaptureInterface> _videoCapture;
bool _isScreenCapture = false;
std::shared_ptr<VideoSinkInterfaceProxyImpl> _incomingVideoSinkProxy;
float _localPreferredVideoAspectRatio = 0.0f;
@ -166,14 +170,15 @@ private:
float _currentAudioLevel = 0.0f;
float _currentMyAudioLevel = 0.0f;
int _myAudioLevelPeakCount = 0;
int _myAudioLevelPeak = 0;
std::unique_ptr<MediaManager::NetworkInterfaceImpl> _audioNetworkInterface;
std::unique_ptr<MediaManager::NetworkInterfaceImpl> _videoNetworkInterface;
std::vector<CallStatsBitrateRecord> _bitrateRecords;
std::vector<float> _externalAudioSamples;
webrtc::Mutex _externalAudioSamplesMutex;
std::shared_ptr<PlatformContext> _platformContext;
};

View File

@ -5,9 +5,9 @@
namespace tgcalls {
std::unique_ptr<VideoCaptureInterface> VideoCaptureInterface::Create(
std::shared_ptr<Threads> threads, std::string deviceId,
std::shared_ptr<Threads> threads, std::string deviceId, bool isScreenCapture,
std::shared_ptr<PlatformContext> platformContext) {
return std::make_unique<VideoCaptureInterfaceImpl>(deviceId, platformContext, std::move(threads));
return std::make_unique<VideoCaptureInterfaceImpl>(deviceId, isScreenCapture, platformContext, std::move(threads));
}
VideoCaptureInterface::~VideoCaptureInterface() = default;

View File

@ -34,10 +34,12 @@ public:
static std::unique_ptr<VideoCaptureInterface> Create(
std::shared_ptr<Threads> threads,
std::string deviceId = std::string(),
bool isScreenCapture = false,
std::shared_ptr<PlatformContext> platformContext = nullptr);
virtual ~VideoCaptureInterface();
virtual bool isScreenCapture() = 0;
virtual void switchToDevice(std::string deviceId) = 0;
virtual void setState(VideoState state) = 0;
virtual void setPreferredAspectRatio(float aspectRatio) = 0;

View File

@ -162,12 +162,11 @@ void VideoCaptureInterfaceObject::setRotationUpdated(std::function<void(int)> ro
_rotationUpdated = rotationUpdated;
}
VideoCaptureInterfaceImpl::VideoCaptureInterfaceImpl(std::string deviceId,
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),
_impl(threads->getMediaThread(), [deviceId, platformContext, threads]() {
return new VideoCaptureInterfaceObject(deviceId, platformContext, *threads);
}) {
}), _isScreenCapture(isScreenCapture) {
}
VideoCaptureInterfaceImpl::~VideoCaptureInterfaceImpl() = default;
@ -178,6 +177,10 @@ void VideoCaptureInterfaceImpl::switchToDevice(std::string deviceId) {
});
}
bool VideoCaptureInterfaceImpl::isScreenCapture() {
return _isScreenCapture;
}
void VideoCaptureInterfaceImpl::withNativeImplementation(std::function<void(void *)> completion) {
_impl.perform(RTC_FROM_HERE, [completion](VideoCaptureInterfaceObject *impl) {
impl->withNativeImplementation(completion);

View File

@ -50,9 +50,10 @@ private:
class VideoCaptureInterfaceImpl : public VideoCaptureInterface {
public:
VideoCaptureInterfaceImpl(std::string deviceId, 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();
bool isScreenCapture() override;
void switchToDevice(std::string deviceId) override;
void withNativeImplementation(std::function<void(void *)> completion) override;
void setState(VideoState state) override;
@ -67,7 +68,10 @@ public:
private:
ThreadLocalObject<VideoCaptureInterfaceObject> _impl;
std::shared_ptr<PlatformContext> _platformContext;
bool _isScreenCapture = false;
std::shared_ptr<PlatformContext> _platformContext;
};

View File

@ -651,6 +651,10 @@ public:
}
}
std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>> getSinks() {
return _sinks;
}
private:
std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>> _sinks;
absl::optional<webrtc::VideoFrame> _lastFrame;
@ -784,23 +788,25 @@ private:
}
}
_externalAudioSamplesMutex->Lock();
if (!_externalAudioSamples->empty()) {
float *bufferData = buffer->channels()[0];
int takenSamples = 0;
for (int i = 0; i < _externalAudioSamples->size() && i < _frameSamples.size(); i++) {
float sample = (*_externalAudioSamples)[i];
sample += bufferData[i];
sample = std::min(sample, 32768.f);
sample = std::max(sample, -32768.f);
bufferData[i] = sample;
takenSamples++;
}
if (takenSamples != 0) {
_externalAudioSamples->erase(_externalAudioSamples->begin(), _externalAudioSamples->begin() + takenSamples);
if (_externalAudioSamplesMutex && _externalAudioSamples) {
_externalAudioSamplesMutex->Lock();
if (!_externalAudioSamples->empty()) {
float *bufferData = buffer->channels()[0];
int takenSamples = 0;
for (int i = 0; i < _externalAudioSamples->size() && i < _frameSamples.size(); i++) {
float sample = (*_externalAudioSamples)[i];
sample += bufferData[i];
sample = std::min(sample, 32768.f);
sample = std::max(sample, -32768.f);
bufferData[i] = sample;
takenSamples++;
}
if (takenSamples != 0) {
_externalAudioSamples->erase(_externalAudioSamples->begin(), _externalAudioSamples->begin() + takenSamples);
}
}
_externalAudioSamplesMutex->Unlock();
}
_externalAudioSamplesMutex->Unlock();
}
virtual std::string ToString() const override {
@ -1100,6 +1106,10 @@ public:
_videoSink->addSink(impl);
}
std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>> getSinks() {
return _videoSink->getSinks();
}
std::string const &endpointId() {
return _endpointId;
}
@ -1331,7 +1341,9 @@ public:
}, threads);
}));
#if USE_RNNOISE
std::unique_ptr<AudioCapturePostProcessor> audioProcessor = nullptr;
#endif
if (_videoContentType != VideoContentType::Screencast) {
PlatformInterface::SharedInstance()->configurePlatformAudio();
@ -1344,11 +1356,14 @@ public:
}
strong->_myAudioLevel = level;
});
}, _noiseSuppressionConfiguration, &_externalAudioSamples, &_externalAudioSamplesMutex);
}, _noiseSuppressionConfiguration, nullptr, nullptr);
#endif
}
_threads->getWorkerThread()->Invoke<void>(RTC_FROM_HERE, [this, audioProcessor = std::move(audioProcessor)
_threads->getWorkerThread()->Invoke<void>(RTC_FROM_HERE, [this
#if USE_RNNOISE
, audioProcessor = std::move(audioProcessor)
#endif
]() mutable {
cricket::MediaEngineDependencies mediaDeps;
mediaDeps.task_queue_factory = _taskQueueFactory.get();
@ -1358,12 +1373,14 @@ public:
mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory(_platformContext);
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext);
#if USE_RNNOISE
if (_audioLevelsUpdated && audioProcessor) {
webrtc::AudioProcessingBuilder builder;
builder.SetCapturePostProcessing(std::move(audioProcessor));
mediaDeps.audio_processing = builder.Create();
}
#endif
_audioDeviceModule = createAudioDeviceModule();
if (!_audioDeviceModule) {
@ -1546,7 +1563,7 @@ public:
rtpParameters.encodings[i].scale_resolution_down_by = 4.0;
rtpParameters.encodings[i].active = _outgoingVideoConstraint >= 180;
} else if (i == 1) {
rtpParameters.encodings[i].max_bitrate_bps = 150000;
rtpParameters.encodings[i].min_bitrate_bps = 150000;
rtpParameters.encodings[i].max_bitrate_bps = 200000;
rtpParameters.encodings[i].scale_resolution_down_by = 2.0;
rtpParameters.encodings[i].active = _outgoingVideoConstraint >= 360;
@ -1584,7 +1601,7 @@ public:
rtpParameters.encodings[i].scale_resolution_down_by = 4.0;
rtpParameters.encodings[i].active = _outgoingVideoConstraint >= 180;
} else if (i == 1) {
rtpParameters.encodings[i].max_bitrate_bps = 100000;
rtpParameters.encodings[i].min_bitrate_bps = 100000;
rtpParameters.encodings[i].max_bitrate_bps = 110000;
rtpParameters.encodings[i].scale_resolution_down_by = 2.0;
rtpParameters.encodings[i].active = _outgoingVideoConstraint >= 360;
@ -2890,35 +2907,9 @@ public:
}
void removeSsrcs(std::vector<uint32_t> ssrcs) {
/*bool updatedIncomingVideoChannels = false;
for (auto ssrc : ssrcs) {
auto it = _ssrcMapping.find(ssrc);
if (it != _ssrcMapping.end()) {
auto mainSsrc = it->second.ssrc;
auto audioChannel = _incomingAudioChannels.find(ChannelId(mainSsrc));
if (audioChannel != _incomingAudioChannels.end()) {
_incomingAudioChannels.erase(audioChannel);
}
auto videoChannel = _incomingVideoChannels.find(mainSsrc);
if (videoChannel != _incomingVideoChannels.end()) {
_incomingVideoChannels.erase(videoChannel);
updatedIncomingVideoChannels = true;
}
}
}
if (updatedIncomingVideoChannels) {
updateIncomingVideoSources();
}*/
}
void removeIncomingVideoSource(uint32_t ssrc) {
/*auto videoChannel = _incomingVideoChannels.find(ssrc);
if (videoChannel != _incomingVideoChannels.end()) {
_incomingVideoChannels.erase(videoChannel);
updateIncomingVideoSources();
}*/
}
void setIsMuted(bool isMuted) {
@ -3199,7 +3190,14 @@ public:
}
for (const auto &endpointId : removeEndpointIds) {
_incomingVideoChannels.erase(VideoChannelId(endpointId));
const auto it = _incomingVideoChannels.find(VideoChannelId(endpointId));
if (it != _incomingVideoChannels.end()) {
auto sinks = it->second->getSinks();
for (const auto &sink : sinks) {
_pendingVideoSinks[VideoChannelId(endpointId)].push_back(sink);
}
_incomingVideoChannels.erase(it);
}
}
if (updated) {

View File

@ -0,0 +1,284 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/audio_device/android/audio_merged_screen_record_jni.h"
#include <string>
#include <utility>
#include "modules/audio_device/android/audio_common.h"
#include "rtc_base/arraysize.h"
#include "rtc_base/checks.h"
#include "rtc_base/format_macros.h"
#include "rtc_base/logging.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/time_utils.h"
#include "system_wrappers/include/metrics.h"
namespace webrtc {
namespace {
// Scoped class which logs its time of life as a UMA statistic. It generates
// a histogram which measures the time it takes for a method/scope to execute.
class ScopedHistogramTimer {
public:
explicit ScopedHistogramTimer(const std::string& name)
: histogram_name_(name), start_time_ms_(rtc::TimeMillis()) {}
~ScopedHistogramTimer() {
const int64_t life_time_ms = rtc::TimeSince(start_time_ms_);
RTC_HISTOGRAM_COUNTS_1000(histogram_name_, life_time_ms);
RTC_LOG(INFO) << histogram_name_ << ": " << life_time_ms;
}
private:
const std::string histogram_name_;
int64_t start_time_ms_;
};
} // namespace
// AudioRecordJni::JavaAudioRecord implementation.
AudioMergedScreenRecordJni::JavaAudioRecord::JavaAudioRecord(
NativeRegistration* native_reg,
std::unique_ptr<GlobalRef> audio_record)
: audio_record_(std::move(audio_record)),
init_recording_(native_reg->GetMethodId("initRecording", "(II)I")),
start_recording_(native_reg->GetMethodId("startRecording", "()Z")),
stop_recording_(native_reg->GetMethodId("stopRecording", "()Z")),
enable_built_in_aec_(native_reg->GetMethodId("enableBuiltInAEC", "(Z)Z")),
enable_built_in_ns_(native_reg->GetMethodId("enableBuiltInNS", "(Z)Z")),
on_destroy_(native_reg->GetMethodId("onDestroy", "()V")) {}
AudioMergedScreenRecordJni::JavaAudioRecord::~JavaAudioRecord() {
audio_record_->CallVoidMethod(on_destroy_);
}
int AudioMergedScreenRecordJni::JavaAudioRecord::InitRecording(int sample_rate,
size_t channels) {
return audio_record_->CallIntMethod(init_recording_,
static_cast<jint>(sample_rate),
static_cast<jint>(channels));
}
bool AudioMergedScreenRecordJni::JavaAudioRecord::StartRecording() {
return audio_record_->CallBooleanMethod(start_recording_);
}
bool AudioMergedScreenRecordJni::JavaAudioRecord::StopRecording() {
return audio_record_->CallBooleanMethod(stop_recording_);
}
bool AudioMergedScreenRecordJni::JavaAudioRecord::EnableBuiltInAEC(bool enable) {
return audio_record_->CallBooleanMethod(enable_built_in_aec_,
static_cast<jboolean>(enable));
}
bool AudioMergedScreenRecordJni::JavaAudioRecord::EnableBuiltInNS(bool enable) {
return audio_record_->CallBooleanMethod(enable_built_in_ns_,
static_cast<jboolean>(enable));
}
// AudioRecordJni implementation.
AudioMergedScreenRecordJni::AudioMergedScreenRecordJni(AudioManager* audio_manager)
: j_environment_(JVM::GetInstance()->environment()),
audio_manager_(audio_manager),
audio_parameters_(audio_manager->GetRecordAudioParameters()),
total_delay_in_milliseconds_(0),
direct_buffer_address_(nullptr),
direct_buffer_capacity_in_bytes_(0),
frames_per_buffer_(0),
initialized_(false),
recording_(false),
audio_device_buffer_(nullptr) {
RTC_LOG(INFO) << "ctor";
RTC_DCHECK(audio_parameters_.is_valid());
RTC_CHECK(j_environment_);
JNINativeMethod native_methods[] = {
{"nativeCacheDirectBufferAddress", "(Ljava/nio/ByteBuffer;J)V",
reinterpret_cast<void*>(
&webrtc::AudioMergedScreenRecordJni::CacheDirectBufferAddress)},
{"nativeDataIsRecorded", "(IJ)V",
reinterpret_cast<void*>(&webrtc::AudioMergedScreenRecordJni::DataIsRecorded)}};
j_native_registration_ = j_environment_->RegisterNatives(
"org/webrtc/voiceengine/WebRtcAudioRecord", native_methods,
arraysize(native_methods));
j_audio_record_.reset(
new JavaAudioRecord(j_native_registration_.get(),
j_native_registration_->NewObject(
"<init>", "(JI)V", PointerTojlong(this), 2)));
// Detach from this thread since we want to use the checker to verify calls
// from the Java based audio thread.
thread_checker_java_.Detach();
}
AudioMergedScreenRecordJni::~AudioMergedScreenRecordJni() {
RTC_LOG(INFO) << "dtor";
RTC_DCHECK(thread_checker_.IsCurrent());
Terminate();
}
int32_t AudioMergedScreenRecordJni::Init() {
RTC_LOG(INFO) << "Init";
RTC_DCHECK(thread_checker_.IsCurrent());
return 0;
}
int32_t AudioMergedScreenRecordJni::Terminate() {
RTC_LOG(INFO) << "Terminate";
RTC_DCHECK(thread_checker_.IsCurrent());
StopRecording();
return 0;
}
int32_t AudioMergedScreenRecordJni::InitRecording() {
RTC_LOG(INFO) << "InitRecording";
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK(!initialized_);
RTC_DCHECK(!recording_);
ScopedHistogramTimer timer("WebRTC.Audio.InitRecordingDurationMs");
int frames_per_buffer = j_audio_record_->InitRecording(
audio_parameters_.sample_rate(), audio_parameters_.channels());
if (frames_per_buffer < 0) {
direct_buffer_address_ = nullptr;
RTC_LOG(LS_ERROR) << "InitRecording failed";
return -1;
}
frames_per_buffer_ = static_cast<size_t>(frames_per_buffer);
RTC_LOG(INFO) << "frames_per_buffer: " << frames_per_buffer_;
const size_t bytes_per_frame = audio_parameters_.channels() * sizeof(int16_t);
RTC_CHECK_EQ(direct_buffer_capacity_in_bytes_,
frames_per_buffer_ * bytes_per_frame);
RTC_CHECK_EQ(frames_per_buffer_, audio_parameters_.frames_per_10ms_buffer());
initialized_ = true;
return 0;
}
int32_t AudioMergedScreenRecordJni::StartRecording() {
RTC_LOG(INFO) << "StartRecording";
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK(!recording_);
if (!initialized_) {
RTC_DLOG(LS_WARNING)
<< "Recording can not start since InitRecording must succeed first";
return 0;
}
ScopedHistogramTimer timer("WebRTC.Audio.StartRecordingDurationMs");
if (!j_audio_record_->StartRecording()) {
RTC_LOG(LS_ERROR) << "StartRecording failed";
return -1;
}
recording_ = true;
return 0;
}
int32_t AudioMergedScreenRecordJni::StopRecording() {
RTC_LOG(INFO) << "StopRecording";
RTC_DCHECK(thread_checker_.IsCurrent());
if (!initialized_ || !recording_) {
return 0;
}
if (!j_audio_record_->StopRecording()) {
RTC_LOG(LS_ERROR) << "StopRecording failed";
return -1;
}
// If we don't detach here, we will hit a RTC_DCHECK in OnDataIsRecorded()
// next time StartRecording() is called since it will create a new Java
// thread.
thread_checker_java_.Detach();
initialized_ = false;
recording_ = false;
direct_buffer_address_ = nullptr;
return 0;
}
void AudioMergedScreenRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
RTC_LOG(INFO) << "AttachAudioBuffer";
RTC_DCHECK(thread_checker_.IsCurrent());
audio_device_buffer_ = audioBuffer;
const int sample_rate_hz = audio_parameters_.sample_rate();
RTC_LOG(INFO) << "SetRecordingSampleRate(" << sample_rate_hz << ")";
audio_device_buffer_->SetRecordingSampleRate(sample_rate_hz);
const size_t channels = audio_parameters_.channels();
RTC_LOG(INFO) << "SetRecordingChannels(" << channels << ")";
audio_device_buffer_->SetRecordingChannels(channels);
total_delay_in_milliseconds_ =
audio_manager_->GetDelayEstimateInMilliseconds();
RTC_DCHECK_GT(total_delay_in_milliseconds_, 0);
RTC_LOG(INFO) << "total_delay_in_milliseconds: "
<< total_delay_in_milliseconds_;
}
int32_t AudioMergedScreenRecordJni::EnableBuiltInAEC(bool enable) {
RTC_LOG(INFO) << "EnableBuiltInAEC(" << enable << ")";
RTC_DCHECK(thread_checker_.IsCurrent());
return j_audio_record_->EnableBuiltInAEC(enable) ? 0 : -1;
}
int32_t AudioMergedScreenRecordJni::EnableBuiltInAGC(bool enable) {
// TODO(henrika): possibly remove when no longer used by any client.
RTC_CHECK_NOTREACHED();
}
int32_t AudioMergedScreenRecordJni::EnableBuiltInNS(bool enable) {
RTC_LOG(INFO) << "EnableBuiltInNS(" << enable << ")";
RTC_DCHECK(thread_checker_.IsCurrent());
return j_audio_record_->EnableBuiltInNS(enable) ? 0 : -1;
}
JNI_FUNCTION_ALIGN
void JNICALL AudioMergedScreenRecordJni::CacheDirectBufferAddress(JNIEnv* env,
jobject obj,
jobject byte_buffer,
jlong nativeAudioRecord) {
webrtc::AudioMergedScreenRecordJni* this_object =
reinterpret_cast<webrtc::AudioMergedScreenRecordJni*>(nativeAudioRecord);
this_object->OnCacheDirectBufferAddress(env, byte_buffer);
}
void AudioMergedScreenRecordJni::OnCacheDirectBufferAddress(JNIEnv* env,
jobject byte_buffer) {
RTC_LOG(INFO) << "OnCacheDirectBufferAddress";
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DCHECK(!direct_buffer_address_);
direct_buffer_address_ = env->GetDirectBufferAddress(byte_buffer);
jlong capacity = env->GetDirectBufferCapacity(byte_buffer);
RTC_LOG(INFO) << "direct buffer capacity: " << capacity;
direct_buffer_capacity_in_bytes_ = static_cast<size_t>(capacity);
}
JNI_FUNCTION_ALIGN
void JNICALL AudioMergedScreenRecordJni::DataIsRecorded(JNIEnv* env,
jobject obj,
jint length,
jlong nativeAudioRecord) {
webrtc::AudioMergedScreenRecordJni* this_object =
reinterpret_cast<webrtc::AudioMergedScreenRecordJni*>(nativeAudioRecord);
this_object->OnDataIsRecorded(length);
}
// This method is called on a high-priority thread from Java. The name of
// the thread is 'AudioRecordThread'.
void AudioMergedScreenRecordJni::OnDataIsRecorded(int length) {
RTC_DCHECK(thread_checker_java_.IsCurrent());
if (!audio_device_buffer_) {
RTC_LOG(LS_ERROR) << "AttachAudioBuffer has not been called";
return;
}
audio_device_buffer_->SetRecordedBuffer(direct_buffer_address_,
frames_per_buffer_);
// We provide one (combined) fixed delay estimate for the APM and use the
// |playDelayMs| parameter only. Components like the AEC only sees the sum
// of |playDelayMs| and |recDelayMs|, hence the distributions does not matter.
audio_device_buffer_->SetVQEData(total_delay_in_milliseconds_, 0);
if (audio_device_buffer_->DeliverRecordedData() == -1) {
RTC_LOG(INFO) << "AudioDeviceBuffer::DeliverRecordedData failed";
}
}
} // namespace webrtc

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef MODULES_AUDIO_DEVICE_ANDROID_AUDIO_MERGED_SCREEN_RECORD_JNI_H_
#define MODULES_AUDIO_DEVICE_ANDROID_AUDIO_MERGED_SCREEN_RECORD_JNI_H_
#include <jni.h>
#include <memory>
#include "api/sequence_checker.h"
#include "modules/audio_device/android/audio_manager.h"
#include "modules/audio_device/audio_device_generic.h"
#include "modules/audio_device/include/audio_device_defines.h"
#include "modules/utility/include/helpers_android.h"
#include "modules/utility/include/jvm_android.h"
namespace webrtc {
// Implements 16-bit mono PCM audio input support for Android using the Java
// AudioRecord interface. Most of the work is done by its Java counterpart in
// WebRtcAudioRecord.java. This class is created and lives on a thread in
// C++-land, but recorded audio buffers are delivered on a high-priority
// thread managed by the Java class.
//
// The Java class makes use of AudioEffect features (mainly AEC) which are
// first available in Jelly Bean. If it is instantiated running against earlier
// SDKs, the AEC provided by the APM in WebRTC must be used and enabled
// separately instead.
//
// An instance must be created and destroyed on one and the same thread.
// All public methods must also be called on the same thread. A thread checker
// will RTC_DCHECK if any method is called on an invalid thread.
//
// This class uses JvmThreadConnector to attach to a Java VM if needed
// and detach when the object goes out of scope. Additional thread checking
// guarantees that no other (possibly non attached) thread is used.
class AudioMergedScreenRecordJni {
public:
// Wraps the Java specific parts of the AudioRecordJni into one helper class.
class JavaAudioRecord {
public:
JavaAudioRecord(NativeRegistration* native_registration,
std::unique_ptr<GlobalRef> audio_track);
~JavaAudioRecord();
int InitRecording(int sample_rate, size_t channels);
bool StartRecording();
bool StopRecording();
bool EnableBuiltInAEC(bool enable);
bool EnableBuiltInNS(bool enable);
private:
std::unique_ptr<GlobalRef> audio_record_;
jmethodID init_recording_;
jmethodID start_recording_;
jmethodID stop_recording_;
jmethodID enable_built_in_aec_;
jmethodID enable_built_in_ns_;
jmethodID on_destroy_;
};
explicit AudioMergedScreenRecordJni(AudioManager* audio_manager);
~AudioMergedScreenRecordJni();
int32_t Init();
int32_t Terminate();
int32_t InitRecording();
bool RecordingIsInitialized() const { return initialized_; }
int32_t StartRecording();
int32_t StopRecording();
bool Recording() const { return recording_; }
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer);
int32_t EnableBuiltInAEC(bool enable);
int32_t EnableBuiltInAGC(bool enable);
int32_t EnableBuiltInNS(bool enable);
private:
// Called from Java side so we can cache the address of the Java-manged
// |byte_buffer| in |direct_buffer_address_|. The size of the buffer
// is also stored in |direct_buffer_capacity_in_bytes_|.
// This method will be called by the WebRtcAudioRecord constructor, i.e.,
// on the same thread that this object is created on.
static void JNICALL CacheDirectBufferAddress(JNIEnv* env,
jobject obj,
jobject byte_buffer,
jlong nativeAudioRecord);
void OnCacheDirectBufferAddress(JNIEnv* env, jobject byte_buffer);
// Called periodically by the Java based WebRtcAudioRecord object when
// recording has started. Each call indicates that there are |length| new
// bytes recorded in the memory area |direct_buffer_address_| and it is
// now time to send these to the consumer.
// This method is called on a high-priority thread from Java. The name of
// the thread is 'AudioRecordThread'.
static void JNICALL DataIsRecorded(JNIEnv* env,
jobject obj,
jint length,
jlong nativeAudioRecord);
void OnDataIsRecorded(int length);
// Stores thread ID in constructor.
SequenceChecker thread_checker_;
// Stores thread ID in first call to OnDataIsRecorded() from high-priority
// thread in Java. Detached during construction of this object.
SequenceChecker thread_checker_java_;
// Calls JavaVM::AttachCurrentThread() if this thread is not attached at
// construction.
// Also ensures that DetachCurrentThread() is called at destruction.
JvmThreadConnector attach_thread_if_needed_;
// Wraps the JNI interface pointer and methods associated with it.
std::unique_ptr<JNIEnvironment> j_environment_;
// Contains factory method for creating the Java object.
std::unique_ptr<NativeRegistration> j_native_registration_;
// Wraps the Java specific parts of the AudioRecordJni class.
std::unique_ptr<AudioMergedScreenRecordJni::JavaAudioRecord> j_audio_record_;
// Raw pointer to the audio manger.
const AudioManager* audio_manager_;
// Contains audio parameters provided to this class at construction by the
// AudioManager.
const AudioParameters audio_parameters_;
// Delay estimate of the total round-trip delay (input + output).
// Fixed value set once in AttachAudioBuffer() and it can take one out of two
// possible values. See audio_common.h for details.
int total_delay_in_milliseconds_;
// Cached copy of address to direct audio buffer owned by |j_audio_record_|.
void* direct_buffer_address_;
// Number of bytes in the direct audio buffer owned by |j_audio_record_|.
size_t direct_buffer_capacity_in_bytes_;
// Number audio frames per audio buffer. Each audio frame corresponds to
// one sample of PCM mono data at 16 bits per sample. Hence, each audio
// frame contains 2 bytes (given that the Java layer only supports mono).
// Example: 480 for 48000 Hz or 441 for 44100 Hz.
size_t frames_per_buffer_;
bool initialized_;
bool recording_;
// Raw pointer handle provided to us in AttachAudioBuffer(). Owned by the
// AudioDeviceModuleImpl class and called by AudioDeviceModule::Create().
AudioDeviceBuffer* audio_device_buffer_;
};
} // namespace webrtc
#endif // MODULES_AUDIO_DEVICE_ANDROID_AUDIO_RECORD_JNI_H_

View File

@ -106,7 +106,7 @@ AudioRecordJni::AudioRecordJni(AudioManager* audio_manager)
j_audio_record_.reset(
new JavaAudioRecord(j_native_registration_.get(),
j_native_registration_->NewObject(
"<init>", "(JZ)V", PointerTojlong(this), false)));
"<init>", "(JI)V", PointerTojlong(this), 0)));
// Detach from this thread since we want to use the checker to verify calls
// from the Java based audio thread.
thread_checker_java_.Detach();

View File

@ -106,7 +106,7 @@ AudioScreenRecordJni::AudioScreenRecordJni(AudioManager* audio_manager)
j_audio_record_.reset(
new JavaAudioRecord(j_native_registration_.get(),
j_native_registration_->NewObject(
"<init>", "(JZ)V", PointerTojlong(this), true)));
"<init>", "(JI)V", PointerTojlong(this), 1)));
// Detach from this thread since we want to use the checker to verify calls
// from the Java based audio thread.
thread_checker_java_.Detach();

View File

@ -34,6 +34,7 @@
#include "modules/audio_device/android/audio_manager.h"
#include "modules/audio_device/android/audio_record_jni.h"
#include "modules/audio_device/android/audio_screen_record_jni.h"
#include "modules/audio_device/android/audio_merged_screen_record_jni.h"
#include "modules/audio_device/android/audio_track_jni.h"
#include "modules/audio_device/android/opensles_player.h"
#include "modules/audio_device/android/opensles_recorder.h"
@ -215,6 +216,10 @@ int32_t AudioDeviceModuleImpl::CreatePlatformSpecificObjects() {
// Java audio for both input and output audio.
audio_device_.reset(new AudioDeviceTemplate<AudioScreenRecordJni, AudioTrackJni>(
audio_layer, audio_manager));
} else if (audio_layer == kAndroidMergedScreenAudio) {
// Java audio for both input and output audio.
audio_device_.reset(new AudioDeviceTemplate<AudioMergedScreenRecordJni, AudioTrackJni>(
audio_layer, audio_manager));
} else if (audio_layer == kAndroidOpenSLESAudio) {
// OpenSL ES based audio for both input and output audio.
audio_device_.reset(

View File

@ -34,7 +34,8 @@ class AudioDeviceModule : public rtc::RefCountInterface {
kAndroidAAudioAudio,
kAndroidJavaInputAndAAudioOutputAudio,
kDummyAudio,
kAndroidScreenAudio
kAndroidScreenAudio,
kAndroidMergedScreenAudio
};
enum WindowsDeviceType {

View File

@ -15,6 +15,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageReceiver;
@ -392,6 +394,20 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
fromX += (int) holder.itemView.getTranslationX();
}
fromY += (int) holder.itemView.getTranslationY();
float imageX = 0;
float imageY = 0;
float imageW = 0;
float imageH = 0;
int[] roundRadius = new int[4];
if (chatMessageCell != null) {
imageX = chatMessageCell.getPhotoImage().getImageX();
imageY = chatMessageCell.getPhotoImage().getImageY();
imageW = chatMessageCell.getPhotoImage().getImageWidth();
imageH = chatMessageCell.getPhotoImage().getImageHeight();
for (int i = 0; i < 4; i++) {
roundRadius[i] = chatMessageCell.getPhotoImage().getRoundRadius()[i];
}
}
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
@ -431,12 +447,20 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
recyclerListView.invalidate();
params.imageChangeBoundsTransition = true;
params.animateToImageX = newImage.getImageX();
params.animateToImageY = newImage.getImageY();
params.animateToImageW = newImage.getImageWidth();
params.animateToImageH = newImage.getImageHeight();
if (chatMessageCell.getMessageObject().isRoundVideo()) {
params.animateToImageX = imageX;
params.animateToImageY = imageY;
params.animateToImageW = imageW;
params.animateToImageH = imageH;
params.animateToRadius = roundRadius;
} else {
params.animateToImageX = newImage.getImageX();
params.animateToImageY = newImage.getImageY();
params.animateToImageW = newImage.getImageWidth();
params.animateToImageH = newImage.getImageHeight();
params.animateToRadius = newImage.getRoundRadius();
}
params.animateToRadius = newImage.getRoundRadius();
params.animateRadius = false;
for (int i = 0; i < 4; i++) {
if (params.imageRoundRadius[i] != params.animateToRadius[i]) {
@ -458,9 +482,11 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
group.transitionParams.captionEnterProgress = group.transitionParams.drawCaptionLayout ? 1f : 0;
}
if (params.animateRadius) {
params.animateToRadius = new int[4];
for (int i = 0; i < 4; i++) {
params.animateToRadius[i] = newImage.getRoundRadius()[i];
if (params.animateToRadius == newImage.getRoundRadius()) {
params.animateToRadius = new int[4];
for (int i = 0; i < 4; i++) {
params.animateToRadius[i] = newImage.getRoundRadius()[i];
}
}
newImage.setRoundRadius(params.imageRoundRadius);
}
@ -695,6 +721,7 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
}
int[] finalFromRoundRadius = fromRoundRadius;
valueAnimator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
float x = moveInfoExtended.imageX * (1f - v) + params.animateToImageX * v;
@ -723,12 +750,6 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
chatMessageCell.setImageCoords(x, y, width, height);
holder.itemView.invalidate();
});
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
params.imageChangeBoundsTransition = false;
}
});
animatorSet.playTogether(valueAnimator);
}
if (moveInfoExtended.deltaBottom != 0 || moveInfoExtended.deltaRight != 0 || moveInfoExtended.deltaTop != 0 || moveInfoExtended.deltaLeft != 0) {
@ -740,15 +761,15 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
valueAnimator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
if (moveInfoExtended.animateBackgroundOnly) {
params.deltaLeft = (int) (-moveInfoExtended.deltaLeft * v);
params.deltaRight = (int) (-moveInfoExtended.deltaRight * v);
params.deltaTop = (int) (-moveInfoExtended.deltaTop * v);
params.deltaBottom = (int) (-moveInfoExtended.deltaBottom * v);
params.deltaLeft = -moveInfoExtended.deltaLeft * v;
params.deltaRight = -moveInfoExtended.deltaRight * v;
params.deltaTop = -moveInfoExtended.deltaTop * v;
params.deltaBottom = -moveInfoExtended.deltaBottom * v;
} else {
params.deltaLeft = (int) (-moveInfoExtended.deltaLeft * v - chatMessageCell.getAnimationOffsetX());
params.deltaRight = (int) (-moveInfoExtended.deltaRight * v - chatMessageCell.getAnimationOffsetX());
params.deltaTop = (int) (-moveInfoExtended.deltaTop * v - chatMessageCell.getTranslationY());
params.deltaBottom = (int) (-moveInfoExtended.deltaBottom * v - chatMessageCell.getTranslationY());
params.deltaLeft = -moveInfoExtended.deltaLeft * v - chatMessageCell.getAnimationOffsetX();
params.deltaRight = -moveInfoExtended.deltaRight * v - chatMessageCell.getAnimationOffsetX();
params.deltaTop = -moveInfoExtended.deltaTop * v - chatMessageCell.getTranslationY();
params.deltaBottom = -moveInfoExtended.deltaBottom * v - chatMessageCell.getTranslationY();
}
chatMessageCell.invalidate();
});

View File

@ -59,7 +59,6 @@ import android.widget.LinearLayout;
import android.widget.OverScroller;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BuildConfig;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLog;
@ -2779,7 +2778,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
* same View may still get the focus as a result of that search.
*/
private boolean isPreferredNextFocus(View focused, View next, int direction) {
if (next == null || next == this) {
if (next == null || next == this || next == focused) {
return false;
}
// panic, result view is not a child anymore, maybe workaround b/37864393
@ -2829,9 +2828,9 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
case View.FOCUS_DOWN:
return downness > 0;
case View.FOCUS_FORWARD:
return downness > 0 || (downness == 0 && rightness * rtl >= 0);
return downness > 0 || (downness == 0 && rightness * rtl > 0);
case View.FOCUS_BACKWARD:
return downness < 0 || (downness == 0 && rightness * rtl <= 0);
return downness < 0 || (downness == 0 && rightness * rtl < 0);
}
throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
}
@ -5110,7 +5109,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
// but some general-purpose code may choose to respond to changes this way.
final int scrollX = getScrollX();
final int scrollY = getScrollY();
onScrollChanged(scrollX, scrollY, scrollX, scrollY);
onScrollChanged(scrollX, scrollY, scrollX - hresult, scrollY - vresult);
// Pass the real deltas to onScrolled, the RecyclerView-specific method.
onScrolled(hresult, vresult);
@ -5330,7 +5329,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
postOnAnimation();
if (mGapWorker != null) {
mGapWorker.postFromTraversal(RecyclerView.this, unconsumedX, unconsumedY);
mGapWorker.postFromTraversal(RecyclerView.this, consumedX, consumedY);
}
}
}

View File

@ -115,6 +115,7 @@ import org.telegram.ui.Cells.TextDetailSettingsCell;
import org.telegram.ui.Components.BackgroundGradientDrawable;
import org.telegram.ui.Components.ForegroundColorSpanThemable;
import org.telegram.ui.Components.ForegroundDetector;
import org.telegram.ui.Components.HideViewAfterAnimation;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.MotionBackgroundDrawable;
import org.telegram.ui.Components.PickerBottomLayout;
@ -188,6 +189,7 @@ public class AndroidUtilities {
public static Point displaySize = new Point();
public static float screenRefreshRate = 60;
public static int roundMessageSize;
public static int roundPlayingMessageSize;
public static int roundMessageInset;
public static boolean incorrectDisplaySizeFix;
public static Integer photoSize = null;
@ -211,12 +213,14 @@ public class AndroidUtilities {
public static Pattern WEB_URL = null;
public static Pattern BAD_CHARS_PATTERN = null;
public static Pattern BAD_CHARS_MESSAGE_PATTERN = null;
public static Pattern BAD_CHARS_MESSAGE_LONG_PATTERN = null;
static {
try {
final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
BAD_CHARS_PATTERN = Pattern.compile("[\u2500-\u25ff]");
BAD_CHARS_MESSAGE_PATTERN = Pattern.compile("[\u0300-\u036f]+");
BAD_CHARS_MESSAGE_LONG_PATTERN = Pattern.compile("[\u0300-\u036f\u2066-\u2067]+");
BAD_CHARS_MESSAGE_PATTERN = Pattern.compile("[\u2066-\u2067]+");
final Pattern IP_ADDRESS = Pattern.compile(
"((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
@ -280,6 +284,14 @@ public class AndroidUtilities {
return false;
}
public static String getSafeString(String str) {
try {
return BAD_CHARS_MESSAGE_PATTERN.matcher(str).replaceAll("\u200C");
} catch (Throwable e) {
return str;
}
}
public static CharSequence ellipsizeCenterEnd(CharSequence str, String query, int availableWidth, TextPaint textPaint, int maxSymbols) {
try {
int lastIndex = str.length();
@ -1661,8 +1673,10 @@ public class AndroidUtilities {
if (roundMessageSize == 0) {
if (AndroidUtilities.isTablet()) {
roundMessageSize = (int) (AndroidUtilities.getMinTabletSide() * 0.6f);
roundPlayingMessageSize = (int) (AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(28));
} else {
roundMessageSize = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.6f);
roundPlayingMessageSize = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(28));
}
roundMessageInset = dp(2);
}
@ -1774,7 +1788,7 @@ public class AndroidUtilities {
public static boolean isTablet() {
if (isTablet == null) switch (NekoConfig.tabletMode) {
case 0:
isTablet = ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet);
isTablet = ApplicationLoader.applicationContext != null && ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet);
break;
case 1:
isTablet = true;
@ -4325,12 +4339,7 @@ public class AndroidUtilities {
} else if (!show && view.getTag() != null){
view.animate().setListener(null).cancel();
if (animated) {
view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
}).setDuration(150).start();
view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start();
} else {
view.setVisibility(View.GONE);
}

View File

@ -274,7 +274,7 @@ public class ApplicationLoader extends Application {
FileLog.d("screen state = " + isScreenOn);
}
} catch (Exception e) {
FileLog.e(e);
e.printStackTrace();
}
for (int a : SharedConfig.activeAccounts) {

View File

@ -308,7 +308,6 @@ public class Emoji {
@Override
public void setAlpha(int alpha) {
placeholderPaint.setAlpha(alpha);
paint.setAlpha(alpha);
}

View File

@ -2297,4 +2297,14 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
}
}
public void startCrossfadeFromStaticThumb(Bitmap thumb) {
currentThumbKey = null;
currentThumbDrawable = null;
thumbShader = null;
staticThumbDrawable = new BitmapDrawable(null, thumb);
crossfadeWithThumb = true;
currentAlpha = 0f;
updateDrawableRadius(staticThumbDrawable);
}
}

View File

@ -1184,6 +1184,8 @@ public class LocaleController {
return LocaleController.formatPluralString("Hours", ttl / 60 / 60);
} else if (ttl < 60 * 60 * 24 * 7) {
return LocaleController.formatPluralString("Days", ttl / 60 / 60 / 24);
} else if (ttl >= 60 * 60 * 24 * 30 && ttl <= 60 * 60 * 24 * 31) {
return LocaleController.formatPluralString("Months", ttl / 60 / 60 / 24 / 30);
} else {
int days = ttl / 60 / 60 / 24;
if (ttl % 7 == 0) {

View File

@ -502,6 +502,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private long lastSaveTime;
private float currentPlaybackSpeed = 1.0f;
private float currentMusicPlaybackSpeed = 1.0f;
private float fastPlaybackSpeed = 1.0f;
private float fastMusicPlaybackSpeed = 1.0f;
private float seekToProgressPending;
private long lastProgress = 0;
private MessageObject playingMessageObject;
@ -893,6 +895,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
currentPlaybackSpeed = MessagesController.getGlobalMainSettings().getFloat("playbackSpeed", 1.0f);
currentMusicPlaybackSpeed = MessagesController.getGlobalMainSettings().getFloat("musicPlaybackSpeed", 1.0f);
fastPlaybackSpeed = MessagesController.getGlobalMainSettings().getFloat("fastPlaybackSpeed", 1.8f);
fastMusicPlaybackSpeed = MessagesController.getGlobalMainSettings().getFloat("fastMusicPlaybackSpeed", 1.8f);
sensorManager = (SensorManager) ApplicationLoader.applicationContext.getSystemService(Context.SENSOR_SERVICE);
linearSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
@ -1043,6 +1047,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
public VideoPlayer getVideoPlayer() {
return videoPlayer;
}
private void startProgressTimer(final MessageObject currentPlayingMessageObject) {
synchronized (progressTimerSync) {
if (progressTimer != null) {
@ -2528,15 +2536,23 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}, 50);
}
currentMusicPlaybackSpeed = speed;
if (Math.abs(speed - 1.0f) > 0.001f) {
fastMusicPlaybackSpeed = speed;
}
} else {
currentPlaybackSpeed = speed;
if (Math.abs(speed - 1.0f) > 0.001f) {
fastPlaybackSpeed = speed;
}
}
if (audioPlayer != null) {
audioPlayer.setPlaybackSpeed(speed);
} else if (videoPlayer != null) {
videoPlayer.setPlaybackSpeed(speed);
}
MessagesController.getGlobalMainSettings().edit().putFloat(music ? "musicPlaybackSpeed" : "playbackSpeed", speed).commit();
MessagesController.getGlobalMainSettings().edit()
.putFloat(music ? "musicPlaybackSpeed" : "playbackSpeed", speed)
.putFloat(music ? "fastMusicPlaybackSpeed" : "fastPlaybackSpeed", music ? fastMusicPlaybackSpeed : fastPlaybackSpeed).commit();
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.messagePlayingSpeedChanged);
}
@ -2544,6 +2560,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return music ? currentMusicPlaybackSpeed : currentPlaybackSpeed;
}
public float getFastPlaybackSpeed(boolean music) {
return music ? fastMusicPlaybackSpeed : fastPlaybackSpeed;
}
private void updateVideoState(MessageObject messageObject, int[] playCount, boolean destroyAtEnd, boolean playWhenReady, int playbackState) {
if (videoPlayer == null) {
return;
@ -3032,9 +3052,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
if (messageObject.isRoundVideo()) {
videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC);
if (currentPlaybackSpeed > 1.0f) {
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
videoPlayer.setPlaybackSpeed(currentPlaybackSpeed);
}
if (messageObject.forceSeekTo >= 0) {
messageObject.audioProgress = seekToProgressPending = messageObject.forceSeekTo;
messageObject.forceSeekTo = -1;
}
} else {
videoPlayer.setStreamType(AudioManager.STREAM_MUSIC);
}
@ -3053,6 +3078,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return;
}
if (playbackState == ExoPlayer.STATE_ENDED || (playbackState == ExoPlayer.STATE_IDLE || playbackState == ExoPlayer.STATE_BUFFERING) && playWhenReady && messageObject.audioProgress >= 0.999f) {
messageObject.audioProgress = 1f;
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, messageObject.getId(), 0);
if (!playlist.isEmpty() && (playlist.size() > 1 || !messageObject.isVoice())) {
playNextMessageWithoutOrder(true);
} else {
@ -3134,7 +3161,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
shouldSavePositionForCurrentAudio = name;
}
if (currentPlaybackSpeed > 1.0f) {
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
audioPlayer.setPlaybackSpeed(currentPlaybackSpeed);
}
audioInfo = null;
@ -3153,7 +3180,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
messageObject.audioProgress = seekToProgressPending = pos;
}
shouldSavePositionForCurrentAudio = name;
if (currentMusicPlaybackSpeed > 1.0f) {
if (Math.abs(currentMusicPlaybackSpeed - 1.0f) > 0.001f) {
audioPlayer.setPlaybackSpeed(currentMusicPlaybackSpeed);
}
}

View File

@ -4577,6 +4577,45 @@ public class MediaDataController extends BaseController {
return runs;
}
public void addStyle(int flags, int spanStart, int spanEnd, ArrayList<TLRPC.MessageEntity> entities) {
if ((flags & TextStyleSpan.FLAG_STYLE_BOLD) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityBold();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_ITALIC) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityItalic();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_MONO) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityCode();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_STRIKE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityStrike();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_UNDERLINE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityUnderline();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_QUOTE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityBlockquote();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
}
public ArrayList<TLRPC.MessageEntity> getEntities(CharSequence[] message, boolean allowStrike) {
if (message == null || message[0] == null) {
return null;
@ -4671,43 +4710,7 @@ public class MediaDataController extends BaseController {
if (entities == null) {
entities = new ArrayList<>();
}
int flags = span.getStyleFlags();
if ((flags & TextStyleSpan.FLAG_STYLE_BOLD) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityBold();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_ITALIC) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityItalic();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_MONO) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityCode();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_STRIKE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityStrike();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_UNDERLINE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityUnderline();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
if ((flags & TextStyleSpan.FLAG_STYLE_QUOTE) != 0) {
TLRPC.MessageEntity entity = new TLRPC.TL_messageEntityBlockquote();
entity.offset = spanStart;
entity.length = spanEnd - spanStart;
entities.add(entity);
}
addStyle(span.getStyleFlags(), spanStart, spanEnd, entities);
}
}
@ -4741,6 +4744,10 @@ public class MediaDataController extends BaseController {
entity.length = Math.min(spannable.getSpanEnd(spansUrlReplacement[b]), message[0].length()) - entity.offset;
entity.url = spansUrlReplacement[b].getURL();
entities.add(entity);
TextStyleSpan.TextStyleRun style = spansUrlReplacement[b].getTextStyleRun();
if (style != null) {
addStyle(style.flags, entity.offset, entity.offset + entity.length, entities);
}
}
}
}

View File

@ -2984,9 +2984,13 @@ public class MessageObject {
} else if (messageOwner.translated) {
messageText = messageOwner.translatedMessage;
} else {
if (messageOwner.message != null && messageOwner.message.length() > 200) {
if (messageOwner.message != null) {
try {
messageText = AndroidUtilities.BAD_CHARS_MESSAGE_PATTERN.matcher(messageOwner.message).replaceAll("\u200C");
if (messageOwner.message.length() > 200) {
messageText = AndroidUtilities.BAD_CHARS_MESSAGE_LONG_PATTERN.matcher(messageOwner.message).replaceAll("\u200C");
} else {
messageText = AndroidUtilities.BAD_CHARS_MESSAGE_PATTERN.matcher(messageOwner.message).replaceAll("\u200C");
}
} catch (Throwable e) {
messageText = messageOwner.message;
}

View File

@ -300,6 +300,9 @@ public class MessagesController extends BaseController implements NotificationCe
public String mapKey;
public int maxMessageLength;
public int maxCaptionLength;
public int roundVideoSize;
public int roundVideoBitrate;
public int roundAudioBitrate;
public boolean blockedCountry;
public boolean preloadFeaturedStickers;
public String youtubePipType;
@ -859,7 +862,9 @@ public class MessagesController extends BaseController implements NotificationCe
autoarchiveAvailable = mainPreferences.getBoolean("autoarchiveAvailable", false);
groipCallVideoMaxParticipants = mainPreferences.getInt("groipCallVideoMaxParticipants", 30);
suggestStickersApiOnly = mainPreferences.getBoolean("suggestStickersApiOnly", false);
roundVideoSize = mainPreferences.getInt("roundVideoSize", 384);
roundVideoBitrate = mainPreferences.getInt("roundVideoBitrate", 1000);
roundAudioBitrate = mainPreferences.getInt("roundAudioBitrate", 64);
pendingSuggestions = mainPreferences.getStringSet("pendingSuggestions", null);
if (pendingSuggestions != null) {
pendingSuggestions = new HashSet<>(pendingSuggestions);
@ -1744,6 +1749,50 @@ public class MessagesController extends BaseController implements NotificationCe
}
break;
}
case "round_video_encoding": {
if (value.value instanceof TLRPC.TL_jsonObject) {
TLRPC.TL_jsonObject jsonObject = (TLRPC.TL_jsonObject) value.value;
for (int b = 0, N2 = jsonObject.value.size(); b < N2; b++) {
TLRPC.TL_jsonObjectValue value2 = jsonObject.value.get(b);
switch (value2.key) {
case "diameter": {
if (value2.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value2.value;
if (number.value != roundVideoSize) {
roundVideoSize = (int) number.value;
editor.putInt("roundVideoSize", roundVideoSize);
changed = true;
}
}
break;
}
case "video_bitrate": {
if (value2.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value2.value;
if (number.value != roundVideoBitrate) {
roundVideoBitrate = (int) number.value;
editor.putInt("roundVideoBitrate", roundVideoBitrate);
changed = true;
}
}
break;
}
case "audio_bitrate": {
if (value2.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value2.value;
if (number.value != roundAudioBitrate) {
roundAudioBitrate = (int) number.value;
editor.putInt("roundAudioBitrate", roundAudioBitrate);
changed = true;
}
}
break;
}
}
}
}
break;
}
case "stickers_emoji_suggest_only_api": {
if (value.value instanceof TLRPC.TL_jsonBool) {
TLRPC.TL_jsonBool bool = (TLRPC.TL_jsonBool) value.value;
@ -9679,7 +9728,7 @@ public class MessagesController extends BaseController implements NotificationCe
cleanup();
getContactsController().deleteUnknownAppAccounts();
if (ConnectionsManager.native_isTestBackend(currentAccount) != 0) {
ConnectionsManager.native_switchBackend(currentAccount);
ConnectionsManager.native_switchBackend(currentAccount, false);
}
SharedConfig.activeAccounts.remove(currentAccount);
SharedConfig.saveAccounts();

View File

@ -18,7 +18,7 @@ import tw.nekomimi.nekogram.utils.FileUtil;
public class NativeLoader {
private final static int LIB_VERSION = 39;
private final static int LIB_VERSION = 40;
private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";

View File

@ -492,6 +492,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
public HashMap<String, String> params;
public boolean isVideo;
public boolean canDeleteAfter;
public boolean forceImage;
}
public static class LocationProvider {
@ -4719,6 +4720,9 @@ public boolean retriedToSend;
inputMediaPhoto.id.access_hash = messageMedia.photo.access_hash;
inputMediaPhoto.id.file_reference = messageMedia.photo.file_reference;
newInputMedia = inputMediaPhoto;
if (BuildVars.DEBUG_VERSION) {
FileLog.d("set uploaded photo");
}
} else if (inputMedia instanceof TLRPC.TL_inputMediaUploadedDocument && messageMedia instanceof TLRPC.TL_messageMediaDocument) {
TLRPC.TL_inputMediaDocument inputMediaDocument = new TLRPC.TL_inputMediaDocument();
inputMediaDocument.id = new TLRPC.TL_inputDocument();
@ -4726,6 +4730,9 @@ public boolean retriedToSend;
inputMediaDocument.id.access_hash = messageMedia.document.access_hash;
inputMediaDocument.id.file_reference = messageMedia.document.file_reference;
newInputMedia = inputMediaDocument;
if (BuildVars.DEBUG_VERSION) {
FileLog.d("set uploaded document");
}
}
}
if (newInputMedia != null) {
@ -4766,7 +4773,14 @@ public boolean retriedToSend;
String key = "group_" + message.groupId;
if (message.finalGroupMessage != message.messageObjects.get(message.messageObjects.size() - 1).getId()) {
if (add) {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("final message not added, add");
}
putToDelayedMessages(key, message);
} else {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("final message not added");
}
}
return;
} else if (add) {
@ -4776,12 +4790,18 @@ public boolean retriedToSend;
if (!message.scheduled) {
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
}
if (BuildVars.DEBUG_VERSION) {
FileLog.d("add message");
}
}
if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) {
TLRPC.TL_messages_sendMultiMedia request = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest;
for (int a = 0; a < request.multi_media.size(); a++) {
TLRPC.InputMedia inputMedia = request.multi_media.get(a).media;
if (inputMedia instanceof TLRPC.TL_inputMediaUploadedPhoto || inputMedia instanceof TLRPC.TL_inputMediaUploadedDocument) {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("multi media not ready");
}
return;
}
}
@ -4793,6 +4813,9 @@ public boolean retriedToSend;
if (message.requests != null) {
maxDelayedMessage.requests.addAll(message.requests);
}
if (BuildVars.DEBUG_VERSION) {
FileLog.d("has maxDelayedMessage, delay");
}
return;
}
}
@ -5721,8 +5744,14 @@ public boolean retriedToSend;
getMessagesController().putUsers(users, true);
getMessagesController().putChats(chats, true);
getMessagesController().putEncryptedChats(encryptedChats, true);
for (int a = 0; a < messages.size(); a++) {
for (int a = 0, N = messages.size(); a < N; a++) {
MessageObject messageObject = new MessageObject(currentAccount, messages.get(a), false, true);
long groupId = messageObject.getGroupId();
if (groupId != 0 && messageObject.messageOwner.params != null && !messageObject.messageOwner.params.containsKey("final")) {
if (a == N - 1 || messages.get(a + 1).grouped_id != groupId) {
messageObject.messageOwner.params.put("final", "1");
}
}
retrySendMessage(messageObject, true);
}
if (scheduledMessages != null) {
@ -6895,6 +6924,28 @@ public boolean retriedToSend;
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) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
try {
if (path != null) {
RandomAccessFile file = new RandomAccessFile(path, "r");
ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, path.length());
Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true);
file.close();
} else {
try (InputStream inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri)) {
BitmapFactory.decodeStream(inputStream, null, bmOptions);
} catch (Exception e) {
}
}
} catch (Exception e) {
FileLog.e(e);
}
return bmOptions.outWidth < 800 && bmOptions.outHeight < 800;
}
@UiThread
public static void prepareSendingMedia(AccountInstance accountInstance, ArrayList<SendingMediaInfo> media, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, InputContentInfoCompat inputContent, boolean forceDocument, boolean groupMedia, MessageObject editingMessageObject, boolean notify, int scheduleDate) {
if (media.isEmpty()) {
@ -6924,13 +6975,22 @@ public boolean retriedToSend;
originalPath = info.uri.toString();
}
if (tempPath != null && info.ttl <= 0 && (tempPath.endsWith(".gif") || tempPath.endsWith(".webp"))) {
continue;
boolean isWebP = false;
if (tempPath != null && info.ttl <= 0 && (tempPath.endsWith(".gif") || (isWebP = tempPath.endsWith(".webp")))) {
if (!isWebP || shouldSendWebPAsSticker(tempPath, null)) {
continue;
} else {
info.forceImage = true;
}
} else if (ImageLoader.shouldSendImageAsDocument(info.path, info.uri)) {
continue;
} else if (tempPath == null && info.uri != null) {
if (MediaController.isGif(info.uri) || MediaController.isWebp(info.uri)) {
continue;
if (MediaController.isGif(info.uri) || (isWebP = MediaController.isWebp(info.uri))) {
if (!isWebP || shouldSendWebPAsSticker(null, info.uri)) {
continue;
} else {
info.forceImage = true;
}
}
}
@ -7403,14 +7463,14 @@ public boolean retriedToSend;
if (forceDocument || ImageLoader.shouldSendImageAsDocument(info.path, info.uri)) {
isDocument = true;
extension = tempPath != null ? FileLoader.getFileExtension(new File(tempPath)) : "";
} else if (tempPath != null && (tempPath.endsWith(".gif") || tempPath.endsWith(".webp")) && info.ttl <= 0) {
} else if (!info.forceImage && tempPath != null && (tempPath.endsWith(".gif") || tempPath.endsWith(".webp")) && info.ttl <= 0) {
if (tempPath.endsWith(".gif")) {
extension = "gif";
} else {
extension = "webp";
}
isDocument = true;
} else if (tempPath == null && info.uri != null) {
} else if (!info.forceImage && tempPath == null && info.uri != null) {
if (MediaController.isGif(info.uri)) {
isDocument = true;
originalPath = info.uri.toString();

View File

@ -1180,7 +1180,7 @@ public class SharedConfig {
streamMedia = preferences.getBoolean("streamMedia", true);
saveStreamMedia = preferences.getBoolean("saveStreamMedia", true);
smoothKeyboard = preferences.getBoolean("smoothKeyboard2", true);
pauseMusicOnRecord = preferences.getBoolean("pauseMusicOnRecord", true);
pauseMusicOnRecord = preferences.getBoolean("pauseMusicOnRecord", false);
streamAllVideo = preferences.getBoolean("streamAllVideo", BuildVars.DEBUG_VERSION);
streamMkv = preferences.getBoolean("streamMkv", false);
suggestStickers = preferences.getInt("suggestStickers", 0);

View File

@ -50,6 +50,9 @@ public class CameraSession {
private boolean optimizeForBarcode;
private boolean useTorch;
private boolean isRound;
private boolean destroyed;
Camera.CameraInfo info = new Camera.CameraInfo();
public static final int ORIENTATION_HYSTERESIS = 5;
@ -60,6 +63,7 @@ public class CameraSession {
}
};
private int displayOrientation;
public CameraSession(CameraInfo info, Size preview, Size picture, int format, boolean round) {
previewSize = preview;
@ -197,7 +201,6 @@ public class CameraSession {
isVideo = true;
Camera camera = cameraInfo.camera;
if (camera != null) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.Parameters params = null;
try {
params = camera.getParameters();
@ -206,42 +209,7 @@ public class CameraSession {
}
Camera.getCameraInfo(cameraInfo.getCameraId(), info);
int displayOrientation = getDisplayOrientation(info, true);
int cameraDisplayOrientation;
if ("samsung".equals(Build.MANUFACTURER) && "sf2wifixx".equals(Build.PRODUCT)) {
cameraDisplayOrientation = 0;
} else {
int degrees = 0;
int temp = displayOrientation;
switch (temp) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.orientation % 90 != 0) {
info.orientation = 0;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
temp = (info.orientation + degrees) % 360;
temp = (360 - temp) % 360;
} else {
temp = (info.orientation - degrees + 360) % 360;
}
cameraDisplayOrientation = temp;
}
camera.setDisplayOrientation(currentOrientation = cameraDisplayOrientation);
diffOrientation = currentOrientation - displayOrientation;
updateRotation();
if (params != null) {
if (initial && BuildVars.LOGS_ENABLED) {
@ -305,11 +273,71 @@ public class CameraSession {
}
}
public void updateRotation() {
if (cameraInfo == null) {
return;
}
try {
Camera.getCameraInfo(cameraInfo.getCameraId(), info);
} catch (Throwable throwable) {
FileLog.e(throwable);
return;
}
Camera camera = (cameraInfo == null || destroyed) ? null : cameraInfo.camera;
displayOrientation = getDisplayOrientation(info, true);
int cameraDisplayOrientation;
if ("samsung".equals(Build.MANUFACTURER) && "sf2wifixx".equals(Build.PRODUCT)) {
cameraDisplayOrientation = 0;
} else {
int degrees = 0;
int temp = displayOrientation;
switch (temp) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.orientation % 90 != 0) {
info.orientation = 0;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
temp = (info.orientation + degrees) % 360;
temp = (360 - temp) % 360;
} else {
temp = (info.orientation - degrees + 360) % 360;
}
cameraDisplayOrientation = temp;
}
currentOrientation = cameraDisplayOrientation;
if (camera != null) {
try {
camera.setDisplayOrientation(currentOrientation);
} catch (Throwable ignore) {
}
}
diffOrientation = currentOrientation - displayOrientation;
if (diffOrientation < 0) {
diffOrientation += 360;
}
}
protected void configurePhotoCamera() {
try {
Camera camera = cameraInfo.camera;
if (camera != null) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.Parameters params = null;
try {
params = camera.getParameters();
@ -319,41 +347,12 @@ public class CameraSession {
Camera.getCameraInfo(cameraInfo.getCameraId(), info);
int displayOrientation = getDisplayOrientation(info, true);
int cameraDisplayOrientation;
updateRotation();
if ("samsung".equals(Build.MANUFACTURER) && "sf2wifixx".equals(Build.PRODUCT)) {
cameraDisplayOrientation = 0;
} else {
int degrees = 0;
int temp = displayOrientation;
switch (temp) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.orientation % 90 != 0) {
info.orientation = 0;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
temp = (info.orientation + degrees) % 360;
temp = (360 - temp) % 360;
} else {
temp = (info.orientation - degrees + 360) % 360;
}
cameraDisplayOrientation = temp;
}
camera.setDisplayOrientation(currentOrientation = cameraDisplayOrientation);
diffOrientation = currentOrientation - displayOrientation;
if (diffOrientation < 0) {
diffOrientation += 360;
}
if (params != null) {
params.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
@ -470,10 +469,7 @@ public class CameraSession {
}
protected void configureRecorder(int quality, MediaRecorder recorder) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraInfo.cameraId, info);
int displayOrientation = getDisplayOrientation(info, false);
int outputOrientation = 0;
if (jpegOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
@ -552,7 +548,6 @@ public class CameraSession {
public int getDisplayOrientation() {
try {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraInfo.getCameraId(), info);
return getDisplayOrientation(info, true);
} catch (Exception e) {
@ -577,6 +572,7 @@ public class CameraSession {
public void destroy() {
initied = false;
destroyed = true;
if (orientationEventListener != null) {
orientationEventListener.disable();
orientationEventListener = null;

View File

@ -53,6 +53,8 @@ import android.widget.ImageView;
import androidx.core.graphics.ColorUtils;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.DispatchQueue;
@ -277,6 +279,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (previewSize != null && cameraSession != null) {
int frameWidth, frameHeight;
cameraSession.updateRotation();
if (cameraSession.getWorldAngle() == 90 || cameraSession.getWorldAngle() == 270) {
frameWidth = previewSize.getWidth();
frameHeight = previewSize.getHeight();
@ -289,6 +292,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
blurredStubView.getLayoutParams().height = textureView.getLayoutParams().height = (int) (s * frameHeight);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
checkPreviewMatrix();
}
public float getTextureHeight(float width, float height) {
@ -490,7 +494,8 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
if (cameraThread != null) {
cameraThread.postRunnable(() -> {
if (cameraThread.currentSession != null) {
final CameraGLThread cameraThread = this.cameraThread;
if (cameraThread != null && cameraThread.currentSession != null) {
int rotationAngle = cameraThread.currentSession.getWorldAngle();
android.opengl.Matrix.setIdentityM(mMVPMatrix, 0);
if (rotationAngle != 0) {
@ -1035,16 +1040,16 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
FileLog.d("CameraView " + "set gl rednderer session");
}
CameraSession newSession = (CameraSession) inputMessage.obj;
if (currentSession == newSession) {
int rotationAngle = currentSession.getWorldAngle();
android.opengl.Matrix.setIdentityM(mMVPMatrix, 0);
if (rotationAngle != 0) {
android.opengl.Matrix.rotateM(mMVPMatrix, 0, rotationAngle, 0, 0, 1);
}
} else {
if (currentSession != newSession) {
currentSession = newSession;
cameraId = newSession.cameraInfo.cameraId;
}
currentSession.updateRotation();
int rotationAngle = currentSession.getWorldAngle();
android.opengl.Matrix.setIdentityM(mMVPMatrix, 0);
if (rotationAngle != 0) {
android.opengl.Matrix.rotateM(mMVPMatrix, 0, rotationAngle, 0, 0, 1);
}
break;
}
case DO_START_RECORDING: {
@ -1153,6 +1158,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
FileLog.d("CameraView " + "camera initied");
}
cameraSession.setInitied();
requestLayout();
}
}, () -> cameraThread.setCurrentSession(cameraSession));
});

View File

@ -0,0 +1,179 @@
package org.telegram.messenger.video;
import com.google.android.exoplayer2.C;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.ui.Components.VideoForwardDrawable;
import org.telegram.ui.Components.VideoPlayer;
public class VideoPlayerRewinder {
public int rewindCount;
private boolean rewindForward;
public boolean rewindByBackSeek;
private long startRewindFrom;
private Runnable updateRewindRunnable;
private long rewindLastTime;
private long rewindLastUpdatePlayerTime;
private long rewindBackSeekPlayerPosition;
private float playSpeed = 1f;
private VideoPlayer videoPlayer;
private final Runnable backSeek = new Runnable() {
@Override
public void run() {
if (videoPlayer == null) {
return;
}
long duration = videoPlayer.getDuration();
if (duration == 0 || duration == C.TIME_UNSET) {
rewindLastTime = System.currentTimeMillis();
return;
}
long t = System.currentTimeMillis();
long dt = t - rewindLastTime;
rewindLastTime = t;
if (rewindCount == 1) {
dt *= 3;
} else if (rewindCount == 2) {
dt *= 6;
} else {
dt *= 12;
}
if (rewindForward) {
rewindBackSeekPlayerPosition += dt;
} else {
rewindBackSeekPlayerPosition -= dt;
}
if (rewindBackSeekPlayerPosition < 0) {
rewindBackSeekPlayerPosition = 0;
} else if (rewindBackSeekPlayerPosition > duration) {
rewindBackSeekPlayerPosition = duration;
}
if (rewindByBackSeek && videoPlayer != null && rewindLastTime - rewindLastUpdatePlayerTime > 350) {
rewindLastUpdatePlayerTime = rewindLastTime;
videoPlayer.seekTo(rewindBackSeekPlayerPosition);
}
if (videoPlayer != null) {
long timeDiff = rewindBackSeekPlayerPosition - startRewindFrom;
float progress = rewindBackSeekPlayerPosition / (float) videoPlayer.getDuration();
updateRewindProgressUi(timeDiff, progress, rewindByBackSeek);
}
if (rewindBackSeekPlayerPosition == 0 || rewindBackSeekPlayerPosition >= duration) {
if (rewindByBackSeek && videoPlayer != null) {
rewindLastUpdatePlayerTime = rewindLastTime;
videoPlayer.seekTo(rewindBackSeekPlayerPosition);
}
cancelRewind();
}
if (rewindCount > 0) {
AndroidUtilities.runOnUIThread(backSeek, 16);
}
}
};
public void startRewind(VideoPlayer videoPlayer, boolean forward, float playbackSpeed) {
this.videoPlayer = videoPlayer;
this.playSpeed = playbackSpeed;
rewindForward = forward;
cancelRewind();
incrementRewindCount();
}
public void cancelRewind() {
if (rewindCount != 0) {
rewindCount = 0;
if (videoPlayer != null) {
if (rewindByBackSeek) {
videoPlayer.seekTo(rewindBackSeekPlayerPosition);
} else {
long current = videoPlayer.getCurrentPosition();
videoPlayer.seekTo(current);
}
videoPlayer.setPlaybackSpeed(playSpeed);
}
}
AndroidUtilities.cancelRunOnUIThread(backSeek);
if (updateRewindRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable);
updateRewindRunnable = null;
}
onRewindCanceled();
}
private void incrementRewindCount() {
if (videoPlayer == null) {
return;
}
rewindCount++;
boolean needUpdate = false;
if (rewindCount == 1) {
if (rewindForward && videoPlayer.isPlaying()) {
rewindByBackSeek = false;
} else {
rewindByBackSeek = true;
}
}
if (rewindForward && !rewindByBackSeek) {
if (rewindCount == 1) {
videoPlayer.setPlaybackSpeed(4);
needUpdate = true;
} else if (rewindCount == 2) {
videoPlayer.setPlaybackSpeed(7);
needUpdate = true;
} else {
videoPlayer.setPlaybackSpeed(13);
}
} else {
if (rewindCount == 1 || rewindCount == 2) {
needUpdate = true;
}
}
if (rewindCount == 1) {
rewindBackSeekPlayerPosition = videoPlayer.getCurrentPosition();
rewindLastTime = System.currentTimeMillis();
rewindLastUpdatePlayerTime = rewindLastTime;
startRewindFrom = videoPlayer.getCurrentPosition();
onRewindStart(rewindForward);
}
AndroidUtilities.cancelRunOnUIThread(backSeek);
AndroidUtilities.runOnUIThread(backSeek);
if (needUpdate) {
if (updateRewindRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable);
}
AndroidUtilities.runOnUIThread(updateRewindRunnable = () -> {
updateRewindRunnable = null;
incrementRewindCount();
}, 2000);
}
}
protected void updateRewindProgressUi(long timeDiff, float progress, boolean rewindByBackSeek) {
}
protected void onRewindStart(boolean rewindForward) {
}
protected void onRewindCanceled() {
}
public float getVideoProgress() {
return rewindBackSeekPlayerPosition / (float) videoPlayer.getDuration();
}
}

View File

@ -198,6 +198,7 @@ public class NativeInstance {
public native void onMediaDescriptionAvailable(long taskPtr, int[] ssrcs);
public native void setNoiseSuppressionEnabled(boolean value);
public native void activateVideoCapturer(long videoCapturer);
public native void clearVideoCapturer();
public native long addIncomingVideoOutput(int quality, String endpointId, SsrcGroup[] ssrcGroups, VideoSink remoteSink);
public native void removeIncomingVideoOutput(long nativeRemoteSink);
public native void setVideoEndpointQuality(String endpointId, int quality);
@ -216,7 +217,7 @@ public class NativeInstance {
public native byte[] getPersistentState();
private native void stopNative();
private native void stopGroupNative();
public native void setupOutgoingVideo(VideoSink localSink, boolean front);
public native void setupOutgoingVideo(VideoSink localSink, int type);
public native void setupOutgoingVideoCreated(long videoCapturer);
public native void switchCamera(boolean front);
public native void setVideoState(int videoState);

View File

@ -24,6 +24,7 @@ import org.webrtc.Logging;
import org.webrtc.ScreenCapturerAndroid;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.voiceengine.WebRtcAudioRecord;
@TargetApi(18)
public class VideoCapturerDevice {
@ -165,6 +166,10 @@ public class VideoCapturerDevice {
nativeCapturerObserver = nativeGetJavaVideoCapturerObserver(nativePtr);
videoCapturer.initialize(videoCapturerSurfaceTextureHelper, ApplicationLoader.applicationContext, nativeCapturerObserver);
videoCapturer.startCapture(size.x, size.y, CAPTURE_FPS);
WebRtcAudioRecord audioRecord = WebRtcAudioRecord.Instance;
if (audioRecord != null) {
audioRecord.initDeviceAudioRecord(((ScreenCapturerAndroid) videoCapturer).getMediaProjection());
}
});
}
} else {
@ -183,7 +188,41 @@ public class VideoCapturerDevice {
}
String cameraName = names[index];
if (videoCapturer == null) {
videoCapturer = enumerator.createCapturer(cameraName, null);
videoCapturer = enumerator.createCapturer(cameraName, new CameraVideoCapturer.CameraEventsHandler() {
@Override
public void onCameraError(String errorDescription) {
}
@Override
public void onCameraDisconnected() {
}
@Override
public void onCameraFreezed(String errorDescription) {
}
@Override
public void onCameraOpening(String cameraName) {
}
@Override
public void onFirstFrameAvailable() {
AndroidUtilities.runOnUIThread(() -> {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().onCameraFirstFrameAvailable();
}
});
}
@Override
public void onCameraClosed() {
}
});
videoCapturerSurfaceTextureHelper = SurfaceTextureHelper.create("VideoCapturerThread", eglBase.getEglBaseContext());
handler.post(() -> {
if (videoCapturerSurfaceTextureHelper == null) {
@ -288,6 +327,12 @@ public class VideoCapturerDevice {
}
}
handler.post(() -> {
if (videoCapturer instanceof ScreenCapturerAndroid) {
WebRtcAudioRecord audioRecord = WebRtcAudioRecord.Instance;
if (audioRecord != null) {
audioRecord.stopDeviceAudioRecord();
}
}
if (videoCapturer != null) {
try {
videoCapturer.stopCapture();

View File

@ -114,6 +114,7 @@ import org.telegram.ui.Components.JoinCallAlert;
import org.telegram.ui.Components.voip.VoIPHelper;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.VoIPFeedbackActivity;
import org.telegram.ui.VoIPFragment;
import org.telegram.ui.VoIPPermissionActivity;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;
@ -196,6 +197,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
private boolean notificationsDisabled;
private boolean switchingCamera;
private boolean isFrontFaceCamera = true;
private boolean isPrivateScreencast;
private String lastError;
private PowerManager.WakeLock proximityWakelock;
private PowerManager.WakeLock cpuWakelock;
@ -448,6 +450,10 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
return isFrontFaceCamera;
}
public boolean isScreencast() {
return isPrivateScreencast;
}
public void setMicMute(boolean mute, boolean hold, boolean send) {
if (micMute == mute || micSwitching) {
return;
@ -1054,11 +1060,17 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
return currentState != STATE_WAIT_INIT && currentState != STATE_CREATING;
}
public void requestVideoCall() {
public void requestVideoCall(boolean screencast) {
if (tgVoip[CAPTURE_DEVICE_CAMERA] == null) {
return;
}
tgVoip[CAPTURE_DEVICE_CAMERA].setupOutgoingVideo(localSink[CAPTURE_DEVICE_CAMERA], isFrontFaceCamera);
if (!screencast && captureDevice[CAPTURE_DEVICE_CAMERA] != 0) {
tgVoip[CAPTURE_DEVICE_CAMERA].setupOutgoingVideoCreated(captureDevice[CAPTURE_DEVICE_CAMERA]);
destroyCaptureDevice[CAPTURE_DEVICE_CAMERA] = false;
} else {
tgVoip[CAPTURE_DEVICE_CAMERA].setupOutgoingVideo(localSink[CAPTURE_DEVICE_CAMERA], screencast ? 2 : (isFrontFaceCamera ? 1 : 0));
}
isPrivateScreencast = screencast;
}
public void switchCamera() {
@ -1080,63 +1092,91 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
} else {
deviceType = isFrontFaceCamera ? 1 : 0;
}
if (index == CAPTURE_DEVICE_SCREEN) {
if (captureDevice[index] != 0) {
return;
if (groupCall == null) {
if (!isPrivateScreencast && screencast) {
setVideoState(false, Instance.VIDEO_STATE_INACTIVE);
}
isPrivateScreencast = screencast;
if (tgVoip[CAPTURE_DEVICE_CAMERA] != null) {
tgVoip[CAPTURE_DEVICE_CAMERA].clearVideoCapturer();
}
}
if (index == CAPTURE_DEVICE_SCREEN) {
if (groupCall != null) {
if (captureDevice[index] != 0) {
return;
}
captureDevice[index] = NativeInstance.createVideoCapturer(localSink[index], deviceType);
createGroupInstance(CAPTURE_DEVICE_SCREEN, false);
setVideoState(true, Instance.VIDEO_STATE_ACTIVE);
AccountInstance.getInstance(currentAccount).getNotificationCenter().postNotificationName(NotificationCenter.groupCallScreencastStateChanged);
} else {
requestVideoCall(true);
setVideoState(true, Instance.VIDEO_STATE_ACTIVE);
if (VoIPFragment.getInstance() != null) {
VoIPFragment.getInstance().onScreenCastStart();
}
}
captureDevice[index] = NativeInstance.createVideoCapturer(localSink[index], deviceType);
createGroupInstance(CAPTURE_DEVICE_SCREEN, false);
setVideoState(true, Instance.VIDEO_STATE_ACTIVE);
AccountInstance.getInstance(currentAccount).getNotificationCenter().postNotificationName(NotificationCenter.groupCallScreencastStateChanged);
} else {
if (captureDevice[index] != 0 || tgVoip[index] == null) {
if (tgVoip[index] != null && captureDevice[index] != 0) {
tgVoip[index].activateVideoCapturer(captureDevice[index]);
}
return;
if (captureDevice[index] != 0) {
return;
}
}
captureDevice[index] = NativeInstance.createVideoCapturer(localSink[index], deviceType);
}
}
public void setupCaptureDevice(boolean screencast, boolean micEnabled) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA;
if (captureDevice[index] == 0 || tgVoip[index] == null) {
return;
if (!screencast) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA;
if (captureDevice[index] == 0 || tgVoip[index] == null) {
return;
}
tgVoip[index].setupOutgoingVideoCreated(captureDevice[index]);
destroyCaptureDevice[index] = false;
videoState[index] = Instance.VIDEO_STATE_ACTIVE;
}
tgVoip[index].setupOutgoingVideoCreated(captureDevice[index]);
destroyCaptureDevice[index] = false;
videoState[index] = Instance.VIDEO_STATE_ACTIVE;
if (micMute == micEnabled) {
setMicMute(!micEnabled, false, false);
micSwitching = true;
}
if (!screencast && groupCall != null) {
editCallMember(UserConfig.getInstance(currentAccount).getCurrentUser(), !micEnabled, videoState[index] != Instance.VIDEO_STATE_ACTIVE, null, null, () -> micSwitching = false);
if (groupCall != null) {
editCallMember(UserConfig.getInstance(currentAccount).getCurrentUser(), !micEnabled, videoState[CAPTURE_DEVICE_CAMERA] != Instance.VIDEO_STATE_ACTIVE, null, null, () -> micSwitching = false);
}
}
public void clearCamera() {
if (tgVoip[CAPTURE_DEVICE_CAMERA] != null) {
tgVoip[CAPTURE_DEVICE_CAMERA].clearVideoCapturer();
}
}
public void setVideoState(boolean screencast, int state) {
int index = screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA;
if (tgVoip[index] == null) {
int trueIndex = groupCall != null ? index : CAPTURE_DEVICE_CAMERA;
if (tgVoip[trueIndex] == null) {
if (captureDevice[index] != 0) {
videoState[index] = state;
NativeInstance.setVideoStateCapturer(captureDevice[index], videoState[index]);
videoState[trueIndex] = state;
NativeInstance.setVideoStateCapturer(captureDevice[index], videoState[trueIndex]);
} else if (state == Instance.VIDEO_STATE_ACTIVE && currentState != STATE_BUSY && currentState != STATE_ENDED) {
captureDevice[index] = NativeInstance.createVideoCapturer(localSink[index], isFrontFaceCamera ? 1 : 0);
videoState[index] = Instance.VIDEO_STATE_ACTIVE;
captureDevice[index] = NativeInstance.createVideoCapturer(localSink[trueIndex], isFrontFaceCamera ? 1 : 0);
videoState[trueIndex] = Instance.VIDEO_STATE_ACTIVE;
}
return;
}
videoState[index] = state;
tgVoip[index].setVideoState(videoState[index]);
videoState[trueIndex] = state;
tgVoip[trueIndex].setVideoState(videoState[trueIndex]);
if (captureDevice[index] != 0) {
NativeInstance.setVideoStateCapturer(captureDevice[index], videoState[index]);
}
if (!screencast && groupCall != null) {
editCallMember(UserConfig.getInstance(currentAccount).getCurrentUser(), null, videoState[CAPTURE_DEVICE_CAMERA] != Instance.VIDEO_STATE_ACTIVE, null, null, null);
NativeInstance.setVideoStateCapturer(captureDevice[index], videoState[trueIndex]);
}
if (!screencast) {
if (groupCall != null) {
editCallMember(UserConfig.getInstance(currentAccount).getCurrentUser(), null, videoState[CAPTURE_DEVICE_CAMERA] != Instance.VIDEO_STATE_ACTIVE, null, null, null);
}
checkIsNear();
}
}
@ -2485,6 +2525,13 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
}
}
protected void onCameraFirstFrameAvailable() {
for (int a = 0; a < stateListeners.size(); a++) {
StateListener l = stateListeners.get(a);
l.onCameraFirstFrameAvailable();
}
}
public void registerStateListener(StateListener l) {
if (stateListeners.contains(l)) {
return;
@ -2856,10 +2903,12 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
}
stopForeground(true);
stopRinging();
if (ApplicationLoader.mainInterfacePaused || !ApplicationLoader.isScreenOn) {
MessagesController.getInstance(currentAccount).ignoreSetOnline = false;
if (currentAccount >= 0) {
if (ApplicationLoader.mainInterfacePaused || !ApplicationLoader.isScreenOn) {
MessagesController.getInstance(currentAccount).ignoreSetOnline = false;
}
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout);
}
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout);
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (proximity != null) {
@ -2963,15 +3012,17 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
}
}
ConnectionsManager.getInstance(currentAccount).setAppPaused(true, false);
VoIPHelper.lastCallTime = SystemClock.elapsedRealtime();
setSinks(null, null);
if (onDestroyRunnable != null) {
onDestroyRunnable.run();
}
if (ChatObject.isChannel(chat)) {
MessagesController.getInstance(currentAccount).startShortPoll(chat, classGuid, true);
if (currentAccount >= 0) {
ConnectionsManager.getInstance(currentAccount).setAppPaused(true, false);
if (ChatObject.isChannel(chat)) {
MessagesController.getInstance(currentAccount).startShortPoll(chat, classGuid, true);
}
}
}
@ -4229,6 +4280,10 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
}
default void onCameraFirstFrameAvailable() {
}
default void onVideoAvailableChange(boolean isAvailable) {
}

View File

@ -430,10 +430,14 @@ public class ConnectionsManager extends BaseController {
}
}
public void switchBackend() {
public void switchBackend(boolean restart) {
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
preferences.edit().remove("language_showed2").apply();
native_switchBackend(currentAccount);
native_switchBackend(currentAccount, restart);
}
public boolean isTestBackend() {
return native_isTestBackend(currentAccount) != 0;
}
public void resumeNetworkMaybe() {
@ -715,8 +719,7 @@ public class ConnectionsManager extends BaseController {
}
}
public static native void native_switchBackend(int currentAccount);
public static native void native_switchBackend(int currentAccount, boolean restart);
public static native int native_isTestBackend(int currentAccount);
public static native void native_pauseNetwork(int currentAccount);

View File

@ -25,6 +25,11 @@ import android.os.Build;
import android.text.SpannableString;
import android.text.TextPaint;
import android.text.TextUtils;
import android.transition.ChangeBounds;
import android.transition.Fade;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@ -33,11 +38,13 @@ import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import org.checkerframework.checker.units.qual.A;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.ui.Adapters.FiltersView;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.EllipsizeSpanAnimator;
import org.telegram.ui.Components.FireworksEffect;
import org.telegram.ui.Components.LayoutHelper;
@ -112,6 +119,8 @@ public class ActionBar extends FrameLayout {
private boolean overlayTitleAnimation;
private boolean titleAnimationRunning;
private boolean fromBottom;
private boolean centerScale;
private CharSequence subtitle;
EllipsizeSpanAnimator ellipsizeSpanAnimator = new EllipsizeSpanAnimator(this);
@ -330,9 +339,13 @@ public class ActionBar extends FrameLayout {
createSubtitleTextView();
}
if (subtitleTextView != null) {
subtitleTextView.setVisibility(!TextUtils.isEmpty(value) && !isSearchFieldVisible ? VISIBLE : GONE);
boolean isEmpty = TextUtils.isEmpty(value);
subtitleTextView.setVisibility(!isEmpty && !isSearchFieldVisible ? VISIBLE : GONE);
subtitleTextView.setAlpha(1f);
subtitleTextView.setText(value);
if (!isEmpty) {
subtitleTextView.setText(value);
}
subtitle = value;
}
}
@ -429,10 +442,10 @@ public class ActionBar extends FrameLayout {
}
public String getSubtitle() {
if (subtitleTextView == null) {
if (subtitleTextView == null || subtitle == null) {
return null;
}
return subtitleTextView.getText().toString();
return subtitle.toString();
}
public ActionBarMenu createMenu() {
@ -576,7 +589,7 @@ public class ActionBar extends FrameLayout {
if (titleTextView[0] != null) {
titleTextView[0].setVisibility(INVISIBLE);
}
if (subtitleTextView != null && !TextUtils.isEmpty(subtitleTextView.getText())) {
if (subtitleTextView != null && !TextUtils.isEmpty(subtitle)) {
subtitleTextView.setVisibility(INVISIBLE);
}
if (menu != null) {
@ -645,7 +658,7 @@ public class ActionBar extends FrameLayout {
if (titleTextView[0] != null) {
titleTextView[0].setVisibility(INVISIBLE);
}
if (subtitleTextView != null && !TextUtils.isEmpty(subtitleTextView.getText())) {
if (subtitleTextView != null && !TextUtils.isEmpty(subtitle)) {
subtitleTextView.setVisibility(INVISIBLE);
}
if (menu != null) {
@ -736,7 +749,7 @@ public class ActionBar extends FrameLayout {
if (titleTextView[0] != null) {
titleTextView[0].setVisibility(VISIBLE);
}
if (subtitleTextView != null && !TextUtils.isEmpty(subtitleTextView.getText())) {
if (subtitleTextView != null && !TextUtils.isEmpty(subtitle)) {
subtitleTextView.setVisibility(VISIBLE);
}
}
@ -820,7 +833,7 @@ public class ActionBar extends FrameLayout {
viewsToHide.add(titleTextView[0]);
}
if (subtitleTextView != null && !TextUtils.isEmpty(subtitleTextView.getText())) {
if (subtitleTextView != null && !TextUtils.isEmpty(subtitle)) {
viewsToHide.add(subtitleTextView);
subtitleTextView.setVisibility(visible ? INVISIBLE : VISIBLE);
}
@ -837,7 +850,8 @@ public class ActionBar extends FrameLayout {
searchVisibleAnimator.playTogether(ObjectAnimator.ofFloat(view, View.SCALE_Y, visible ? 0.95f : 1f));
searchVisibleAnimator.playTogether(ObjectAnimator.ofFloat(view, View.SCALE_X, visible ? 0.95f : 1f));
}
centerScale = true;
requestLayout();
searchVisibleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -1015,10 +1029,15 @@ public class ActionBar extends FrameLayout {
}
if (titleTextView[i] != null && titleTextView[i].getVisibility() != GONE) {
CharSequence text = titleTextView[i].getText();
titleTextView[i].setPivotX(titleTextView[i].getTextPaint().measureText(text, 0, text.length()) / 2f);
titleTextView[i].setPivotY((AndroidUtilities.dp(24) >> 1));
titleTextView[i].measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(24), MeasureSpec.AT_MOST));
if (centerScale) {
CharSequence text = titleTextView[i].getText();
titleTextView[i].setPivotX(titleTextView[i].getTextPaint().measureText(text, 0, text.length()) / 2f);
titleTextView[i].setPivotY((AndroidUtilities.dp(24) >> 1));
} else {
titleTextView[i].setPivotX(0);
titleTextView[i].setPivotY(0);
}
}
if (subtitleTextView != null && subtitleTextView.getVisibility() != GONE) {
subtitleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.AT_MOST));
@ -1222,6 +1241,8 @@ public class ActionBar extends FrameLayout {
} else {
animator.scaleY(0.7f).scaleX(0.7f);
}
requestLayout();
centerScale = true;
animator.setDuration(220).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -1335,7 +1356,7 @@ public class ActionBar extends FrameLayout {
setTitle(title);
return;
}
boolean crossfade = overlayTitleAnimation && !TextUtils.isEmpty(subtitleTextView.getText());
boolean crossfade = overlayTitleAnimation && !TextUtils.isEmpty(subtitle);
if (crossfade) {
if (subtitleTextView.getVisibility() != View.VISIBLE) {
subtitleTextView.setVisibility(View.VISIBLE);
@ -1423,4 +1444,68 @@ public class ActionBar extends FrameLayout {
public void setOverlayTitleAnimation(boolean ovelayTitleAnimation) {
this.overlayTitleAnimation = ovelayTitleAnimation;
}
public void beginDelayedTransition() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
transitionSet.addTransition(new Fade());
transitionSet.addTransition(new ChangeBounds() {
public void captureStartValues(TransitionValues transitionValues) {
super.captureStartValues(transitionValues);
if (transitionValues.view instanceof SimpleTextView) {
float textSize = ((SimpleTextView) transitionValues.view).getTextPaint().getTextSize();
transitionValues.values.put("text_size", textSize);
}
}
public void captureEndValues(TransitionValues transitionValues) {
super.captureEndValues(transitionValues);
if (transitionValues.view instanceof SimpleTextView) {
float textSize= ((SimpleTextView) transitionValues.view).getTextPaint().getTextSize();
transitionValues.values.put("text_size", textSize);
}
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues != null && startValues.view instanceof SimpleTextView) {
AnimatorSet animatorSet = new AnimatorSet();
Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
float s = (float) startValues.values.get("text_size") / (float) endValues.values.get("text_size");
startValues.view.setScaleX(s);
startValues.view.setScaleY(s);
if (animator != null) {
animatorSet.playTogether(animator);
}
animatorSet.playTogether(ObjectAnimator.ofFloat(startValues.view, SCALE_X, 1f));
animatorSet.playTogether(ObjectAnimator.ofFloat(startValues.view, SCALE_Y, 1f));
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
startValues.view.setLayerType(LAYER_TYPE_HARDWARE, null);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
startValues.view.setLayerType(LAYER_TYPE_NONE, null);
}
});
return animatorSet;
} else {
return super.createAnimator(sceneRoot, startValues, endValues);
}
}
});
centerScale = false;
transitionSet.setDuration(220);
transitionSet.setInterpolator(CubicBezierInterpolator.DEFAULT);
TransitionManager.beginDelayedTransition(this, transitionSet);
}
}
}

View File

@ -230,7 +230,7 @@ public class ActionBarMenuItem extends FrameLayout {
toggleSubMenu();
return true;
}
} else if (popupWindow != null && popupWindow.isShowing()) {
} else if (showSubmenuByMove && popupWindow != null && popupWindow.isShowing()) {
getLocationOnScreen(location);
float x = event.getX() + location[0];
float y = event.getY() + location[1];
@ -246,14 +246,14 @@ public class ActionBarMenuItem extends FrameLayout {
if (!rect.contains((int) x, (int) y)) {
child.setPressed(false);
child.setSelected(false);
if (Build.VERSION.SDK_INT == 21) {
if (Build.VERSION.SDK_INT == 21 && child.getBackground() != null) {
child.getBackground().setVisible(false, false);
}
} else {
child.setPressed(true);
child.setSelected(true);
if (Build.VERSION.SDK_INT >= 21) {
if (Build.VERSION.SDK_INT == 21) {
if (Build.VERSION.SDK_INT == 21 && child.getBackground() != null) {
child.getBackground().setVisible(true, false);
}
child.drawableHotspotChanged(x, y - child.getTop());
@ -272,7 +272,7 @@ public class ActionBarMenuItem extends FrameLayout {
delegate.onItemClick((Integer) selectedMenuView.getTag());
}
popupWindow.dismiss(allowCloseAnimation);
} else {
} else if (showSubmenuByMove) {
popupWindow.dismiss();
}
} else {
@ -1553,6 +1553,16 @@ public class ActionBarMenuItem extends FrameLayout {
}
}
public void hideAllSubItems() {
if (popupLayout == null) {
return;
}
for (int a = 0, N = popupLayout.getItemsCount(); a < N; a++) {
popupLayout.getItemAt(a).setVisibility(GONE);
}
measurePopup = true;
}
public boolean isSubItemVisible(int id) {
if (popupLayout == null) {
return false;

View File

@ -23,6 +23,7 @@ public class ActionBarMenuSubItem extends FrameLayout {
private TextView subtextView;
private ImageView imageView;
private ImageView checkView;
private ImageView rightIcon;
private int textColor = Theme.getColor(Theme.key_actionBarDefaultSubmenuItem);
private int iconColor = Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon);
@ -88,6 +89,20 @@ public class ActionBarMenuSubItem extends FrameLayout {
checkView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
}
public void setRightIcon(int icon) {
if (rightIcon == null) {
rightIcon = new ImageView(getContext());
rightIcon.setScaleType(ImageView.ScaleType.CENTER);
rightIcon.setColorFilter(textColor, PorterDuff.Mode.MULTIPLY);
if (LocaleController.isRTL) {
rightIcon.setScaleX(-1);
}
addView(rightIcon, LayoutHelper.createFrame(24, LayoutHelper.MATCH_PARENT, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT)));
}
setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 8 : 18), 0, AndroidUtilities.dp(LocaleController.isRTL ? 18 : 8), 0);
rightIcon.setImageResource(icon);
}
public void setTextAndIcon(CharSequence text, int icon) {
setTextAndIcon(text, icon, null);
}
@ -108,9 +123,10 @@ public class ActionBarMenuSubItem extends FrameLayout {
}
}
public void setColors(int textColor, int iconColor) {
public ActionBarMenuSubItem setColors(int textColor, int iconColor) {
setTextColor(textColor);
setIconColor(iconColor);
return this;
}
public void setTextColor(int textColor) {

View File

@ -14,6 +14,8 @@ import android.view.Window;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import androidx.recyclerview.widget.ChatListItemAnimator;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.SharedConfig;
@ -24,7 +26,7 @@ import java.util.ArrayList;
public class AdjustPanLayoutHelper {
public final static Interpolator keyboardInterpolator = CubicBezierInterpolator.DEFAULT;
public final static Interpolator keyboardInterpolator = ChatListItemAnimator.DEFAULT_INTERPOLATOR;
public final static long keyboardDuration = 250;
private final View parent;
@ -33,6 +35,15 @@ public class AdjustPanLayoutHelper {
private ViewGroup contentView;
private View resizableView;
private boolean animationInProgress;
private boolean needDelay;
private Runnable delayedAnimationRunnable = new Runnable() {
@Override
public void run() {
if (animator != null && !animator.isRunning()) {
animator.start();
}
}
};
int previousHeight = -1;
int previousContentHeight = -1;
@ -148,11 +159,16 @@ public class AdjustPanLayoutHelper {
onTransitionEnd();
}
});
animator.setDuration(220);
animator.setInterpolator(CubicBezierInterpolator.DEFAULT);
animator.setDuration(keyboardDuration);
animator.setInterpolator(keyboardInterpolator);
notificationsIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationsIndex, null);
animator.start();
if (needDelay) {
needDelay = false;
AndroidUtilities.runOnUIThread(delayedAnimationRunnable, 100);
} else {
animator.start();
}
}
private void setViewHeight(int height) {
@ -269,4 +285,13 @@ public class AdjustPanLayoutHelper {
public void setCheckHierarchyHeight(boolean checkHierarchyHeight) {
this.checkHierarchyHeight = checkHierarchyHeight;
}
public void delayAnimation() {
needDelay = true;
}
public void runDelayedAnimation() {
AndroidUtilities.cancelRunOnUIThread(delayedAnimationRunnable);
delayedAnimationRunnable.run();
}
}

View File

@ -17,6 +17,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MotionEvent;

View File

@ -32,6 +32,7 @@ import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
@ -105,6 +106,7 @@ import org.telegram.ui.Components.SendingFileDrawable;
import org.telegram.ui.Components.StatusDrawable;
import org.telegram.ui.Components.ThemeEditorView;
import org.telegram.ui.Components.TypingDotsDrawable;
import org.telegram.ui.RoundVideoProgressShadow;
import java.io.File;
import java.io.FileInputStream;
@ -125,6 +127,9 @@ import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import tw.nekomimi.nekogram.NekoConfig;
public class Theme {
@ -2222,6 +2227,9 @@ public class Theme {
public static Paint chat_composeBackgroundPaint;
public static Paint chat_radialProgressPaint;
public static Paint chat_radialProgress2Paint;
public static Paint chat_radialProgressPausedPaint;
public static Paint chat_radialProgressPausedSeekbarPaint;
public static TextPaint chat_msgTextPaint;
public static TextPaint chat_actionTextPaint;
public static TextPaint chat_msgBotButtonPaint;
@ -3167,6 +3175,9 @@ public class Theme {
private static ThreadLocal<float[]> hsvTemp4Local = new ThreadLocal<>();
private static ThreadLocal<float[]> hsvTemp5Local = new ThreadLocal<>();
private static FragmentContextViewWavesDrawable fragmentContextViewWavesDrawable;
private static RoundVideoProgressShadow roundPlayDrawable;
static {
defaultColors.put(key_dialogBackground, 0xffffffff);
defaultColors.put(key_dialogBackgroundGray, 0xfff0f0f0);
@ -7490,10 +7501,6 @@ public class Theme {
chat_statusRecordPaint.setStrokeCap(Paint.Cap.ROUND);
chat_actionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_actionTextPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_actionBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_actionBackgroundSelectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_actionBackgroundPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_actionBackgroundSelectedPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_actionBackgroundGradientDarkenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_actionBackgroundGradientDarkenPaint.setColor(0x2a000000);
chat_timeBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@ -7501,6 +7508,20 @@ public class Theme {
chat_contextResult_titleTextPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_contextResult_descriptionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_composeBackgroundPaint = new Paint();
chat_radialProgressPausedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_radialProgressPausedSeekbarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
if (chat_actionBackgroundPaint == null) {
chat_actionBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
if (chat_actionBackgroundSelectedPaint == null) {
chat_actionBackgroundSelectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
if (chat_actionBackgroundPaint2 == null) {
chat_actionBackgroundPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
}
if (chat_actionBackgroundSelectedPaint2 == null) {
chat_actionBackgroundSelectedPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
}
}
@ -8074,6 +8095,8 @@ public class Theme {
}
if (servicePressedColor == null) {
servicePressedColor = serviceSelectedMessageColor;
}
if (servicePressedColor2 == null) {
servicePressedColor2 = serviceSelectedMessage2Color;
}
@ -9046,12 +9069,17 @@ public class Theme {
return statusDrawable;
}
private static FragmentContextViewWavesDrawable fragmentContextViewWavesDrawable;
public static FragmentContextViewWavesDrawable getFragmentContextViewWavesDrawable() {
if (fragmentContextViewWavesDrawable == null) {
fragmentContextViewWavesDrawable = new FragmentContextViewWavesDrawable();
}
return fragmentContextViewWavesDrawable;
}
public static RoundVideoProgressShadow getRadialSeekbarShadowDrawable() {
if (roundPlayDrawable == null) {
roundPlayDrawable = new RoundVideoProgressShadow();
}
return roundPlayDrawable;
}
}

View File

@ -8,8 +8,11 @@
package org.telegram.ui;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
@ -22,17 +25,23 @@ import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.camera.CameraController;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.DrawerLayoutContainer;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.PasscodeView;
import org.telegram.ui.Components.ThemeEditorView;
import java.util.ArrayList;
@ -231,6 +240,106 @@ public class BubbleActivity extends Activity implements ActionBarLayout.ActionBa
onFinish();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ThemeEditorView editorView = ThemeEditorView.getInstance();
if (editorView != null) {
editorView.onActivityResult(requestCode, resultCode, data);
}
if (actionBarLayout.fragmentsStack.size() != 0) {
BaseFragment fragment = actionBarLayout.fragmentsStack.get(actionBarLayout.fragmentsStack.size() - 1);
fragment.onActivityResultFragment(requestCode, resultCode, data);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults == null) {
grantResults = new int[0];
}
if (permissions == null) {
permissions = new String[0];
}
boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (requestCode == 104) {
if (granted) {
if (GroupCallActivity.groupCallInstance != null) {
GroupCallActivity.groupCallInstance.enableCamera();
}
} else {
showPermissionErrorAlert(LocaleController.getString("VoipNeedCameraPermission", R.string.VoipNeedCameraPermission));
}
} else if (requestCode == 4) {
if (!granted) {
showPermissionErrorAlert(LocaleController.getString("PermissionStorage", R.string.PermissionStorage));
} else {
ImageLoader.getInstance().checkMediaPaths();
}
} else if (requestCode == 5) {
if (!granted) {
showPermissionErrorAlert(LocaleController.getString("PermissionContacts", R.string.PermissionContacts));
return;
} else {
ContactsController.getInstance(currentAccount).forceImportContacts();
}
} else if (requestCode == 3) {
boolean audioGranted = true;
boolean cameraGranted = true;
for (int i = 0, size = Math.min(permissions.length, grantResults.length); i < size; i++) {
if (Manifest.permission.RECORD_AUDIO.equals(permissions[i])) {
audioGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
} else if (Manifest.permission.CAMERA.equals(permissions[i])) {
cameraGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
}
}
if (!audioGranted) {
showPermissionErrorAlert(LocaleController.getString("PermissionNoAudio", R.string.PermissionNoAudio));
} else if (!cameraGranted) {
showPermissionErrorAlert(LocaleController.getString("PermissionNoCamera", R.string.PermissionNoCamera));
} else {
if (SharedConfig.inappCamera) {
CameraController.getInstance().initCamera(null);
}
return;
}
} else if (requestCode == 18 || requestCode == 19 || requestCode == 20 || requestCode == 22) {
if (!granted) {
showPermissionErrorAlert(LocaleController.getString("PermissionNoCamera", R.string.PermissionNoCamera));
}
} else if (requestCode == 2) {
if (granted) {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.locationPermissionGranted);
}
}
if (actionBarLayout.fragmentsStack.size() != 0) {
BaseFragment fragment = actionBarLayout.fragmentsStack.get(actionBarLayout.fragmentsStack.size() - 1);
fragment.onRequestPermissionsResultFragment(requestCode, permissions, grantResults);
}
VoIPFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private void showPermissionErrorAlert(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setMessage(message);
builder.setNegativeButton(LocaleController.getString("PermissionOpenSettings", R.string.PermissionOpenSettings), (dialog, which) -> {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + ApplicationLoader.applicationContext.getPackageName()));
startActivity(intent);
} catch (Exception e) {
FileLog.e(e);
}
});
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
builder.show();
}
@Override
protected void onResume() {
super.onResume();

View File

@ -97,7 +97,11 @@ public class AboutLinkCell extends FrameLayout {
if (TextUtils.isEmpty(text) || TextUtils.equals(text, oldText)) {
return;
}
oldText = text;
try {
oldText = AndroidUtilities.getSafeString(text);
} catch (Throwable e) {
oldText = text;
}
stringBuilder = new SpannableStringBuilder(oldText);
MessageObject.addLinks(false, stringBuilder, false, false, !parseLinks);
if (TextUtils.isEmpty(value)) {

View File

@ -10,7 +10,6 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.BottomPagesView;
import org.telegram.ui.Components.LayoutHelper;
@ -54,12 +53,12 @@ public class ArchiveHintCell extends FrameLayout {
@Override
public void onPageSelected(int i) {
FileLog.d("test1");
}
@Override
public void onPageScrollStateChanged(int i) {
FileLog.d("test1");
}
});

View File

@ -38,10 +38,11 @@ public abstract class BaseCell extends ViewGroup {
if (!NekoConfig.disableVibration) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
onLongPress();
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
onTouchEvent(event);
event.recycle();
if (onLongPress()) {
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
onTouchEvent(event);
event.recycle();
}
}
}
}
@ -103,7 +104,7 @@ public abstract class BaseCell extends ViewGroup {
return false;
}
protected void onLongPress() {
protected boolean onLongPress() {
return true;
}
}

View File

@ -78,7 +78,7 @@ public class BotHelpCell extends View {
if (text != null && text.equals(oldText)) {
return;
}
oldText = text;
oldText = AndroidUtilities.getSafeString(text);
setVisibility(VISIBLE);
int maxWidth;
if (AndroidUtilities.isTablet()) {

View File

@ -246,10 +246,11 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD
}
@Override
protected void onLongPress() {
protected boolean onLongPress() {
if (delegate != null) {
delegate.didLongPress(this, lastTouchX, lastTouchY);
}
return true;
}
@Override

View File

@ -48,7 +48,7 @@ import tw.nekomimi.nekogram.NekoConfig;
public class SharedLinkCell extends FrameLayout {
public interface SharedLinkCellDelegate {
void needOpenWebView(TLRPC.WebPage webPage);
void needOpenWebView(TLRPC.WebPage webPage, MessageObject messageObject);
boolean canPerformActions();
void onLinkPress(final String urlFinal, boolean longPress);
}
@ -452,6 +452,10 @@ public class SharedLinkCell extends FrameLayout {
requestLayout();
}
public ImageReceiver getLinkImageView() {
return linkImageView;
}
public void setDelegate(SharedLinkCellDelegate sharedLinkCellDelegate) {
delegate = sharedLinkCellDelegate;
}
@ -508,7 +512,7 @@ public class SharedLinkCell extends FrameLayout {
try {
TLRPC.WebPage webPage = pressedLink == 0 && message.messageOwner.media != null ? message.messageOwner.media.webpage : null;
if (webPage != null && webPage.embed_url != null && webPage.embed_url.length() != 0) {
delegate.needOpenWebView(webPage);
delegate.needOpenWebView(webPage, message);
} else {
delegate.onLinkPress(links.get(pressedLink), false);
}

View File

@ -435,7 +435,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
if (messageObject1.isVoice() || messageObject1.isMusic()) {
cell.updateButtonState(false, true, false);
} else if (messageObject1.isRoundVideo()) {
cell.checkVideoPlayback(false);
cell.checkVideoPlayback(false, null);
if (!MediaController.getInstance().isPlayingMessage(messageObject1)) {
if (messageObject1.audioProgress != 0) {
messageObject1.resetPlayingProgress();
@ -460,7 +460,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
cell.updateButtonState(false, true, false);
} else if (messageObject.isRoundVideo()) {
if (!MediaController.getInstance().isPlayingMessage(messageObject)) {
cell.checkVideoPlayback(true);
cell.checkVideoPlayback(true, null);
}
}
}
@ -2227,8 +2227,8 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
}
@Override
public void needOpenWebView(String url, String title, String description, String originalUrl, int w, int h) {
EmbedBottomSheet.show(mContext, title, description, originalUrl, url, w, h, false);
public void needOpenWebView(MessageObject message, String url, String title, String description, String originalUrl, int w, int h) {
EmbedBottomSheet.show(getParentActivity(), message, provider, title, description, originalUrl, url, w, h, false);
}
@Override

View File

@ -648,6 +648,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
private boolean scrollToVideo;
private Path aspectPath;
private Paint aspectPaint;
private Runnable destroyTextureViewRunnable = new Runnable() {
@Override
public void run() {
destroyTextureView();
}
};
private static boolean noForwardQuote;
private TLRPC.ChatParticipant selectedParticipant;
@ -1192,6 +1198,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
chatListView.startMultiselect(position, false, new RecyclerListView.onMultiSelectionChanged() {
boolean limitReached;
@Override
public void onSelectionChanged(int position, boolean selected, float x, float y) {
int i = position - chatAdapter.messagesStartRow;
@ -2799,7 +2806,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
invalidateChatListViewTopPadding();
invalidateMessagesVisiblePart();
}
chatListView.setItemAnimator(null);
chatListView.invalidate();
updateBulletinLayout();
}
@ -2939,9 +2945,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (Theme.chat_roundVideoShadow != null && aspectRatioFrameLayout.isDrawingReady()) {
int x = (int) child.getX() - AndroidUtilities.dp(3);
int y = (int) child.getY() - AndroidUtilities.dp(2);
canvas.save();
canvas.scale(videoPlayerContainer.getScaleX(), videoPlayerContainer.getScaleY(), child.getX(), child.getY());
Theme.chat_roundVideoShadow.setAlpha(255);
Theme.chat_roundVideoShadow.setBounds(x, y, x + AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6), y + AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6));
Theme.chat_roundVideoShadow.setBounds(x, y, x + AndroidUtilities.roundPlayingMessageSize + AndroidUtilities.dp(6), y + AndroidUtilities.roundPlayingMessageSize + AndroidUtilities.dp(6));
Theme.chat_roundVideoShadow.draw(canvas);
canvas.restore();
}
result = super.drawChild(canvas, child, drawingTime);
} else {
@ -2963,7 +2972,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
canvas.translate(drawLaterRoundProgressCell.getX(), drawLaterRoundProgressCell.getTop() + chatListView.getY());
if (isRoundVideo) {
drawLaterRoundProgressCell.drawRoundProgress(canvas);
drawLaterRoundProgressCell.drawOverlays(canvas);
invalidate();
drawLaterRoundProgressCell.invalidate();
// drawLaterRoundProgressCell.drawOverlays(canvas);
} else {
drawLaterRoundProgressCell.drawOverlays(canvas);
if (drawLaterRoundProgressCell.needDrawTime()) {
@ -3012,6 +3023,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
invalidateMessagesVisiblePart = false;
updateMessagesVisiblePart(false);
}
updateTextureViewPosition(false);
super.dispatchDraw(canvas);
if (fragmentContextView != null && fragmentContextView.isCallStyle()) {
canvas.save();
@ -3109,6 +3121,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
groupedBackgroundWasDraw = true;
}
if (cell != null && cell.getPhotoImage().isAnimationRunning()) {
invalidate();
}
float viewClipLeft = Math.max(chatListView.getLeft(), chatListView.getLeft() + child.getX());
float viewClipTop = Math.max(listTop, chatListView.getTop() + child.getY());
float viewClipRight = Math.min(chatListView.getRight(), chatListView.getLeft() + child.getX() + child.getMeasuredWidth());
@ -3253,6 +3269,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (actionBar.getVisibility() == VISIBLE) {
heightSize -= actionBarHeight;
}
int keyboardHeightOld = keyboardHeight + chatEmojiViewPadding;
boolean keyboardVisibleOld = keyboardHeight + chatEmojiViewPadding >= AndroidUtilities.dp(20);
if (lastHeight != allHeight) {
measureKeyboardHeight();
}
@ -3266,9 +3284,34 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
chatEmojiViewPadding = 0;
}
}
setEmojiKeyboardHeight(chatEmojiViewPadding);
boolean keyboardVisible = keyboardHeight + chatEmojiViewPadding >= AndroidUtilities.dp(20);
boolean waitingChatListItemAnimator = false;
if (MediaController.getInstance().getPlayingMessageObject() != null && MediaController.getInstance().getPlayingMessageObject().isRoundVideo() && keyboardVisibleOld != keyboardVisible) {
for (int i = 0; i < chatListView.getChildCount(); i++) {
View child = chatListView.getChildAt(i);
if (child instanceof ChatMessageCell) {
MessageObject messageObject = ((ChatMessageCell) child).getMessageObject();
if (messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) {
int p = chatListView.getChildAdapterPosition(child);
if (p >= 0) {
chatLayoutManager.scrollToPositionWithOffset(p, (int) ((chatListView.getMeasuredHeight() - chatListViewPaddingTop + (keyboardHeight + chatEmojiViewPadding - keyboardHeightOld) - (keyboardVisible ? AndroidUtilities.roundMessageSize : AndroidUtilities.roundPlayingMessageSize)) / 2), false);
chatAdapter.notifyItemChanged(p);
adjustPanLayoutHelper.delayAnimation();
waitingChatListItemAnimator = true;
break;
}
}
}
}
}
if (!waitingChatListItemAnimator) {
chatActivityEnterView.runEmojiPanelAnimation();
}
int childCount = getChildCount();
measureChildWithMargins(chatActivityEnterView, widthMeasureSpec, 0, heightMeasureSpec, 0);
@ -3422,7 +3465,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
final int count = getChildCount();
int keyboardSize = getKeyboardHeight();
int paddingBottom;
long time = System.currentTimeMillis();
if (fixedKeyboardHeight > 0 && keyboardSize <= AndroidUtilities.dp(20)) {
paddingBottom = fixedKeyboardHeight;
} else {
@ -3430,10 +3473,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
if (!SharedConfig.smoothKeyboard) {
setBottomClip(paddingBottom);
} else if (!inPreviewMode && chatActivityEnterView.getEmojiPadding() == 0) {
setBottomClip(AndroidUtilities.dp(48));
} else {
setBottomClip(0);
}
for (int i = 0; i < count; i++) {
@ -3793,6 +3832,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
private RectF rect = new RectF();
private void drawReplyButton(Canvas canvas) {
if (slidingView == null) {
return;
@ -4023,10 +4063,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
drawReplyButton(c);
}
if (chatListView.isFastScrollAnimationRunning()) {
updateTextureViewPosition(false);
}
}
ArrayList<MessageObject.GroupedMessages> drawingGroups = new ArrayList<>(10);
@ -4562,11 +4598,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
float tx = chatMessageCell.getSlidingOffsetX() + chatMessageCell.getCheckBoxTranslation();
int y = (int) ((replaceAnimation ? child.getTop() : child.getY()) + chatMessageCell.getLayoutHeight());
int y = (int) ((replaceAnimation ? child.getTop() : child.getY()) + chatMessageCell.getLayoutHeight() + chatMessageCell.getTransitionParams().deltaBottom);
int maxY = chatListView.getMeasuredHeight() - chatListView.getPaddingBottom();
if (y > maxY) {
y = maxY;
if (chatMessageCell.isPlayingRound() || chatMessageCell.getTransitionParams().animatePlayingRound) {
if (chatMessageCell.getTransitionParams().animatePlayingRound) {
float progressLocal = chatMessageCell.getTransitionParams().animateChangeProgress;
if (!chatMessageCell.isPlayingRound()) {
progressLocal = 1f - progressLocal;
}
int fromY = y;
int toY = y > maxY ? maxY : y;
y = (int) (fromY * progressLocal + toY * (1f - progressLocal));
}
} else {
if (y > maxY) {
y = maxY;
}
}
if (!replaceAnimation && child.getTranslationY() != 0) {
@ -4655,7 +4702,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
y = top + AndroidUtilities.dp(48);
}
if (!chatMessageCell.drawPinnedBottom()) {
int cellBottom = replaceAnimation ? chatMessageCell.getBottom() : (int) (chatMessageCell.getY() + chatMessageCell.getMeasuredHeight());
int cellBottom = replaceAnimation ? chatMessageCell.getBottom() : (int) (chatMessageCell.getY() + chatMessageCell.getMeasuredHeight() + chatMessageCell.getTransitionParams().deltaBottom);
if (y > cellBottom) {
y = cellBottom;
}
@ -4748,6 +4795,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (BuildVars.LOGS_ENABLED) {
FileLog.d("chatItemAnimator disable notifications");
}
chatActivityEnterView.getAdjustPanLayoutHelper().runDelayedAnimation();
chatActivityEnterView.runEmojiPanelAnimation();
}
@Override
@ -6153,7 +6202,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
if (result.type.equals("video") || result.type.equals("web_player_video")) {
int[] size = MessageObject.getInlineResultWidthAndHeight(result);
EmbedBottomSheet.show(getParentActivity(), result.title != null ? result.title : "", result.description, result.content.url, result.content.url, size[0], size[1], isKeyboardVisible());
EmbedBottomSheet.show(getParentActivity(), null, botContextProvider, result.title != null ? result.title : "", result.description, result.content.url, result.content.url, size[0], size[1], isKeyboardVisible());
} else {
processExternalUrl(0, result.content.url, false);
}
@ -6394,8 +6443,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
contentView.addView(fragmentLocationContextView = new FragmentContextView(context, this, true), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0));
contentView.addView(fragmentContextView = new FragmentContextView(context, this, false) {
@Override
protected void playbackSpeedChanged(boolean enabled) {
undoView.showWithAction(0, enabled ? UndoView.ACTION_PLAYBACK_SPEED_ENABLED : UndoView.ACTION_PLAYBACK_SPEED_DISABLED, null);
protected void playbackSpeedChanged(float value) {
if (Math.abs(value - 1.0f) < 0.001f || Math.abs(value - 1.8f) < 0.001f) {
undoView.showWithAction(0, Math.abs(value - 1.0f) > 0.001f ? UndoView.ACTION_PLAYBACK_SPEED_ENABLED : UndoView.ACTION_PLAYBACK_SPEED_DISABLED, value, null, null);
}
}
}, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0));
fragmentContextView.setAdditionalContextView(fragmentLocationContextView);
@ -6607,7 +6658,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
messageEditTextAnimator = a;
a.setDuration(ChatListItemAnimator.DEFAULT_DURATION);
// a.setStartDelay(chatActivityEnterViewAnimateBeforeSending ? 20 : 0);
// a.setStartDelay(chatActivityEnterViewAnimateBeforeSending ? 20 : 0);
a.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
a.start();
shouldAnimateEditTextWithBounds = false;
@ -8335,6 +8386,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (parentLayout == null) {
return null;
}
AndroidUtilities.cancelRunOnUIThread(destroyTextureViewRunnable);
if (videoPlayerContainer == null) {
if (Build.VERSION.SDK_INT >= 21) {
videoPlayerContainer = new FrameLayout(getParentActivity()) {
@ -8358,7 +8410,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), maxRad);
} else {
outline.setOval(0, 0, AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize);
outline.setOval(0, 0, AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize);
}
}
});
@ -8432,7 +8484,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
parent = null;
}
if (parent == null) {
contentView.addView(videoPlayerContainer, 1, new FrameLayout.LayoutParams(AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize));
contentView.addView(videoPlayerContainer, 1, new FrameLayout.LayoutParams(AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize));
}
videoPlayerContainer.setTag(null);
aspectRatioFrameLayout.setDrawingReady(false);
@ -8443,12 +8495,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (videoPlayerContainer == null || videoPlayerContainer.getParent() == null) {
return;
}
contentView.removeView(videoPlayerContainer);
chatListView.invalidateViews();
aspectRatioFrameLayout.setDrawingReady(false);
videoPlayerContainer.setTag(null);
if (Build.VERSION.SDK_INT < 21) {
videoPlayerContainer.setLayerType(View.LAYER_TYPE_NONE, null);
}
contentView.removeView(videoPlayerContainer);
}
private void openForward() {
@ -9171,7 +9224,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
String str = StrUtil.utf8Str(os.toByteArray());
if (StrUtil.isBlank(str)) return;
getSendMessagesHelper().sendMessage(str, dialog_id, null, null, null,
false, null, null, null, true, 0,null);
false, null, null, null, true, 0, null);
afterMessageSend();
hideFieldPanel(false);
break;
@ -10870,12 +10923,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) videoPlayerContainer.getLayoutParams();
if (messageObject.isRoundVideo()) {
videoPlayerContainer.setTag(R.id.parent_tag, null);
if (layoutParams.width != AndroidUtilities.roundMessageSize || layoutParams.height != AndroidUtilities.roundMessageSize) {
layoutParams.width = layoutParams.height = AndroidUtilities.roundMessageSize;
if (layoutParams.width != AndroidUtilities.roundPlayingMessageSize || layoutParams.height != AndroidUtilities.roundPlayingMessageSize) {
layoutParams.width = layoutParams.height = AndroidUtilities.roundPlayingMessageSize;
aspectRatioFrameLayout.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
videoPlayerContainer.setLayoutParams(layoutParams);
}
float scale = (AndroidUtilities.roundMessageSize + AndroidUtilities.roundMessageInset * 2) / (float) AndroidUtilities.roundMessageSize;
float scale = (AndroidUtilities.roundPlayingMessageSize + AndroidUtilities.roundMessageInset * 2) / (float) AndroidUtilities.roundPlayingMessageSize;
float transitionScale = messageCell.getPhotoImage().getImageWidth() / AndroidUtilities.roundPlayingMessageSize;
if (videoPlayerContainer.getScaleX() != transitionScale) {
videoPlayerContainer.invalidate();
fragmentView.invalidate();
}
videoPlayerContainer.setPivotX(0);
videoPlayerContainer.setPivotY(0);
videoPlayerContainer.setScaleX(transitionScale);
videoPlayerContainer.setScaleY(transitionScale);
videoTextureView.setScaleX(scale);
videoTextureView.setScaleY(scale);
} else {
@ -10903,9 +10965,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (checkTextureViewPosition && messageObject.isVideo()) {
MediaController.getInstance().cleanupPlayer(true, true);
} else {
videoPlayerContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100);
videoPlayerContainer.setTranslationY(-AndroidUtilities.roundPlayingMessageSize - 100);
fragmentView.invalidate();
if (messageObject != null && (messageObject.isRoundVideo() || messageObject.isVideo())) {
if (messageObject.isRoundVideo() || messageObject.isVideo()) {
if (checkTextureViewPosition || PipRoundVideoView.getInstance() != null) {
MediaController.getInstance().setCurrentVideoVisible(false);
} else {
@ -10916,7 +10978,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else {
MediaController.getInstance().setCurrentVideoVisible(true);
if (messageObject.isRoundVideo() || scrollToVideo) {
scrollToMessageId(messageObject.getId(), 0, false, 0, true, 0);
// scrollToMessageId(messageObject.getId(), 0, false, 0, true, 0);
} else {
chatListView.invalidate();
}
@ -11062,9 +11124,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
foundTextureViewMessage = true;
}
}
if (startFromVideoTimestamp >= 0 && fragmentOpened && !chatListView.isFastScrollAnimationRunning() && startFromVideoMessageId == messageObject.getId()) {
if (startFromVideoTimestamp >= 0 && fragmentOpened && !chatListView.isFastScrollAnimationRunning() && startFromVideoMessageId == messageObject.getId() && (messageObject.isVideo() || messageObject.isRoundVideo() || messageObject.isVoice() || messageObject.isMusic())) {
messageObject.forceSeekTo = startFromVideoTimestamp / (float) messageObject.getDuration();
openPhotoViewerForMessage(messageCell, messageObject);
MessageObject finalMessage = messageObject;
AndroidUtilities.runOnUIThread(() -> {
if (finalMessage.isVideo()) {
openPhotoViewerForMessage(null, finalMessage);
} else {
MediaController.getInstance().playMessage(finalMessage);
}
}, 40);
startFromVideoTimestamp = -1;
}
} else if (view instanceof ChatActionCell) {
@ -11161,7 +11230,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (checkTextureViewPosition && messageObject.isVideo()) {
MediaController.getInstance().cleanupPlayer(true, true);
} else {
videoPlayerContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100);
videoPlayerContainer.setTranslationY(-AndroidUtilities.roundPlayingMessageSize - 100);
fragmentView.invalidate();
if ((messageObject.isRoundVideo() || messageObject.isVideo()) && messageObject.eventId == 0 && checkTextureViewPosition && !chatListView.isFastScrollAnimationRunning()) {
MediaController.getInstance().setCurrentVideoVisible(false);
@ -12927,30 +12996,30 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
postponedScrollToLastMessageQueryIndex = 0;
}
if (index == -1) {
if (chatMode == MODE_SCHEDULED && mode == MODE_SCHEDULED && !isCache) {
waitingForReplyMessageLoad = true;
waitingForLoad.add(lastLoadIndex);
getMessagesController().loadMessages(dialog_id, mergeDialogId, false, AndroidUtilities.isTablet() ? 30 : 20, 0, 0, true, 0, classGuid, 2, 0, ChatObject.isChannel(currentChat), chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++);
}
return;
} else if (!doNotRemoveLoadIndex) {
waitingForLoad.remove(index);
if (index == -1) {
if (chatMode == MODE_SCHEDULED && mode == MODE_SCHEDULED && !isCache) {
waitingForReplyMessageLoad = true;
waitingForLoad.add(lastLoadIndex);
getMessagesController().loadMessages(dialog_id, mergeDialogId, false, AndroidUtilities.isTablet() ? 30 : 20, 0, 0, true, 0, classGuid, 2, 0, ChatObject.isChannel(currentChat), chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++);
}
return;
} else if (!doNotRemoveLoadIndex) {
waitingForLoad.remove(index);
}
ArrayList<MessageObject> messArr = (ArrayList<MessageObject>) args[2];
if (messages.isEmpty() && messArr.size() == 1 && MessageObject.isSystemSignUp(messArr.get(0))) {
forceHistoryEmpty = true;
endReached[0] = endReached[1] = true;
forwardEndReached[0] = forwardEndReached[1] = true;
firstLoading = false;
showProgressView(false);
if (!fragmentOpened) {
chatListView.setAnimateEmptyView(false, 1);
chatListView.setEmptyView(emptyViewContainer);
chatListView.setAnimateEmptyView(true, 1);
} else {
chatListView.setEmptyView(emptyViewContainer);
}
ArrayList<MessageObject> messArr = (ArrayList<MessageObject>) args[2];
if (messages.isEmpty() && messArr.size() == 1 && MessageObject.isSystemSignUp(messArr.get(0))) {
forceHistoryEmpty = true;
endReached[0] = endReached[1] = true;
forwardEndReached[0] = forwardEndReached[1] = true;
firstLoading = false;
showProgressView(false);
if (!fragmentOpened) {
chatListView.setAnimateEmptyView(false, 1);
chatListView.setEmptyView(emptyViewContainer);
chatListView.setAnimateEmptyView(true, 1);
} else {
chatListView.setEmptyView(emptyViewContainer);
}
chatAdapter.notifyDataSetChanged();
resumeDelayedFragmentAnimation();
@ -13366,19 +13435,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
if (messArr.size() <= 3 &&
obj.messageOwner.action instanceof TLRPC.TL_messageActionChatDeleteUser &&
obj.messageOwner.from_id instanceof TLRPC.TL_peerUser &&
obj.messageOwner.from_id.user_id == getUserConfig().getClientUserId()) {
TLObject nekoxBot = getMessagesController().getUserOrChat("NekoXBot");
if (nekoxBot instanceof TLRPC.User &&
action.user_id == ((TLRPC.User) nekoxBot).id) {
ArrayList<Integer> mids = new ArrayList<>();
mids.add(obj.messageOwner.id);
getMessagesController().deleteMessages(mids, null, null, dialog_id, 0, true, false);
continue;
}
if (messArr.size() <= 3 &&
obj.messageOwner.action instanceof TLRPC.TL_messageActionChatDeleteUser &&
obj.messageOwner.from_id instanceof TLRPC.TL_peerUser &&
obj.messageOwner.from_id.user_id == getUserConfig().getClientUserId()) {
TLObject nekoxBot = getMessagesController().getUserOrChat("NekoXBot");
if (nekoxBot instanceof TLRPC.User &&
action.user_id == ((TLRPC.User) nekoxBot).id) {
ArrayList<Integer> mids = new ArrayList<>();
mids.add(obj.messageOwner.id);
getMessagesController().deleteMessages(mids, null, null, dialog_id, 0, true, false);
continue;
}
}
if (needAnimateToMessage != null && needAnimateToMessage.getId() == messageId && messageId < 0 && chatMode != MODE_SCHEDULED) {
obj = needAnimateToMessage;
@ -14672,7 +14741,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (messageObject1 != null) {
boolean isVideo = messageObject1.isVideo();
if (messageObject1.isRoundVideo() || isVideo) {
cell.checkVideoPlayback(!messageObject.equals(messageObject1));
cell.checkVideoPlayback(!messageObject.equals(messageObject1), null);
if (!MediaController.getInstance().isPlayingMessage(messageObject1)) {
if (isVideo && !MediaController.getInstance().isGoingToShowMessageObject(messageObject1)) {
AnimatedFileDrawable animation = cell.getPhotoImage().getAnimation();
@ -14687,6 +14756,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else if (isVideo) {
cell.updateButtonState(false, true, false);
}
if (messageObject1.isRoundVideo()) {
int position = chatListView.getChildAdapterPosition(cell);
if (position >= 0) {
if (MediaController.getInstance().isPlayingMessage(messageObject1)) {
boolean keyboardIsVisible = contentView.getKeyboardHeight() >= AndroidUtilities.dp(20);
chatLayoutManager.scrollToPositionWithOffset(position, (int) ((chatListView.getMeasuredHeight() - chatListViewPaddingTop - (keyboardIsVisible ? AndroidUtilities.roundMessageSize : AndroidUtilities.roundPlayingMessageSize)) / 2), false);
}
chatAdapter.notifyItemChanged(position);
}
}
} else if (messageObject1.isVoice() || messageObject1.isMusic()) {
cell.updateButtonState(false, true, false);
}
@ -14745,8 +14824,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
} else if (id == NotificationCenter.messagePlayingDidReset || id == NotificationCenter.messagePlayingPlayStateChanged) {
if (id == NotificationCenter.messagePlayingDidReset) {
destroyTextureView();
AndroidUtilities.runOnUIThread(destroyTextureViewRunnable);
}
int messageId = (int) args[0];
if (chatListView != null) {
int count = chatListView.getChildCount();
for (int a = 0; a < count; a++) {
@ -14767,7 +14847,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
} else if (messageObject.isRoundVideo()) {
if (!MediaController.getInstance().isPlayingMessage(messageObject)) {
cell.checkVideoPlayback(true);
Bitmap bitmap = null;
if (id == NotificationCenter.messagePlayingDidReset && cell.getMessageObject().getId() == messageId && videoTextureView != null) {
bitmap = videoTextureView.getBitmap();
if (bitmap != null && bitmap.getPixel(0, 0) == Color.TRANSPARENT) {
bitmap = null;
}
}
cell.checkVideoPlayback(true, bitmap);
}
int position = chatListView.getChildAdapterPosition(cell);
messageObject.forceUpdate = true;
if (position >= 0) {
chatAdapter.notifyItemChanged(position);
}
}
}
@ -21366,7 +21458,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
}
SendMessagesHelper.getInstance(currentAccount).sendMessage(toSend.toString(), dialog_id, selectedObject, null, null,
false, null, null, null, true, 0,null);
false, null, null, null, true, 0, null);
return 2;
}
}
@ -22356,18 +22448,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
hideFieldPanel(false);
}
}
} else if (messageObject != null && str.startsWith("video")) {
} else if (messageObject != null && str.startsWith("video") && !longPress) {
int seekTime = Utilities.parseInt(str);
TLRPC.WebPage webPage;
if (messageObject.isYouTubeVideo()) {
webPage = messageObject.messageOwner.media.webpage;
} else if (messageObject.replyMessageObject != null && messageObject.replyMessageObject.isYouTubeVideo()) {
webPage = messageObject.replyMessageObject.messageOwner.media.webpage;
messageObject = messageObject.replyMessageObject;
} else {
webPage = null;
}
if (webPage != null) {
EmbedBottomSheet.show(getParentActivity(), webPage.site_name, webPage.title, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, seekTime, isKeyboardVisible());
EmbedBottomSheet.show(getParentActivity(), messageObject, photoViewerProvider, webPage.site_name, webPage.title, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, seekTime, isKeyboardVisible());
} else {
if (!messageObject.isVideo() && messageObject.replyMessageObject != null) {
MessageObject obj = messagesDict[messageObject.replyMessageObject.getDialogId() == dialog_id ? 0 : 1].get(messageObject.replyMessageObject.getId());
@ -22443,15 +22536,61 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else {
if (longPress) {
BottomBuilder builder = new BottomBuilder(getParentActivity());
builder.addTitle(str);
int timestamp = -1;
if (str.startsWith("video?")) {
timestamp = Utilities.parseInt(str);
}
if (timestamp >= 0) {
builder.addTitle(AndroidUtilities.formatDuration(timestamp, false));
} else {
builder.addTitle(str);
}
final int finalTimestamp = timestamp;
ChatMessageCell finalCell = cell;
MessageObject finalMessageObject = messageObject;
builder.addItems(
new String[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy), str.startsWith("#") || str.startsWith("$") ? null : LocaleController.getString("ShareQRCode", R.string.ShareQRCode)},
new String[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("ShareQRCode", R.string.ShareQRCode)},
new int[]{R.drawable.baseline_open_in_browser_24, R.drawable.baseline_content_copy_24, R.drawable.wallet_qr}, (which, text, __) -> {
if (which == 0 || which == 2) {
openClickableLink(str, which == 2);
if (which == 0) {
if (str.startsWith("video?")) {
didPressMessageUrl(url, false, finalMessageObject, finalCell);
} else {
openClickableLink(str, which == 2);
}
} else if (which == 1) {
AndroidUtilities.addToClipboard(str);
if (str.startsWith("video?") && finalMessageObject != null && !finalMessageObject.scheduled) {
MessageObject messageObject1 = finalMessageObject;
boolean isMedia = finalMessageObject.isVideo() || finalMessageObject.isRoundVideo() || finalMessageObject.isVoice() || finalMessageObject.isMusic();
if (!isMedia && finalMessageObject.replyMessageObject != null) {
messageObject1 = finalMessageObject.replyMessageObject;
}
int lower_id = (int) messageObject1.getDialogId();
int messageId = messageObject1.getId();
String link = null;
if (messageObject1.messageOwner.fwd_from != null) {
lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer);
messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id;
}
if (lower_id < 0) {
TLRPC.Chat currentChat = MessagesController.getInstance(currentAccount).getChat(-lower_id);
if (currentChat != null && currentChat.username != null) {
link = "https://t.me/" + currentChat.username + "/" + messageId + "?t=" + finalTimestamp;
}
} else {
TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(lower_id);
if (user != null && user.username != null) {
link = "https://t.me/" + user.username + "/" + messageId + "?t=" + finalTimestamp;
}
}
if (link == null) {
return Unit.INSTANCE;
}
AndroidUtilities.addToClipboard(link);
} else {
AndroidUtilities.addToClipboard(str);
}
if (str.startsWith("@")) {
undoView.showWithAction(0, UndoView.ACTION_USERNAME_COPIED, null);
} else if (str.startsWith("#") || str.startsWith("$")) {
@ -22459,7 +22598,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else {
undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null);
}
}
return Unit.INSTANCE;
});
@ -22481,14 +22619,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else if (which == 1) {
String url1 = urlFinal;
boolean tel = false;
boolean mail = false;
if (url1.startsWith("mailto:")) {
url1 = url1.substring(7);
mail = true;
} else if (url1.startsWith("tel:")) {
url1 = url1.substring(4);
tel = true;
}
AndroidUtilities.addToClipboard(url1);
if (tel) {
if (mail) {
undoView.showWithAction(0, UndoView.ACTION_EMAIL_COPIED, null);
} else if (tel) {
undoView.showWithAction(0, UndoView.ACTION_PHONE_COPIED, null);
} else {
undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null);
@ -23118,9 +23260,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
@Override
public void needOpenWebView(String url, String title, String description, String originalUrl, int w, int h) {
public void needOpenWebView(MessageObject message, String url, String title, String description, String originalUrl, int w, int h) {
try {
EmbedBottomSheet.show(mContext, title, description, originalUrl, url, w, h, isKeyboardVisible());
EmbedBottomSheet.show(getParentActivity(), message, photoViewerProvider, title, description, originalUrl, url, w, h, isKeyboardVisible());
} catch (Throwable e) {
FileLog.e(e);
}
@ -23419,6 +23561,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
public PinchToZoomHelper getPinchToZoomHelper() {
return pinchToZoomHelper;
}
@Override
public boolean keyboardIsOpened() {
return contentView.getKeyboardHeight() + chatEmojiViewPadding >= AndroidUtilities.dp(20);
}
public boolean isLandscape() {
return contentView.getMeasuredWidth() > contentView.getMeasuredHeight();
}
});
if (currentEncryptedChat == null) {
chatMessageCell.setAllowAssistant(true);
@ -23721,6 +23872,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
allAnimators.setStartDelay(120);
allAnimators.setDuration(180);
instantCameraView.setIsMessageTransition(true);
allAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -23748,6 +23900,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
instantCameraView.setIsMessageTransition(false);
instantCameraView.hideCamera(true);
instantCameraView.setVisibility(View.INVISIBLE);
}
@ -24311,6 +24464,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
chatActivityDelegate.openReplyMessage(messageId);
finishFragment();
} else {
startFromVideoTimestamp = LaunchActivity.getTimestampFromLink(data);
if (startFromVideoTimestamp >= 0) {
startFromVideoMessageId = messageId;
}
scrollToMessageId(messageId, fromMessageId, true, 0, false, 0);
}
}
@ -24433,7 +24590,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
public static boolean isClickableLink(String str) {
return str.startsWith("https://") || str.startsWith("vmess://") || str.startsWith("vmess1://") || str.startsWith("ss://") || str.startsWith("ssr://") || str.startsWith("ws://") || str.startsWith("wss://") || str.startsWith("@") || str.startsWith("#") || str.startsWith("$");
return str.startsWith("https://")
|| str.startsWith("vmess://")
|| str.startsWith("vmess1://")
|| str.startsWith("ss://")
|| str.startsWith("ssr://")
|| str.startsWith("ws://")
|| str.startsWith("wss://")
|| str.startsWith("@")
|| str.startsWith("#")
|| str.startsWith("$")
|| str.startsWith("video?");
}
public SimpleTextView getReplyNameTextView() {

View File

@ -122,7 +122,8 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
private LineProgressView progressView;
private SeekBarView seekBarView;
private SimpleTextView timeTextView;
private ImageView playbackSpeedButton;
private ActionBarMenuItem playbackSpeedButton;
private ActionBarMenuSubItem[] speedItems = new ActionBarMenuSubItem[4];
private TextView durationTextView;
private ActionBarMenuItem repeatButton;
private ActionBarMenuSubItem repeatSongItem;
@ -172,6 +173,11 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
long lastRewindingTime;
long lastUpdateRewindingPlayerTime;
private final static int menu_speed_slow = 1;
private final static int menu_speed_normal = 2;
private final static int menu_speed_fast = 3;
private final static int menu_speed_veryfast = 4;
private final Runnable forwardSeek = new Runnable() {
@Override
public void run() {
@ -663,23 +669,47 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
durationTextView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
playerLayout.addView(durationTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.RIGHT, 0, 96, 20, 0));
playbackSpeedButton = new ImageView(context);
playbackSpeedButton.setScaleType(ImageView.ScaleType.CENTER);
playbackSpeedButton.setImageResource(R.drawable.voice2x);
playbackSpeedButton = new ActionBarMenuItem(context, null, 0, Theme.getColor(Theme.key_dialogTextBlack));
playbackSpeedButton.setLongClickEnabled(false);
playbackSpeedButton.setShowSubmenuByMove(false);
playbackSpeedButton.setAdditionalYOffset(-AndroidUtilities.dp(224));
playbackSpeedButton.setContentDescription(LocaleController.getString("AccDescrPlayerSpeed", R.string.AccDescrPlayerSpeed));
if (AndroidUtilities.density >= 3.0f) {
playbackSpeedButton.setPadding(0, 1, 0, 0);
}
playerLayout.addView(playbackSpeedButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.RIGHT, 0, 86, 20, 0));
playbackSpeedButton.setOnClickListener(v -> {
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(true);
if (currentPlaybackSpeed > 1) {
playbackSpeedButton.setDelegate(id -> {
float oldSpeed = MediaController.getInstance().getPlaybackSpeed(true);
if (id == menu_speed_slow) {
MediaController.getInstance().setPlaybackSpeed(true, 0.5f);
} else if (id == menu_speed_normal) {
MediaController.getInstance().setPlaybackSpeed(true, 1.0f);
} else if (id == menu_speed_fast) {
MediaController.getInstance().setPlaybackSpeed(true, 1.5f);
} else {
MediaController.getInstance().setPlaybackSpeed(true, 1.8f);
}
updatePlaybackButton();
});
speedItems[0] = playbackSpeedButton.addSubItem(menu_speed_slow, R.drawable.msg_speed_0_5, LocaleController.getString("SpeedSlow", R.string.SpeedSlow));
speedItems[1] = playbackSpeedButton.addSubItem(menu_speed_normal, R.drawable.msg_speed_1, LocaleController.getString("SpeedNormal", R.string.SpeedNormal));
speedItems[2] = playbackSpeedButton.addSubItem(menu_speed_fast, R.drawable.msg_speed_1_5, LocaleController.getString("SpeedFast", R.string.SpeedFast));
speedItems[3] = playbackSpeedButton.addSubItem(menu_speed_veryfast, R.drawable.msg_speed_2, LocaleController.getString("SpeedVeryFast", R.string.SpeedVeryFast));
if (AndroidUtilities.density >= 3.0f) {
playbackSpeedButton.setPadding(0, 1, 0, 0);
}
playbackSpeedButton.setAdditionalXOffset(AndroidUtilities.dp(8));
playbackSpeedButton.setShowedFromBottom(true);
playerLayout.addView(playbackSpeedButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.RIGHT, 0, 86, 20, 0));
playbackSpeedButton.setOnClickListener(v -> {
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(true);
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
MediaController.getInstance().setPlaybackSpeed(true, 1.0f);
} else {
MediaController.getInstance().setPlaybackSpeed(true, MediaController.getInstance().getFastPlaybackSpeed(true));
}
updatePlaybackButton();
});
playbackSpeedButton.setOnLongClickListener(view -> {
playbackSpeedButton.toggleSubMenu();
return true;
});
updatePlaybackButton();
FrameLayout bottomView = new FrameLayout(context) {
@ -1278,12 +1308,34 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
private void updatePlaybackButton() {
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(true);
if (currentPlaybackSpeed > 1) {
playbackSpeedButton.setTag(Theme.key_inappPlayerPlayPause);
playbackSpeedButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_inappPlayerPlayPause), PorterDuff.Mode.SRC_IN));
String key;
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
key = Theme.key_inappPlayerPlayPause;
} else {
playbackSpeedButton.setTag(Theme.key_inappPlayerClose);
playbackSpeedButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_inappPlayerClose), PorterDuff.Mode.SRC_IN));
key = Theme.key_inappPlayerClose;
}
playbackSpeedButton.setTag(key);
float speed = MediaController.getInstance().getFastPlaybackSpeed(true);
if (Math.abs(speed - 1.8f) < 0.001f) {
playbackSpeedButton.setIcon(R.drawable.voice_mini_2_0);
} else if (Math.abs(speed - 1.5f) < 0.001f) {
playbackSpeedButton.setIcon(R.drawable.voice_mini_1_5);
} else {
playbackSpeedButton.setIcon(R.drawable.voice_mini_0_5);
}
playbackSpeedButton.setIconColor(Theme.getColor(key));
if (Build.VERSION.SDK_INT >= 21) {
playbackSpeedButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(key) & 0x19ffffff, 1, AndroidUtilities.dp(14)));
}
for (int a = 0; a < speedItems.length; a++) {
if (a == 0 && Math.abs(currentPlaybackSpeed - 0.5f) < 0.001f ||
a == 1 && Math.abs(currentPlaybackSpeed - 1.0f) < 0.001f ||
a == 2 && Math.abs(currentPlaybackSpeed - 1.5f) < 0.001f ||
a == 3 && Math.abs(currentPlaybackSpeed - 1.8f) < 0.001f) {
speedItems[a].setColors(Theme.getColor(Theme.key_inappPlayerPlayPause), Theme.getColor(Theme.key_inappPlayerPlayPause));
} else {
speedItems[a].setColors(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon));
}
}
}
@ -1428,9 +1480,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
if (path == null || path.length() == 0) {
path = FileLoader.getPathToMessage(messageObject.messageOwner).toString();
}
MediaController.saveFile(path, parentActivity, 3, fileName, messageObject.getDocument() != null ? messageObject.getDocument().mime_type : "", () -> {
BulletinFactory.of((FrameLayout) containerView).createDownloadBulletin(BulletinFactory.FileType.AUDIO).show();
});
MediaController.saveFile(path, parentActivity, 3, fileName, messageObject.getDocument() != null ? messageObject.getDocument().mime_type : "", () -> BulletinFactory.of((FrameLayout) containerView).createDownloadBulletin(BulletinFactory.FileType.AUDIO).show());
}
}
@ -1850,7 +1900,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
durationTextView.setText(duration != 0 ? AndroidUtilities.formatShortDuration(duration) : "-:--");
}
if (duration > 60 * 20) {
if (duration > 60 * 10) {
playbackSpeedButton.setVisibility(View.VISIBLE);
} else {
playbackSpeedButton.setVisibility(View.GONE);

View File

@ -284,7 +284,7 @@ public final class Bulletin {
layout.onExitTransitionEnd();
layout.onHide();
if (containerLayout != null) {
containerLayout.removeView(parentLayout);
AndroidUtilities.runOnUIThread(() -> containerLayout.removeView(parentLayout));
}
layout.onDetach();
}

View File

@ -788,6 +788,15 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
private Drawable lockShadowDrawable;
private Runnable runEmojiPanelAnimation = new Runnable() {
@Override
public void run() {
if (panelAnimation != null && !panelAnimation.isRunning()) {
panelAnimation.start();
}
}
};
public class RecordCircle extends View {
private float scale;
@ -1029,9 +1038,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
tooltipWidth = w;
}
}
if (tooltipLayout.getLineCount() > 1) {
h += tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0);
}
}
if (tooltipLayout != null && tooltipLayout.getLineCount() > 1) {
h += tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0);
}
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
@ -1860,9 +1869,12 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
return false;
}
if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) {
boolean rez = false;
if (searchingType != 0) {
searchingType = 0;
emojiView.closeSearch(false);
requestFocus();
rez = true;
}
showPopup(AndroidUtilities.usingHardwareInput ? 0 : 2, 0);
if (stickersExpanded) {
@ -1875,7 +1887,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
} else {
openKeyboardInternal();
}
return false;
return rez;
}
try {
return super.onTouchEvent(event);
@ -2307,7 +2319,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
if (isInScheduleMode()) {
AlertsCreator.createScheduleDatePickerDialog(parentActivity, dialog_id, (notify, scheduleDate) -> {
SendMessagesHelper.getInstance(currentAccount).sendMessage((String) command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null);
SendMessagesHelper.getInstance(currentAccount).sendMessage(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null);
setFieldText("");
botCommandsMenuContainer.dismiss();
});
@ -4795,7 +4807,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
if (stickersExpanded) {
setStickersExpanded(false, true, false);
if (searchingType != 0) {
if (searchingType != 0 && emojiView != null) {
emojiView.closeSearch(false);
emojiView.hideSearchKeyboard();
}
@ -4875,6 +4887,20 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
return;
}
if (searchingType != 0) {
searchingType = 0;
emojiView.closeSearch(false);
if (stickersExpanded) {
setStickersExpanded(false, true, false);
waitingForKeyboardOpenAfterAnimation = true;
AndroidUtilities.runOnUIThread(() -> {
waitingForKeyboardOpenAfterAnimation = false;
openKeyboardInternal();
}, 200);
}
}
CharSequence[] message = new CharSequence[]{AndroidUtilities.getTrimmedString(messageEditText.getText())};
ArrayList<TLRPC.MessageEntity> entities = MediaDataController.getInstance(currentAccount).getEntities(message, supportsSendingNewEntities());
if (!TextUtils.equals(message[0], editingMessageObject.messageText) || entities != null && !entities.isEmpty() || entities == null && !editingMessageObject.messageOwner.entities.isEmpty() || editingMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
@ -7681,7 +7707,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
requestLayout();
}
});
panelAnimation.start();
AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50);
notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null);
requestLayout();
}
@ -7725,7 +7751,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
});
notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null);
panelAnimation.start();
AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50);
requestLayout();
} else {
if (delegate != null) {
@ -7772,7 +7798,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
});
notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null);
panelAnimation.start();
AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50);
requestLayout();
} else {
if (!waitingForKeyboardOpen) {
@ -8064,7 +8090,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex);
}
});
panelAnimation.start();
AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50);
notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null);
requestLayout();
}
@ -8106,6 +8132,10 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
return emojiPadding;
}
public int getVisibleEmojiPadding() {
return emojiViewVisible ? emojiPadding : 0;
}
private MessageObject getThreadMessage() {
return parentFragment != null ? parentFragment.getThreadMessage() : null;
}
@ -9178,4 +9208,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
}
return 0;
}
public void runEmojiPanelAnimation() {
AndroidUtilities.cancelRunOnUIThread(runEmojiPanelAnimation);
runEmojiPanelAnimation.run();
}
}

View File

@ -629,8 +629,10 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou
chatActivity = null;
type = 4;
}
AndroidUtilities.hideKeyboard(parentAlert.baseFragment.getFragmentView().findFocus());
AndroidUtilities.hideKeyboard(parentAlert.getContainer().findFocus());
if (!parentAlert.delegate.needEnterComment()) {
AndroidUtilities.hideKeyboard(parentAlert.baseFragment.getFragmentView().findFocus());
AndroidUtilities.hideKeyboard(parentAlert.getContainer().findFocus());
}
PhotoViewer.getInstance().openPhotoForSelect(arrayList, position, type, false, photoViewerProvider, chatActivity);
} else {
if (SharedConfig.inappCamera) {
@ -1426,7 +1428,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou
return false;
}
int locked = Settings.System.getInt(parentAlert.baseFragment.getParentActivity().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0);
return true;//sameTakePictureOrientation || locked == 1;
return sameTakePictureOrientation || locked == 1;
}
@Override

View File

@ -26,7 +26,6 @@ import android.widget.TextView;
import androidx.core.widget.NestedScrollView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
@ -123,8 +122,10 @@ public class ClearHistoryAlert extends BottomSheet {
newTimer = currentTimer = 0;
} else if (ttl == 24 * 60 * 60) {
newTimer = currentTimer = 1;
} else {
} else if (ttl == 7 * 24 * 60 * 60) {
newTimer = currentTimer = 2;
} else {
newTimer = currentTimer = 3;
}
shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate();
@ -345,7 +346,8 @@ public class ClearHistoryAlert extends BottomSheet {
String[] strings = new String[]{
LocaleController.getString("AutoDeleteNever", R.string.AutoDeleteNever),
LocaleController.getString("AutoDelete24Hours", R.string.AutoDelete24Hours),
LocaleController.getString("AutoDelete7Days", R.string.AutoDelete7Days)
LocaleController.getString("AutoDelete7Days", R.string.AutoDelete7Days),
LocaleController.getString("AutoDelete1Month", R.string.AutoDelete1Month)
};
slideChooseView.setOptions(currentTimer, strings);
linearLayout.addView(slideChooseView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 8, 0, 0));
@ -379,7 +381,10 @@ public class ClearHistoryAlert extends BottomSheet {
dismissedDelayed = true;
int time;
int action;
if (newTimer == 2) {
if (newTimer == 3) {
time = 31 * 24 * 60 * 60;
action = UndoView.ACTION_AUTO_DELETE_ON;
} else if (newTimer == 2) {
time = 7 * 24 * 60 * 60;
action = UndoView.ACTION_AUTO_DELETE_ON;
} else if (newTimer == 1) {

View File

@ -89,6 +89,9 @@ public class CounterView extends View {
if (count > 0) {
setVisibility(View.VISIBLE);
}
if (Math.abs(count - currentCount) > 99) {
animated = false;
}
if (!animated) {
currentCount = count;
if (count == 0) {

View File

@ -764,12 +764,11 @@ public class EditTextBoldCursor extends EditText {
private boolean updateCursorPosition() {
final Layout layout = getLayout();
final int offset = getSelectionStart();
if (offset != lastOffset || lastText != layout.getText()) {
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
final int bottom = layout.getLineTop(line + 1);
updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset));
}
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
final int bottom = layout.getLineTop(line + 1);
updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset));
lastText = layout.getText();
lastOffset = offset;
return true;

View File

@ -53,6 +53,7 @@ import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BringAppForegroundService;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
@ -60,6 +61,7 @@ import org.telegram.messenger.browser.Browser;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.PhotoViewer;
import java.util.HashMap;
import java.util.Locale;
@ -217,17 +219,23 @@ public class EmbedBottomSheet extends BottomSheet {
@SuppressLint("StaticFieldLeak")
private static EmbedBottomSheet instance;
public static void show(Context context, String title, String description, String originalUrl, final String url, int w, int h, boolean keyboardVisible) {
show(context, title, description, originalUrl, url, w, h, -1, keyboardVisible);
public static void show(Activity activity, MessageObject message, PhotoViewer.PhotoViewerProvider photoViewerProvider, String title, String description, String originalUrl, final String url, int w, int h, boolean keyboardVisible) {
show(activity, message, photoViewerProvider, title, description, originalUrl, url, w, h, -1, keyboardVisible);
}
public static void show(Context context, String title, String description, String originalUrl, final String url, int w, int h, int seekTime, boolean keyboardVisible) {
public static void show(Activity activity, MessageObject message, PhotoViewer.PhotoViewerProvider photoViewerProvider, String title, String description, String originalUrl, final String url, int w, int h, int seekTime, boolean keyboardVisible) {
if (instance != null) {
instance.destroy();
}
EmbedBottomSheet sheet = new EmbedBottomSheet(context, title, description, originalUrl, url, w, h, seekTime);
sheet.setCalcMandatoryInsets(keyboardVisible);
sheet.show();
String youtubeId = message != null && message.messageOwner.media != null && message.messageOwner.media.webpage != null ? WebPlayerView.getYouTubeVideoId(url) : null;
if (youtubeId != null) {
PhotoViewer.getInstance().setParentActivity(activity);
PhotoViewer.getInstance().openPhoto(message, seekTime, null, 0, 0, photoViewerProvider);
} else {
EmbedBottomSheet sheet = new EmbedBottomSheet(activity, title, description, originalUrl, url, w, h, seekTime);
sheet.setCalcMandatoryInsets(keyboardVisible);
sheet.show();
}
}
@SuppressLint("SetJavaScriptEnabled")
@ -259,10 +267,8 @@ public class EmbedBottomSheet extends BottomSheet {
fullscreenVideoContainer.setFitsSystemWindows(true);
}
fullscreenVideoContainer.setOnTouchListener((v, event) -> true);
container.addView(fullscreenVideoContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
fullscreenVideoContainer.setVisibility(View.INVISIBLE);
fullscreenVideoContainer.setOnTouchListener((v, event) -> true);
containerLayout = new FrameLayout(context) {
@Override

View File

@ -66,6 +66,8 @@ import org.telegram.messenger.UserObject;
import org.telegram.messenger.voip.VoIPService;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.ActionBarMenuItem;
import org.telegram.ui.ActionBar.ActionBarMenuSubItem;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.Theme;
@ -96,7 +98,8 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
private RLottieImageView muteButton;
private RLottieDrawable muteDrawable;
private ImageView closeButton;
private ImageView playbackSpeedButton;
private ActionBarMenuItem playbackSpeedButton;
private ActionBarMenuSubItem[] speedItems = new ActionBarMenuSubItem[4];
private FragmentContextView additionalContextView;
private TextView joinButton;
@ -170,6 +173,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
private boolean checkPlayerAfterAnimation;
private boolean checkImportAfterAnimation;
private final static int menu_speed_slow = 1;
private final static int menu_speed_normal = 2;
private final static int menu_speed_fast = 3;
private final static int menu_speed_veryfast = 4;
@Override
public void onAudioSettingsChanged() {
boolean newMuted = VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().isMicMute();
@ -351,22 +359,49 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
joinButton.setOnClickListener(v -> FragmentContextView.this.callOnClick());
if (!location) {
playbackSpeedButton = new ImageView(context);
playbackSpeedButton.setScaleType(ImageView.ScaleType.CENTER);
playbackSpeedButton.setImageResource(R.drawable.voice2x);
playbackSpeedButton = new ActionBarMenuItem(context, null, 0, Theme.getColor(Theme.key_dialogTextBlack));
playbackSpeedButton.setLongClickEnabled(false);
playbackSpeedButton.setShowSubmenuByMove(false);
playbackSpeedButton.setContentDescription(LocaleController.getString("AccDescrPlayerSpeed", R.string.AccDescrPlayerSpeed));
if (AndroidUtilities.density >= 3.0f) {
playbackSpeedButton.setPadding(0, 1, 0, 0);
}
addView(playbackSpeedButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.RIGHT, 0, 0, 36, 0));
playbackSpeedButton.setOnClickListener(v -> {
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic);
if (currentPlaybackSpeed > 1) {
playbackSpeedButton.setDelegate(id -> {
float oldSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic);
if (id == menu_speed_slow) {
MediaController.getInstance().setPlaybackSpeed(isMusic, 0.5f);
} else if (id == menu_speed_normal) {
MediaController.getInstance().setPlaybackSpeed(isMusic, 1.0f);
} else if (id == menu_speed_fast) {
MediaController.getInstance().setPlaybackSpeed(isMusic, 1.5f);
} else {
MediaController.getInstance().setPlaybackSpeed(isMusic, 1.8f);
}
playbackSpeedChanged(currentPlaybackSpeed <= 1);
float newSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic);
if (oldSpeed != newSpeed) {
playbackSpeedChanged(newSpeed);
}
updatePlaybackButton();
});
speedItems[0] = playbackSpeedButton.addSubItem(menu_speed_slow, R.drawable.msg_speed_0_5, LocaleController.getString("SpeedSlow", R.string.SpeedSlow));
speedItems[1] = playbackSpeedButton.addSubItem(menu_speed_normal, R.drawable.msg_speed_1, LocaleController.getString("SpeedNormal", R.string.SpeedNormal));
speedItems[2] = playbackSpeedButton.addSubItem(menu_speed_fast, R.drawable.msg_speed_1_5, LocaleController.getString("SpeedFast", R.string.SpeedFast));
speedItems[3] = playbackSpeedButton.addSubItem(menu_speed_veryfast, R.drawable.msg_speed_2, LocaleController.getString("SpeedVeryFast", R.string.SpeedVeryFast));
if (AndroidUtilities.density >= 3.0f) {
playbackSpeedButton.setPadding(0, 1, 0, 0);
}
playbackSpeedButton.setAdditionalXOffset(AndroidUtilities.dp(8));
addView(playbackSpeedButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.RIGHT, 0, 0, 36, 0));
playbackSpeedButton.setOnClickListener(v -> {
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic);
float newSpeed;
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
MediaController.getInstance().setPlaybackSpeed(isMusic, newSpeed = 1.0f);
} else {
MediaController.getInstance().setPlaybackSpeed(isMusic, newSpeed = MediaController.getInstance().getFastPlaybackSpeed(isMusic));
}
playbackSpeedChanged(newSpeed);
});
playbackSpeedButton.setOnLongClickListener(view -> {
playbackSpeedButton.toggleSubMenu();
return true;
});
updatePlaybackButton();
}
@ -653,15 +688,33 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
}
float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic);
String key;
if (currentPlaybackSpeed > 1) {
if (Math.abs(currentPlaybackSpeed - 1.0f) > 0.001f) {
key = Theme.key_inappPlayerPlayPause;
} else {
key = Theme.key_inappPlayerClose;
}
playbackSpeedButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(key), PorterDuff.Mode.SRC_IN));
float speed = MediaController.getInstance().getFastPlaybackSpeed(isMusic);
if (Math.abs(speed - 1.8f) < 0.001f) {
playbackSpeedButton.setIcon(R.drawable.voice_mini_2_0);
} else if (Math.abs(speed - 1.5f) < 0.001f) {
playbackSpeedButton.setIcon(R.drawable.voice_mini_1_5);
} else {
playbackSpeedButton.setIcon(R.drawable.voice_mini_0_5);
}
playbackSpeedButton.setIconColor(Theme.getColor(key));
if (Build.VERSION.SDK_INT >= 21) {
playbackSpeedButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(key) & 0x19ffffff, 1, AndroidUtilities.dp(14)));
}
for (int a = 0; a < speedItems.length; a++) {
if (a == 0 && Math.abs(currentPlaybackSpeed - 0.5f) < 0.001f ||
a == 1 && Math.abs(currentPlaybackSpeed - 1.0f) < 0.001f ||
a == 2 && Math.abs(currentPlaybackSpeed - 1.5f) < 0.001f ||
a == 3 && Math.abs(currentPlaybackSpeed - 1.8f) < 0.001f) {
speedItems[a].setColors(Theme.getColor(Theme.key_inappPlayerPlayPause), Theme.getColor(Theme.key_inappPlayerPlayPause));
} else {
speedItems[a].setColors(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon));
}
}
}
public void setAdditionalContextView(FragmentContextView contextView) {
@ -727,7 +780,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
}
}
protected void playbackSpeedChanged(boolean enabled) {
protected void playbackSpeedChanged(float value) {
}

View File

@ -0,0 +1,20 @@
package org.telegram.ui.Components;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;
public class HideViewAfterAnimation extends AnimatorListenerAdapter {
private final View view;
public HideViewAfterAnimation(View view) {
this.view = view;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
}

View File

@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Outline;
import android.graphics.Paint;
@ -57,6 +58,8 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.core.graphics.ColorUtils;
import com.google.android.exoplayer2.ExoPlayer;
import org.telegram.messenger.AndroidUtilities;
@ -68,6 +71,7 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
@ -84,6 +88,7 @@ import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.ChatActivity;
import org.telegram.ui.Components.voip.CellFlickerDrawable;
import java.io.File;
import java.io.FileOutputStream;
@ -154,6 +159,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
private TextureView textureView;
private BackupImageView textureOverlayView;
private CameraSession cameraSession;
private boolean needDrawFlickerStub;
private float panTranslationY;
private float animationTranslationY;
@ -214,6 +220,9 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
boolean maybePinchToZoomTouchMode;
private int pointerId1, pointerId2;
private int textureViewSize;
private boolean isMessageTransition;
private boolean updateTextureViewSize;
@SuppressLint("ClickableViewAccessibility")
public InstantCameraView(Context context, ChatActivity parentFragment) {
@ -256,7 +265,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize);
outline.setOval(0, 0, textureViewSize, textureViewSize);
}
});
cameraContainer.setClipToOutline(true);
@ -296,7 +305,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
cameraContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
addView(cameraContainer, new FrameLayout.LayoutParams(AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize, Gravity.CENTER));
addView(cameraContainer, new FrameLayout.LayoutParams(AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize, Gravity.CENTER));
switchCameraButton = new ImageView(context);
switchCameraButton.setScaleType(ImageView.ScaleType.CENTER);
@ -323,16 +332,58 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
muteImageView.setImageResource(R.drawable.video_mute);
muteImageView.setAlpha(0.0f);
addView(muteImageView, LayoutHelper.createFrame(48, 48, Gravity.CENTER));
((LayoutParams) muteImageView.getLayoutParams()).topMargin = AndroidUtilities.roundMessageSize / 2 - AndroidUtilities.dp(24);
textureOverlayView = new BackupImageView(getContext());
textureOverlayView.setRoundRadius(AndroidUtilities.roundMessageSize / 2);
addView(textureOverlayView, new FrameLayout.LayoutParams(AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize, Gravity.CENTER));
Paint blackoutPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
blackoutPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, 40));
textureOverlayView = new BackupImageView(getContext()) {
CellFlickerDrawable flickerDrawable = new CellFlickerDrawable();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (needDrawFlickerStub) {
flickerDrawable.setParentWidth(textureViewSize);
AndroidUtilities.rectTmp.set(0, 0, textureViewSize, textureViewSize);
float rad = AndroidUtilities.rectTmp.width() / 2f;
canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, blackoutPaint);
AndroidUtilities.rectTmp.inset(AndroidUtilities.dp(1), AndroidUtilities.dp(1));
flickerDrawable.draw(canvas, AndroidUtilities.rectTmp, rad);
invalidate();
}
}
};
addView(textureOverlayView, new FrameLayout.LayoutParams(AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize, Gravity.CENTER));
setVisibility(INVISIBLE);
blurBehindDrawable = new BlurBehindDrawable(parentView, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (updateTextureViewSize) {
int newSize;
if (MeasureSpec.getSize(heightMeasureSpec) > MeasureSpec.getSize(widthMeasureSpec) * 1.3f) {
newSize = AndroidUtilities.roundPlayingMessageSize;
} else {
newSize = AndroidUtilities.roundMessageSize;
}
if (newSize != textureViewSize) {
textureViewSize = newSize;
textureOverlayView.getLayoutParams().width = textureOverlayView.getLayoutParams().height = textureViewSize;
cameraContainer.getLayoutParams().width = cameraContainer.getLayoutParams().height = textureViewSize;
((LayoutParams) muteImageView.getLayoutParams()).topMargin = textureViewSize / 2 - AndroidUtilities.dp(24);
textureOverlayView.setRoundRadius(textureViewSize / 2);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cameraContainer.invalidateOutline();
}
}
updateTextureViewSize = false;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private boolean checkPointerIds(MotionEvent ev) {
if (ev.getPointerCount() < 2) {
return false;
@ -420,9 +471,13 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
int x1 = (int) x - AndroidUtilities.dp(3);
int y1 = (int) y - AndroidUtilities.dp(2);
canvas.save();
canvas.scale(cameraContainer.getScaleX(), cameraContainer.getScaleY(), x1 + AndroidUtilities.roundMessageSize / 2 + AndroidUtilities.dp(3), y1 + AndroidUtilities.roundMessageSize / 2 + AndroidUtilities.dp(3));
if (isMessageTransition) {
canvas.scale(cameraContainer.getScaleX(), cameraContainer.getScaleY(), x, y);
} else {
canvas.scale(cameraContainer.getScaleX(), cameraContainer.getScaleY(), x + textureViewSize / 2f, y + textureViewSize / 2f);
}
Theme.chat_roundVideoShadow.setAlpha((int) (cameraContainer.getAlpha() * 255));
Theme.chat_roundVideoShadow.setBounds(x1, y1, x1 + AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6), y1 + AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6));
Theme.chat_roundVideoShadow.setBounds(x1, y1, x1 + textureViewSize + AndroidUtilities.dp(6), y1 + textureViewSize + AndroidUtilities.dp(6));
Theme.chat_roundVideoShadow.draw(canvas);
canvas.restore();
}
@ -468,6 +523,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
switchCameraButton.setImageResource(R.drawable.camera_revert1);
textureOverlayView.setAlpha(1.0f);
textureOverlayView.invalidate();
if (lastBitmap == null) {
try {
File file = new File(ApplicationLoader.getFilesDirFixed(), "icthumb.jpg");
@ -491,6 +547,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
encryptedFile = null;
key = null;
iv = null;
needDrawFlickerStub = true;
if (!initCamera()) {
return;
@ -546,6 +603,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
});
cameraContainer.addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
updateTextureViewSize = true;
setVisibility(VISIBLE);
startAnimation(true);
@ -664,7 +722,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
long endTime = videoEditedInfo.endTime >= 0 ? videoEditedInfo.endTime : videoEditedInfo.estimatedDuration;
videoEditedInfo.estimatedDuration = endTime - startTime;
videoEditedInfo.estimatedSize = Math.max(1, (long) (size * (videoEditedInfo.estimatedDuration / totalDuration)));
videoEditedInfo.bitrate = 400000;
videoEditedInfo.bitrate = 1000000;
if (videoEditedInfo.startTime > 0) {
videoEditedInfo.startTime *= 1000;
}
@ -718,7 +776,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
private void saveLastCameraBitmap() {
Bitmap bitmap = textureView.getBitmap();
if (bitmap != null && bitmap.getPixel(0, 0) != 0) {
lastBitmap = Bitmap.createScaledBitmap(textureView.getBitmap(), 80, 80, true);
lastBitmap = Bitmap.createScaledBitmap(textureView.getBitmap(), 50, 50, true);
if (lastBitmap != null) {
Utilities.blurBitmap(lastBitmap, 7, 1, lastBitmap.getWidth(), lastBitmap.getHeight(), lastBitmap.getRowBytes());
try {
@ -793,6 +851,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
private void switchCamera() {
saveLastCameraBitmap();
if (lastBitmap != null) {
needDrawFlickerStub = false;
textureOverlayView.setImageBitmap(lastBitmap);
textureOverlayView.setAlpha(1f);
}
@ -859,7 +918,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
Size preview = previewSizes.get(a);
for (int b = pictureSizes.size() - 1; b >= 0; b--) {
Size picture = pictureSizes.get(b);
if (preview.mWidth >= 240 && preview.mHeight >= 240 && preview.mWidth == picture.mWidth && preview.mHeight == picture.mHeight) {
if (preview.mWidth >= 360 && preview.mHeight >= 360 && preview.mWidth == picture.mWidth && preview.mHeight == picture.mHeight) {
previewSize = preview;
pictureSize = picture;
found = true;
@ -981,6 +1040,10 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
return textureView;
}
public void setIsMessageTransition(boolean isMessageTransition) {
this.isMessageTransition = isMessageTransition;
}
public class CameraGLThread extends DispatchQueue {
private final static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
@ -1600,19 +1663,8 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
};
public void startRecording(File outputFile, android.opengl.EGLContext sharedContext) {
int resolution;
int bitrate;
String model = Build.DEVICE;
if (model == null) {
model = "";
}
if (model.startsWith("zeroflte") || model.startsWith("zenlte")) {
resolution = 320;
bitrate = 600000;
} else {
resolution = 240;
bitrate = 400000;
}
int resolution = MessagesController.getInstance(currentAccount).roundVideoSize;
int bitrate = MessagesController.getInstance(currentAccount).roundVideoBitrate * 1024;
videoFile = outputFile;
videoWidth = resolution;
@ -1982,8 +2034,8 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
videoEditedInfo.iv = iv;
videoEditedInfo.estimatedSize = Math.max(1, size);
videoEditedInfo.framerate = 25;
videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 240;
videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 240;
videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 360;
videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360;
videoEditedInfo.originalPath = videoFile.getAbsolutePath();
if (send == 1) {
if (baseFragment.isInScheduleMode()) {
@ -2105,7 +2157,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
audioFormat.setString(MediaFormat.KEY_MIME, AUDIO_MIME_TYPE);
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 32000);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, MessagesController.getInstance(currentAccount).roundAudioBitrate * 1024);
audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 2048 * AudioBufferInfo.MAX_SAMPLES);
audioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
@ -2460,6 +2512,10 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter
}
if (imageReceiver != null) {
canvas.save();
if (imageReceiver.getImageWidth() != textureViewSize) {
float s = textureViewSize / imageReceiver.getImageWidth();
canvas.scale(s, s);
}
canvas.translate(-imageReceiver.getImageX(), -imageReceiver.getImageY());
float oldAlpha = imageReceiver.getAlpha();
imageReceiver.setAlpha(imageProgress);

View File

@ -897,9 +897,9 @@ public class PasscodeView extends FrameLayout {
numbersFrameLayout.setVisibility(INVISIBLE);
}
AndroidUtilities.hideKeyboard(passwordEditText);
AndroidUtilities.cancelRunOnUIThread(checkRunnable);
AndroidUtilities.runOnUIThread(checkRunnable, 100);
}
AndroidUtilities.cancelRunOnUIThread(checkRunnable);
AndroidUtilities.runOnUIThread(checkRunnable, 100);
} else {
AndroidUtilities.cancelRunOnUIThread(checkRunnable);
if (passwordFrameLayout.getVisibility() != VISIBLE) {

View File

@ -0,0 +1,376 @@
package org.telegram.ui.Components;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.BringAppForegroundService;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.browser.Browser;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.PhotoViewer;
import java.util.HashMap;
import java.util.Locale;
public class PhotoViewerWebView extends FrameLayout {
private int currentAccount = UserConfig.selectedAccount;
private PipVideoView pipVideoView;
private WebView webView;
private View progressBarBlackBackground;
private RadialProgressView progressBar;
private View pipItem;
private boolean isYouTube;
private TLRPC.WebPage currentWebpage;
private float playbackSpeed;
private boolean setPlaybackSpeed;
private class YoutubeProxy {
@JavascriptInterface
public void postEvent(final String eventName, final String eventData) {
if ("loaded".equals(eventName)) {
AndroidUtilities.runOnUIThread(() -> {
progressBar.setVisibility(View.INVISIBLE);
progressBarBlackBackground.setVisibility(View.INVISIBLE);
if (setPlaybackSpeed) {
setPlaybackSpeed = false;
setPlaybackSpeed(playbackSpeed);
}
pipItem.setEnabled(true);
pipItem.setAlpha(1.0f);
});
}
}
}
private static final String youtubeFrame = "<!DOCTYPE html><html><head><style>" +
"body { margin: 0; width:100%%; height:100%%; background-color:#000; }" +
"html { width:100%%; height:100%%; background-color:#000; }" +
".embed-container iframe," +
".embed-container object," +
" .embed-container embed {" +
" position: absolute;" +
" top: 0;" +
" left: 0;" +
" width: 100%% !important;" +
" height: 100%% !important;" +
" }" +
" </style></head><body>" +
" <div class=\"embed-container\">" +
" <div id=\"player\"></div>" +
" </div>" +
" <script src=\"https://www.youtube.com/iframe_api\"></script>" +
" <script>" +
" var player;" +
" var posted = false;" +
" YT.ready(function() {" +
" player = new YT.Player(\"player\", {" +
" \"width\" : \"100%%\"," +
" \"events\" : {" +
" \"onReady\" : \"onReady\"," +
" \"onError\" : \"onError\"," +
" \"onStateChange\" : \"onStateChange\"," +
" }," +
" \"videoId\" : \"%1$s\"," +
" \"height\" : \"100%%\"," +
" \"playerVars\" : {" +
" \"start\" : %2$d," +
" \"rel\" : 1," +
" \"showinfo\" : 0," +
" \"modestbranding\" : 0," +
" \"iv_load_policy\" : 3," +
" \"autohide\" : 1," +
" \"autoplay\" : 1," +
" \"cc_load_policy\" : 1," +
" \"playsinline\" : 1," +
" \"controls\" : 1" +
" }" +
" });" +
" player.setSize(window.innerWidth, window.innerHeight);" +
" });" +
" function setPlaybackSpeed(speed) { " +
" player.setPlaybackRate(speed);" +
" }" +
" function onError(event) {" +
" if (!posted) {" +
" if (window.YoutubeProxy !== undefined) {" +
" YoutubeProxy.postEvent(\"loaded\", null); " +
" }" +
" posted = true;" +
" }" +
" }" +
" function onStateChange(event) {" +
" if (event.data == YT.PlayerState.PLAYING && !posted) {" +
" if (window.YoutubeProxy !== undefined) {" +
" YoutubeProxy.postEvent(\"loaded\", null); " +
" }" +
" posted = true;" +
" }" +
" }" +
" function onReady(event) {" +
" player.playVideo();" +
" }" +
" window.onresize = function() {" +
" player.setSize(window.innerWidth, window.innerHeight);" +
" player.playVideo();" +
" }" +
" </script>" +
"</body>" +
"</html>";
@SuppressLint("SetJavaScriptEnabled")
public PhotoViewerWebView(Context context, View pip) {
super(context);
pipItem = pip;
webView = new WebView(context) {
@Override
public boolean onTouchEvent(MotionEvent event) {
processTouch(event);
return super.onTouchEvent(event);
}
};
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
if (Build.VERSION.SDK_INT >= 17) {
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
}
if (Build.VERSION.SDK_INT >= 21) {
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
webView.setWebViewClient(new WebViewClient() {
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!isYouTube || Build.VERSION.SDK_INT < 17) {
progressBar.setVisibility(View.INVISIBLE);
progressBarBlackBackground.setVisibility(View.INVISIBLE);
pipItem.setEnabled(true);
pipItem.setAlpha(1.0f);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isYouTube) {
Browser.openUrl(view.getContext(), url);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT));
progressBarBlackBackground = new View(context) {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBlackBackground(canvas, getMeasuredWidth(), getMeasuredHeight());
}
};
progressBarBlackBackground.setBackgroundColor(0xff000000);
progressBarBlackBackground.setVisibility(View.INVISIBLE);
addView(progressBarBlackBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
progressBar = new RadialProgressView(context);
progressBar.setVisibility(View.INVISIBLE);
addView(progressBar, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
}
private void runJsCode(String code) {
if (Build.VERSION.SDK_INT >= 21) {
webView.evaluateJavascript(code, null);
} else {
try {
webView.loadUrl("javascript:" + code);
} catch (Exception e) {
FileLog.e(e);
}
}
}
protected void processTouch(MotionEvent event) {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (webView.getParent() == this) {
int w = currentWebpage.embed_width != 0 ? currentWebpage.embed_width : 100;
int h = currentWebpage.embed_height != 0 ? currentWebpage.embed_height : 100;
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
float minScale = Math.min(viewWidth / (float) w, viewHeight / (float) h);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) webView.getLayoutParams();
layoutParams.width = (int) (w * minScale);
layoutParams.height = (int) (h * minScale);
layoutParams.topMargin = (viewHeight - layoutParams.height) / 2;
layoutParams.leftMargin = (viewWidth - layoutParams.width) / 2;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void drawBlackBackground(Canvas canvas, int w, int h) {
}
public boolean isLoaded() {
return progressBar.getVisibility() != View.VISIBLE;
}
public PipVideoView openInPip() {
boolean inAppOnly = isYouTube && "inapp".equals(MessagesController.getInstance(currentAccount).youtubePipType);
if (!inAppOnly && !checkInlinePermissions()) {
return null;
}
if (progressBar.getVisibility() == View.VISIBLE) {
return null;
}
boolean animated = false;
pipVideoView = new PipVideoView(inAppOnly);
pipVideoView.show((Activity) getContext(), PhotoViewer.getInstance(), currentWebpage.embed_width != 0 && currentWebpage.embed_height != 0 ? currentWebpage.embed_width / (float) currentWebpage.embed_height : 1.0f, 0, webView);
return pipVideoView;
}
public void setPlaybackSpeed(float speed) {
playbackSpeed = speed;
if (progressBar.getVisibility() != View.VISIBLE) {
if (isYouTube) {
runJsCode("setPlaybackSpeed(" + speed + ");");
}
} else {
setPlaybackSpeed = true;
}
}
@SuppressLint("AddJavascriptInterface")
public void init(int seekTime, TLRPC.WebPage webPage) {
currentWebpage = webPage;
String currentYoutubeId = WebPlayerView.getYouTubeVideoId(webPage.embed_url);
String originalUrl = webPage.url;
requestLayout();
try {
if (currentYoutubeId != null) {
progressBarBlackBackground.setVisibility(View.VISIBLE);
isYouTube = true;
if (Build.VERSION.SDK_INT >= 17) {
webView.addJavascriptInterface(new YoutubeProxy(), "YoutubeProxy");
}
int seekToTime = 0;
if (originalUrl != null) {
try {
Uri uri = Uri.parse(originalUrl);
String t = seekTime > 0 ? "" + seekTime : null;
if (t == null) {
t = uri.getQueryParameter("t");
if (t == null) {
t = uri.getQueryParameter("time_continue");
}
}
if (t != null) {
if (t.contains("m")) {
String[] arg = t.split("m");
seekToTime = Utilities.parseInt(arg[0]) * 60 + Utilities.parseInt(arg[1]);
} else {
seekToTime = Utilities.parseInt(t);
}
}
} catch (Exception e) {
FileLog.e(e);
}
}
webView.loadDataWithBaseURL("https://messenger.telegram.org/", String.format(Locale.US, youtubeFrame, currentYoutubeId, seekToTime), "text/html", "UTF-8", "https://youtube.com");
} else {
HashMap<String, String> args = new HashMap<>();
args.put("Referer", "messenger.telegram.org");
webView.loadUrl(webPage.embed_url, args);
}
} catch (Exception e) {
FileLog.e(e);
}
pipItem.setEnabled(false);
pipItem.setAlpha(0.5f);
progressBar.setVisibility(View.VISIBLE);
if (currentYoutubeId != null) {
progressBarBlackBackground.setVisibility(View.VISIBLE);
}
webView.setVisibility(View.VISIBLE);
webView.setKeepScreenOn(true);
if (currentYoutubeId != null && "disabled".equals(MessagesController.getInstance(currentAccount).youtubePipType)) {
pipItem.setVisibility(View.GONE);
}
}
public boolean checkInlinePermissions() {
if (Build.VERSION.SDK_INT < 23 || Settings.canDrawOverlays(getContext())) {
return true;
} else {
AlertsCreator.createDrawOverlayPermissionDialog((Activity) getContext(), null);
}
return false;
}
public void exitFromPip() {
if (webView == null || pipVideoView == null) {
return;
}
if (ApplicationLoader.mainInterfacePaused) {
try {
getContext().startService(new Intent(ApplicationLoader.applicationContext, BringAppForegroundService.class));
} catch (Throwable e) {
FileLog.e(e);
}
}
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
addView(webView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT));
pipVideoView.close();
pipVideoView = null;
}
public void release() {
webView.stopLoading();
webView.loadUrl("about:blank");
webView.destroy();
}
}

View File

@ -278,6 +278,10 @@ public class PipVideoView {
return show(activity, null, sheet, controls, aspectRatio, rotation, webview);
}
public TextureView show(Activity activity, PhotoViewer viewer, float aspectRatio, int rotation, WebView webview) {
return show(activity, viewer, null, null, aspectRatio, rotation, webview);
}
public TextureView show(Activity activity, PhotoViewer viewer, float aspectRatio, int rotation) {
return show(activity, viewer, null, null, aspectRatio, rotation, null);
}
@ -432,7 +436,6 @@ public class PipVideoView {
return null;
}
return textureView;
}

View File

@ -238,6 +238,7 @@ public class RecyclerAnimationScrollHelper {
}
}
recyclerView.setScrollEnabled(true);
recyclerView.setVerticalScrollBarEnabled(true);
if (BuildVars.DEBUG_VERSION) {

View File

@ -1948,7 +1948,7 @@ public class RecyclerListView extends RecyclerView {
@Override
public boolean onTouchEvent(MotionEvent e) {
if (multiSelectionGesture && e.getAction() != MotionEvent.ACTION_UP && e.getAction() != MotionEvent.ACTION_CANCEL) {
if (multiSelectionGesture && e.getAction() != MotionEvent.ACTION_DOWN &&e.getAction() != MotionEvent.ACTION_UP && e.getAction() != MotionEvent.ACTION_CANCEL) {
if (lastX == Float.MAX_VALUE && lastY == Float.MAX_VALUE) {
lastX = e.getX();
lastY = e.getY();

View File

@ -30,6 +30,7 @@ public class RoundVideoPlayingDrawable extends Drawable {
private int progress2Direction = 1;
private int progress3Direction = 1;
private View parentView;
int alpha = 255;
public RoundVideoPlayingDrawable(View view) {
super();
@ -92,6 +93,9 @@ public class RoundVideoPlayingDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
paint.setColor(Theme.getColor(Theme.key_chat_serviceText));
if (alpha != 255) {
paint.setAlpha((int) (alpha * (paint.getAlpha() / 255f)));
}
int x = getBounds().left;
int y = getBounds().top;
for (int a = 0; a < 3; a++) {
@ -106,7 +110,7 @@ public class RoundVideoPlayingDrawable extends Drawable {
@Override
public void setAlpha(int alpha) {
this.alpha = alpha;
}
@Override

View File

@ -428,9 +428,9 @@ public class ScrollSlidingTextTabStrip extends HorizontalScrollView {
int currentScrollX = getScrollX();
int left = child.getLeft();
int width = child.getMeasuredWidth();
if (left < currentScrollX) {
smoothScrollTo(left, 0);
} else if (left + width > currentScrollX + getWidth()) {
if (left - AndroidUtilities.dp(50) < currentScrollX) {
smoothScrollTo(left - AndroidUtilities.dp(50), 0);
} else if (left + width + AndroidUtilities.dp(21) > currentScrollX + getWidth()) {
smoothScrollTo(left + width, 0);
}
}

View File

@ -488,7 +488,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
@Override
public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) {
if (messageObject == null || mediaPages[0].selectedType != 0 && mediaPages[0].selectedType != 1 && mediaPages[0].selectedType != 5) {
if (messageObject == null || mediaPages[0].selectedType != 0 && mediaPages[0].selectedType != 1 && mediaPages[0].selectedType != 3 && mediaPages[0].selectedType != 5) {
return null;
}
final RecyclerListView listView = mediaPages[0].listView;
@ -544,6 +544,13 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
imageReceiver = cell.getPhotoImage();
cell.getLocationInWindow(coords);
}
} else if (view instanceof SharedLinkCell) {
SharedLinkCell cell = (SharedLinkCell) view;
MessageObject message = cell.getMessage();
if (message != null && message.getId() == messageObject.getId()) {
imageReceiver = cell.getLinkImageView();
cell.getLocationInWindow(coords);
}
}
if (imageReceiver != null) {
PhotoViewer.PlaceProviderObject object = new PhotoViewer.PlaceProviderObject();
@ -3024,7 +3031,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
ArticleViewer.getInstance().open(message);
return;
} else if (webPage.embed_url != null && webPage.embed_url.length() != 0) {
openWebView(webPage);
openWebView(webPage, message);
return;
} else {
link = webPage.url;
@ -3051,8 +3058,8 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
}
}
private void openWebView(TLRPC.WebPage webPage) {
EmbedBottomSheet.show(profileActivity.getParentActivity(), webPage.site_name, webPage.description, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, false);
private void openWebView(TLRPC.WebPage webPage, MessageObject message) {
EmbedBottomSheet.show(profileActivity.getParentActivity(), message, provider, webPage.site_name, webPage.description, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, false);
}
private void recycleAdapter(RecyclerView.Adapter adapter) {
@ -3092,8 +3099,8 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
SharedLinkCell.SharedLinkCellDelegate sharedLinkCellDelegate = new SharedLinkCell.SharedLinkCellDelegate() {
@Override
public void needOpenWebView(TLRPC.WebPage webPage) {
openWebView(webPage);
public void needOpenWebView(TLRPC.WebPage webPage, MessageObject message) {
openWebView(webPage, message);
}
@Override

View File

@ -66,6 +66,11 @@ public class TimerDrawable extends Drawable {
if (timeString.length() < 2) {
timeString += LocaleController.getString("SecretChatTimerDays", R.string.SecretChatTimerDays);
}
} else if (time >= 30 * 60 * 60 * 24 && time <= 60 * 60 * 24 * 31) {
timeString = "" + value / 60 / 60 / 24 / 30;
if (timeString.length() < 2) {
timeString += LocaleController.getString("SecretChatTimerMonths", R.string.SecretChatTimerMonths);
}
} else {
timeString = "" + value / 60 / 60 / 24 / 7;
if (timeString.length() < 2) {
@ -74,11 +79,6 @@ public class TimerDrawable extends Drawable {
timeString = "c";
}
}
/*
<string name="SecretChatTimerDays">d</string>
<string name="SecretChatTimerSeconds">s</string>
<string name="SecretChatTimerMinutes">m</string>
*/
timeWidth = timePaint.measureText(timeString);
try {

View File

@ -167,6 +167,7 @@ public class UndoView extends FrameLayout {
public final static int ACTION_PAYMENT_SUCCESS = 77;
public final static int ACTION_PIN_DIALOGS = 78;
public final static int ACTION_UNPIN_DIALOGS = 79;
public final static int ACTION_EMAIL_COPIED = 80;
public final static int ACTION_NEED_RESATRT = 100;
@ -854,7 +855,7 @@ public class UndoView extends FrameLayout {
currentAction == ACTION_FWD_MESSAGES || currentAction == ACTION_NOTIFY_ON || currentAction == ACTION_NOTIFY_OFF || currentAction == ACTION_USERNAME_COPIED ||
currentAction == ACTION_HASHTAG_COPIED || currentAction == ACTION_TEXT_COPIED || currentAction == ACTION_LINK_COPIED || currentAction == ACTION_PHONE_COPIED ||
currentAction == ACTION_AUTO_DELETE_OFF || currentAction == ACTION_AUTO_DELETE_ON || currentAction == ACTION_GIGAGROUP_CANCEL || currentAction == ACTION_GIGAGROUP_SUCCESS ||
currentAction == ACTION_VOIP_INVITE_LINK_SENT || currentAction == ACTION_PIN_DIALOGS || currentAction == ACTION_UNPIN_DIALOGS || currentAction == ACTION_SHARE_BACKGROUND) {
currentAction == ACTION_VOIP_INVITE_LINK_SENT || currentAction == ACTION_PIN_DIALOGS || currentAction == ACTION_UNPIN_DIALOGS || currentAction == ACTION_SHARE_BACKGROUND || currentAction == ACTION_EMAIL_COPIED) {
undoImageView.setVisibility(GONE);
leftImageView.setVisibility(VISIBLE);
@ -877,7 +878,9 @@ public class UndoView extends FrameLayout {
int ttl = (Integer) infoObject2;
String time;
subinfoTextView.setSingleLine(false);
if (ttl > 24 * 60 * 60) {
if (ttl >= 30 * 24 * 60 * 60) {
time = LocaleController.formatPluralString("Months", ttl / (30 * 24 * 60 * 60));
} else if (ttl > 24 * 60 * 60) {
time = LocaleController.formatPluralString("Days", ttl / (24 * 60 * 60));
} else if (ttl >= 60 * 60) {
time = LocaleController.formatPluralString("Hours", ttl / (60 * 60));
@ -927,9 +930,11 @@ public class UndoView extends FrameLayout {
leftImageView.setAnimation(R.raw.audio_speed, 36, 36);
timeLeft = 3000;
infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
} else if (currentAction == ACTION_MESSAGE_COPIED || currentAction == ACTION_USERNAME_COPIED || currentAction == ACTION_HASHTAG_COPIED || currentAction == ACTION_TEXT_COPIED || currentAction == ACTION_LINK_COPIED || currentAction == ACTION_PHONE_COPIED) {
} else if (currentAction == ACTION_MESSAGE_COPIED || currentAction == ACTION_USERNAME_COPIED || currentAction == ACTION_HASHTAG_COPIED || currentAction == ACTION_TEXT_COPIED || currentAction == ACTION_LINK_COPIED || currentAction == ACTION_PHONE_COPIED || currentAction == ACTION_EMAIL_COPIED) {
int iconRawId = R.raw.copy;
if (currentAction == ACTION_PHONE_COPIED) {
if (currentAction == ACTION_EMAIL_COPIED) {
infoTextView.setText(LocaleController.getString("EmailCopied", R.string.EmailCopied));
} else if (currentAction == ACTION_PHONE_COPIED) {
infoTextView.setText(LocaleController.getString("PhoneCopied", R.string.PhoneCopied));
} else if (currentAction == ACTION_USERNAME_COPIED) {
infoTextView.setText(LocaleController.getString("UsernameCopied", R.string.UsernameCopied));

View File

@ -17,6 +17,9 @@ public class VideoForwardDrawable extends Drawable {
private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private Path path1 = new Path();
private boolean leftSide;
private boolean isRound;
private Path clippingPath;
private int lastClippingPath;
private final static int[] playPath = new int[] {10, 7, 26, 16, 10, 25};
@ -46,7 +49,8 @@ public class VideoForwardDrawable extends Drawable {
void invalidate();
}
public VideoForwardDrawable() {
public VideoForwardDrawable(boolean isRound) {
this.isRound = isRound;
paint.setColor(0xffffffff);
textPaint.setColor(0xffffffff);
textPaint.setTextSize(AndroidUtilities.dp(12));
@ -131,7 +135,21 @@ public class VideoForwardDrawable extends Drawable {
}
canvas.save();
canvas.clipRect(rect.left, rect.top, rect.right, rect.bottom);
if (isRound) {
if (clippingPath == null) {
clippingPath = new Path();
}
int clippingPathHash = rect.left + (rect.top << 8) + (rect.bottom << 16) + (rect.right << 24);
if (lastClippingPath != clippingPathHash) {
clippingPath.reset();
AndroidUtilities.rectTmp.set(rect);
clippingPath.addOval(AndroidUtilities.rectTmp, Path.Direction.CCW);
lastClippingPath = clippingPathHash;
}
canvas.clipPath(clippingPath);
} else {
canvas.clipRect(rect.left, rect.top, rect.right, rect.bottom);
}
if (!isOneShootAnimation) {
paint.setAlpha((int) (80 * enterAnimationProgress));
textPaint.setAlpha((int) (255 * enterAnimationProgress));

View File

@ -54,8 +54,8 @@ public class ViewPagerFixed extends FrameLayout {
int currentPosition;
int nextPosition;
private View viewPages[];
private int viewTypes[];
private View[] viewPages;
private int[] viewTypes;
protected SparseArray<View> viewsByType = new SparseArray<>();
@ -947,11 +947,6 @@ public class ViewPagerFixed extends FrameLayout {
startSmoothScroll(linearSmoothScroller);
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
return super.scrollHorizontallyBy(dx, recycler, state);
}
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(recycler, state, info);

View File

@ -2071,7 +2071,7 @@ public class WebPlayerView extends ViewGroup implements VideoPlayer.VideoPlayerD
return inFullscreen;
}
public String getYouTubeVideoId(String url) {
public static String getYouTubeVideoId(String url) {
Matcher matcher = youtubeIdRegex.matcher(url);
String id = null;
if (matcher.find()) {

View File

@ -1144,6 +1144,7 @@ public class GroupCallRenderersContainer extends FrameLayout {
super.onAnimationEnd(animation);
swipeToBackAnimator = null;
swipeToBackDy = 0;
invalidate();
}
});
swipeToBackAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);

View File

@ -0,0 +1,585 @@
package org.telegram.ui.Components.voip;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Parcelable;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
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.ApplicationLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.voip.VideoCapturerDevice;
import org.telegram.messenger.voip.VoIPService;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.BackDrawable;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.MotionBackgroundDrawable;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.RLottieImageView;
import org.telegram.ui.LaunchActivity;
import org.webrtc.RendererCommon;
import java.io.File;
import java.io.FileOutputStream;
import androidx.core.graphics.ColorUtils;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@TargetApi(21)
public abstract class PrivateVideoPreviewDialog extends FrameLayout implements VoIPService.StateListener {
private boolean isDismissed;
private float outProgress;
private ViewPager viewPager;
private TextView positiveButton;
private ActionBar actionBar;
private LinearLayout titlesLayout;
private RLottieImageView micIconView;
private TextView[] titles;
private VoIPTextureView textureView;
private int currentTexturePage = 1;
private int visibleCameraPage = 1;
private boolean cameraReady;
public boolean micEnabled;
private float pageOffset;
private int currentPage;
private boolean needScreencast;
public PrivateVideoPreviewDialog(Context context, boolean mic, boolean screencast) {
super(context);
needScreencast = screencast;
titles = new TextView[needScreencast ? 3 : 2];
viewPager = new ViewPager(context);
AndroidUtilities.setViewPagerEdgeEffectColor(viewPager, 0x7f000000);
viewPager.setAdapter(new Adapter());
viewPager.setPageMargin(0);
viewPager.setOffscreenPageLimit(1);
addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
private int scrollState = ViewPager.SCROLL_STATE_IDLE;
private int willSetPage;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
currentPage = position;
pageOffset = positionOffset;
updateTitlesLayout();
}
@Override
public void onPageSelected(int i) {
if (scrollState == ViewPager.SCROLL_STATE_IDLE) {
if (i <= (needScreencast ? 1 : 0)) {
currentTexturePage = 1;
} else {
currentTexturePage = 2;
}
onFinishMoveCameraPage();
} else {
if (i <= (needScreencast ? 1 : 0)) {
willSetPage = 1;
} else {
willSetPage = 2;
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
scrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
currentTexturePage = willSetPage;
onFinishMoveCameraPage();
}
}
});
textureView = new VoIPTextureView(context, false, false);
textureView.renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
textureView.scaleType = VoIPTextureView.SCALE_TYPE_FIT;
textureView.clipToTexture = true;
textureView.renderer.setAlpha(0);
textureView.renderer.setRotateTextureWitchScreen(true);
textureView.renderer.setUseCameraRotation(true);
addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
actionBar = new ActionBar(context);
actionBar.setBackButtonDrawable(new BackDrawable(false));
actionBar.setBackgroundColor(Color.TRANSPARENT);
actionBar.setItemsColor(Theme.getColor(Theme.key_voipgroup_actionBarItems), false);
actionBar.setOccupyStatusBar(true);
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1) {
dismiss(false, false);
}
}
});
addView(actionBar);
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 && needScreencast) {
color1 = 0xff77E55C;
color2 = 0xff56C7FE;
color3 = 0;
} else if (a == 0 || a == 1 && needScreencast) {
color1 = 0xff57A4FE;
color2 = 0xff766EE9;
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("VoipShareVideo", R.string.VoipShareVideo));
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 -> {
if (isDismissed) {
return;
}
if (currentPage == 0 && needScreencast) {
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getContext().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
((Activity) getContext()).startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), LaunchActivity.SCREEN_CAPTURE_REQUEST_CODE);
} else {
dismiss(false, true);
}
});
addView(positiveButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 0, 0, 0, 64));
titlesLayout = new LinearLayout(context);
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 && needScreencast) {
titles[a].setText(LocaleController.getString("VoipPhoneScreen", R.string.VoipPhoneScreen));
} else if (a == 0 || a == 1 && needScreencast) {
titles[a].setText(LocaleController.getString("VoipFrontCamera", R.string.VoipFrontCamera));
} else {
titles[a].setText(LocaleController.getString("VoipBackCamera", R.string.VoipBackCamera));
}
int num = a;
titles[a].setOnClickListener(view -> viewPager.setCurrentItem(num, true));
}
setAlpha(0);
setTranslationX(AndroidUtilities.dp(32));
animate().alpha(1f).translationX(0).setDuration(150).start();
setWillNotDraw(false);
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
textureView.renderer.setMirror(service.isFrontFaceCamera());
textureView.renderer.init(VideoCapturerDevice.getEglBase().getEglBaseContext(), new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
}
});
service.setLocalSink(textureView.renderer, false);
}
viewPager.setCurrentItem(needScreencast ? 1 : 0);
if (mic) {
micIconView = new RLottieImageView(context);
micIconView.setPadding(AndroidUtilities.dp(9), AndroidUtilities.dp(9), AndroidUtilities.dp(9), AndroidUtilities.dp(9));
micIconView.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(48), ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.3f))));
RLottieDrawable micIcon = new RLottieDrawable(R.raw.voice_mini, "" + R.raw.voice_mini, AndroidUtilities.dp(24), AndroidUtilities.dp(24), true, null);
micIconView.setAnimation(micIcon);
micIconView.setScaleType(ImageView.ScaleType.FIT_CENTER);
micEnabled = true;
micIcon.setCurrentFrame(micEnabled ? 69 : 36);
micIconView.setOnClickListener(v -> {
micEnabled = !micEnabled;
if (micEnabled) {
micIcon.setCurrentFrame(36);
micIcon.setCustomEndFrame(69);
} else {
micIcon.setCurrentFrame(69);
micIcon.setCustomEndFrame(99);
}
micIcon.start();
});
addView(micIconView, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.BOTTOM, 24, 0, 0, 136));
}
}
public void setBottomPadding(int padding) {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) positiveButton.getLayoutParams();
layoutParams.bottomMargin = AndroidUtilities.dp(64) + padding;
layoutParams = (FrameLayout.LayoutParams) titlesLayout.getLayoutParams();
layoutParams.bottomMargin = padding;
}
private void updateTitlesLayout() {
View current = titles[currentPage];
View next = currentPage < titles.length - 1 ? titles[currentPage + 1] : null;
float cx = getMeasuredWidth() / 2;
float currentCx = current.getLeft() + current.getMeasuredWidth() / 2;
float tx = 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();
if (needScreencast && currentPage == 0 && pageOffset <= 0) {
textureView.setVisibility(INVISIBLE);
} else {
textureView.setVisibility(VISIBLE);
if (currentPage + (needScreencast ? 0 : 1) == currentTexturePage) {
textureView.setTranslationX(-pageOffset * getMeasuredWidth());
} else {
textureView.setTranslationX((1.0f - pageOffset) * getMeasuredWidth());
}
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
service.registerStateListener(this);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
service.unregisterStateListener(this);
}
}
private void onFinishMoveCameraPage() {
VoIPService service = VoIPService.getSharedInstance();
if (currentTexturePage == visibleCameraPage || service == null) {
return;
}
boolean currentFrontface = service.isFrontFaceCamera();
if (currentTexturePage == 1 && !currentFrontface || currentTexturePage == 2 && currentFrontface) {
saveLastCameraBitmap();
cameraReady = false;
VoIPService.getSharedInstance().switchCamera();
textureView.setAlpha(0.0f);
}
visibleCameraPage = currentTexturePage;
}
private void saveLastCameraBitmap() {
if (!cameraReady) {
return;
}
try {
Bitmap bitmap = textureView.renderer.getBitmap();
if (bitmap != null) {
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), textureView.renderer.getMatrix(), true);
bitmap.recycle();
bitmap = newBitmap;
Bitmap lastBitmap = Bitmap.createScaledBitmap(bitmap, 80, (int) (bitmap.getHeight() / (bitmap.getWidth() / 80.0f)), true);
if (lastBitmap != null) {
if (lastBitmap != bitmap) {
bitmap.recycle();
}
Utilities.blurBitmap(lastBitmap, 7, 1, lastBitmap.getWidth(), lastBitmap.getHeight(), lastBitmap.getRowBytes());
File file = new File(ApplicationLoader.getFilesDirFixed(), "cthumb" + visibleCameraPage + ".jpg");
FileOutputStream stream = new FileOutputStream(file);
lastBitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream);
View view = viewPager.findViewWithTag(visibleCameraPage - (needScreencast ? 0 : 1));
if (view instanceof ImageView) {
((ImageView) view).setImageBitmap(lastBitmap);
}
}
}
} catch (Throwable ignore) {
}
}
@Override
public void onCameraFirstFrameAvailable() {
if (!cameraReady) {
cameraReady = true;
textureView.animate().alpha(1f).setDuration(250);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateTitlesLayout();
}
public void dismiss(boolean screencast, boolean apply) {
if (isDismissed) {
return;
}
isDismissed = true;
saveLastCameraBitmap();
onDismiss(screencast, apply);
animate().alpha(0f).translationX(AndroidUtilities.dp(32)).setDuration(150).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (getParent() != null) {
((ViewGroup) getParent()).removeView(PrivateVideoPreviewDialog.this);
}
}
});
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
protected void onDismiss(boolean screencast, boolean apply) {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
boolean isLandscape = MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) positiveButton.getLayoutParams();
if (isLandscape) {
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(80);
} else {
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(16);
}
if (micIconView != null) {
marginLayoutParams = (MarginLayoutParams) micIconView.getLayoutParams();
if (isLandscape) {
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(88);
} else {
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(24);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildWithMargins(titlesLayout, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY), 0);
}
public int getBackgroundColor() {
int color = Theme.getColor(Theme.key_voipgroup_actionBar);
color = ColorUtils.setAlphaComponent(color, (int) (255 * (getAlpha() * (1f - outProgress))));
return color;
}
@Override
public void invalidate() {
super.invalidate();
if (getParent() != null) {
((View) getParent()).invalidate();
}
}
public void update() {
if (VoIPService.getSharedInstance() != null) {
textureView.renderer.setMirror(VoIPService.getSharedInstance().isFrontFaceCamera());
}
}
private class Adapter extends PagerAdapter {
@Override
public int getCount() {
return titles.length;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view;
if (needScreencast && position == 0) {
FrameLayout frameLayout = new FrameLayout(getContext());
frameLayout.setBackground(new MotionBackgroundDrawable(0xff212E3A, 0xff2B5B4D, 0xff245863, 0xff274558, true));
view = frameLayout;
ImageView imageView = new ImageView(getContext());
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setImageResource(R.drawable.screencast_big);
frameLayout.addView(imageView, LayoutHelper.createFrame(82, 82, Gravity.CENTER, 0, 0, 0, 60));
TextView textView = new TextView(getContext());
textView.setText(LocaleController.getString("VoipVideoPrivateScreenSharing", R.string.VoipVideoPrivateScreenSharing));
textView.setGravity(Gravity.CENTER);
textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
textView.setTextColor(0xffffffff);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
frameLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 21, 28, 21, 0));
} else {
ImageView imageView = new ImageView(getContext());
imageView.setImageResource(R.drawable.icplaceholder);
imageView.setTag(position);
Bitmap bitmap = null;
try {
File file = new File(ApplicationLoader.getFilesDirFixed(), "cthumb" + (position == 0 || position == 1 && needScreencast ? 1 : 2) + ".jpg");
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
} catch (Throwable ignore) {
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.icplaceholder);
}
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
view = imageView;
}
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);
}
}
}
}

View File

@ -1,394 +0,0 @@
package org.telegram.ui.Components.voip;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Build;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
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 androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import com.google.android.exoplayer2.C;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.voip.VideoCapturerDevice;
import org.telegram.messenger.voip.VoIPService;
import org.telegram.ui.ActionBar.ActionBar;
import org.telegram.ui.ActionBar.BackDrawable;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.RLottieImageView;
import org.telegram.ui.Components.RecyclerListView;
import org.telegram.ui.GroupCallActivity;
import org.webrtc.RendererCommon;
public abstract class VideoPreviewDialog extends FrameLayout {
VoIPTextureView textureView;
Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
boolean isDismissed;
float outProgress;
FrameLayout container;
View negativeButton;
View positiveButton;
private final LinearLayout buttonsLayout;
private final ActionBar actionBar;
private final RLottieImageView flipIconView;
private final RLottieImageView micIconView;
int flipIconEndFrame;
private final TextView subtitle;
public boolean micEnabled;
CellFlickerDrawable drawable = new CellFlickerDrawable();
public VideoPreviewDialog(@NonNull Context context, RecyclerListView listView, RecyclerListView fullscreenListView) {
super(context);
backgroundPaint.setColor(Theme.getColor(Theme.key_voipgroup_dialogBackground));
actionBar = new ActionBar(context);
actionBar.setBackButtonDrawable(new BackDrawable(false));
actionBar.setBackgroundColor(Color.TRANSPARENT);
actionBar.setItemsColor(Theme.getColor(Theme.key_voipgroup_actionBarItems), false);
actionBar.setTitle(LocaleController.getString("CallVideoPreviewTitle", R.string.CallVideoPreviewTitle));
actionBar.setOccupyStatusBar(false);
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
@Override
public void onItemClick(int id) {
if (id == -1) {
dismiss(false);
}
super.onItemClick(id);
}
});
container = new FrameLayout(context);
container.setClipChildren(false);
addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
container.addView(actionBar);
textureView = new VoIPTextureView(context, false, false);
textureView.renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
textureView.setRoundCorners(AndroidUtilities.dp(8));
if (VoIPService.getSharedInstance() != null) {
textureView.renderer.setMirror(VoIPService.getSharedInstance().isFrontFaceCamera());
}
textureView.scaleType = VoIPTextureView.SCALE_TYPE_FIT;
textureView.clipToTexture = true;
textureView.renderer.setAlpha(0);
textureView.renderer.setRotateTextureWitchScreen(true);
textureView.renderer.setUseCameraRotation(true);
subtitle = new TextView(context);
subtitle.setTextColor(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.4f)));
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
subtitle.setText(LocaleController.getString("VideoPreviewDesrciption", R.string.VideoPreviewDesrciption));
subtitle.setGravity(Gravity.CENTER_HORIZONTAL);
container.addView(subtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 24, 0, 24, 108));
buttonsLayout = new LinearLayout(context);
buttonsLayout.setOrientation(LinearLayout.HORIZONTAL);
TextView negative = new TextView(getContext()) {
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
setAlpha(enabled ? 1.0f : 0.5f);
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(color));
}
};
negative.setMinWidth(AndroidUtilities.dp(64));
negative.setTag(Dialog.BUTTON_POSITIVE);
negative.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
negative.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText));
negative.setGravity(Gravity.CENTER);
negative.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
negative.setText(LocaleController.getString("Cancel", R.string.Cancel));
negative.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_voipgroup_listViewBackground), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.3f))));
negative.setPadding(0, AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12));
negativeButton = negative;
TextView positive = new TextView(getContext()) {
Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Shader gradient = new LinearGradient(0, 0, getMeasuredWidth(), 0, new int[]{Theme.getColor(Theme.key_voipgroup_unmuteButton), Theme.getColor(Theme.key_voipgroup_unmuteButton2)}, null, Shader.TileMode.CLAMP);
gradientPaint.setShader(gradient);
}
@Override
protected void onDraw(Canvas canvas) {
AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), gradientPaint);
super.onDraw(canvas);
}
};
positive.setMinWidth(AndroidUtilities.dp(64));
positive.setTag(Dialog.BUTTON_POSITIVE);
positive.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
positive.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText));
positive.setGravity(Gravity.CENTER);
positive.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
positive.setText(LocaleController.getString("ShareVideo", R.string.ShareVideo));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
positive.setForeground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.3f))));
}
positive.setPadding(0, AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12));
positiveButton = positive;
buttonsLayout.addView(negative, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 1f, 0, 4, 0, 4, 0));
buttonsLayout.addView(positive, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 1f, 0, 4, 0, 4, 0));
addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
container.addView(buttonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
if (VoIPService.getSharedInstance() != null) {
textureView.renderer.init(VideoCapturerDevice.getEglBase().getEglBaseContext(), new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
textureView.animate().alpha(1f).setDuration(250);
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
}
});
VoIPService.getSharedInstance().setLocalSink(textureView.renderer, false);
}
negative.setOnClickListener(view -> {
dismiss(false);
});
positive.setOnClickListener(view -> {
if (isDismissed) {
return;
}
dismiss(true);
});
setAlpha(0);
setTranslationX(AndroidUtilities.dp(32));
animate().alpha(1f).translationX(0).setDuration(150).start();
flipIconView = new RLottieImageView(context);
flipIconView.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10));
flipIconView.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(48), ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.3f))));
RLottieDrawable flipIcon = new RLottieDrawable(R.raw.camera_flip, "" + R.raw.camera_flip, AndroidUtilities.dp(24), AndroidUtilities.dp(24), true, null);
flipIconView.setAnimation(flipIcon);
flipIconView.setScaleType(ImageView.ScaleType.FIT_CENTER);
flipIconView.setOnClickListener(v -> {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().switchCamera();
if (flipIconEndFrame == 18) {
flipIcon.setCustomEndFrame(flipIconEndFrame = 39);
flipIcon.start();
} else {
flipIcon.setCurrentFrame(0, false);
flipIcon.setCustomEndFrame(flipIconEndFrame = 18);
flipIcon.start();
}
}
});
addView(flipIconView, LayoutHelper.createFrame(48, 48));
micIconView = new RLottieImageView(context);
micIconView.setPadding(AndroidUtilities.dp(9), AndroidUtilities.dp(9), AndroidUtilities.dp(9), AndroidUtilities.dp(9));
micIconView.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(48), ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.3f))));
RLottieDrawable micIcon = new RLottieDrawable(R.raw.voice_mini, "" + R.raw.voice_mini, AndroidUtilities.dp(24), AndroidUtilities.dp(24), true, null);
micIconView.setAnimation(micIcon);
micIconView.setScaleType(ImageView.ScaleType.FIT_CENTER);
micEnabled = true;
micIcon.setCurrentFrame(micEnabled ? 69 : 36);
micIconView.setOnClickListener(v -> {
micEnabled = !micEnabled;
if (micEnabled) {
micIcon.setCurrentFrame(36);
micIcon.setCustomEndFrame(69);
} else {
micIcon.setCurrentFrame(69);
micIcon.setCustomEndFrame(99);
}
micIcon.start();
});
addView(micIconView, LayoutHelper.createFrame(48, 48));
setWillNotDraw(false);
}
public void dismiss(boolean apply) {
if (isDismissed) {
return;
}
isDismissed = true;
animate().alpha(0f).translationX(AndroidUtilities.dp(32)).setDuration(150).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (getParent() != null) {
((ViewGroup) getParent()).removeView(VideoPreviewDialog.this);
}
onDismiss(apply);
}
});
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
float x = textureView.getRight() - AndroidUtilities.dp(48 + 12) - textureView.currentClipHorizontal;
float y = textureView.getBottom() - AndroidUtilities.dp(48 + 12) - textureView.currentClipVertical;
flipIconView.setTranslationX(x);
flipIconView.setTranslationY(y);
flipIconView.setScaleX(textureView.getScaleX());
flipIconView.setScaleY(textureView.getScaleY());
flipIconView.setPivotX(getMeasuredWidth() / 2f - x);
flipIconView.setPivotY(getMeasuredHeight() / 2f - y);
flipIconView.setAlpha(textureView.renderer.getAlpha() * (1f - outProgress));
x = textureView.getLeft() + AndroidUtilities.dp(12) + textureView.currentClipHorizontal;
micIconView.setTranslationX(x);
micIconView.setTranslationY(y);
micIconView.setScaleX(textureView.getScaleX());
micIconView.setScaleY(textureView.getScaleY());
micIconView.setPivotX(getMeasuredWidth() / 2f - x);
micIconView.setPivotY(getMeasuredHeight() / 2f - y);
micIconView.setAlpha(textureView.renderer.getAlpha() * (1f - outProgress));
canvas.drawColor(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_actionBar), (int) (255 * (1f - outProgress))));
if (isDismissed || textureView.renderer.getAlpha() != 1f) {
invalidate();
}
if (!textureView.renderer.isFirstFrameRendered() && textureView.renderer.getAlpha() != 1f) {
MarginLayoutParams layoutParams = (MarginLayoutParams) textureView.getLayoutParams();
AndroidUtilities.rectTmp.set(layoutParams.leftMargin, layoutParams.topMargin, getMeasuredWidth() - layoutParams.rightMargin, getMeasuredHeight() - layoutParams.bottomMargin);
float k = !GroupCallActivity.isLandscapeMode ? 9f / 16f : 16f / 9f;
if (AndroidUtilities.rectTmp.width() / AndroidUtilities.rectTmp.height() > k) {
float padding = (AndroidUtilities.rectTmp.width() - AndroidUtilities.rectTmp.height() * k) / 2f;
AndroidUtilities.rectTmp.left += padding;
AndroidUtilities.rectTmp.right -= padding;
} else {
float padding = (AndroidUtilities.rectTmp.height() - AndroidUtilities.rectTmp.width() * k) / 2f;
AndroidUtilities.rectTmp.top += padding;
AndroidUtilities.rectTmp.bottom -= padding;
}
drawable.setParentWidth(getMeasuredWidth());
drawable.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(8));
invalidate();
}
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
protected void onDismiss(boolean apply) {
}
boolean ignoreLayout = false;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
boolean isLandscape = MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec);
ignoreLayout = true;
if (isLandscape) {
actionBar.setTitle(null);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) textureView.getLayoutParams();
marginLayoutParams.topMargin = AndroidUtilities.dp(8);
marginLayoutParams.bottomMargin = AndroidUtilities.dp(76);
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(48);
negativeButton.setVisibility(View.VISIBLE);
subtitle.setVisibility(View.GONE);
marginLayoutParams = (MarginLayoutParams) buttonsLayout.getLayoutParams();
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(80);
marginLayoutParams.bottomMargin = AndroidUtilities.dp(16);
} else {
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) textureView.getLayoutParams();
actionBar.setTitle(LocaleController.getString("CallVideoPreviewTitle", R.string.CallVideoPreviewTitle));
marginLayoutParams.topMargin = ActionBar.getCurrentActionBarHeight() + AndroidUtilities.dp(8);
marginLayoutParams.bottomMargin = AndroidUtilities.dp(168);
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(12);
negativeButton.setVisibility(View.GONE);
subtitle.setVisibility(View.VISIBLE);
marginLayoutParams = (MarginLayoutParams) buttonsLayout.getLayoutParams();
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = marginLayoutParams.bottomMargin = AndroidUtilities.dp(16);
}
ignoreLayout = false;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void requestLayout() {
if (ignoreLayout) {
return;
}
super.requestLayout();
}
public int getBackgroundColor() {
int color = Theme.getColor(Theme.key_voipgroup_actionBar);
color = ColorUtils.setAlphaComponent(color, (int) (255 * (getAlpha() * (1f - outProgress))));
return color;
}
@Override
public void invalidate() {
super.invalidate();
if (getParent() != null) {
((View) getParent()).invalidate();
}
}
public void update() {
if (VoIPService.getSharedInstance() != null) {
textureView.renderer.setMirror(VoIPService.getSharedInstance().isFrontFaceCamera());
}
}
}

View File

@ -67,7 +67,7 @@ public class VoIPFloatingLayout extends FrameLayout {
private boolean floatingMode;
private boolean setedFloatingMode;
private boolean switchingToFloatingMode;
private boolean measuredAsFloatingMode;
public boolean measuredAsFloatingMode;
private float overrideCornerRadius = -1f;
private boolean active = true;
@ -81,9 +81,15 @@ public class VoIPFloatingLayout extends FrameLayout {
Drawable outerShadow;
ValueAnimator switchToFloatingModeAnimator;
private ValueAnimator.AnimatorUpdateListener progressUpdateListener = valueAnimator -> {
toFloatingModeProgress = (float) valueAnimator.getAnimatedValue();
invalidate();
private ValueAnimator.AnimatorUpdateListener progressUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
toFloatingModeProgress = (float) valueAnimator.getAnimatedValue();
if (delegate != null) {
delegate.onChange(toFloatingModeProgress, measuredAsFloatingMode);
}
invalidate();
}
};
ValueAnimator mutedAnimator;
@ -94,6 +100,11 @@ public class VoIPFloatingLayout extends FrameLayout {
OnClickListener tapListener;
private VoIPFloatingLayoutDelegate delegate;
public interface VoIPFloatingLayoutDelegate {
void onChange(float progress, boolean value);
}
public VoIPFloatingLayout(@NonNull Context context) {
super(context);
@ -139,6 +150,9 @@ public class VoIPFloatingLayout extends FrameLayout {
setTranslationY(0);
}
}
if (delegate != null) {
delegate.onChange(toFloatingModeProgress, measuredAsFloatingMode);
}
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
@ -166,6 +180,10 @@ public class VoIPFloatingLayout extends FrameLayout {
return true;
}
public void setDelegate(VoIPFloatingLayoutDelegate voIPFloatingLayoutDelegate) {
delegate = voIPFloatingLayoutDelegate;
}
long startTime;
@Override

View File

@ -448,6 +448,8 @@ public class VoIPPiPView implements VoIPService.StateListener, NotificationCente
callingUserIsVideo = service.getRemoteVideoState() == Instance.VIDEO_STATE_ACTIVE;
currentUserIsVideo = service.getVideoState(false) == Instance.VIDEO_STATE_ACTIVE || service.getVideoState(false) == Instance.VIDEO_STATE_PAUSED;
currentUserTextureView.renderer.setMirror(service.isFrontFaceCamera());
currentUserTextureView.setIsScreencast(service.isScreencast());
currentUserTextureView.setScreenshareMiniProgress(1.0f, false);
}
if (!animated) {

View File

@ -10,6 +10,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.os.Build;
import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
import android.view.TextureView;
@ -18,13 +19,18 @@ import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.MotionBackgroundDrawable;
import org.telegram.ui.GroupCallActivity;
import org.webrtc.RendererCommon;
import org.webrtc.TextureViewRenderer;
@ -39,10 +45,15 @@ public class VoIPTextureView extends FrameLayout {
float roundRadius;
private boolean screencast;
public final TextureViewRenderer renderer;
public TextureView blurRenderer;
public final ImageView imageView;
public View backgroundView;
private FrameLayout screencastView;
private ImageView screencastImage;
private TextView screencastText;
private Bitmap thumb;
public Bitmap cameraLastBitmap;
@ -139,6 +150,25 @@ public class VoIPTextureView extends FrameLayout {
blurRenderer.setOpaque(false);
}
screencastView = new FrameLayout(getContext());
screencastView.setBackground(new MotionBackgroundDrawable(0xff212E3A, 0xff2B5B4D, 0xff245863, 0xff274558, true));
addView(screencastView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
screencastView.setVisibility(GONE);
screencastImage = new ImageView(getContext());
screencastImage.setScaleType(ImageView.ScaleType.CENTER);
screencastImage.setImageResource(R.drawable.screencast_big);
screencastView.addView(screencastImage, LayoutHelper.createFrame(82, 82, Gravity.CENTER, 0, 0, 0, 60));
screencastText = new TextView(getContext());
screencastText.setText(LocaleController.getString("VoipVideoScreenSharing", R.string.VoipVideoScreenSharing));
screencastText.setGravity(Gravity.CENTER);
screencastText.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
screencastText.setTextColor(0xffffffff);
screencastText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
screencastText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
screencastView.addView(screencastText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 21, 28, 21, 0));
if (applyRoundRadius) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setOutlineProvider(new ViewOutlineProvider() {
@ -179,6 +209,40 @@ public class VoIPTextureView extends FrameLayout {
}
}
public void setScreenshareMiniProgress(float progress, boolean value) {
if (!screencast) {
return;
}
float scale = ((View) getParent()).getScaleX();
screencastText.setAlpha(1.0f - progress);
float sc;
if (!value) {
sc = 1.0f / scale - 0.4f / scale * progress;
} else {
sc = 1.0f - 0.4f * progress;
}
screencastImage.setScaleX(sc);
screencastImage.setScaleY(sc);
screencastImage.setTranslationY(AndroidUtilities.dp(60) * progress);
}
public void setIsScreencast(boolean value) {
screencast = value;
screencastView.setVisibility(screencast ? VISIBLE : GONE);
if (screencast) {
renderer.setVisibility(GONE);
if (blurRenderer != null) {
blurRenderer.setVisibility(GONE);
}
imageView.setVisibility(GONE);
} else {
renderer.setVisibility(VISIBLE);
if (blurRenderer != null) {
blurRenderer.setVisibility(VISIBLE);
}
}
}
protected void onFirstFrameRendered() {
VoIPTextureView.this.invalidate();
if (renderer.getAlpha() != 1f) {
@ -233,6 +297,9 @@ public class VoIPTextureView extends FrameLayout {
}
public void setStub(VoIPTextureView from) {
if (screencast) {
return;
}
Bitmap bitmap = from.renderer.getBitmap();
if (bitmap == null || bitmap.getPixel(0, 0) == 0) {
imageView.setImageDrawable(from.imageView.getDrawable());

View File

@ -3247,8 +3247,10 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
fragmentContextView = new FragmentContextView(context, this, false) {
@Override
protected void playbackSpeedChanged(boolean enabled) {
getUndoView().showWithAction(0, enabled ? UndoView.ACTION_PLAYBACK_SPEED_ENABLED : UndoView.ACTION_PLAYBACK_SPEED_DISABLED, null);
protected void playbackSpeedChanged(float value) {
if (Math.abs(value - 1.0f) > 0.001f || Math.abs(value - 1.8f) > 0.001f) {
getUndoView().showWithAction(0, Math.abs(value - 1.0f) > 0.001f ? UndoView.ACTION_PLAYBACK_SPEED_ENABLED : UndoView.ACTION_PLAYBACK_SPEED_DISABLED, value, null, null);
}
}
};
fragmentContextView.setLayoutParams(LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0));
@ -4248,7 +4250,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
if (searchIsShowed) {
AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid);
}
updateVisibleRows(0);
if (viewPages != null) {
viewPages[0].dialogsAdapter.notifyDataSetChanged();
}
}
@Override

View File

@ -1041,7 +1041,7 @@ public class FilteredSearchView extends FrameLayout implements NotificationCente
ArticleViewer.getInstance().open(message);
return;
} else if (webPage.embed_url != null && webPage.embed_url.length() != 0) {
openWebView(webPage);
openWebView(webPage, message);
return;
} else {
link = webPage.url;
@ -1066,8 +1066,8 @@ public class FilteredSearchView extends FrameLayout implements NotificationCente
private final SharedLinkCell.SharedLinkCellDelegate sharedLinkCellDelegate = new SharedLinkCell.SharedLinkCellDelegate() {
@Override
public void needOpenWebView(TLRPC.WebPage webPage) {
openWebView(webPage);
public void needOpenWebView(TLRPC.WebPage webPage, MessageObject message) {
openWebView(webPage, message);
}
@Override
@ -1446,8 +1446,8 @@ public class FilteredSearchView extends FrameLayout implements NotificationCente
}
}
private void openWebView(TLRPC.WebPage webPage) {
EmbedBottomSheet.show(parentActivity, webPage.site_name, webPage.description, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, false);
private void openWebView(TLRPC.WebPage webPage, MessageObject message) {
EmbedBottomSheet.show(parentActivity, message, provider, webPage.site_name, webPage.description, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height, false);
}
int lastAccount;

View File

@ -145,7 +145,7 @@ import org.telegram.ui.Components.voip.GroupCallGridCell;
import org.telegram.ui.Components.voip.GroupCallMiniTextureView;
import org.telegram.ui.Components.voip.GroupCallRenderersContainer;
import org.telegram.ui.Components.voip.GroupCallStatusIcon;
import org.telegram.ui.Components.voip.VideoPreviewDialog;
import org.telegram.ui.Components.voip.PrivateVideoPreviewDialog;
import org.telegram.ui.Components.voip.VoIPToggleButton;
import java.io.File;
@ -229,7 +229,6 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
private int buttonsVisibility;
private TextView speakingMembersSubtitle;
public final ArrayList<ChatObject.VideoParticipant> visibleVideoParticipants = new ArrayList<>();
float progressToHideUi;
@ -248,7 +247,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
private float scrollOffsetY;
VideoPreviewDialog previewDialog;
PrivateVideoPreviewDialog previewDialog;
private TLRPC.Peer selfPeer;
private TLObject userSwitchObject;
@ -1291,6 +1290,9 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
AndroidUtilities.updateVisibleRows(listView);
}
} else if (id == NotificationCenter.groupCallScreencastStateChanged) {
if (previewDialog != null) {
previewDialog.dismiss(true, true);
}
updateItems();
}
}
@ -1368,7 +1370,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
try {
UpdateCallback updateCallback = new UpdateCallback(listAdapter);
setOldRows(listAdapter.addMemberRow, listAdapter.usersStartRow, listAdapter.usersEndRow, listAdapter.invitedStartRow, listAdapter.invitedEndRow, listAdapter.usersVideoGridStartRow, listAdapter.usersVideoGridEndRow, listAdapter.videoGridDividerRow);
setOldRows(listAdapter.addMemberRow, listAdapter.usersStartRow, listAdapter.usersEndRow, listAdapter.invitedStartRow, listAdapter.invitedEndRow, listAdapter.usersVideoGridStartRow, listAdapter.usersVideoGridEndRow, listAdapter.videoGridDividerRow, listAdapter.videoNotAvailableRow);
listAdapter.updateRows();
DiffUtil.calculateDiff(diffUtilsCallback).dispatchUpdatesTo(updateCallback);
} catch (Exception e) {
@ -3030,7 +3032,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
for (int a = 0, N = getChildCount(); a < N; a++) {
View child = getChildAt(a);
ViewHolder holder = findContainingViewHolder(child);
if (holder == null || holder.getItemViewType() == 3 || holder.getItemViewType() == 4 || holder.getItemViewType() == 5) {
if (holder == null || holder.getItemViewType() == 3 || holder.getItemViewType() == 4 || holder.getItemViewType() == 5 || holder.getItemViewType() == 6) {
continue;
}
@ -3855,7 +3857,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
canvas.restore();
if (isLandscapeMode && switchToButtonInt2 == 0) {
paint.setAlpha((int) (255));
paint.setAlpha(255);
float x = scheduleButtonTextView.getX() - getX();
float y = scheduleButtonTextView.getY() - getY();
rect.set(x, y, x + scheduleButtonTextView.getMeasuredWidth(), y + scheduleButtonTextView.getMeasuredHeight());
@ -4990,18 +4992,24 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
if (VoIPService.getSharedInstance().getVideoState(false) != Instance.VIDEO_STATE_ACTIVE) {
undoView[0].hide(false, 1);
if (previewDialog == null) {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().createCaptureDevice(false);
VoIPService voIPService = VoIPService.getSharedInstance();
if (voIPService != null) {
voIPService.createCaptureDevice(false);
}
previewDialog = new VideoPreviewDialog(context, listView, fullscreenUsersListView) {
previewDialog = new PrivateVideoPreviewDialog(context, true, VoIPService.getSharedInstance().getVideoState(true) != Instance.VIDEO_STATE_ACTIVE) {
@Override
public void onDismiss(boolean apply) {
public void onDismiss(boolean screencast, boolean apply) {
boolean showMicIcon = previewDialog.micEnabled;
previewDialog = null;
VoIPService service = VoIPService.getSharedInstance();
if (apply) {
if (service != null) {
service.setupCaptureDevice(false, showMicIcon);
service.setupCaptureDevice(screencast, showMicIcon);
}
if (screencast) {
if (service != null) {
service.setVideoState(false, Instance.VIDEO_STATE_INACTIVE);
}
}
updateState(true, false);
call.sortParticipants();
@ -5009,14 +5017,14 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
buttonsContainer.requestLayout();
} else {
if (service != null) {
VoIPService.getSharedInstance().setVideoState(false, Instance.VIDEO_STATE_INACTIVE);
service.setVideoState(false, Instance.VIDEO_STATE_INACTIVE);
}
}
}
};
containerView.addView(previewDialog);
if (!VoIPService.getSharedInstance().isFrontFaceCamera()) {
VoIPService.getSharedInstance().switchCamera();
container.addView(previewDialog);
if (voIPService != null && !voIPService.isFrontFaceCamera()) {
voIPService.switchCamera();
}
}
} else {
@ -5775,6 +5783,8 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
}
}
ObjectAnimator subtitleYAnimator;
private void updateLayout(boolean animated) {
float minY = Integer.MAX_VALUE;
int N = listView.getChildCount();
@ -5811,11 +5821,22 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
.setInterpolator(CubicBezierInterpolator.DEFAULT)
.start();
actionBar.getSubtitleTextView().animate()
.translationY(show ? 0.0f : AndroidUtilities.dp(20))
.setDuration(300)
.setInterpolator(CubicBezierInterpolator.DEFAULT)
.start();
if (subtitleYAnimator != null) {
subtitleYAnimator.removeAllListeners();
subtitleYAnimator.cancel();
}
subtitleYAnimator = ObjectAnimator.ofFloat(actionBar.getSubtitleTextView(), View.TRANSLATION_Y, actionBar.getSubtitleTextView().getTranslationY(), show ? 0.0f : AndroidUtilities.dp(20));
subtitleYAnimator.setDuration(300);
subtitleYAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
subtitleYAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
subtitleYAnimator = null;
actionBar.getSubtitleTextView().setTranslationY(show ? 0.0f : AndroidUtilities.dp(20));
}
});
subtitleYAnimator.start();
actionBar.getAdditionalSubtitleTextView().animate()
.translationY(show ? 0.0f : AndroidUtilities.dp(20))
@ -7349,7 +7370,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
private int usersVideoGridStartRow;
private int usersVideoGridEndRow;
private int videoGridDividerRow;
private int videoCount;
private int videoNotAvailableRow;
private boolean hasSelfUser;
@ -7380,13 +7401,18 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
rowsCount += visibleVideoParticipants.size();
usersVideoGridEndRow = rowsCount;
videoCount = visibleVideoParticipants.size();
int videoCount = visibleVideoParticipants.size();
if (videoCount > 0) {
videoGridDividerRow = rowsCount++;
} else {
videoGridDividerRow = -1;
}
if (!visibleVideoParticipants.isEmpty() && ChatObject.canManageCalls(currentChat) && call.call.participants_count > accountInstance.getMessagesController().groipCallVideoMaxParticipants) {
videoNotAvailableRow = rowsCount++;
} else {
videoNotAvailableRow = -1;
}
usersStartRow = rowsCount;
rowsCount += call.visibleParticipants.size();
usersEndRow = rowsCount;
@ -7591,7 +7617,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
int type = holder.getItemViewType();
if (type == 1) {
return true;
} else if (type == 3 || type == 4 || type == 5) {
} else if (type == 3 || type == 4 || type == 5 || type == 6) {
return false;
}
return true;
@ -7670,6 +7696,15 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
}
};
break;
case 6:
TextView textView = new TextView(mContext);
textView.setTextColor(0xff7B8389);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
textView.setGravity(Gravity.CENTER_HORIZONTAL);
textView.setPadding(0, 0, 0, AndroidUtilities.dp(10));
textView.setText(LocaleController.formatString("VoipVideoNotAvailableAdmin", R.string.VoipVideoNotAvailableAdmin, LocaleController.formatPluralString("Members", accountInstance.getMessagesController().groipCallVideoMaxParticipants)));
view = textView;
break;
case 3:
default:
view = new View(mContext);
@ -7684,18 +7719,16 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
public int getItemViewType(int position) {
if (position == lastRow) {
return 3;
}
if (position == addMemberRow) {
} else if (position == addMemberRow) {
return 0;
}
if (position == videoGridDividerRow) {
} else if (position == videoGridDividerRow) {
return 5;
}
if (position >= usersStartRow && position < usersEndRow) {
} else if (position >= usersStartRow && position < usersEndRow) {
return 1;
}
if (position >= usersVideoGridStartRow && position < usersVideoGridEndRow) {
} else if (position >= usersVideoGridStartRow && position < usersVideoGridEndRow) {
return 4;
} else if (position == videoNotAvailableRow) {
return 6;
}
return 2;
}
@ -7723,8 +7756,9 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
private int oldUsersVideoStartRow;
private int oldUsersVideoEndRow;
private int oldVideoDividerRow;
private int oldVideoNotAvailableRow;
public void setOldRows(int addMemberRow, int usersStartRow, int usersEndRow, int invitedStartRow, int invitedEndRow, int usersVideoStartRow, int usersVideoEndRow, int videoDividerRow) {
public void setOldRows(int addMemberRow, int usersStartRow, int usersEndRow, int invitedStartRow, int invitedEndRow, int usersVideoStartRow, int usersVideoEndRow, int videoDividerRow, int videoNotAvailableRow) {
oldAddMemberRow = addMemberRow;
oldUsersStartRow = usersStartRow;
oldUsersEndRow = usersEndRow;
@ -7733,6 +7767,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
oldUsersVideoStartRow = usersVideoStartRow;
oldUsersVideoEndRow = usersVideoEndRow;
oldVideoDividerRow = videoDividerRow;
oldVideoNotAvailableRow = videoNotAvailableRow;
}
private DiffUtil.Callback diffUtilsCallback = new DiffUtil.Callback() {
@ -7757,6 +7792,14 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
return false;
}
}
if (listAdapter.videoNotAvailableRow >= 0) {
if (oldItemPosition == oldVideoNotAvailableRow && newItemPosition == listAdapter.videoNotAvailableRow) {
return true;
} else if (oldItemPosition == oldVideoNotAvailableRow && newItemPosition != listAdapter.videoNotAvailableRow ||
oldItemPosition != oldVideoNotAvailableRow && newItemPosition == listAdapter.videoNotAvailableRow) {
return false;
}
}
if (listAdapter.videoGridDividerRow >= 0 && listAdapter.videoGridDividerRow == newItemPosition && oldItemPosition == oldVideoDividerRow) {
return true;
}
@ -7839,7 +7882,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter
@Override
public void onBackPressed() {
if (previewDialog != null) {
previewDialog.dismiss(false);
previewDialog.dismiss(false, false);
return;
}
if (avatarsPreviewShowed) {

View File

@ -266,12 +266,6 @@ public class IntroActivity extends Activity implements NotificationCenter.Notifi
destroyed = true;
finish();
});
if (BuildVars.DEBUG_PRIVATE_VERSION) {
startMessagingButton.setOnLongClickListener(v -> {
ConnectionsManager.getInstance(currentAccount).switchBackend();
return true;
});
}
bottomPages = new BottomPagesView(this, viewPager, 6);
frameLayout.addView(bottomPages, LayoutHelper.createFrame(66, 5, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 350, 0, 0));

View File

@ -1851,17 +1851,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
}
}
}
if (messageId != null && segments.contains("video")) {
String str = data.getQuery();
DateFormat dateFormat = new SimpleDateFormat("mm:ss");
Date reference = null;
try {
reference = dateFormat.parse("00:00");
Date date = dateFormat.parse(str);
videoTimestamp = (int) ((date.getTime() - reference.getTime()) / 1000L);
} catch (ParseException e) {
e.printStackTrace();
}
if (messageId != null) {
videoTimestamp = getTimestampFromLink(data);
}
botUser = data.getQueryParameter("start");
botChat = data.getQueryParameter("startgroup");
@ -2608,6 +2599,36 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
return pushOpened;
}
public static int getTimestampFromLink(Uri data) {
List<String> segments = data.getPathSegments();
String timestampStr = null;
if (segments.contains("video")) {
timestampStr = data.getQuery();
} else if (data.getQueryParameter("t") != null) {
timestampStr = data.getQueryParameter("t");
}
int videoTimestamp = -1;
if (timestampStr != null) {
try {
videoTimestamp = Integer.parseInt(timestampStr);
} catch (Throwable ignore) {
}
if (videoTimestamp == - 1) {
DateFormat dateFormat = new SimpleDateFormat("mm:ss");
Date reference = null;
try {
reference = dateFormat.parse("00:00");
Date date = dateFormat.parse(timestampStr);
videoTimestamp = (int) ((date.getTime() - reference.getTime()) / 1000L);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
return videoTimestamp;
}
private void openDialogsToSend(boolean animated) {
Bundle args = new Bundle();
args.putBoolean("onlySelect", true);
@ -4007,7 +4028,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
if (requestCode == SCREEN_CAPTURE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
VoIPService service = VoIPService.getSharedInstance();
if (service != null && service.groupCall != null) {
if (service != null) {
VideoCapturerDevice.mediaProjectionPermissionResultData = data;
service.createCaptureDevice(true);
}

View File

@ -157,6 +157,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
private boolean checkShowPermissions = true;
private boolean newAccount;
private boolean syncContacts;
private boolean testBackend = false;
private int scrollHeight;
@ -1686,6 +1687,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
private CheckBoxCell syncCell;
private CheckBoxCell infoCell;
private CheckBoxCell testBackendCheckBox;
private int countryState = 0;
@ -2010,6 +2012,20 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
}
allowFlashCall = simcardAvailable && allowCall && allowCancelCall && allowReadCallLog;
if (BuildVars.DEBUG_PRIVATE_VERSION) {
testBackendCheckBox = new CheckBoxCell(context, 2);
testBackendCheckBox.setText("Test Backend", "", testBackend, false);
addView(testBackendCheckBox, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0));
testBackendCheckBox.setOnClickListener(v -> {
if (getParentActivity() == null) {
return;
}
CheckBoxCell cell = (CheckBoxCell) v;
testBackend = !testBackend;
cell.setChecked(testBackend, true);
});
}
HashMap<String, String> languageMap = new HashMap<>();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(getResources().getAssets().open("countries.txt")));
@ -2138,6 +2154,11 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
return;
}
String phone = PhoneFormat.stripExceptNumbers("" + codeField.getText() + phoneField.getText());
boolean isTestBakcend = BuildVars.DEBUG_PRIVATE_VERSION && getConnectionsManager().isTestBackend();
if (isTestBakcend != testBackend) {
getConnectionsManager().switchBackend(false);
isTestBakcend = testBackend;
}
if (getParentActivity() instanceof LaunchActivity) {
for (int a : SharedConfig.activeAccounts) {
UserConfig userConfig = UserConfig.getInstance(a);
@ -2145,7 +2166,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
continue;
}
String userPhone = userConfig.getCurrentUser().phone;
if (PhoneNumberUtils.compare(phone, userPhone) && ConnectionsManager.native_isTestBackend(currentAccount) == ConnectionsManager.native_isTestBackend(a)) {
if (PhoneNumberUtils.compare(phone, userPhone) && ConnectionsManager.getInstance(a).isTestBackend() == isTestBakcend) {
final int num = a;
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("NekogramWithEmoji", R.string.NekoX));

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