mirror of
https://github.com/google/pebble.git
synced 2025-05-24 20:24:53 +00:00
172 lines
5.6 KiB
C
172 lines
5.6 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 <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "applib/app.h"
|
|
#include "applib/app_timer.h"
|
|
#include "applib/battery_state_service.h"
|
|
#include "applib/ui/dialogs/simple_dialog.h"
|
|
#include "applib/ui/window.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "kernel/util/standby.h"
|
|
#include "process_management/pebble_process_md.h"
|
|
#include "process_management/worker_manager.h"
|
|
#include "process_state/app_state/app_state.h"
|
|
#include "resource/resource.h"
|
|
#include "resource/resource_ids.auto.h"
|
|
#include "services/runlevel.h"
|
|
#include "services/common/status_led.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
#include "system/reboot_reason.h"
|
|
#include "system/reset.h"
|
|
|
|
static const uint32_t CHARGER_DISCONNECT_TIMEOUT_MS = 3000;
|
|
|
|
typedef enum DialogState {
|
|
DialogState_Uninitialized = 0,
|
|
DialogState_Charging,
|
|
DialogState_FullyCharged,
|
|
} DialogState;
|
|
|
|
|
|
struct AppData {
|
|
SimpleDialog *dialog;
|
|
AppTimer *poweroff_timer;
|
|
DialogState last_dialog_state;
|
|
bool was_plugged;
|
|
};
|
|
|
|
static void prv_reboot_on_click(ClickRecognizerRef recognizer, void *data) {
|
|
// Don't try to return to normal functioning; just reboot the watch. The user
|
|
// thinks the watch is already off anyway.
|
|
RebootReason reboot_reason = { RebootReasonCode_ShutdownMenuItem };
|
|
reboot_reason_set(&reboot_reason);
|
|
system_reset();
|
|
}
|
|
|
|
static void prv_config_provider(void *context) {
|
|
for (int i = 0; i < NUM_BUTTONS; ++i) {
|
|
window_long_click_subscribe(i, 0, prv_reboot_on_click, NULL);
|
|
}
|
|
}
|
|
|
|
static void prv_power_off_timer_expired(void *data) {
|
|
enter_standby(RebootReasonCode_ShutdownMenuItem);
|
|
}
|
|
|
|
static void prv_battery_state_handler(BatteryChargeState charge) {
|
|
struct AppData *data = app_state_get_user_data();
|
|
if (charge.is_plugged && !data->was_plugged) {
|
|
app_timer_cancel(data->poweroff_timer);
|
|
} else if (!charge.is_plugged && data->was_plugged) {
|
|
data->poweroff_timer = app_timer_register(
|
|
CHARGER_DISCONNECT_TIMEOUT_MS, prv_power_off_timer_expired, NULL);
|
|
}
|
|
|
|
DialogState next_dialog_state = DialogState_Uninitialized;
|
|
|
|
if (charge.is_charging) {
|
|
next_dialog_state = DialogState_Charging;
|
|
} else if (charge.is_plugged) {
|
|
next_dialog_state = DialogState_FullyCharged;
|
|
} else {
|
|
// Unplugged. We'll be shutting down in a couple seconds if the user doesn't
|
|
// plug the charger back in, so don't change the dialog.
|
|
next_dialog_state = data->last_dialog_state;
|
|
}
|
|
|
|
Dialog *dialog = simple_dialog_get_dialog(data->dialog);
|
|
|
|
if (next_dialog_state != data->last_dialog_state) {
|
|
// Setting the dialog icon to itself restarts the animation, which looks
|
|
// bad, so we want to avoid that if we can help it.
|
|
switch (next_dialog_state) {
|
|
case DialogState_FullyCharged:
|
|
dialog_set_text(dialog, i18n_get("Fully Charged", data));
|
|
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED);
|
|
break;
|
|
case DialogState_Charging:
|
|
default:
|
|
dialog_set_text(dialog, i18n_get("Charging", data));
|
|
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (charge.is_plugged) {
|
|
if (charge.is_charging) {
|
|
status_led_set(StatusLedState_Charging);
|
|
} else {
|
|
status_led_set(StatusLedState_FullyCharged);
|
|
}
|
|
} else {
|
|
status_led_set(StatusLedState_Off);
|
|
}
|
|
|
|
data->was_plugged = charge.is_plugged;
|
|
data->last_dialog_state = next_dialog_state;
|
|
}
|
|
|
|
static void prv_handle_init(void) {
|
|
struct AppData *data = app_malloc_check(sizeof(struct AppData));
|
|
*data = (struct AppData){};
|
|
app_state_set_user_data(data);
|
|
|
|
data->dialog = simple_dialog_create(WINDOW_NAME("Shutdown Charging"));
|
|
Dialog *dialog = simple_dialog_get_dialog(data->dialog);
|
|
dialog_set_background_color(dialog, GColorBlack);
|
|
dialog_set_text_color(dialog, GColorWhite);
|
|
window_set_click_config_provider(&dialog->window, prv_config_provider);
|
|
|
|
// The assumption is that this app is launched when the charger is connected
|
|
// and the shutdown menu item is selected.
|
|
data->was_plugged = true;
|
|
data->last_dialog_state = DialogState_Uninitialized;
|
|
battery_state_service_subscribe(prv_battery_state_handler);
|
|
// Handle the edge-case where the charger is disconnected between the user
|
|
// selecting shut down and this app subscribing to battery state events.
|
|
// Also set the initial battery charge level.
|
|
prv_battery_state_handler(battery_state_service_peek());
|
|
|
|
app_simple_dialog_push(data->dialog);
|
|
// TODO: have the runlevel machinery disable bluetooth and worker.
|
|
services_set_runlevel(RunLevel_BareMinimum);
|
|
worker_manager_disable();
|
|
}
|
|
|
|
|
|
static void s_main(void) {
|
|
prv_handle_init();
|
|
app_event_loop();
|
|
}
|
|
|
|
const PebbleProcessMd* shutdown_charging_get_app_info(void) {
|
|
static const PebbleProcessMdSystem s_app_md = {
|
|
.common = {
|
|
.main_func = s_main,
|
|
.visibility = ProcessVisibilityHidden,
|
|
// UUID: 48fa66c4-4e6f-4b32-bf75-a16e12d630c3
|
|
.uuid = {0x48, 0xfa, 0x66, 0xc4, 0x4e, 0x6f, 0x4b, 0x32,
|
|
0xbf, 0x75, 0xa1, 0x6e, 0x12, 0xd6, 0x30, 0xc3},
|
|
},
|
|
.name = "Shutdown Charging",
|
|
};
|
|
return (const PebbleProcessMd*) &s_app_md;
|
|
}
|