Skip to content

Commit d04bcb8

Browse files
committed
convert unlock animation to a async task
Remove timer interrupt, but use the screen render counter to determine the animation duration. Currently it is tweaked to finish in slightly longer than it takes BIP39 unlock to finish. This computation is accurate, as it uses the number of unlock steps, not actual timings.
1 parent ac6f26b commit d04bcb8

File tree

10 files changed

+311
-0
lines changed

10 files changed

+311
-0
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ set(DBB-FIRMWARE-UI-SOURCES
8787
${CMAKE_SOURCE_DIR}/src/ui/components/orientation_arrows.c
8888
${CMAKE_SOURCE_DIR}/src/ui/components/info_centered.c
8989
${CMAKE_SOURCE_DIR}/src/ui/components/lockscreen.c
90+
${CMAKE_SOURCE_DIR}/src/ui/components/unlock_animation.c
9091
${CMAKE_SOURCE_DIR}/src/ui/components/menu.c
9192
${CMAKE_SOURCE_DIR}/src/ui/components/status.c
9293
${CMAKE_SOURCE_DIR}/src/ui/components/image.c

src/rust/bitbox02-rust/src/workflow.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod transaction;
2727
pub mod trinary_choice;
2828
pub mod trinary_input_string;
2929
pub mod unlock;
30+
pub mod unlock_animation;
3031
pub mod verify_message;
3132

3233
use alloc::string::String;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::bb02_async::option_no_screensaver;
16+
use core::cell::RefCell;
17+
18+
/// Performs the unlock animation. Its duration is determined by the component render rate, see
19+
/// unlock_animation.c
20+
pub async fn animate() {
21+
let result = RefCell::new(None as Option<()>);
22+
let mut component = bitbox02::ui::unlock_animation_create(|| {
23+
*result.borrow_mut() = Some(());
24+
});
25+
component.screen_stack_push();
26+
option_no_screensaver(&result).await
27+
}

src/rust/bitbox02-sys/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const ALLOWLIST_FNS: &[&str] = &[
6767
"delay_ms",
6868
"delay_us",
6969
"empty_create",
70+
"unlock_animation_create",
7071
"keystore_bip39_mnemonic_to_seed",
7172
"keystore_copy_seed",
7273
"keystore_copy_bip39_seed",

src/rust/bitbox02-sys/wrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <ui/components/status.h>
4040
#include <ui/components/trinary_choice.h>
4141
#include <ui/components/trinary_input_string.h>
42+
#include <ui/components/unlock_animation.h>
4243
#include <ui/fonts/font_a_11X10.h>
4344
#include <ui/fonts/font_a_9X9.h>
4445
#include <ui/fonts/monogram_5X9.h>

src/rust/bitbox02/src/ui/ui.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,31 @@ pub fn empty_create<'a>() -> Component<'a> {
476476
_p: PhantomData,
477477
}
478478
}
479+
480+
pub fn unlock_animation_create<'a, F>(on_done: F) -> Component<'a>
481+
where
482+
// Callback must outlive component.
483+
F: FnMut() + 'a,
484+
{
485+
unsafe extern "C" fn c_on_done<F2>(param: *mut c_void)
486+
where
487+
F2: FnMut(),
488+
{
489+
// The callback is dropped afterwards. This is safe because
490+
// this C callback is guaranteed to be called only once.
491+
let mut on_done = unsafe { Box::from_raw(param as *mut F2) };
492+
on_done();
493+
}
494+
let component = unsafe {
495+
bitbox02_sys::unlock_animation_create(
496+
Some(c_on_done::<F>),
497+
Box::into_raw(Box::new(on_done)) as *mut _, // passed to c_on_done as `param`.
498+
)
499+
};
500+
Component {
501+
component,
502+
is_pushed: false,
503+
on_drop: None,
504+
_p: PhantomData,
505+
}
506+
}

src/rust/bitbox02/src/ui/ui_stub.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,14 @@ pub fn empty_create<'a>() -> Component<'a> {
140140
_p: PhantomData,
141141
}
142142
}
143+
144+
pub fn unlock_animation_create<'a, F>(mut on_done: F) -> Component<'a>
145+
where
146+
F: FnMut() + 'a,
147+
{
148+
on_done();
149+
Component {
150+
is_pushed: false,
151+
_p: PhantomData,
152+
}
153+
}

