| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // This software is licensed under the terms of the GNU General Public |
| // License version 2, as published by the Free Software Foundation, and |
| // may be copied, distributed, and modified under those terms. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| #include "android/skin/qt/extended-pages/help-page.h" |
| |
| #include "android/android.h" |
| #include "android/base/system/System.h" |
| #include "android/emulation/ConfigDirs.h" |
| #include "android/globals.h" |
| #include "android/update-check/UpdateChecker.h" |
| #include "android/update-check/VersionExtractor.h" |
| |
| #include <QDesktopServices> |
| #include <QThread> |
| #include <QUrl> |
| |
| const char DOCS_URL[] = |
| "http://developer.android.com/r/studio-ui/emulator.html"; |
| const char FILE_BUG_URL[] = |
| "https://code.google.com/p/android/issues/entry?template=Android%20Emulator%20Bug"; |
| const char SEND_FEEDBACK_URL[] = |
| "https://code.google.com/p/android/issues/entry?template=Emulator%20Feature%20Request"; |
| |
| HelpPage::HelpPage(QWidget *parent) : |
| QWidget(parent), |
| mUi(new Ui::HelpPage) |
| { |
| mUi->setupUi(this); |
| |
| // Get the version of this code |
| android::update_check::VersionExtractor vEx; |
| |
| android::base::Version curVersion = vEx.getCurrentVersion(); |
| auto verStr = curVersion.isValid() |
| ? QString(curVersion.toString().c_str()) : "Unknown"; |
| |
| mUi->help_versionBox->setPlainText(verStr); |
| |
| int apiLevel = avdInfo_getApiLevel(android_avdInfo); |
| char versionString[128]; |
| avdInfo_getFullApiName(apiLevel, versionString, 128); |
| mUi->help_androidVersionBox->setPlainText(versionString); |
| |
| // launch the latest version loader in a separate thread |
| auto latestVersionThread = new QThread(); |
| auto latestVersionTask = new LatestVersionLoadTask(); |
| latestVersionTask->moveToThread(latestVersionThread); |
| connect(latestVersionThread, SIGNAL(started()), latestVersionTask, SLOT(run())); |
| connect(latestVersionTask, SIGNAL(finished(QString)), |
| mUi->help_latestVersionBox, SLOT(setPlainText(QString))); |
| connect(latestVersionTask, SIGNAL(finished(QString)), latestVersionThread, SLOT(quit())); |
| connect(latestVersionThread, SIGNAL(finished()), latestVersionTask, SLOT(deleteLater())); |
| connect(latestVersionThread, SIGNAL(finished()), latestVersionThread, SLOT(deleteLater())); |
| mUi->help_latestVersionBox->setPlainText(tr("Loading...")); |
| latestVersionThread->start(); |
| |
| QObject::connect(this, &HelpPage::adbPortUpdateRequired, |
| this, &HelpPage::updateAdbPortNumber); |
| } |
| |
| void HelpPage::initialize(const ShortcutKeyStore<QtUICommand>* key_store) { |
| initializeLicenseText(); |
| initializeKeyboardShortcutList(key_store); |
| } |
| |
| void HelpPage::initializeLicenseText() { |
| // Read the license text into the display box |
| // The file is <SDK path>/tools/NOTICE.txt |
| |
| QString lFileName = android::base::System::get()->getLauncherDirectory().c_str(); |
| lFileName += "/NOTICE.txt"; |
| |
| QFile licenseFile(lFileName); |
| if (licenseFile.open(QIODevice::ReadOnly)) { |
| // Read the file into the text box |
| QTextStream lText(&licenseFile); |
| mUi->help_licenseText->setPlainText(lText.readAll()); |
| } else { |
| // Can't read the file. Give a backup notice. |
| mUi->help_licenseText->setPlainText( |
| tr("Find Android Emulator License NOTICE files here:") + |
| "\n\nhttps://android.googlesource.com/platform/external/" |
| "qemu/+/emu-master-dev/"); |
| } |
| } |
| |
| static void addShortcutsTableRow(QTableWidget* table_widget, |
| const QString& key_sequence, |
| const QString& description) { |
| int table_row = table_widget->rowCount(); |
| table_widget->insertRow(table_row); |
| table_widget->setItem(table_row, 0, new QTableWidgetItem(description)); |
| table_widget->setItem(table_row, 1, new QTableWidgetItem(key_sequence)); |
| } |
| |
| void HelpPage::initializeKeyboardShortcutList(const ShortcutKeyStore<QtUICommand>* key_store) |
| { |
| QTableWidget* table_widget = mUi->shortcutsTableWidget; |
| if (key_store) { |
| for (auto key_sequence_and_command = key_store->begin(); |
| key_sequence_and_command != key_store->end(); |
| ++key_sequence_and_command) { |
| QString key_combo; |
| |
| // Unfortunately, QKeySequence doesn't handle modifier-only key |
| // sequences very well. In this case, "multitouch" is Ctrl and |
| // QKeySequence::toString sometimes produces strings with weird |
| // characters. To mitigate this problem, we simply hardcode the |
| // string for the "multitouch" key combo. |
| if (key_sequence_and_command.value() == |
| QtUICommand::SHOW_MULTITOUCH) { |
| #ifdef Q_OS_MAC |
| key_combo = "\u2318"; // Cmd |
| #else |
| key_combo = "Ctrl"; |
| #endif |
| } else { |
| key_combo = key_sequence_and_command.key().toString(QKeySequence::NativeText); |
| } |
| |
| addShortcutsTableRow(table_widget, |
| key_combo, |
| getQtUICommandDescription(key_sequence_and_command.value())); |
| } |
| } |
| |
| table_widget->sortItems(0); |
| } |
| |
| void HelpPage::setAdbPort() { |
| // The UI can only be updated from the proper thread. |
| // Emit a signal so that happens now, but in the right |
| // context. |
| emit adbPortUpdateRequired(); |
| } |
| |
| void HelpPage::updateAdbPortNumber() { |
| // Show the "serial number" that can be used to connect ADB |
| // to this device |
| mUi->help_adbSerialNumberBox->setPlainText( |
| "emulator-" + QString::number(android_serial_number_port) ); |
| } |
| |
| void HelpPage::on_help_docs_clicked() { |
| QDesktopServices::openUrl(QUrl::fromEncoded(DOCS_URL)); |
| } |
| |
| void HelpPage::on_help_fileBug_clicked() { |
| QDesktopServices::openUrl(QUrl::fromEncoded(FILE_BUG_URL)); |
| } |
| |
| void HelpPage::on_help_sendFeedback_clicked() { |
| QDesktopServices::openUrl(QUrl::fromEncoded(SEND_FEEDBACK_URL)); |
| } |
| |
| static const char* updateChannelName(android::studio::UpdateChannel channel) { |
| switch (channel) { |
| case android::studio::UpdateChannel::Stable: |
| return "Stable"; |
| case android::studio::UpdateChannel::Dev: |
| return "Dev"; |
| case android::studio::UpdateChannel::Beta: |
| return "Beta"; |
| case android::studio::UpdateChannel::Canary: |
| return "Canary"; |
| default: |
| return nullptr; |
| } |
| } |
| |
| void LatestVersionLoadTask::run() { |
| // Get latest version that is available online |
| android::update_check::UpdateChecker upCheck( |
| android::ConfigDirs::getUserDirectory().c_str()); |
| const auto latestVersion = upCheck.getLatestVersion(); |
| QString latestVerString; |
| if (!latestVersion) { |
| latestVerString = tr("Unavailable"); |
| } else { |
| latestVerString = |
| QString::fromStdString(latestVersion->first.toString()); |
| if (const auto channelName = updateChannelName(latestVersion->second)) { |
| latestVerString += tr(" (%1 update channel)").arg(channelName); |
| } |
| } |
| emit finished(latestVerString); |
| } |