app_directory: Add ADSI support to Directory.

This adds optional ADSI support to the Directory
application, which allows callers with ADSI CPE
to navigate the Directory system significantly
faster than is possible using the audio prompts.
Callers can see the directory name (and optionally
extension) on their screenphone and confirm or
reject a match immediately rather than waiting
for it to be spelled out, enhancing usability.

Resolves: #356
This commit is contained in:
Naveen Albert 2023-09-27 08:49:07 -04:00
parent 2191a0d33f
commit 5046620fa3
1 changed files with 91 additions and 1 deletions

View File

@ -39,6 +39,7 @@
#include "asterisk/say.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/adsi.h"
/*** DOCUMENTATION
<application name="Directory" language="en_US">
@ -111,12 +112,17 @@
<para>Skip calling the extension, instead set it in the <variable>DIRECTORY_EXTEN</variable>
channel variable.</para>
</option>
<option name="d">
<para>Enable ADSI support for screen phone searching and retrieval
of directory results.</para>
<para>Additionally, the channel must be ADSI-enabled and you must
have an ADSI-compatible (Type III) CPE for this to work.</para>
</option>
</optionlist>
<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as
if <replaceable>b</replaceable> was specified. The number
of characters for the user to type defaults to <literal>3</literal>.</para></note>
</parameter>
</syntax>
<description>
@ -167,6 +173,7 @@ enum {
OPT_ALIAS = (1 << 7),
OPT_CONFIG_FILE = (1 << 8),
OPT_SKIP = (1 << 9),
OPT_ADSI = (1 << 10),
};
enum {
@ -200,8 +207,72 @@ AST_APP_OPTIONS(directory_app_options, {
AST_APP_OPTION('a', OPT_ALIAS),
AST_APP_OPTION_ARG('c', OPT_CONFIG_FILE, OPT_ARG_FILENAME),
AST_APP_OPTION('s', OPT_SKIP),
AST_APP_OPTION('d', OPT_ADSI), /* (Would've used 'a', but that was taken already) */
});
static int adsi_search_input(struct ast_channel *chan)
{
unsigned char buf[256];
int bytes = 0;
unsigned char keys[6];
memset(keys, 0, sizeof(keys));
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Query: ***", "");
bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Search", "Search", "#", 1);
bytes += ast_adsi_set_keys(buf + bytes, keys);
bytes += ast_adsi_voice_mode(buf + bytes, 0);
ast_debug(3, "Sending ADSI search input screen on %s\n", ast_channel_name(chan));
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
}
static int adsi_confirm_match(struct ast_channel *chan, int seq, int total, const char *exten, const char *name, int showexten)
{
unsigned char buf[4096];
int alignments[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
char *lines[5] = {NULL, NULL, NULL, NULL, NULL};
int x, bytes = 0;
unsigned char keys[8];
char matchbuf[32];
snprintf(matchbuf, sizeof(matchbuf), "%d of %d", seq + 1, total); /* Make it 1-indexed for user consumption */
lines[0] = " "; /* Leave the first line empty so the following lines stand out more */
lines[1] = matchbuf;
lines[2] = (char*) name;
if (showexten) {
/* If say extension option is set, show it for ADSI as well */
lines[3] = (char*) exten;
}
/* Don't use ast_adsi_print here, this way we can send it all at once instead of in 2 transmissions */
for (x = 0; lines[x]; x++) {
bytes += ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x + 1, alignments[x], 0, lines[x], "");
}
bytes += ast_adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
keys[3] = ADSI_KEY_APPS + 3;
keys[4] = ADSI_KEY_APPS + 4;
keys[5] = ADSI_KEY_APPS + 5;
/* You might think we only need to set the keys up the first time, but nope, we've got to do it each time. */
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Dial", "Dial", "1", 0);
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Next", "Next", "*", 0);
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 0);
bytes += ast_adsi_set_keys(buf + bytes, keys);
bytes += ast_adsi_voice_mode(buf + bytes, 0);
ast_debug(3, "Sending ADSI confirmation menu for %s\n", name);
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
}
static int compare(const char *text, const char *template)
{
char digit;
@ -376,6 +447,10 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite
for (ptr = items, i = 0; i < count; i++, ptr++) {
item = *ptr;
if (ast_test_flag(flags, OPT_ADSI) && adsi_confirm_match(chan, i, count, item->exten, item->name, ast_test_flag(flags, OPT_SAYEXTENSION))) {
return -1;
}
for (loop = 3 ; loop > 0; loop--) {
if (!res)
res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
@ -935,6 +1010,18 @@ static int directory_exec(struct ast_channel *chan, const char *data)
}
digits[7] = digit + '0';
if (ast_test_flag(&flags, OPT_ADSI)) {
if (!ast_adsi_available(chan)) {
ast_log(LOG_WARNING, "ADSI not available on %s\n", ast_channel_name(chan));
ast_clear_flag(&flags, OPT_ADSI);
} else {
res = ast_adsi_load_session(chan, NULL, 0, 1);
if (res < 0) {
return res;
}
}
}
if (ast_channel_state(chan) != AST_STATE_UP) {
if (!ast_test_flag(&flags, OPT_NOANSWER)) {
/* Otherwise answer unless we're supposed to read while on-hook */
@ -942,6 +1029,9 @@ static int directory_exec(struct ast_channel *chan, const char *data)
}
}
for (;;) {
if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
return -1;
}
if (!ast_strlen_zero(dirintro) && !res) {
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
} else if (!res) {