src/rust/bitbox02/src/ui/ui_stub_c_unit_tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,14 @@ pub fn empty_create<'a>() -> Component<'a> {
183183
_p: PhantomData,
184184
}
185185
}
186+
187+
pub fn unlock_animation_create<'a, F>(mut on_done: F) -> Component<'a>
188+
where
189+
F: FnMut() + 'a,
190+
{
191+
on_done();
192+
Component {
193+
is_pushed: false,
194+
_p: PhantomData,
195+
}
196+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "unlock_animation.h"
16+
17+
#include <hardfault.h>
18+
#include <screen.h>
19+
#include <ui/component.h>
20+
#include <ui/ui_util.h>
21+
22+
#include <stdint.h>
23+
#include <string.h>
24+
25+
// This many iterations times the slowdown factor to render the whole animation.
26+
#define LOCK_ANIMATION_N_FRAMES (38)
27+
28+
#define SLOWDOWN_FACTOR (10)
29+
30+
#define LOCK_ANIMATION_FRAME_WIDTH (28)
31+
#define LOCK_ANIMATION_FRAME_HEIGHT (25)
32+
#define LOCK_ANIMATION_FRAME_SIZE \
33+
((LOCK_ANIMATION_FRAME_WIDTH * LOCK_ANIMATION_FRAME_HEIGHT + 7) / 8)
34+
35+
typedef struct {
36+
int frame;
37+
void (*on_done)(void*);
38+
void* on_done_param;
39+
} data_t;
40+
41+
#define LOCK_ANIMATION_ACTUAL_N_FRAMES (10)
42+
/**
43+
* Use less frames than ticks for the animation.
44+
*
45+
* Split the remaining animation time into long frame times
46+
* for the closed lock, open lock, and turned lock.
47+
*/
48+
#define LOCK_ANIMATION_FRAMES_STOP_TIME \
49+
((LOCK_ANIMATION_N_FRAMES - LOCK_ANIMATION_ACTUAL_N_FRAMES) / 3)
50+
51+
/**
52+
* At frame 4, the animation will pause for a second.
53+
*/
54+
#define LOCK_ANIMATION_PAUSE_FRAME (2)
55+
56+
static const uint8_t LOCK_ANIMATION[LOCK_ANIMATION_ACTUAL_N_FRAMES][LOCK_ANIMATION_FRAME_SIZE] = {
57+
// Frame 0 - Closed lock
58+
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59+
0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0xff, 0x80, 0x00, 0x1c, 0x1c, 0x00, 0x03,
60+
0x80, 0xe0, 0x00, 0x30, 0x06, 0x00, 0x06, 0x00, 0x30, 0x00, 0x60, 0x03, 0x00, 0x06, 0x00,
61+
0x30, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
62+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
63+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
64+
// Frame 1
65+
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00,
66+
0x00, 0xff, 0x80, 0x00, 0x1c, 0x1c, 0x00, 0x03, 0x80, 0xe0, 0x00, 0x30, 0x06, 0x00, 0x06,
67+
0x00, 0x30, 0x00, 0x60, 0x03, 0x00, 0x06, 0x00, 0x30, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
68+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
69+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
70+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
71+
// Frame 2 - Open lock
72+
{0x00, 0x00, 0x3e, 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x01, 0xc1, 0xc0, 0x00, 0x38, 0x0e, 0x00,
73+
0x03, 0x00, 0x60, 0x00, 0x60, 0x03, 0x00, 0x06, 0x00, 0x30, 0x00, 0x60, 0x03, 0x00, 0x06,
74+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
75+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
76+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
77+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
78+
// Frame 3
79+
{0x00, 0x00, 0x38, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x01, 0xc7, 0x00, 0x00, 0x38, 0x38, 0x00,
80+
0x03, 0x01, 0x80, 0x00, 0x60, 0x0c, 0x00, 0x06, 0x00, 0xc0, 0x00, 0x60, 0x0c, 0x00, 0x06,
81+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
82+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
83+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
84+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
85+
// Frame 4
86+
{0x00, 0x00, 0xe0, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x31, 0x80, 0x00,
87+
0x03, 0x18, 0x00, 0x00, 0x61, 0x80, 0x00, 0x06, 0x18, 0x00, 0x00, 0x61, 0x80, 0x00, 0x06,
88+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
89+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
90+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
91+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
92+
// Frame 5
93+
{0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x38, 0x00, 0x00,
94+
0x03, 0x80, 0x00, 0x00, 0x68, 0x00, 0x00, 0x06, 0x80, 0x00, 0x00, 0x68, 0x00, 0x00, 0x06,
95+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
96+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
97+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
98+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
99+
// Frame 6
100+
{0x00, 0x08, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00,
101+
0x1c, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x16, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x06,
102+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
103+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
104+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
105+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
106+
// Frame 7
107+
{0x00, 0x70, 0x00, 0x00, 0x0d, 0x80, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x18, 0xc0, 0x00, 0x01,
108+
0x8c, 0x00, 0x00, 0x18, 0x60, 0x00, 0x01, 0x86, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x06,
109+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
110+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
111+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
112+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
113+
// Frame 8
114+
{0x01, 0xc0, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x0e, 0x38, 0x00, 0x01, 0xc1, 0xc0, 0x00, 0x18,
115+
0x0c, 0x00, 0x03, 0x00, 0x60, 0x00, 0x30, 0x06, 0x00, 0x03, 0x00, 0x60, 0x00, 0x00, 0x06,
116+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
117+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
118+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
119+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0},
120+
// Frame 9 - Turned lock
121+
{0x07, 0xc0, 0x00, 0x01, 0xff, 0x00, 0x00, 0x38, 0x38, 0x00, 0x07, 0x01, 0xc0, 0x00, 0x60,
122+
0x0c, 0x00, 0x0c, 0x00, 0x60, 0x00, 0xc0, 0x06, 0x00, 0x0c, 0x00, 0x60, 0x00, 0x00, 0x06,
123+
0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x00,
124+
0x00, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0,
125+
0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00,
126+
0x7f, 0xff, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xff, 0xe0}};
127+
128+
/**
129+
* Gets a frame of the lock animation.
130+
*/
131+
static const uint8_t* _get_frame(int frame_idx)
132+
{
133+
if (frame_idx >= LOCK_ANIMATION_N_FRAMES) {
134+
Abort("Invalid lock animation frame requested.");
135+
}
136+
/* First part of the animation: Closed lock for LOCK_ANIMATION_FRAMES_STOP_TIME frames. */
137+
if (frame_idx < LOCK_ANIMATION_FRAMES_STOP_TIME) {
138+
return LOCK_ANIMATION[0];
139+
}
140+
/* Second part: show the lock opening */
141+
int actual_frame_idx = frame_idx - LOCK_ANIMATION_FRAMES_STOP_TIME;
142+
if (actual_frame_idx < LOCK_ANIMATION_PAUSE_FRAME) {
143+
return LOCK_ANIMATION[actual_frame_idx];
144+
}
145+
/* Third part: keep the lock open for a while. */
146+
if (actual_frame_idx < LOCK_ANIMATION_FRAMES_STOP_TIME + LOCK_ANIMATION_PAUSE_FRAME) {
147+
return LOCK_ANIMATION[LOCK_ANIMATION_PAUSE_FRAME];
148+
}
149+
/* Fourth/fifth part: Spin the lock, then keep it open for a while. */
150+
actual_frame_idx -= LOCK_ANIMATION_FRAMES_STOP_TIME;
151+
if (actual_frame_idx >= LOCK_ANIMATION_ACTUAL_N_FRAMES) {
152+
return LOCK_ANIMATION[LOCK_ANIMATION_ACTUAL_N_FRAMES - 1];
153+
}
154+
return LOCK_ANIMATION[actual_frame_idx];
155+
}
156+
157+
static void _render(component_t* component)
158+
{
159+
data_t* data = (data_t*)component->data;
160+
int frame = data->frame / SLOWDOWN_FACTOR;
161+
162+
if (frame >= LOCK_ANIMATION_N_FRAMES) {
163+
/* End of the animation */
164+
if (data->on_done) {
165+
data->on_done(data->on_done_param);
166+
data->on_done = NULL;
167+
}
168+
return;
169+
}
170+
171+
/* Draw the frame. */
172+
position_t pos = {
173+
.left = (SCREEN_WIDTH - LOCK_ANIMATION_FRAME_WIDTH) / 2,
174+
.top = (SCREEN_HEIGHT - LOCK_ANIMATION_FRAME_HEIGHT) / 2};
175+
dimension_t dim = {.width = LOCK_ANIMATION_FRAME_WIDTH, .height = LOCK_ANIMATION_FRAME_HEIGHT};
176+
in_buffer_t image = {.data = _get_frame(frame), .len = LOCK_ANIMATION_FRAME_SIZE};
177+
graphics_draw_image(&pos, &dim, &image);
178+
data->frame++;
179+
}
180+
181+
static const component_functions_t _component_functions = {
182+
.cleanup = ui_util_component_cleanup,
183+
.render = _render,
184+
.on_event = ui_util_on_event_noop,
185+
};
186+
187+
component_t* unlock_animation_create(void (*on_done)(void*), void* on_done_param)
188+
{
189+
data_t* data = malloc(sizeof(data_t));
190+
if (!data) {
191+
Abort("Error: malloc unlock_animation data");
192+
}
193+
memset(data, 0, sizeof(data_t));
194+
data->on_done = on_done;
195+
data->on_done_param = on_done_param;
196+
197+
component_t* component = malloc(sizeof(component_t));
198+
if (!component) {
199+
Abort("Error: malloc unlock_animation_data");
200+
}
201+
memset(component, 0, sizeof(component_t));
202+
203+
component->f = &_component_functions;
204+
component->data = data;
205+
component->dimension.width = SCREEN_WIDTH;
206+
component->dimension.height = SCREEN_HEIGHT;
207+
return component;
208+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef _UNLOCK_ANIMATION_H_
16+
#define _UNLOCK_ANIMATION_H_
17+
18+
#include <ui/component.h>
19+
20+
component_t* unlock_animation_create(void (*on_done)(void*), void* on_done_param);
21+
22+
#endif

0 commit comments

Comments
 (0)