screen_ui: Word-wrap menu headers.

This CL adds ScreenRecoveryUI::DrawWrappedTextLines() to better handle
long menu header texts. It does a word wrap at spaces, if available.
This avoids fixed-length menu headers being truncated on small screens.

Bug: 64293520
Test: On bullhead, boot into recovery with --prompt_and_wipe_data, and
      check the prompt texts.
Change-Id: Ia22746583516dd230567a267584aca558429395e
diff --git a/recovery.cpp b/recovery.cpp
index 233e562..c5ead45 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -757,12 +757,13 @@
 }
 
 static bool prompt_and_wipe_data(Device* device) {
+  // Use a single string and let ScreenRecoveryUI handles the wrapping.
   const char* const headers[] = {
-    "Can't load Android system. Your data may be corrupt.",
-    "If you continue to get this message, you may need to",
-    "perform a factory data reset and erase all user data",
+    "Can't load Android system. Your data may be corrupt. "
+    "If you continue to get this message, you may need to "
+    "perform a factory data reset and erase all user data "
     "stored on this device.",
-    NULL
+    nullptr
   };
   const char* const items[] = {
     "Try again",
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 8f792f1..e056512 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -278,6 +278,34 @@
   return offset;
 }
 
+int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const {
+  int offset = 0;
+  for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
+    // The line will be wrapped if it exceeds text_cols_.
+    std::string line(lines[i]);
+    size_t next_start = 0;
+    while (next_start < line.size()) {
+      std::string sub = line.substr(next_start, text_cols_ + 1);
+      if (sub.size() <= text_cols_) {
+        next_start += sub.size();
+      } else {
+        // Line too long and must be wrapped to text_cols_ columns.
+        size_t last_space = sub.find_last_of(" \t\n");
+        if (last_space == std::string::npos) {
+          // No space found, just draw as much as we can
+          sub.resize(text_cols_);
+          next_start += text_cols_;
+        } else {
+          sub.resize(last_space);
+          next_start += last_space + 1;
+        }
+      }
+      offset += DrawTextLine(x, y + offset, sub.c_str(), false);
+    }
+  }
+  return offset;
+}
+
 static const char* REGULAR_HELP[] = {
   "Use volume up/down and power.",
   NULL
@@ -316,7 +344,7 @@
     y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
 
     SetColor(HEADER);
-    y += DrawTextLines(x, y, menu_headers_);
+    y += DrawWrappedTextLines(x, y, menu_headers_);
 
     SetColor(MENU);
     y += DrawHorizontalRule(y) + 4;
diff --git a/screen_ui.h b/screen_ui.h
index 8402fac..df7cc25 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -187,6 +187,9 @@
   virtual int DrawTextLine(int x, int y, const char* line, bool bold) const;
   // Draws multiple text lines. Returns the offset it should be moving along Y-axis.
   int DrawTextLines(int x, int y, const char* const* lines) const;
+  // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines.
+  // Returns the offset it should be moving along Y-axis.
+  int DrawWrappedTextLines(int x, int y, const char* const* lines) const;
 };
 
 #endif  // RECOVERY_UI_H