Add Softkey UIState to Viewer

We can use this to simulate any key/command on Android UI.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2035923002

Review-Url: https://codereview.chromium.org/2035923002
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index c73b1ba..7e345c5 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -65,6 +65,9 @@
 const char* kOptions = "options";
 const char* kSlideStateName = "Slide";
 const char* kBackendStateName = "Backend";
+const char* kSoftkeyStateName = "Softkey";
+const char* kSoftkeyHint = "Please select a softkey";
+
 
 Viewer::Viewer(int argc, char** argv, void* platformData)
     : fCurrentMeasurement(0)
@@ -411,6 +414,7 @@
 }
 
 void Viewer::updateUIState() {
+    // Slide state
     Json::Value slideState(Json::objectValue);
     slideState[kName] = kSlideStateName;
     slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
@@ -420,19 +424,29 @@
     }
     slideState[kOptions] = allSlideNames;
 
-    // This state is currently a demo for the one without options.
-    // We will be able to change the backend too.
+    // Backend state
     Json::Value backendState(Json::objectValue);
     backendState[kName] = kBackendStateName;
     backendState[kValue] = kBackendTypeStrings[fBackendType];
     backendState[kOptions] = Json::Value(Json::arrayValue);
-    for(auto str : kBackendTypeStrings) {
+    for (auto str : kBackendTypeStrings) {
         backendState[kOptions].append(Json::Value(str));
     }
 
+    // Softkey state
+    Json::Value softkeyState(Json::objectValue);
+    softkeyState[kName] = kSoftkeyStateName;
+    softkeyState[kValue] = kSoftkeyHint;
+    softkeyState[kOptions] = Json::Value(Json::arrayValue);
+    softkeyState[kOptions].append(kSoftkeyHint);
+    for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
+        softkeyState[kOptions].append(Json::Value(softkey.c_str()));
+    }
+
     Json::Value state(Json::arrayValue);
     state.append(slideState);
     state.append(backendState);
+    state.append(softkeyState);
 
     fWindow->setUIState(state);
 }
@@ -470,6 +484,11 @@
                 break;
             }
         }
+    } else if (stateName.equals(kSoftkeyStateName)) {
+        if (!stateValue.equals(kSoftkeyHint)) {
+            fCommands.onSoftkey(stateValue);
+            updateUIState(); // This is still needed to reset the value to kSoftkeyHint
+        }
     } else {
         SkDebugf("Unknown stateName: %s", stateName.c_str());
     }
diff --git a/tools/viewer/sk_app/CommandSet.cpp b/tools/viewer/sk_app/CommandSet.cpp
index e426eaa..4805e6a 100644
--- a/tools/viewer/sk_app/CommandSet.cpp
+++ b/tools/viewer/sk_app/CommandSet.cpp
@@ -71,6 +71,16 @@
     return false;
 }
 
+bool CommandSet::onSoftkey(const SkString& softkey) {
+    for (const Command& cmd : fCommands) {
+        if (cmd.getSoftkeyString().equals(softkey)) {
+            cmd.fFunction();
+            return true;
+        }
+    }
+    return false;
+}
+
 void CommandSet::addCommand(SkUnichar c, const char* group, const char* description,
                             std::function<void(void)> function) {
     fCommands.push_back(Command(c, group, description, function));
@@ -154,4 +164,12 @@
     }
 }
 
+std::vector<SkString> CommandSet::getCommandsAsSoftkeys() const {
+    std::vector<SkString> result;
+    for(const Command& command : fCommands) {
+        result.push_back(command.getSoftkeyString());
+    }
+    return result;
+}
+
 }   // namespace sk_app
diff --git a/tools/viewer/sk_app/CommandSet.h b/tools/viewer/sk_app/CommandSet.h
index 5e6373c..4cbb367 100644
--- a/tools/viewer/sk_app/CommandSet.h
+++ b/tools/viewer/sk_app/CommandSet.h
@@ -12,6 +12,7 @@
 #include "Window.h"
 
 #include <functional>
+#include <vector>
 
 class SkCanvas;
 
@@ -40,6 +41,7 @@
     void attach(Window* window);
     bool onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers);
     bool onChar(SkUnichar, uint32_t modifiers);
+    bool onSoftkey(const SkString& softkey);
 
     void addCommand(SkUnichar c, const char* group, const char* description,
                     std::function<void(void)> function);
@@ -48,6 +50,8 @@
 
     void drawHelp(SkCanvas* canvas);
 
+    std::vector<SkString> getCommandsAsSoftkeys() const;
+
 private:
     struct Command {
         enum CommandType {
@@ -86,6 +90,10 @@
         SkString fGroup;
         SkString fDescription;
         std::function<void(void)> fFunction;
+
+        SkString getSoftkeyString() const {
+            return SkStringPrintf("%s (%s)", fKeyName.c_str(), fDescription.c_str());
+        }
     };
 
     static bool compareCommandKey(const Command& first, const Command& second);