Files
pebble/src/fw/apps/demo_apps/dialogs_demo.c
T

411 lines
16 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dialogs_demo.h"
#include "applib/app.h"
#include "applib/graphics/gdraw_command_image.h"
#include "applib/graphics/gdraw_command_transforms.h"
#include "applib/ui/app_window_stack.h"
#include "applib/ui/dialogs/dialog.h"
#include "applib/ui/dialogs/simple_dialog.h"
#include "applib/ui/dialogs/actionable_dialog.h"
#include "applib/ui/dialogs/expandable_dialog.h"
#include "applib/ui/window.h"
#include "applib/voice/transcription_dialog.h"
#include "kernel/pbl_malloc.h"
#include "process_management/app_menu_data_source.h"
#include "shell/normal/watchface.h"
#include "process_state/app_state/app_state.h"
#include "resource/resource_ids.auto.h"
#include "services/common/i18n/i18n.h"
#include "system/passert.h"
#include "util/size.h"
#include <stdio.h>
#include <string.h>
typedef struct DialogsData {
Window window;
MenuLayer menu_layer;
const char *lorem_ipsum;
const char *long_message;
uint32_t resource_id_80;
uint32_t resource_id_25;
} DialogsData;
/////////////////////////////
// Simple Dialog with timeout
static void prv_show_simple_dialog(DialogsData *data) {
SimpleDialog *simple_dialog = simple_dialog_create("Simple Dialog");
Dialog *dialog = simple_dialog_get_dialog(simple_dialog);
dialog_set_text(dialog, "Mama");
dialog_set_background_color(dialog, GColorRajah);
dialog_set_icon(dialog, data->resource_id_80);
dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT);
app_simple_dialog_push(simple_dialog);
}
///////////////////////////////
// Simple Dialog with vibration
static void prv_show_simple_dialog_vibe(DialogsData *data) {
SimpleDialog *simple_dialog = simple_dialog_create("Simple Vibe Dialog");
Dialog *dialog = simple_dialog_get_dialog(simple_dialog);
dialog_set_text(dialog, "A Simple Dialog For Flow Testing!\nHello!");
dialog_set_background_color(dialog, GColorLavenderIndigo);
dialog_set_icon(dialog, data->resource_id_80);
dialog_set_vibe(dialog, true);
dialog_show_status_bar_layer(dialog, true);
app_simple_dialog_push(simple_dialog);
}
//////////////////////
// Confirmation Dialog
static void prv_confirm_click_handler(ClickRecognizerRef recognizer, void *context) {
app_window_stack_pop(true);
}
static void prv_confirm_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, prv_confirm_click_handler);
}
static void prv_show_confirm_dialog(DialogsData *data) {
ActionableDialog *actionable_dialog = actionable_dialog_create("Confirm Dialog");
Dialog *dialog = actionable_dialog_get_dialog(actionable_dialog);
dialog_set_text(dialog, "Confirmation");
dialog_set_background_color(dialog, GColorGreen);
dialog_set_icon(dialog, data->resource_id_80);
dialog_show_status_bar_layer(dialog, true);
actionable_dialog_set_action_bar_type(actionable_dialog, DialogActionBarConfirm, NULL);
actionable_dialog_set_click_config_provider(actionable_dialog,
prv_confirm_config_provider);
app_actionable_dialog_push(actionable_dialog);
}
/////////////////
// Decline Dialog
static void prv_decline_click_handler(ClickRecognizerRef recognizer, void *context) {
app_window_stack_pop(true);
}
static void prv_decline_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, prv_decline_click_handler);
}
static void prv_show_decline_dialog(DialogsData *data) {
ActionableDialog *actionable_dialog = actionable_dialog_create("Decline Dialog");
Dialog *dialog = actionable_dialog_get_dialog(actionable_dialog);
dialog_set_text(dialog, i18n_get("Decline dialog.", dialog));
dialog_set_background_color(dialog, GColorRed);
dialog_set_icon(dialog, data->resource_id_80);
actionable_dialog_set_action_bar_type(actionable_dialog, DialogActionBarDecline, NULL);
actionable_dialog_set_click_config_provider(actionable_dialog,
prv_decline_config_provider);
app_actionable_dialog_push(actionable_dialog);
}
//////////////////////////////////////////
// ActionableDialog with custom action bar
static void prv_custom_action_bar_click_up(ClickRecognizerRef recognizer, void *context) {
Dialog *dialog = context;
dialog_set_text(dialog, "The text has changed!");
layer_mark_dirty(&dialog->text_layer.layer);
}
static void prv_custom_action_bar_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_UP, prv_custom_action_bar_click_up);
}
static void prv_show_custom_actionable_dialog(DialogsData *data) {
ActionableDialog *actionable_dialog = actionable_dialog_create("Custom Actionable Dialog");
Dialog *dialog = actionable_dialog_get_dialog(actionable_dialog);
dialog_set_text(dialog, "Custom Actionable Dialog");
dialog_set_background_color(dialog, GColorRed);
dialog_set_icon(dialog, data->resource_id_80);
// Create custom action bar for the dialog.
// TODO destroy action bar / icon in unload callback
ActionBarLayer *custom_action_bar = action_bar_layer_create();
action_bar_layer_set_icon(custom_action_bar, BUTTON_ID_UP,
gbitmap_create_with_resource(RESOURCE_ID_ACTION_BAR_ICON_CHECK));
action_bar_layer_set_context(custom_action_bar, dialog);
action_bar_layer_set_click_config_provider(custom_action_bar,
prv_custom_action_bar_config_provider);
actionable_dialog_set_action_bar_type(actionable_dialog, DialogActionBarCustom,
custom_action_bar);
app_actionable_dialog_push(actionable_dialog);
}
////////////////////
// Expandable Dialog
// Has a custom icon/clickhandler for the select button
static void prv_my_select_click_handler(ClickRecognizerRef recognizer, void *context) {
ExpandableDialog *expandable_dialog = context;
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_background_color(dialog, GColorRed);
}
static void prv_show_expandable_dialog(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_set_icon(dialog, data->resource_id_25);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_set_select_action(expandable_dialog,
RESOURCE_ID_ACTION_BAR_ICON_CHECK, prv_my_select_click_handler);
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////
// Expandable Dialog with header
static void prv_show_expandable_dialog_header(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_set_icon(dialog, data->resource_id_25);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_set_header(expandable_dialog, "Header");
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////
// Expandable Dialog with multi line header
static void prv_show_expandable_dialog_long_header(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_set_icon(dialog, data->resource_id_25);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_set_header(expandable_dialog, "A very long header");
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////////////
// Expandable Dialog no icon and header
static void prv_show_expandable_dialog_header_no_icon(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_set_header(expandable_dialog, "Header");
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////
// Expandable Dialog with header
static void prv_show_expandable_dialog_no_action_bar(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_set_icon(dialog, data->resource_id_25);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_show_action_bar(expandable_dialog, false);
expandable_dialog_set_header(expandable_dialog, "Header");
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////
// Expandable Dialog with icon
static void prv_show_expandable_dialog_no_icon(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, data->lorem_ipsum);
dialog_set_background_color(dialog, GColorLightGray);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_show_action_bar(expandable_dialog, true);
app_expandable_dialog_push(expandable_dialog);
}
///////////////////////////////////
// Expandable Dialog with no scroll
static void prv_show_expandable_dialog_no_scroll(DialogsData *data) {
ExpandableDialog *expandable_dialog = expandable_dialog_create("Expandable Dialog");
Dialog *dialog = expandable_dialog_get_dialog(expandable_dialog);
dialog_set_text(dialog, "Look mah, no scroll!");
dialog_set_background_color(dialog, GColorLightGray);
dialog_show_status_bar_layer(dialog, true);
expandable_dialog_show_action_bar(expandable_dialog, true);
app_expandable_dialog_push(expandable_dialog);
}
////////////////////////////////
// Voice Dialog
static void prv_transcription_dialog_cb(void *context) {
DialogsData *data = context;
SimpleDialog *simple_dialog = simple_dialog_create("Simple Dialog");
Dialog *dialog = simple_dialog_get_dialog(simple_dialog);
dialog_set_text(dialog, "Pop!");
dialog_set_background_color(dialog, GColorRed);
dialog_set_icon(dialog, data->resource_id_80);
dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT);
app_simple_dialog_push(simple_dialog);
}
static void prv_show_transcription_dialog(DialogsData *data) {
TranscriptionDialog *transcription_dialog = transcription_dialog_create();
static char transcription[500] = {0};
uint16_t len = strlen(data->long_message);
strncpy(transcription, data->long_message, len);
transcription_dialog_update_text(transcription_dialog, transcription, len);
app_transcription_dialog_push(transcription_dialog);
transcription_dialog_set_callback(transcription_dialog, prv_transcription_dialog_cb, data);
}
/////////////////////////////////////
// Set up dialog labels and callbacks
typedef struct DialogNode {
const char *label;
void (*show)(DialogsData *data);
} DialogNode;
static const DialogNode nodes[] = {
{ .label = "D1 - Confirm", .show = prv_show_confirm_dialog, },
{ .label = "D2 - Decline", .show = prv_show_decline_dialog, },
{ .label = "D3 - Actionable", .show = prv_show_custom_actionable_dialog, },
{ .label = "D4 - Expandable", .show = prv_show_expandable_dialog, },
{ .label = "D4 - Exp with header", .show = prv_show_expandable_dialog_header, },
{ .label = "D4 - Exp with long header", .show = prv_show_expandable_dialog_long_header, },
{ .label = "D5 - Simple Timeout", .show = prv_show_simple_dialog, },
{ .label = "D5 - Simple Vibe", .show = prv_show_simple_dialog_vibe, },
{ .label = "D6 - Exp no action bar", .show = prv_show_expandable_dialog_no_action_bar, },
{ .label = "D7 - Exp no icon", .show = prv_show_expandable_dialog_no_icon, },
{ .label = "D8 - Exp no scroll", .show = prv_show_expandable_dialog_no_scroll, },
{ .label = "D9 - Exp header only", .show = prv_show_expandable_dialog_header_no_icon },
{ .label = "D10 - Transcription", .show = prv_show_transcription_dialog, }
};
static const uint16_t NUM_ITEMS = ARRAY_LENGTH(nodes);
//////////////
// MenuLayer callbacks
static void prv_draw_row_callback(GContext* ctx, Layer *cell_layer, MenuIndex *cell_index,
DialogsData *data) {
menu_cell_basic_draw(ctx, cell_layer, nodes[cell_index->row].label, NULL, NULL);
}
static void prv_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index,
DialogsData *data) {
nodes[cell_index->row].show(data);
}
static uint16_t prv_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index,
DialogsData *data) {
return NUM_ITEMS;
}
///////////////////
// Window callbacks
static void prv_window_load(Window *window) {
DialogsData *data = window_get_user_data(window);
MenuLayer *menu_layer = &data->menu_layer;
menu_layer_init(menu_layer, &window->layer.bounds);
data->lorem_ipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua. Utem ad happy.";
data->long_message = "Don't you see how great this is? You, you are a... Jesse look at me. "
"You... are a blowfish. A blowfish! Think about it. Small in stature, "
"not swift, not cunning. Easy prey for predators but the blowfish has a "
"secret weapon doesn't he. Doesn't he? What does the blowfish do, Jesse."
" What does the blowfish do? The blowfish puffs up, okay?";
data->resource_id_80 = RESOURCE_ID_GENERIC_CONFIRMATION_LARGE;
data->resource_id_25 = RESOURCE_ID_GENERIC_CONFIRMATION_TINY;
menu_layer_set_callbacks(menu_layer, data, &(MenuLayerCallbacks) {
.get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) prv_get_num_rows_callback,
.draw_row = (MenuLayerDrawRowCallback) prv_draw_row_callback,
.select_click = (MenuLayerSelectCallback) prv_select_callback,
});
menu_layer_set_click_config_onto_window(menu_layer, window);
layer_add_child(&window->layer, menu_layer_get_layer(menu_layer));
}
static void prv_handle_init(void) {
DialogsData *data = app_zalloc_check(sizeof(DialogsData));
app_state_set_user_data(data);
Window *window = &data->window;
window_init(window, WINDOW_NAME("Dialogs"));
window_set_user_data(window, data);
window_set_window_handlers(window, &(WindowHandlers) {
.load = prv_window_load,
});
const bool animated = true;
app_window_stack_push(window, animated);
}
static void prv_handle_deinit() {
DialogsData *data = app_state_get_user_data();
menu_layer_deinit(&data->menu_layer);
i18n_free_all(data);
app_free(data);
}
////////////////////
// App boilerplate
static void s_main(void) {
prv_handle_init();
app_event_loop();
prv_handle_deinit();
}
const PebbleProcessMd* dialogs_demo_get_app_info() {
static const PebbleProcessMdSystem s_app_md = {
.common = {
.main_func = s_main,
// UUID: ab470e5f-5ffd-46f2-9aa9-f48352ea5499
.uuid = {0xab, 0x47, 0x0e, 0x5f, 0x5f, 0xfd, 0x46, 0xf2,
0x9a, 0xa9, 0xf4, 0x83, 0x52, 0xea, 0x54, 0x99},
},
.name = "Dialogs",
};
return (const PebbleProcessMd*) &s_app_md;
}