| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "minios/draw_utils.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| |
| namespace minios { |
| |
| // Dropdown Menu Colors. |
| const char kMenuBlack[] = "0x202124"; |
| const char kMenuBlue[] = "0x8AB4F8"; |
| const char kMenuGrey[] = "0x3F4042"; |
| const char kMenuDropdownFrameNavy[] = "0x435066"; |
| const char kMenuDropdownBackgroundBlack[] = "0x2D2E30"; |
| const char kMenuButtonFrameGrey[] = "0x9AA0A6"; |
| |
| // Dimension Constants |
| const int kButtonHeight = 32; |
| const int kButtonMargin = 8; |
| const int kDefaultMessageWidth = 720; |
| const int kMonospaceGlyphHeight = 20; |
| const int kMonospaceGlyphWidth = 10; |
| const int kDefaultButtonWidth = 80; |
| |
| // Frecon constants |
| constexpr char kScreens[] = "etc/screens"; |
| constexpr int kFreconScalingFactor = 1; |
| constexpr int kCanvasSize = 1080; |
| constexpr int kSmallCanvasSize = 900; |
| |
| namespace { |
| constexpr char kConsole0[] = "dev/pts/0"; |
| |
| // Dimensions and spacing. |
| constexpr int kNewLineChar = 10; |
| |
| constexpr char kButtonWidthToken[] = "DEBUG_OPTIONS_BTN_WIDTH"; |
| |
| // The index for en-US in `supported_locales`. |
| constexpr int kEnglishIndex = 9; |
| } // namespace |
| |
| bool DrawUtils::Init() { |
| ReadHardwareId(); |
| // TODO(vyshu): Change constants.sh and lang_constants.sh to simple text file. |
| ReadDimensionConstants(); |
| if (!ReadLangConstants()) { |
| return false; |
| } |
| GetFreconConstants(); |
| return true; |
| } |
| |
| bool DrawUtils::ShowText(const std::string& text, |
| int glyph_offset_h, |
| int glyph_offset_v, |
| const std::string& color) { |
| base::FilePath glyph_dir = screens_path_.Append("glyphs").Append(color); |
| const int kTextStart = glyph_offset_h; |
| |
| for (const auto& chr : text) { |
| int char_num = static_cast<int>(chr); |
| base::FilePath chr_file_path = |
| glyph_dir.Append(base::NumberToString(char_num) + ".png"); |
| if (char_num == kNewLineChar) { |
| glyph_offset_v += kMonospaceGlyphHeight; |
| glyph_offset_h = kTextStart; |
| } else { |
| int offset_rtl = IsLocaleRightToLeft() ? -glyph_offset_h : glyph_offset_h; |
| if (!ShowImage(chr_file_path, offset_rtl, glyph_offset_v)) { |
| LOG(ERROR) << "Failed to show image " << chr_file_path << " for text " |
| << text; |
| return false; |
| } |
| glyph_offset_h += kMonospaceGlyphWidth; |
| } |
| } |
| return true; |
| } |
| |
| bool DrawUtils::ShowImage(const base::FilePath& image_name, |
| int offset_x, |
| int offset_y) { |
| if (IsLocaleRightToLeft()) |
| offset_x = -offset_x; |
| std::string command = base::StringPrintf( |
| "\033]image:file=%s;offset=%d,%d;scale=%d\a", image_name.value().c_str(), |
| offset_x, offset_y, frecon_scale_factor_); |
| if (!base::AppendToFile(base::FilePath(root_).Append(kConsole0), |
| command.c_str(), command.size())) { |
| LOG(ERROR) << "Could not write " << image_name << " to console."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DrawUtils::ShowBox(int offset_x, |
| int offset_y, |
| int size_x, |
| int size_y, |
| const std::string& color) { |
| size_x = std::max(size_x, 1); |
| size_y = std::max(size_y, 1); |
| if (IsLocaleRightToLeft()) |
| offset_x = -offset_x; |
| |
| std::string command = base::StringPrintf( |
| "\033]box:color=%s;size=%d,%d;offset=%d,%d;scale=%d\a", color.c_str(), |
| size_x, size_y, offset_x, offset_y, frecon_scale_factor_); |
| |
| if (!base::AppendToFile(base::FilePath(root_).Append(kConsole0), |
| command.c_str(), command.size())) { |
| LOG(ERROR) << "Could not write show box command to console."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DrawUtils::ShowMessage(const std::string& message_token, |
| int offset_x, |
| int offset_y) { |
| // Determine the filename of the message resource. Fall back to en-US if |
| // the localized version of the message is not available. |
| base::FilePath message_file_path = |
| screens_path_.Append(locale_).Append(message_token + ".png"); |
| if (!base::PathExists(message_file_path)) { |
| if (locale_ == "en-US") { |
| LOG(ERROR) << "Message " << message_token |
| << " not found in en-US. No fallback available."; |
| return false; |
| } |
| LOG(WARNING) << "Could not find " << message_token << " in " << locale_ |
| << " trying default locale en-US."; |
| message_file_path = |
| screens_path_.Append("en-US").Append(message_token + ".png"); |
| if (!base::PathExists(message_file_path)) { |
| LOG(ERROR) << "Message " << message_token << " not found in path " |
| << message_file_path; |
| return false; |
| } |
| } |
| return ShowImage(message_file_path, offset_x, offset_y); |
| } |
| |
| void DrawUtils::ShowInstructions(const std::string& message_token) { |
| const int kXOffset = (-frecon_canvas_size_ / 2) + (kDefaultMessageWidth / 2); |
| const int kYOffset = (-frecon_canvas_size_ / 4); |
| if (!ShowMessage(message_token, kXOffset, kYOffset)) |
| LOG(WARNING) << "Unable to show " << message_token; |
| } |
| |
| void DrawUtils::ShowInstructionsWithTitle(const std::string& message_token) { |
| const int kXOffset = (-frecon_canvas_size_ / 2) + (kDefaultMessageWidth / 2); |
| |
| int title_height; |
| if (!GetDimension("TITLE_" + message_token + "_HEIGHT", &title_height)) { |
| title_height = 40; |
| LOG(WARNING) << "Unable to get title constant for " << message_token |
| << ". Defaulting to " << title_height; |
| } |
| int desc_height; |
| if (!GetDimension("DESC_" + message_token + "_HEIGHT", &desc_height)) { |
| desc_height = 40; |
| LOG(WARNING) << "Unable to get description constant for " << message_token |
| << ". Defaulting to " << desc_height; |
| } |
| |
| const int kTitleY = (-frecon_canvas_size_ / 2) + 220 + (title_height / 2); |
| const int kDescY = kTitleY + (title_height / 2) + 16 + (desc_height / 2); |
| if (!ShowMessage("title_" + message_token, kXOffset, kTitleY)) |
| LOG(WARNING) << "Unable to show title " << message_token; |
| if (!ShowMessage("desc_" + message_token, kXOffset, kDescY)) |
| LOG(WARNING) << "Unable to show description " << message_token; |
| } |
| |
| int DrawUtils::FindLocaleIndex(int current_index) { |
| auto locale = |
| std::find(supported_locales_.begin(), supported_locales_.end(), locale_); |
| if (locale == supported_locales_.end()) { |
| // Default to en-US. |
| LOG(WARNING) << " Could not find an index to match current locale " |
| << locale_ << ". Defaulting to index " << kEnglishIndex |
| << " for " << supported_locales_[kEnglishIndex]; |
| return kEnglishIndex; |
| } |
| return std::distance(supported_locales_.begin(), locale); |
| } |
| |
| void DrawUtils::ShowProgressPercentage(double progress) { |
| if (progress < 0 || progress > 1) { |
| LOG(WARNING) << "Invalid value of progress: " << progress; |
| return; |
| } |
| // Should be at canvas width at 100%. |
| const double kProgressIncrement = frecon_canvas_size_ / 100.0; |
| constexpr int kProgressHeight = 4; |
| const int kLeftIncrement = -frecon_canvas_size_ / 2; |
| int progress_length = kProgressIncrement * progress * 100; |
| ShowBox(kLeftIncrement + progress_length / 2, 0, progress_length, |
| kProgressHeight, kMenuBlue); |
| } |
| |
| void DrawUtils::ClearMainArea() { |
| constexpr int kFooterHeight = 142; |
| if (!ShowBox(0, -kFooterHeight / 2, frecon_canvas_size_ + 200, |
| (frecon_canvas_size_ - kFooterHeight), kMenuBlack)) |
| LOG(WARNING) << "Could not clear main area."; |
| } |
| |
| void DrawUtils::ClearScreen() { |
| if (!ShowBox(0, 0, frecon_canvas_size_ + 100, frecon_canvas_size_, |
| kMenuBlack)) |
| LOG(WARNING) << "Could not clear screen."; |
| } |
| |
| void DrawUtils::ShowButton(const std::string& message_token, |
| int offset_y, |
| bool is_selected, |
| int inner_width, |
| bool is_text) { |
| const int kBtnPadding = 32; // Left and right padding. |
| int left_padding_x = (-frecon_canvas_size_ / 2) + (kBtnPadding / 2); |
| const int kOffsetX = left_padding_x + (kBtnPadding / 2) + (inner_width / 2); |
| int right_padding_x = kOffsetX + (kBtnPadding / 2) + (inner_width / 2); |
| // Clear previous state. |
| if (!ShowBox(kOffsetX, offset_y, (kBtnPadding * 2 + inner_width), |
| kButtonHeight, kMenuBlack)) { |
| LOG(WARNING) << "Could not clear button area."; |
| } |
| |
| if (IsLocaleRightToLeft()) { |
| std::swap(left_padding_x, right_padding_x); |
| } |
| |
| if (is_selected) { |
| ShowImage(screens_path_.Append("btn_bg_left_focused.png"), left_padding_x, |
| offset_y); |
| ShowImage(screens_path_.Append("btn_bg_right_focused.png"), right_padding_x, |
| offset_y); |
| |
| ShowBox(kOffsetX, offset_y, inner_width, kButtonHeight, kMenuBlue); |
| if (is_text) { |
| ShowText(message_token, left_padding_x, offset_y, "black"); |
| } else { |
| ShowMessage(message_token + "_focused", kOffsetX, offset_y); |
| } |
| } else { |
| ShowImage(screens_path_.Append("btn_bg_left.png"), left_padding_x, |
| offset_y); |
| ShowImage(screens_path_.Append("btn_bg_right.png"), right_padding_x, |
| offset_y); |
| ShowBox(kOffsetX, offset_y - (kButtonHeight / 2) + 1, inner_width, 1, |
| kMenuButtonFrameGrey); |
| ShowBox(kOffsetX, offset_y + (kButtonHeight / 2), inner_width, 1, |
| kMenuButtonFrameGrey); |
| if (is_text) { |
| ShowText(message_token, left_padding_x, offset_y, "white"); |
| } else { |
| ShowMessage(message_token, kOffsetX, offset_y); |
| } |
| } |
| } |
| |
| void DrawUtils::ShowStepper(const std::vector<std::string>& steps) { |
| // The icon real size is 24x24, but it occupies a 36x36 block. Use 36 here for |
| // simplicity. |
| constexpr int kIconSize = 36; |
| constexpr int kSeparatorLength = 46; |
| constexpr int kPadding = 6; |
| |
| int stepper_x = (-frecon_canvas_size_ / 2) + (kIconSize / 2); |
| constexpr int kStepperXStep = kIconSize + kSeparatorLength + (kPadding * 2); |
| const int kStepperY = 144 - (frecon_canvas_size_ / 2); |
| int separator_x = (-frecon_canvas_size_ / 2) + kIconSize + kPadding + |
| (kSeparatorLength / 2); |
| |
| for (const auto& step : steps) { |
| base::FilePath stepper_image = screens_path_.Append("ic_" + step + ".png"); |
| if (!base::PathExists(stepper_image)) { |
| // TODO(vyshu): Create a new generic icon to be used instead of done. |
| LOG(WARNING) << "Stepper icon " << stepper_image |
| << " not found. Defaulting to the done icon."; |
| stepper_image = screens_path_.Append("ic_done.png"); |
| if (!base::PathExists(stepper_image)) { |
| LOG(ERROR) << "Could not find stepper icon done. Cannot show stepper."; |
| return; |
| } |
| } |
| ShowImage(stepper_image, stepper_x, kStepperY); |
| stepper_x += kStepperXStep; |
| } |
| |
| for (int i = 0; i < steps.size() - 1; ++i) { |
| ShowBox(separator_x, kStepperY, kSeparatorLength, 1, kMenuGrey); |
| separator_x += kStepperXStep; |
| } |
| } |
| |
| void DrawUtils::ShowLanguageDropdown(int current_index) { |
| constexpr int kItemHeight = 40; |
| const int kItemPerPage = (frecon_canvas_size_ - 260) / kItemHeight; |
| |
| // Pick begin index such that the selected index is centered on the screen if |
| // possible. |
| int begin_index = |
| std::clamp(current_index - kItemPerPage / 2, 0, |
| static_cast<int>(supported_locales_.size()) - kItemPerPage); |
| |
| int offset_y = -frecon_canvas_size_ / 2 + 88; |
| const int kBackgroundX = -frecon_canvas_size_ / 2 + 360; |
| for (int i = begin_index; |
| i < (begin_index + kItemPerPage) && i < supported_locales_.size(); i++) { |
| // Get placement for the language image. |
| int language_width; |
| if (!GetLangConstants(supported_locales_[i], &language_width)) { |
| language_width = 95; |
| LOG(WARNING) << "Could not get width for " << supported_locales_[i] |
| << ". Defaulting to " << language_width; |
| } |
| int lang_x = -frecon_canvas_size_ / 2 + language_width / 2 + 40; |
| |
| // This is the currently selected language. Show in blue. |
| if (current_index == i) { |
| ShowBox(kBackgroundX, offset_y, 720, 40, kMenuBlue); |
| ShowImage(screens_path_.Append(supported_locales_[i]) |
| .Append("language_focused.png"), |
| lang_x, offset_y); |
| } else { |
| ShowBox(kBackgroundX, offset_y, 720, 40, kMenuDropdownFrameNavy); |
| ShowBox(kBackgroundX, offset_y, 718, 38, kMenuDropdownBackgroundBlack); |
| ShowImage( |
| screens_path_.Append(supported_locales_[i]).Append("language.png"), |
| lang_x, offset_y); |
| } |
| offset_y += kItemHeight; |
| } |
| } |
| |
| void DrawUtils::ShowLanguageMenu(bool is_selected) { |
| const int kOffsetY = -frecon_canvas_size_ / 2 + 40; |
| const int kBgX = -frecon_canvas_size_ / 2 + 145; |
| const int kGlobeX = -frecon_canvas_size_ / 2 + 20; |
| const int kArrowX = -frecon_canvas_size_ / 2 + 268; |
| int language_width; |
| if (!GetLangConstants(locale_, &language_width)) { |
| language_width = 100; |
| LOG(WARNING) << "Could not get language width for " << locale_ |
| << ". Defaulting to 100."; |
| } |
| const int kTextX = -frecon_canvas_size_ / 2 + 40 + language_width / 2; |
| |
| base::FilePath menu_background = |
| is_selected ? screens_path_.Append("language_menu_bg_focused.png") |
| : screens_path_.Append("language_menu_bg.png"); |
| |
| ShowImage(menu_background, kBgX, kOffsetY); |
| ShowImage(screens_path_.Append("ic_language-globe.png"), kGlobeX, kOffsetY); |
| |
| ShowImage(screens_path_.Append("ic_dropdown.png"), kArrowX, kOffsetY); |
| ShowMessage("language_folded", kTextX, kOffsetY); |
| } |
| |
| void DrawUtils::ShowFooter() { |
| constexpr int kQrCodeSize = 86; |
| const int kQrCodeX = (-frecon_canvas_size_ / 2) + (kQrCodeSize / 2); |
| const int kQrCodeY = (frecon_canvas_size_ / 2) - (kQrCodeSize / 2) - 56; |
| |
| const int kSeparatorX = 410 - (frecon_canvas_size_ / 2); |
| const int kSeparatorY = kQrCodeY; |
| constexpr int kFooterLineHeight = 18; |
| |
| const int kFooterY = (frecon_canvas_size_ / 2) - kQrCodeSize + 9 - 56; |
| const int kFooterLeftX = |
| kQrCodeX + (kQrCodeSize / 2) + 16 + (kDefaultMessageWidth / 2); |
| const int kFooterRightX = kSeparatorX + 32 + (kDefaultMessageWidth / 2); |
| |
| ShowMessage("footer_left_1", kFooterLeftX, kFooterY); |
| ShowMessage("footer_left_2", kFooterLeftX, |
| kFooterY + kFooterLineHeight * 2 + 14); |
| ShowMessage("footer_left_3", kFooterLeftX, |
| kFooterY + kFooterLineHeight * 3 + 14); |
| |
| constexpr int kNavButtonHeight = 24; |
| const int kNavButtonY = |
| (frecon_canvas_size_ / 2) - (kNavButtonHeight / 2) - 56; |
| int nav_btn_x = kSeparatorX + 32; |
| // Navigation key icons. |
| const std::string kFooterType = is_detachable_ ? "tablet" : "clamshell"; |
| const std::string kNavKeyEnter = |
| is_detachable_ ? "button_power" : "key_enter"; |
| const std::string kNavKeyUp = is_detachable_ ? "button_volume_up" : "key_up"; |
| const std::string kNavKeyDown = |
| is_detachable_ ? "button_volume_down" : "key_down"; |
| |
| constexpr int kUpDownIconWidth = 24; |
| constexpr int kIconPadding = 8; |
| const int kEnterIconWidth = is_detachable_ ? 40 : 66; |
| |
| ShowMessage("footer_right_1_" + kFooterType, kFooterRightX, kFooterY); |
| ShowMessage("footer_right_2_" + kFooterType, kFooterRightX, |
| kFooterY + kFooterLineHeight + 8); |
| |
| nav_btn_x += kEnterIconWidth / 2; |
| ShowImage(screens_path_.Append("nav-" + kNavKeyEnter + ".png"), nav_btn_x, |
| kNavButtonY); |
| nav_btn_x += kEnterIconWidth / 2 + kIconPadding + kUpDownIconWidth / 2; |
| ShowImage(screens_path_.Append("nav-" + kNavKeyUp + ".png"), nav_btn_x, |
| kNavButtonY); |
| nav_btn_x += kIconPadding + kUpDownIconWidth; |
| ShowImage(screens_path_.Append("nav-" + kNavKeyDown + ".png"), nav_btn_x, |
| kNavButtonY); |
| |
| ShowImage(screens_path_.Append("qr_code.png"), kQrCodeX, kQrCodeY); |
| int hwid_len = hwid_.size(); |
| int hwid_x = kQrCodeX + (kQrCodeSize / 2) + 16 + 5; |
| const int kHwidY = kFooterY + kFooterLineHeight; |
| |
| if (IsLocaleRightToLeft()) { |
| hwid_x = -hwid_x - kMonospaceGlyphWidth * (hwid_len - 2); |
| } |
| |
| ShowText(hwid_, hwid_x, kHwidY, "grey"); |
| ShowBox(kSeparatorX, kSeparatorY, 1, kQrCodeSize, kMenuGrey); |
| } |
| |
| void DrawUtils::LocaleChange(int selected_locale) { |
| // Change locale and update constants. |
| locale_ = supported_locales_[selected_locale]; |
| ReadDimensionConstants(); |
| ClearScreen(); |
| ShowFooter(); |
| } |
| |
| void DrawUtils::MessageBaseScreen() { |
| ClearMainArea(); |
| ShowLanguageMenu(false); |
| ShowFooter(); |
| } |
| |
| void DrawUtils::ReadDimensionConstants() { |
| image_dimensions_.clear(); |
| base::FilePath path = screens_path_.Append(locale_).Append("constants.sh"); |
| std::string dimension_consts; |
| if (!ReadFileToString(path, &dimension_consts)) { |
| LOG(ERROR) << "Could not read constants.sh file for language " << locale_; |
| return; |
| } |
| if (!base::SplitStringIntoKeyValuePairs(dimension_consts, '=', '\n', |
| &image_dimensions_)) { |
| LOG(WARNING) << "Unable to parse all dimension information for " << locale_; |
| return; |
| } |
| |
| // Save default button width for this locale. |
| if (!GetDimension(kButtonWidthToken, &default_button_width_)) { |
| default_button_width_ = kDefaultButtonWidth; |
| LOG(WARNING) << "Unable to get dimension for " << kButtonWidthToken |
| << ". Defaulting to width " << kDefaultButtonWidth; |
| } |
| } |
| |
| bool DrawUtils::GetDimension(const std::string& token, int* token_dimension) { |
| if (image_dimensions_.empty()) { |
| LOG(ERROR) << "No dimensions available."; |
| return false; |
| } |
| |
| // Find the dimension for the token. |
| for (const auto& dimension : image_dimensions_) { |
| if (dimension.first == token) { |
| if (!base::StringToInt(dimension.second, token_dimension)) { |
| LOG(ERROR) << "Could not convert " << dimension.second |
| << " to a number."; |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void DrawUtils::GetFreconConstants() { |
| base::FilePath scale_factor_path = |
| root_.Append("etc").Append("frecon").Append("scale"); |
| std::string frecon_scale_factor; |
| if (!ReadFileToString(scale_factor_path, &frecon_scale_factor)) { |
| frecon_scale_factor_ = kFreconScalingFactor; |
| LOG(WARNING) << "Could not read frecon scale factor from /etc. Defaulting " |
| "to scale " |
| << kFreconScalingFactor; |
| } else { |
| base::TrimString(frecon_scale_factor, " \n", &frecon_scale_factor); |
| if (!base::StringToInt(frecon_scale_factor, &frecon_scale_factor_)) { |
| frecon_scale_factor_ = kFreconScalingFactor; |
| LOG(WARNING) << "Could not convert " << frecon_scale_factor_ |
| << " to an int. Defaulting to scale " |
| << kFreconScalingFactor; |
| } |
| } |
| |
| base::FilePath canvas_size_path = |
| root_.Append("etc").Append("frecon").Append("size"); |
| std::string frecon_canvas_size; |
| if (!ReadFileToString(canvas_size_path, &frecon_canvas_size)) { |
| frecon_canvas_size_ = kCanvasSize; |
| LOG(WARNING) << "Could not read frecon canvas size from /etc/frecon." |
| << " Defaulting to canvas size " << kCanvasSize; |
| } else { |
| base::TrimString(frecon_canvas_size, " \n", &frecon_canvas_size); |
| if (!base::StringToInt(frecon_canvas_size, &frecon_canvas_size_)) { |
| frecon_canvas_size_ = kCanvasSize; |
| LOG(WARNING) << "Could not convert " << frecon_canvas_size |
| << " to int. Defaulting to canvas size " << kCanvasSize; |
| } |
| } |
| } |
| |
| bool DrawUtils::ReadLangConstants() { |
| lang_constants_.clear(); |
| supported_locales_.clear(); |
| // Read language widths from lang_constants.sh into memory. |
| auto lang_constants_path = screens_path_.Append("lang_constants.sh"); |
| if (!base::PathExists(lang_constants_path)) { |
| LOG(ERROR) << "Language constants path: " << lang_constants_path |
| << " not found."; |
| return false; |
| } |
| |
| std::string const_values; |
| if (!ReadFileToString(lang_constants_path, &const_values)) { |
| LOG(ERROR) << "Could not read lang constants file " << lang_constants_path; |
| return false; |
| } |
| |
| if (!base::SplitStringIntoKeyValuePairs(const_values, '=', '\n', |
| &lang_constants_)) { |
| LOG(ERROR) << "Unable to parse language width information."; |
| return false; |
| } |
| for (const auto& pair : lang_constants_) { |
| if (pair.first == "SUPPORTED_LOCALES") { |
| // Parse list of supported locales and store separately. |
| std::string locale_list; |
| if (!base::RemoveChars(pair.second, "\"", &locale_list)) |
| LOG(WARNING) << "Unable to remove surrounding quotes from locale list."; |
| supported_locales_ = base::SplitString( |
| locale_list, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| } |
| } |
| |
| if (supported_locales_.empty()) { |
| LOG(ERROR) << "Unable to get supported locales. Will not be able to " |
| "change locale."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool DrawUtils::GetLangConstants(const std::string& locale, int* lang_width) { |
| if (lang_constants_.empty()) { |
| LOG(ERROR) << "No language widths available."; |
| return false; |
| } |
| |
| // Lang_consts uses '_' while supported locale list uses '-'. |
| std::string token; |
| base::ReplaceChars(locale, "-", "_", &token); |
| token = "LANGUAGE_" + token + "_WIDTH"; |
| |
| // Find the width for the token. |
| for (const auto& width_token : lang_constants_) { |
| if (width_token.first == token) { |
| if (!base::StringToInt(width_token.second, lang_width)) { |
| LOG(ERROR) << "Could not convert " << width_token.second |
| << " to a number."; |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool DrawUtils::IsLocaleRightToLeft() { |
| return (locale_ == "ar" || locale_ == "fa" || locale_ == "he"); // nocheck |
| } |
| |
| bool DrawUtils::IsDetachable() { |
| is_detachable_ = |
| base::PathExists(root_.Append("etc/cros-initramfs/is_detachable")); |
| return is_detachable_; |
| } |
| |
| void DrawUtils::ReadHardwareId() { |
| int exit_code = 0; |
| std::string output, error; |
| if (!process_manager_->RunCommandWithOutput({"/bin/crossystem", "hwid"}, |
| &exit_code, &output, &error) || |
| exit_code) { |
| hwid_ = "CHROMEBOOK"; |
| PLOG(WARNING) |
| << "Could not get hwid from crossystem. Exited with exit code " |
| << exit_code << " and error " << error |
| << ". Defaulting to 'CHROMEBOOK'."; |
| return; |
| } |
| |
| // Truncate HWID. |
| std::vector<std::string> hwid_parts = base::SplitString( |
| output, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| hwid_ = hwid_parts[0]; |
| return; |
| } |
| |
| } // namespace minios |