Add push-to-talk with global shortcut on Windows.

This commit is contained in:
John Preston 2020-12-03 21:17:15 +03:00
parent f4dfd738ec
commit d41e93fb1c
12 changed files with 217 additions and 21 deletions

View File

@ -1836,6 +1836,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_new_muted" = "Mute new members";
"lng_group_call_speakers" = "Speakers";
"lng_group_call_microphone" = "Microphone";
"lng_group_call_push_to_talk" = "Push to Talk";
"lng_group_call_ptt_shortcut" = "Edit Shortcut";
"lng_group_call_ptt_recording" = "Stop Recording";
"lng_group_call_share" = "Share Invite Link";
"lng_group_call_end" = "End Voice Chat";
"lng_group_call_join" = "Join";

View File

@ -641,20 +641,22 @@ groupCallCheckbox: Checkbox(defaultBoxCheckbox) {
rippleBgActive: groupCallMembersBgRipple;
}
groupCallSettingsButton: SettingsButton {
groupCallSettingsToggle: Toggle(defaultToggle) {
toggledBg: groupCallMembersBg;
toggledFg: groupCallActiveFg;
untoggledBg: groupCallMembersBg;
untoggledFg: groupCallMemberNotJoinedStatus;
}
groupCallSettingsButton: SettingsButton(defaultSettingsButton) {
textFg: groupCallMembersFg;
textFgOver: groupCallMembersFg;
textBg: groupCallMembersBg;
textBgOver: groupCallMembersBgOver;
font: boxTextFont;
rightLabel: FlatLabel(defaultSettingsRightLabel) {
textFg: groupCallActiveFg;
}
height: 20px;
padding: margins(22px, 10px, 22px, 8px);
toggle: groupCallSettingsToggle;
toggleOver: groupCallSettingsToggle;
ripple: groupCallRipple;
}
groupCallSettingsAttentionButton: SettingsButton(groupCallSettingsButton) {

View File

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_group_call.h"
#include "data/data_session.h"
#include "base/platform/base_platform_global_shortcuts.h"
#include <tgcalls/group/GroupInstanceImpl.h>
@ -92,6 +93,11 @@ void GroupCall::setState(State state) {
}
_state = state;
if (_state.current() == State::Joined && !_pushToTalkStarted) {
_pushToTalkStarted = true;
applyGlobalShortcutChanges();
}
if (false
|| state == State::Ended
|| state == State::Failed) {
@ -706,6 +712,57 @@ std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
return result;
}
auto GroupCall::ensureGlobalShortcutManager()
-> std::shared_ptr<GlobalShortcutManager> {
if (!_shortcutManager) {
_shortcutManager = base::Platform::CreateGlobalShortcutManager();
}
return _shortcutManager;
}
void GroupCall::applyGlobalShortcutChanges() {
auto &settings = Core::App().settings();
if (!settings.groupCallPushToTalk()) {
_shortcutManager = nullptr;
_pushToTalk = nullptr;
return;
} else if (settings.groupCallPushToTalkShortcut().isEmpty()) {
settings.setGroupCallPushToTalk(false);
Core::App().saveSettingsDelayed();
_shortcutManager = nullptr;
_pushToTalk = nullptr;
return;
}
ensureGlobalShortcutManager();
if (!_shortcutManager) {
settings.setGroupCallPushToTalk(false);
Core::App().saveSettingsDelayed();
_pushToTalk = nullptr;
return;
}
const auto shortcut = _shortcutManager->shortcutFromSerialized(
settings.groupCallPushToTalkShortcut());
if (!shortcut) {
settings.setGroupCallPushToTalkShortcut(QByteArray());
settings.setGroupCallPushToTalk(false);
Core::App().saveSettingsDelayed();
_shortcutManager = nullptr;
_pushToTalk = nullptr;
return;
}
if (_pushToTalk) {
if (shortcut->serialize() == _pushToTalk->serialize()) {
return;
}
_shortcutManager->stopWatching(_pushToTalk);
}
_pushToTalk = shortcut;
_shortcutManager->startWatching(_pushToTalk, [=](bool pressed) {
if (_muted.current() != MuteState::ForceMuted) {
setMuted(pressed ? MuteState::Active : MuteState::Muted);
}
});
}
//void GroupCall::setAudioVolume(bool input, float level) {
// if (_instance) {
// if (input) {

View File

@ -19,6 +19,13 @@ namespace tgcalls {
class GroupInstanceImpl;
} // namespace tgcalls
namespace base {
namespace Platform {
class GlobalShortcutManager;
class GlobalShortcutValue;
} // namespace Platform
} // namespace base
namespace Calls {
enum class MuteState {
@ -44,6 +51,8 @@ public:
};
using GlobalShortcutManager = base::Platform::GlobalShortcutManager;
GroupCall(
not_null<Delegate*> delegate,
not_null<ChannelData*> channel,
@ -102,11 +111,16 @@ public:
std::variant<int, not_null<UserData*>> inviteUsers(
const std::vector<not_null<UserData*>> &users);
std::shared_ptr<GlobalShortcutManager> ensureGlobalShortcutManager();
void applyGlobalShortcutChanges();
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
private:
using GlobalShortcutValue = base::Platform::GlobalShortcutValue;
enum class FinishType {
None,
Ended,
@ -160,6 +174,10 @@ private:
crl::time _lastSendProgressUpdate = 0;
std::shared_ptr<GlobalShortcutManager> _shortcutManager;
std::shared_ptr<GlobalShortcutValue> _pushToTalk;
bool _pushToTalkStarted = false;
rpl::lifetime _lifetime;
};

View File

@ -10,10 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_call.h"
#include "calls/calls_group_panel.h" // LeaveGroupCallBox.
#include "calls/calls_instance.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/level_meter.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/toast/toast.h"
#include "lang/lang_keys.h"
#include "base/platform/base_platform_global_shortcuts.h"
#include "data/data_channel.h"
#include "data/data_group_call.h"
#include "core/application.h"
@ -90,12 +92,10 @@ void GroupCallSettingsBox(
AddSkip(layout);
}
const auto muteJoined = addCheck
? box->addRow(object_ptr<Ui::Checkbox>(
box.get(),
? AddButton(
layout,
tr::lng_group_call_new_muted(),
joinMuted,
st::groupCallCheckbox,
st::groupCallCheck))
st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted))
: nullptr;
if (addCheck) {
AddSkip(layout);
@ -155,6 +155,97 @@ void GroupCallSettingsBox(
});
AddSkip(layout);
//AddDivider(layout);
//AddSkip(layout);
using namespace base::Platform;
struct PushToTalkState {
rpl::variable<QString> recordText = tr::lng_group_call_ptt_shortcut();
rpl::variable<QString> shortcutText;
GlobalShortcut shortcut;
bool recording = false;
};
const auto manager = call->ensureGlobalShortcutManager();
if (manager) {
const auto state = box->lifetime().make_state<PushToTalkState>();
state->shortcut = manager->shortcutFromSerialized(
settings.groupCallPushToTalkShortcut());
state->shortcutText = state->shortcut
? state->shortcut->toDisplayString()
: QString();
const auto pushToTalk = AddButton(
layout,
tr::lng_group_call_push_to_talk(),
st::groupCallSettingsButton
)->toggleOn(rpl::single(settings.groupCallPushToTalk()));
const auto recordingWrap = layout->add(
object_ptr<Ui::SlideWrap<Button>>(
layout,
object_ptr<Button>(
layout,
state->recordText.value(),
st::groupCallSettingsButton)));
const auto recording = recordingWrap->entity();
CreateRightLabel(
recording,
state->shortcutText.value(),
st::groupCallSettingsButton,
state->recordText.value());
const auto startRecording = [=] {
state->recording = true;
state->recordText = tr::lng_group_call_ptt_recording();
manager->startRecording([=](GlobalShortcut shortcut) {
state->shortcutText = shortcut->toDisplayString();
}, [=](GlobalShortcut shortcut) {
state->recording = false;
state->shortcut = shortcut;
state->shortcutText = shortcut
? shortcut->toDisplayString()
: QString();
state->recordText = tr::lng_group_call_ptt_shortcut();
Core::App().settings().setGroupCallPushToTalkShortcut(shortcut
? shortcut->serialize()
: QByteArray());
Core::App().saveSettingsDelayed();
});
};
const auto stopRecording = [=] {
state->recording = false;
state->recordText = tr::lng_group_call_ptt_shortcut();
state->shortcutText = state->shortcut
? state->shortcut->toDisplayString()
: QString();
manager->stopRecording();
};
recording->addClickHandler([=] {
if (state->recording) {
stopRecording();
} else {
startRecording();
}
});
recordingWrap->toggle(
settings.groupCallPushToTalk(),
anim::type::instant);
pushToTalk->toggledChanges(
) | rpl::start_with_next([=](bool toggled) {
if (!toggled) {
stopRecording();
}
Core::App().settings().setGroupCallPushToTalk(toggled);
Core::App().saveSettingsDelayed();
recordingWrap->toggle(toggled, anim::type::normal);
}, pushToTalk->lifetime());
box->boxClosing(
) | rpl::start_with_next([=] {
call->applyGlobalShortcutChanges();
}, box->lifetime());
}
AddSkip(layout);
//AddDivider(layout);
//AddSkip(layout);
const auto lookupLink = [=] {
return channel->hasUsername()
@ -226,8 +317,8 @@ void GroupCallSettingsBox(
) | rpl::start_with_next([=] {
if (canChangeJoinMuted
&& muteJoined
&& muteJoined->checked() != joinMuted) {
SaveCallJoinMuted(channel, id, muteJoined->checked());
&& muteJoined->toggled() != joinMuted) {
SaveCallJoinMuted(channel, id, muteJoined->toggled());
}
}, box->lifetime());
box->addButton(tr::lng_box_done(), [=] {

View File

@ -109,7 +109,9 @@ QByteArray Settings::serialize() const {
<< qint32(_nativeWindowFrame.current() ? 1 : 0)
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
<< _callVideoInputDeviceId
<< qint32(_ipRevealWarning ? 1 : 0);
<< qint32(_ipRevealWarning ? 1 : 0)
<< qint32(_groupCallPushToTalk ? 1 : 0)
<< _groupCallPushToTalkShortcut;
}
return result;
}
@ -177,6 +179,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 nativeWindowFrame = _nativeWindowFrame.current() ? 1 : 0;
qint32 systemDarkModeEnabled = _systemDarkModeEnabled.current() ? 1 : 0;
qint32 ipRevealWarning = _ipRevealWarning ? 1 : 0;
qint32 groupCallPushToTalk = _groupCallPushToTalk ? 1 : 0;
QByteArray groupCallPushToTalkShortcut = _groupCallPushToTalkShortcut;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@ -263,6 +267,11 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> ipRevealWarning;
}
if (!stream.atEnd()) {
stream
>> groupCallPushToTalk
>> groupCallPushToTalkShortcut;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@ -354,6 +363,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
_notifyFromAll = (notifyFromAll == 1);
_nativeWindowFrame = (nativeWindowFrame == 1);
_systemDarkModeEnabled = (systemDarkModeEnabled == 1);
_groupCallPushToTalk = (groupCallPushToTalk == 1);
_groupCallPushToTalkShortcut = groupCallPushToTalkShortcut;
}
bool Settings::chatWide() const {

View File

@ -217,6 +217,18 @@ public:
void setCallAudioDuckingEnabled(bool value) {
_callAudioDuckingEnabled = value;
}
[[nodiscard]] bool groupCallPushToTalk() const {
return _groupCallPushToTalk;
}
void setGroupCallPushToTalk(bool value) {
_groupCallPushToTalk = value;
}
[[nodiscard]] QByteArray groupCallPushToTalkShortcut() const {
return _groupCallPushToTalkShortcut;
}
void setGroupCallPushToTalkShortcut(const QByteArray &serialized) {
_groupCallPushToTalkShortcut = serialized;
}
[[nodiscard]] Window::Theme::AccentColors &themesAccentColors() {
return _themesAccentColors;
}
@ -513,6 +525,8 @@ private:
int _callOutputVolume = 100;
int _callInputVolume = 100;
bool _callAudioDuckingEnabled = true;
bool _groupCallPushToTalk = false;
QByteArray _groupCallPushToTalkShortcut;
Window::Theme::AccentColors _themesAccentColors;
bool _lastSeenWarningSeen = false;
Ui::SendFilesWay _sendFilesWay;

@ -1 +1 @@
Subproject commit 3562a43685fdac00c277292ce2c83d92132cc319
Subproject commit 7d1df24be11eb6b3e6382735b73418b4a282bad4

@ -1 +1 @@
Subproject commit 8aede3acc9d386484d2774316e9d1a3d0c265dd5
Subproject commit 1b540b38ed78e9a3cba93e9ba4ce4525ab692277

@ -1 +1 @@
Subproject commit 6fc06b5f9645005143f09f72e1c052a28d5f26ed
Subproject commit cbe51722b73cfa9ff27bd59294b08aa5ee33c936

@ -1 +1 @@
Subproject commit ab4ad89c4c709b2ec0f8296451d49c99d2ae4372
Subproject commit 5f44304a305f9d02823e2d7ded9aadd59463e5a5

@ -1 +1 @@
Subproject commit 52d52cad4e554dac1907224372d51fd40b9da92f
Subproject commit 0ed2a6cc048e30ee8bacf7212f3f12f4f7ae2b5a