mirror of
https://github.com/google/pebble.git
synced 2026-02-21 12:36:50 -05:00
Import of the watch repository from Pebble
This commit is contained in:
226
src/fw/apps/sdk/sdk_app.c
Normal file
226
src/fw/apps/sdk/sdk_app.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 "sdk_app.h"
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/graphics/perimeter.h"
|
||||
#include "applib/ui/app_window_stack.h"
|
||||
#include "applib/ui/text_layer.h"
|
||||
#include "applib/ui/window_private.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
#include "apps/system_apps/timeline/timeline.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "services/common/clock.h"
|
||||
#include "shell/sdk/shell_sdk.h"
|
||||
#include "shell/sdk/watchface.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
typedef struct SdkAppData {
|
||||
Window window;
|
||||
TextLayer info_text_layer;
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
TextLayer time_text_layer;
|
||||
char time_buffer[TIME_STRING_TIME_LENGTH];
|
||||
#endif
|
||||
} SdkAppData;
|
||||
|
||||
static void prv_sdk_home_update_proc(Layer *layer, GContext *ctx) {
|
||||
window_do_layer_update_proc(layer, ctx);
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
SdkAppData *data = app_state_get_user_data();
|
||||
const int time_max_y = grect_get_max_y(&data->time_text_layer.layer.frame);
|
||||
const int line_top_margin = 14;
|
||||
const int line_stroke_width = 2;
|
||||
graphics_context_set_fill_color(ctx, GColorBlack);
|
||||
graphics_fill_rect(ctx, &GRect(0, time_max_y + line_top_margin, DISP_COLS, line_stroke_width));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_update_time(SdkAppData *data) {
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
clock_copy_time_string(data->time_buffer, sizeof(data->time_buffer));
|
||||
text_layer_set_text(&data->time_text_layer, data->time_buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_update_info(SdkAppData *data) {
|
||||
const AppInstallId app_id = shell_sdk_get_last_installed_app();
|
||||
const char *text;
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
const unsigned int tip_delay_s = 5;
|
||||
if ((app_id == INSTALL_ID_INVALID) || shell_sdk_last_installed_app_is_watchface()) {
|
||||
text = "Install an app to continue";
|
||||
} else if ((rtc_get_time() / tip_delay_s) & 1) {
|
||||
// Show the launcher help on every odd set of five seconds since the epoch
|
||||
text = "Press Select to access Launcher";
|
||||
} else {
|
||||
text = "Press Down to access Timeline";
|
||||
}
|
||||
#else
|
||||
GColor color;
|
||||
if (app_id == INSTALL_ID_INVALID) {
|
||||
text = "Install an app to continue or press up / down to browse the timeline";
|
||||
color = GColorChromeYellow;
|
||||
} else {
|
||||
text = "Press select to launch your app or press up / down to browse the timeline";
|
||||
color = GColorPictonBlue;
|
||||
}
|
||||
window_set_background_color(&data->window, PBL_IF_COLOR_ELSE(color, GColorWhite));
|
||||
#endif
|
||||
text_layer_set_text(&data->info_text_layer, text);
|
||||
}
|
||||
|
||||
static void prv_update_ui(SdkAppData *data) {
|
||||
prv_update_time(data);
|
||||
prv_update_info(data);
|
||||
}
|
||||
|
||||
static void prv_handle_tick_timer(struct tm *tick_time, TimeUnits units_changed) {
|
||||
prv_update_ui(app_state_get_user_data());
|
||||
}
|
||||
|
||||
#if !CAPABILITY_HAS_SDK_SHELL4
|
||||
static void prv_launch_last_installed_app(ClickRecognizerRef recognizer, void *data) {
|
||||
const AppInstallId app_id = shell_sdk_get_last_installed_app();
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Last installed app is %"PRId32, app_id);
|
||||
|
||||
if (app_id != INSTALL_ID_INVALID) {
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) { .id = app_id });
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) {
|
||||
static TimelineArgs s_timeline_args = {
|
||||
.launch_into_pin = false,
|
||||
.pin_id = UUID_INVALID_INIT,
|
||||
};
|
||||
|
||||
const bool is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP);
|
||||
if (is_up) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in past mode.");
|
||||
s_timeline_args.direction = TimelineIterDirectionPast;
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in future mode.");
|
||||
s_timeline_args.direction = TimelineIterDirectionFuture;
|
||||
}
|
||||
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_TIMELINE,
|
||||
.common.args = &s_timeline_args,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
static void prv_config_provider(void *data) {
|
||||
#if !CAPABILITY_HAS_SDK_SHELL4
|
||||
window_single_click_subscribe(BUTTON_ID_UP, prv_launch_timeline);
|
||||
window_single_click_subscribe(BUTTON_ID_SELECT, prv_launch_last_installed_app);
|
||||
window_single_click_subscribe(BUTTON_ID_DOWN, prv_launch_timeline);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_init(void) {
|
||||
SdkAppData *data = app_malloc_check(sizeof(SdkAppData));
|
||||
|
||||
Window *window = &data->window;
|
||||
window_init(window, WINDOW_NAME("SDK Home"));
|
||||
window_set_click_config_provider(window, prv_config_provider);
|
||||
window_set_overrides_back_button(window, true);
|
||||
window_set_fullscreen(window, true);
|
||||
layer_set_update_proc(&window->layer, prv_sdk_home_update_proc);
|
||||
|
||||
GRect frame = data->window.layer.frame;
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
const int top_margin = PBL_IF_RECT_ELSE(19, 25);
|
||||
const int time_height = 46;
|
||||
frame.origin.y = top_margin;
|
||||
frame.size.h = time_height;
|
||||
|
||||
TextLayer *time_text_layer = &data->time_text_layer;
|
||||
text_layer_init(time_text_layer, &frame);
|
||||
text_layer_set_font(time_text_layer, fonts_get_system_font(FONT_KEY_LECO_42_NUMBERS));
|
||||
text_layer_set_text_alignment(time_text_layer, GTextAlignmentCenter);
|
||||
text_layer_set_overflow_mode(time_text_layer, GTextOverflowModeWordWrap);
|
||||
text_layer_set_background_color(&data->time_text_layer, GColorClear);
|
||||
layer_add_child(&window->layer, &time_text_layer->layer);
|
||||
|
||||
const int time_padding = 22;
|
||||
frame.origin.y += time_height + time_padding;
|
||||
frame.size.h = DISP_ROWS - frame.origin.y;
|
||||
#elif PBL_RECT
|
||||
const int top_margin = 23;
|
||||
frame.origin.y = top_margin;
|
||||
frame.size.h -= top_margin;
|
||||
#endif
|
||||
|
||||
TextLayer *info_text_layer = &data->info_text_layer;
|
||||
text_layer_init(info_text_layer, &frame);
|
||||
text_layer_set_font(info_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
|
||||
text_layer_set_text_alignment(info_text_layer, GTextAlignmentCenter);
|
||||
text_layer_set_background_color(&data->info_text_layer, GColorClear);
|
||||
layer_add_child(&window->layer, &info_text_layer->layer);
|
||||
|
||||
#if PBL_ROUND
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
const uint8_t inset = 8;
|
||||
#else
|
||||
const uint8_t inset = 18;
|
||||
#endif
|
||||
text_layer_enable_screen_text_flow_and_paging(info_text_layer, inset);
|
||||
#endif
|
||||
|
||||
app_state_set_user_data(data);
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
window_set_background_color(window, PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite));
|
||||
tick_timer_service_subscribe(SECOND_UNIT, prv_handle_tick_timer);
|
||||
#endif
|
||||
|
||||
prv_update_ui(data);
|
||||
|
||||
app_window_stack_push(window, true /* is_animated */);
|
||||
}
|
||||
|
||||
static void s_main(void) {
|
||||
prv_init();
|
||||
|
||||
app_event_loop();
|
||||
}
|
||||
|
||||
const PebbleProcessMd* sdk_app_get_info(void) {
|
||||
static const PebbleProcessMdSystem s_app_md = {
|
||||
.common = {
|
||||
.main_func = s_main,
|
||||
// UUID: 1197fc39-47e7-439b-82be-f56d9ba1dbd8
|
||||
.uuid = { 0x11, 0x97, 0xfc, 0x39, 0x47, 0xe7, 0x43, 0x9b,
|
||||
0x82, 0xbe, 0xf5, 0x6d, 0x9b, 0xa1, 0xdb, 0xd8 },
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
.process_type = ProcessTypeWatchface
|
||||
#endif
|
||||
},
|
||||
.icon_resource_id = RESOURCE_ID_MENU_ICON_TICTOC_WATCH,
|
||||
.name = "TicToc"
|
||||
};
|
||||
return (const PebbleProcessMd*) &s_app_md;
|
||||
}
|
||||
21
src/fw/apps/sdk/sdk_app.h
Normal file
21
src/fw/apps/sdk/sdk_app.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "process_management/pebble_process_md.h"
|
||||
|
||||
const PebbleProcessMd* sdk_app_get_info(void);
|
||||
Reference in New Issue
Block a user