mirror of
https://github.com/google/pebble
synced 2026-10-16 08:14:11 +02:00
411 lines
16 KiB
C
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;
|
|
}
|