!7 cmodule SpeechRecognitionControl { S method = 'google; bool enabled = true; transient JPanel stack; // TODO: add elements when showing transient new RestartableCountdown deleteIfUnused; transient Set activeRequests; transient ReliableSingleThread rstStartStop; start { rstStartStop = dm_rst(this, r startStop); activeRequests = setWithNotify(rstStartStop); startCountdown(); if (dm_getBounds() == null) { int w = getWidth(dm_desktopPane()), h = getHeight(dm_desktopPane()); dm_setBounds(this, w-300, h-200, 300, 200); } } visualize { ret centerAndSouth( withRightMargin(stack = dynamicVStack2()), withMargin(dm_rightAlignedFieldCheckBox('enabled))); } // start countdown to delete module if unused void startCountdown { if (enabled) deleteIfUnused.start(10*60.0, rEnter dm_deleteModule); } void startStop { if (enabled && nempty(activeRequests)) dm_requireAndCallModule("#1018551/SpeechRecognizer", 'startListening); else dm_findAndCallModule("#1018551/SpeechRecognizer", 'stopListening); } // API // Note: cancelMe will be called in GUI thread AutoCloseable activateSpeechRecognition(S reason) { deleteIfUnused.stop(); final JLabel lbl = jlabel(reason); final new Var c; final AutoCloseable release = autoCloseable { removeAndValidate(c!); activeRequests.remove(this); startCountdown(); }; c.set(centerAndEast(lbl, jbutton("Cancel", r { close(release) }))); addAndValidate(stack, c!); ret addAndReturn(activeRequests, release); } void cancelAll { closeAll_cloneList(activeRequests); } }