3
\$\begingroup\$

I am attempting to create my own speech to text assistant. This involves the user speaking and the assistant responding to the user's input. I do this by looking for keywords that indicate the user wants to initiate a certain command. However, some commands can only be accessed if prior commands have been called already (e.g. Open Contacts > Search for Jim > Edit Number). I don't want Edit Number to be called unless Open Contacts and "Search for XXX" is called first.

Because of this, I have resorted to using an Enum class to label where the user is in the sequence of commands. Then, I use a switch to determine which lines of code to execute once I identify where the user is in the program. This seems unnecessarily complex and not easily maintainable. I was not able to find anything online about this since I don't know any keywords for this technique/process.

Is there a more efficient and maintainable way of programming the navigation for a menu interface such as this?

public void interpretResults(String results){
        switch (primaryStage) {
            case firstStage:
                switch (results) {
                    case "identification":
                    case "id":
                        setMessage(EnumPPSAssistantMessages.message_IDNumber);
                        id = 0;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "lotnumber":
                    case "lot":
                        setMessage(EnumPPSAssistantMessages.message_lotNumber);
                        id = 1;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "list":
                    case "listall":
                        startNewActivity(ListAllOnline.class);
                        break;
                    case "usedcomponents":
                    case "usedcomponent":
                        setInput("Use component(s).");
                    case "usecomponents":
                    case "usecomponent":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 3;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "bom":
                    case "billofmaterials":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 2;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "returncomponents":
                    case "returncomponent":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 4;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "usedparts":
                    case "usedpart":
                        setInput("Use part(s).");
                    case "useparts":
                    case "usepart":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 5;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "returnparts":
                    case "returnpart":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 6;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "pdf":
                    case "displaypdf":
                        setMessage(EnumPPSAssistantMessages.message_shopOrderNumber);
                        id = 7;
                        primaryStage = EnumStages.secondStage;
                        break;
                    case "commands":
                        setMessage(EnumPPSAssistantMessages.message_commands);
                        break;
                    case "":
                    default:
                        System.out.println("No match. Input is: " + temp);
                        setMessage(EnumPPSAssistantMessages.error_noMatch);
                        break;
                }
                break;
            case secondStage:
                switch (results) {
                    case "back":
                    case "go back":
                        id = -1;
                        primaryStage = EnumStages.firstStage;
                        setMessage(EnumPPSAssistantMessages.start_message);
                        return;
                }

                Variables.setVoiceOutput(func.formatSpeechInput(var.getVoiceOutput()));
                setInput(var.getVoiceOutput());
                switch (id) {
                    case 0:
                    case 1:
                        primaryStage = EnumStages.thirdStage;
                        break;
                    case 2:
                        startNewActivity(DisplayBillOfMaterials.class);
                        break;
                    case 3:
                        startNewActivity(ShopOrderTransactionPreCheck.class, new String[]{"type"}, new String[]{"usecomponents"});
                        break;
                    case 4:
                        startNewActivity(ShopOrderTransactionPreCheck.class, new String[]{"type"}, new String[]{"returncomponents"});
                        break;
                    case 5:
                        startNewActivity(ShopOrderTransactionPreCheck.class, new String[]{"type"}, new String[]{"useparts"});
                        break;
                    case 6:
                        startNewActivity(ShopOrderTransactionPreCheck.class, new String[]{"type"}, new String[]{"returnparts"});
                        break;
                    case 7:
                        startNewActivity(DownloadShopOrderPDF.class);
                        break;
                    case -1:
                        primaryStage = EnumStages.firstStage;
                        setMessage(EnumPPSAssistantMessages.start_message);
                }
                break;
        }
    }
\$\endgroup\$
0

1 Answer 1

3
\$\begingroup\$

Something I might do instead of using a switch with a bunch of constant strings. I might have a HashMap<String, Runnable> Where I would register each string and the action that should be taken.

e.g.

HashMap<String, Runnable> registeredAction = new HashMap<>();
        Runnable runnable = new Runnable() {
        @Override
        public void run() {
            setMessage(EnumPPSAssistantMessages.message_IDNumber);
            id = 0;
            primaryStage = EnumStages.secondStage;
        }
    });


    registeredAction.put("some-value", runnable);
    registeredAction.put("some-value-2", runnable);

Now when you want to perform an action you can just look it up.

Runnable action = null;
        if((action = registeredAction.get("SomeString")) != null) {
            action.run();
        }

I don't think its best practice this is just how I would go about doing it. Instead of having a massive switch statement.

\$\endgroup\$
5
  • \$\begingroup\$ The problem with this approach is that it doesn't take into account dynamic changes to the actions one can take. It also is not remotely well suited for configurable actions nor anything more complex than a simple tree. I'd highly recommend a simplistic graph structure \$\endgroup\$
    – Vogel612
    Commented Aug 16, 2018 at 20:53
  • \$\begingroup\$ @Vogel612 Can you elaborate? I am very interested in what sort of solution you are talking about. This is the best way I could think of. \$\endgroup\$
    – Sam Orozco
    Commented Aug 16, 2018 at 21:31
  • \$\begingroup\$ I'm not quite sure what part exactly is unclear. Could you make your question a bit more specific? Do you know about Graphs, the mathematical ones with nodes and edges? \$\endgroup\$
    – Vogel612
    Commented Aug 16, 2018 at 21:53
  • \$\begingroup\$ @Vogel612 Yes, can you provide an example? \$\endgroup\$
    – Sam Orozco
    Commented Aug 16, 2018 at 21:54
  • \$\begingroup\$ The simplest way to think of this is to use nodes that are aware of their neighbors and to encode the edges into the nodes. Then the menu choices become edges and their labels become menu labels. Attaching the runnables becomes a matter of implementing a field in that node and building the registered actions becomes creating a graph. That can in turn be used to keep state (by keeping track of a "current node") and to model even complex (and dynamic) structures. At the level of each action it's not so different from your proposed solution, it's just taking it two steps further \$\endgroup\$
    – Vogel612
    Commented Aug 16, 2018 at 21:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.