Use app config value for QR login.

This commit is contained in:
John Preston 2019-11-27 12:45:23 +03:00
parent 55f83129b7
commit e6c86b19db
25 changed files with 191 additions and 91 deletions

View file

@ -186,6 +186,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_phone_title" = "Your Phone Number";
"lng_phone_desc" = "Please confirm your country code and\nenter your mobile phone number.";
"lng_phone_to_qr" = "Quick log in using QR code";
"lng_country_code" = "Country Code";
"lng_bad_country_code" = "Invalid Country Code";
"lng_country_ph" = "Search";

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/emoji_config.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "mainwindow.h" // App::wnd()->sessionController.
#include "window/window_session_controller.h" // isGifPausedAtLeastFor.
@ -31,7 +32,9 @@ namespace HistoryView {
namespace {
double GetEmojiStickerZoom(not_null<Main::Session*> session) {
return session->appConfig().get<double>("emojies_animated_zoom", 0.625);
return session->account().appConfig().get<double>(
"emojies_animated_zoom",
0.625);
}
} // namespace

View file

@ -107,6 +107,7 @@ introPhone: InputField(introCountry) {
textMargins: margins(12px, 27px, 12px, 6px);
width: 225px;
}
introQrLoginLinkTop: 368px;
introCode: introCountry;
introName: introCountry;
introPassword: introCountry;

View file

@ -5,11 +5,11 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/introcode.h"
#include "intro/intro_code.h"
#include "lang/lang_keys.h"
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "intro/intro_signup.h"
#include "intro/intro_password_check.h"
#include "core/update_checker.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
@ -355,7 +355,7 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
getData()->hasRecovery = d.is_has_recovery();
getData()->pwdHint = qs(d.vhint().value_or_empty());
getData()->pwdNotEmptyPassport = d.is_has_secure_values();
goReplace<PwdCheckWidget>();
goReplace<PasswordCheckWidget>();
}
void CodeWidget::submit() {

View file

@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "intro/intro_step.h"
#include "intro/intro_widget.h"
#include "ui/widgets/input_fields.h"
#include "intro/introwidget.h"
#include "base/timer.h"
namespace Ui {

View file

@ -5,14 +5,14 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/intropwdcheck.h"
#include "intro/intro_password_check.h"
#include "intro/introwidget.h"
#include "intro/intro_widget.h"
#include "core/file_utilities.h"
#include "core/core_cloud_password.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "intro/introsignup.h"
#include "intro/intro_signup.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Intro {
namespace details {
PwdCheckWidget::PwdCheckWidget(
PasswordCheckWidget::PasswordCheckWidget(
QWidget *parent,
not_null<Main::Account*> account,
not_null<Data*> data)
@ -63,7 +63,7 @@ PwdCheckWidget::PwdCheckWidget(
setMouseTracking(true);
}
void PwdCheckWidget::refreshLang() {
void PasswordCheckWidget::refreshLang() {
if (_toRecover) {
_toRecover->setText(tr::lng_signin_recover(tr::now));
}
@ -78,16 +78,16 @@ void PwdCheckWidget::refreshLang() {
updateControlsGeometry();
}
int PwdCheckWidget::errorTop() const {
int PasswordCheckWidget::errorTop() const {
return contentTop() + st::introErrorBelowLinkTop;
}
void PwdCheckWidget::resizeEvent(QResizeEvent *e) {
void PasswordCheckWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
updateControlsGeometry();
}
void PwdCheckWidget::updateControlsGeometry() {
void PasswordCheckWidget::updateControlsGeometry() {
_pwdField->moveToLeft(contentLeft(), contentTop() + st::introPasswordTop);
_pwdHint->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introPasswordHintTop);
_codeField->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
@ -96,7 +96,7 @@ void PwdCheckWidget::updateControlsGeometry() {
_toPassword->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
}
void PwdCheckWidget::setInnerFocus() {
void PasswordCheckWidget::setInnerFocus() {
if (_pwdField->isHidden()) {
_codeField->setFocusFast();
} else {
@ -104,7 +104,7 @@ void PwdCheckWidget::setInnerFocus() {
}
}
void PwdCheckWidget::activate() {
void PasswordCheckWidget::activate() {
if (_pwdField->isHidden() && _codeField->isHidden()) {
Step::activate();
_pwdField->show();
@ -114,11 +114,11 @@ void PwdCheckWidget::activate() {
setInnerFocus();
}
void PwdCheckWidget::cancelled() {
void PasswordCheckWidget::cancelled() {
_api.request(base::take(_sentRequest)).cancel();
}
void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
void PasswordCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
_sentRequest = 0;
if (recover) {
cSetPasswordRecovered(true);
@ -131,7 +131,7 @@ void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &re
finish(d.vuser());
}
void PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
void PasswordCheckWidget::pwdSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
_sentRequest = 0;
showError(tr::lng_flood_error());
@ -161,7 +161,7 @@ void PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
}
}
void PwdCheckWidget::handleSrpIdInvalid() {
void PasswordCheckWidget::handleSrpIdInvalid() {
const auto now = crl::now();
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
@ -173,7 +173,7 @@ void PwdCheckWidget::handleSrpIdInvalid() {
}
}
void PwdCheckWidget::checkPasswordHash() {
void PasswordCheckWidget::checkPasswordHash() {
if (_request.id) {
passwordChecked();
} else {
@ -181,7 +181,7 @@ void PwdCheckWidget::checkPasswordHash() {
}
}
void PwdCheckWidget::requestPasswordData() {
void PasswordCheckWidget::requestPasswordData() {
_api.request(base::take(_sentRequest)).cancel();
_sentRequest = _api.request(
MTPaccount_GetPassword()
@ -194,7 +194,7 @@ void PwdCheckWidget::requestPasswordData() {
}).send();
}
void PwdCheckWidget::passwordChecked() {
void PasswordCheckWidget::passwordChecked() {
if (!_request || !_request.id) {
return serverError();
}
@ -214,11 +214,11 @@ void PwdCheckWidget::passwordChecked() {
}).send();
}
void PwdCheckWidget::serverError() {
void PasswordCheckWidget::serverError() {
showError(rpl::single(Lang::Hard::ServerError()));
}
void PwdCheckWidget::codeSubmitFail(const RPCError &error) {
void PasswordCheckWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
showError(tr::lng_flood_error());
_codeField->showError();
@ -249,12 +249,12 @@ void PwdCheckWidget::codeSubmitFail(const RPCError &error) {
}
}
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
void PasswordCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = qs(result.c_auth_passwordRecovery().vemail_pattern());
updateDescriptionText();
}
void PwdCheckWidget::recoverStartFail(const RPCError &error) {
void PasswordCheckWidget::recoverStartFail(const RPCError &error) {
_pwdField->show();
_pwdHint->show();
_codeField->hide();
@ -264,7 +264,7 @@ void PwdCheckWidget::recoverStartFail(const RPCError &error) {
hideError();
}
void PwdCheckWidget::toRecover() {
void PasswordCheckWidget::toRecover() {
if (_hasRecovery) {
if (_sentRequest) {
_api.request(base::take(_sentRequest)).cancel();
@ -294,13 +294,13 @@ void PwdCheckWidget::toRecover() {
}
}
void PwdCheckWidget::toPassword() {
void PasswordCheckWidget::toPassword() {
Ui::show(Box<InformBox>(
tr::lng_signin_cant_email_forgot(tr::now),
[=] { showReset(); }));
}
void PwdCheckWidget::showReset() {
void PasswordCheckWidget::showReset() {
if (_sentRequest) {
_api.request(base::take(_sentRequest)).cancel();
}
@ -316,7 +316,7 @@ void PwdCheckWidget::showReset() {
update();
}
void PwdCheckWidget::updateDescriptionText() {
void PasswordCheckWidget::updateDescriptionText() {
auto pwdHidden = _pwdField->isHidden();
auto emailPattern = _emailPattern;
setDescriptionText(pwdHidden
@ -324,7 +324,7 @@ void PwdCheckWidget::updateDescriptionText() {
: tr::lng_signin_desc());
}
void PwdCheckWidget::submit() {
void PasswordCheckWidget::submit() {
if (_sentRequest) {
return;
}
@ -370,7 +370,7 @@ void PwdCheckWidget::submit() {
}
}
rpl::producer<QString> PwdCheckWidget::nextButtonText() const {
rpl::producer<QString> PasswordCheckWidget::nextButtonText() const {
return tr::lng_intro_submit();
}

View file

@ -22,9 +22,9 @@ class LinkButton;
namespace Intro {
namespace details {
class PwdCheckWidget final : public Step {
class PasswordCheckWidget final : public Step {
public:
PwdCheckWidget(
PasswordCheckWidget(
QWidget *parent,
not_null<Main::Account*> account,
not_null<Data*> data);

View file

@ -5,16 +5,18 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/introphone.h"
#include "intro/intro_phone.h"
#include "lang/lang_keys.h"
#include "intro/introcode.h"
#include "intro/intro_code.h"
#include "intro/intro_qr.h"
#include "styles/style_intro.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/special_fields.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "boxes/confirm_phone_box.h"
#include "boxes/confirm_box.h"
#include "core/application.h"
@ -54,6 +56,7 @@ PhoneWidget::PhoneWidget(
setDescriptionText(tr::lng_phone_desc());
subscribe(getData()->updated, [=] { countryChanged(); });
setErrorCentered(true);
setupQrLogin();
if (!_country->onChooseCountry(getData()->country)) {
_country->onChooseCountry(qsl("US"));
@ -61,6 +64,36 @@ PhoneWidget::PhoneWidget(
_changed = false;
}
void PhoneWidget::setupQrLogin() {
rpl::single(
rpl::empty_value()
) | rpl::then(
account().appConfig().refreshed()
) | rpl::map([=] {
return account().appConfig().get<QString>(
"qr_login_code",
"disabled");
}) | rpl::filter([](const QString &value) {
return (value != "disabled");
}) | rpl::take(1) | rpl::start_with_next([=] {
const auto qrLogin = Ui::CreateChild<Ui::LinkButton>(
this,
tr::lng_phone_to_qr(tr::now));
qrLogin->show();
rpl::combine(
sizeValue(),
qrLogin->widthValue()
) | rpl::start_with_next([=](QSize size, int qrLoginWidth) {
qrLogin->moveToLeft(
(size.width() - qrLoginWidth) / 2,
contentTop() + st::introQrLoginLinkTop);
}, qrLogin->lifetime());
qrLogin->setClickedCallback([=] { goReplace<QrWidget>(); });
}, lifetime());
}
void PhoneWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_country->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
@ -200,9 +233,7 @@ void PhoneWidget::setInnerFocus() {
void PhoneWidget::activate() {
Step::activate();
_country->show();
_phone->show();
_code->show();
showChildren();
setInnerFocus();
}

View file

@ -44,6 +44,7 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
void setupQrLogin();
void phoneChanged();
void checkRequest();
void countryChanged();

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/intro_qr.h"
#include "intro/introphone.h"
#include "intro/intro_phone.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"

View file

@ -5,9 +5,9 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/introsignup.h"
#include "intro/intro_signup.h"
#include "intro/introwidget.h"
#include "intro/intro_widget.h"
#include "core/file_utilities.h"
#include "boxes/photo_crop_box.h"
#include "boxes/confirm_box.h"

View file

@ -5,13 +5,15 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/introstart.h"
#include "intro/intro_start.h"
#include "lang/lang_keys.h"
#include "intro/intro_qr.h"
#include "intro/intro_phone.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
namespace Intro {
namespace details {
@ -29,7 +31,14 @@ StartWidget::StartWidget(
void StartWidget::submit() {
account().destroyStaleAuthorizationKeys();
goNext<QrWidget>();
const auto qrLogin = account().appConfig().get<QString>(
"qr_login_code",
"disabled");
if (qrLogin == "primary") {
goNext<QrWidget>();
} else {
goNext<PhoneWidget>();
}
}
rpl::producer<QString> StartWidget::nextButtonText() const {

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/intro_step.h"
#include "intro/introwidget.h"
#include "intro/intro_widget.h"
#include "storage/localstorage.h"
#include "lang/lang_keys.h"
#include "lang/lang_cloud_manager.h"

View file

@ -5,13 +5,13 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "intro/introwidget.h"
#include "intro/intro_widget.h"
#include "intro/introstart.h"
#include "intro/introphone.h"
#include "intro/introcode.h"
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "intro/intro_start.h"
#include "intro/intro_phone.h"
#include "intro/intro_code.h"
#include "intro/intro_signup.h"
#include "intro/intro_password_check.h"
#include "lang/lang_keys.h"
#include "lang/lang_cloud_manager.h"
#include "storage/localstorage.h"
@ -82,7 +82,7 @@ Widget::Widget(QWidget *parent, not_null<Main::Account*> account)
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
getNearestDC();
if (_changeLanguage) {
_changeLanguage->finishAnimating();
}

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_audio.h"
#include "mainwidget.h"
#include "observer_peer.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "facades.h"
@ -26,6 +27,7 @@ namespace Main {
Account::Account(const QString &dataName) {
watchProxyChanges();
watchSessionChanges();
_appConfig = std::make_unique<AppConfig>(this);
}
Account::~Account() = default;

View file

@ -15,6 +15,7 @@ namespace Main {
class Session;
class Settings;
class AppConfig;
class Account final : public base::has_weak_ptr {
public:
@ -35,6 +36,10 @@ public:
void logOut();
void forcedLogOut();
[[nodiscard]] AppConfig &appConfig() {
return *_appConfig;
}
[[nodiscard]] bool sessionExists() const;
[[nodiscard]] Session &session();
[[nodiscard]] const Session &session() const;
@ -92,6 +97,8 @@ private:
rpl::event_stream<> _mtpNewSessionCreated;
rpl::event_stream<> _configUpdates;
std::unique_ptr<AppConfig> _appConfig;
std::unique_ptr<Session> _session;
rpl::variable<Session*> _sessionValue;

View file

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "base/call_delayed.h"
#include "apiwrap.h"
@ -18,15 +18,25 @@ constexpr auto kRefreshTimeout = 3600 * crl::time(1000);
} // namespace
AppConfig::AppConfig(not_null<Session*> session) : _session(session) {
AppConfig::AppConfig(not_null<Account*> account) : _account(account) {
account->mtpValue(
) | rpl::start_with_next([=](MTP::Instance *instance) {
if (instance) {
_api.emplace(instance);
refresh();
} else {
_api.reset();
_requestId = 0;
}
}, _lifetime);
refresh();
}
void AppConfig::refresh() {
if (_requestId) {
if (_requestId || !_api) {
return;
}
_requestId = _session->api().request(MTPhelp_GetAppConfig(
_requestId = _api->request(MTPhelp_GetAppConfig(
)).done([=](const MTPJSONValue &result) {
_requestId = 0;
refreshDelayed();
@ -37,6 +47,7 @@ void AppConfig::refresh() {
});
}
}
_refreshed.fire({});
}).fail([=](const RPCError &error) {
_requestId = 0;
refreshDelayed();
@ -44,20 +55,42 @@ void AppConfig::refresh() {
}
void AppConfig::refreshDelayed() {
base::call_delayed(kRefreshTimeout, _session, [=] {
base::call_delayed(kRefreshTimeout, _account, [=] {
refresh();
});
}
double AppConfig::getDouble(const QString &key, double fallback) const {
rpl::producer<> AppConfig::refreshed() const {
return _refreshed.events();
}
template <typename Extractor>
auto AppConfig::getValue(const QString &key, Extractor &&extractor) const {
const auto i = _data.find(key);
if (i == end(_data)) {
return fallback;
}
return i->second.match([&](const MTPDjsonNumber &data) {
return data.vvalue().v;
}, [&](const auto &data) {
return fallback;
return extractor((i != end(_data))
? i->second
: MTPJSONValue(MTP_jsonNull()));
}
double AppConfig::getDouble(const QString &key, double fallback) const {
return getValue(key, [&](const MTPJSONValue &value) {
return value.match([&](const MTPDjsonNumber &data) {
return data.vvalue().v;
}, [&](const auto &data) {
return fallback;
});
});
}
QString AppConfig::getString(
const QString &key,
const QString &fallback) const {
return getValue(key, [&](const MTPJSONValue &value) {
return value.match([&](const MTPDjsonString &data) {
return qs(data.vvalue());
}, [&](const auto &data) {
return fallback;
});
});
}

View file

@ -7,30 +7,49 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "mtproto/sender.h"
namespace Main {
class Session;
class Account;
class AppConfig final {
public:
explicit AppConfig(not_null<Session*> session);
explicit AppConfig(not_null<Account*> account);
template <typename Type>
Type get(const QString &key, Type fallback) const {
[[nodiscard]] Type get(const QString &key, Type fallback) const {
if constexpr (std::is_same_v<Type, double>) {
return getDouble(key, fallback);
} else if constexpr (std::is_same_v<Type, QString>) {
return getString(key, fallback);
}
}
[[nodiscard]] rpl::producer<> refreshed() const;
private:
void refresh();
void refreshDelayed();
double getDouble(const QString &key, double fallback) const;
template <typename Extractor>
[[nodiscard]] auto getValue(
const QString &key,
Extractor &&extractor) const;
not_null<Session*> _session;
[[nodiscard]] double getDouble(
const QString &key,
double fallback) const;
[[nodiscard]] QString getString(
const QString &key,
const QString &fallback) const;
const not_null<Account*> _account;
std::optional<MTP::Sender> _api;
mtpRequestId _requestId = 0;
base::flat_map<QString, MTPJSONValue> _data;
rpl::event_stream<> _refreshed;
rpl::lifetime _lifetime;
};

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/changelogs.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "chat_helpers/stickers_emoji_pack.h"
#include "storage/file_download.h"
#include "storage/file_upload.h"
@ -44,7 +43,6 @@ Session::Session(
, _saveSettingsTimer([=] { Local::writeUserSettings(); })
, _autoLockTimer([=] { checkAutoLock(); })
, _api(std::make_unique<ApiWrap>(this))
, _appConfig(std::make_unique<AppConfig>(this))
, _calls(std::make_unique<Calls::Instance>(this))
, _downloader(std::make_unique<Storage::Downloader>(_api.get()))
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))

View file

@ -55,7 +55,6 @@ class Changelogs;
namespace Main {
class Account;
class AppConfig;
class Session final
: public base::has_weak_ptr
@ -93,9 +92,6 @@ public:
[[nodiscard]] Stickers::EmojiPack &emojiStickersPack() {
return *_emojiStickersPack;
}
[[nodiscard]] AppConfig &appConfig() {
return *_appConfig;
}
[[nodiscard]] base::Observable<void> &downloaderTaskFinished();
@ -148,7 +144,6 @@ private:
base::Timer _autoLockTimer;
const std::unique_ptr<ApiWrap> _api;
const std::unique_ptr<AppConfig> _appConfig;
const std::unique_ptr<Calls::Instance> _calls;
const std::unique_ptr<Storage::Downloader> _downloader;
const std::unique_ptr<Storage::Uploader> _uploader;

View file

@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/sandbox.h"
#include "core/application.h"
#include "main/main_session.h"
#include "intro/introwidget.h"
#include "intro/intro_widget.h"
#include "main/main_account.h" // Account::sessionValue.
#include "mainwidget.h"
#include "boxes/confirm_box.h"

View file

@ -432,22 +432,22 @@
<(src_loc)/inline_bots/inline_bot_send_data.h
<(src_loc)/inline_bots/inline_results_widget.cpp
<(src_loc)/inline_bots/inline_results_widget.h
<(src_loc)/intro/introwidget.cpp
<(src_loc)/intro/introwidget.h
<(src_loc)/intro/introcode.cpp
<(src_loc)/intro/introcode.h
<(src_loc)/intro/introphone.cpp
<(src_loc)/intro/introphone.h
<(src_loc)/intro/intropwdcheck.cpp
<(src_loc)/intro/intropwdcheck.h
<(src_loc)/intro/introsignup.cpp
<(src_loc)/intro/introsignup.h
<(src_loc)/intro/introstart.cpp
<(src_loc)/intro/introstart.h
<(src_loc)/intro/intro_code.cpp
<(src_loc)/intro/intro_code.h
<(src_loc)/intro/intro_password_check.cpp
<(src_loc)/intro/intro_password_check.h
<(src_loc)/intro/intro_phone.cpp
<(src_loc)/intro/intro_phone.h
<(src_loc)/intro/intro_qr.cpp
<(src_loc)/intro/intro_qr.h
<(src_loc)/intro/intro_signup.cpp
<(src_loc)/intro/intro_signup.h
<(src_loc)/intro/intro_start.cpp
<(src_loc)/intro/intro_start.h
<(src_loc)/intro/intro_step.cpp
<(src_loc)/intro/intro_step.h
<(src_loc)/intro/intro_widget.cpp
<(src_loc)/intro/intro_widget.h
<(src_loc)/lang/lang_cloud_manager.cpp
<(src_loc)/lang/lang_cloud_manager.h
<(src_loc)/lang/lang_file_parser.cpp