mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-04 22:33:36 -07:00
Added Count Down Timer
This commit is contained in:
@@ -19,6 +19,7 @@ Thank you to all the supporters!
|
||||
- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-12-15 15:50 EST`
|
||||
- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-12-15 15:50 EST`
|
||||
- Updated: [NRF24 Scanner v1.8 (By vad7)](https://github.com/vad7/nrf24scan)
|
||||
- Added: [Count Down Timer (By 0w0mewo)](https://github.com/0w0mewo/fpz_cntdown_timer)
|
||||
|
||||
## Install from Release
|
||||
FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE!
|
||||
@@ -186,6 +187,7 @@ $ ./fbt resources icons dolphin_ext
|
||||
- [Calculator (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e)
|
||||
- [Ceasar Cipher (By panki27)](https://github.com/panki27/caesar-cipher)
|
||||
- [Clock/Stopwatch (By CompaqDisc, Stopwatch & Sound Alert By RogueMaster)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) [12/24HR (By non-bin)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/254) [Refactoring (By GMMan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/256)
|
||||
- [Count Down Timer (By 0w0mewo)](https://github.com/0w0mewo/fpz_cntdown_timer)
|
||||
- [Counter (By Krulknul)](https://github.com/Krulknul/dolphin-counter)
|
||||
- [DAP Link (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1897)
|
||||
- [Deauther PWNDTOOLS V2.6.0 (By HEX0DAYS)](https://github.com/HEX0DAYS/FlipperZero-PWNDTOOLS) `Req: ESP8266` [Original](https://github.com/SpacehuhnTech/esp8266_deauther)
|
||||
@@ -213,7 +215,7 @@ $ ./fbt resources icons dolphin_ext
|
||||
- [Music Player (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/1189)
|
||||
- [NFC Magic (By gornekich)](https://github.com/flipperdevices/flipperzero-firmware/pull/1966)
|
||||
- [NRF Sniff (By mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/nrfsniff) ([Pin Out](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/420/applications/nrfsniff) from nocomp/Frog/UberGuidoZ) `Req: NRF24`
|
||||
- [NRF24 Scanner v1.7 (By vad7)](https://github.com/vad7/nrf24scan)
|
||||
- [NRF24 Scanner v1.8 (By vad7)](https://github.com/vad7/nrf24scan)
|
||||
- [Ocarina (By invalidna-me)](https://github.com/invalidna-me/flipperzero-ocarina) [Here are the LOTZ Songs](https://www.zeldadungeon.net/wiki/Ocarina_of_Time_Songs)
|
||||
- [Paint (By n-o-T-I-n-s-a-n-e)](https://github.com/n-o-T-I-n-s-a-n-e)
|
||||
- [Password Generator (By anakod)](https://github.com/anakod/flipper_passgen)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
## Simple count down timer application for flipper zero
|
||||
|
||||
### How to use
|
||||
`up/down`: set second/minute/hour value.
|
||||
|
||||
`ok`: start/stop counting.
|
||||
|
||||
`long press on ok`: stop counting and reset counter.
|
||||
|
||||
`left/right`: select second/minute/hour value.
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "views/countdown_view.h"
|
||||
#include "app.h"
|
||||
|
||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid);
|
||||
|
||||
int32_t app_main(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
CountDownTimerApp* app = countdown_app_new();
|
||||
|
||||
countdown_app_run(app);
|
||||
|
||||
countdown_app_delete(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t view_exit(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
CountDownTimerApp* countdown_app_new(void) {
|
||||
CountDownTimerApp* app = (CountDownTimerApp*)(malloc(sizeof(CountDownTimerApp)));
|
||||
|
||||
// 1.1 open gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// 2.1 setup view dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
// 2.2 attach view dispatcher to gui
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// 2.3 attach views to the dispatcher
|
||||
// helloworld view
|
||||
app->helloworld_view = helloworld_view_new();
|
||||
register_view(app->view_dispatcher, countdown_timer_view_get_view(app->helloworld_view), 0xff);
|
||||
|
||||
// 2.5 switch to default view
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0xff);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void countdown_app_delete(CountDownTimerApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// delete views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0xff);
|
||||
countdown_timer_view_delete(app->helloworld_view); // hello world view
|
||||
|
||||
// delete view dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
// self
|
||||
free(app);
|
||||
}
|
||||
|
||||
void countdown_app_run(CountDownTimerApp* app) {
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
}
|
||||
|
||||
static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid) {
|
||||
view_dispatcher_add_view(dispatcher, viewid, view);
|
||||
|
||||
view_set_previous_callback(view, view_exit);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef __APP_H__
|
||||
#define __APP_H__
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
|
||||
// app
|
||||
typedef struct {
|
||||
Gui* gui; // gui object
|
||||
ViewDispatcher* view_dispatcher; // view dispacther of the gui
|
||||
|
||||
// views
|
||||
CountDownTimView* helloworld_view;
|
||||
|
||||
} CountDownTimerApp;
|
||||
|
||||
CountDownTimerApp* countdown_app_new(void);
|
||||
void countdown_app_delete(CountDownTimerApp* app);
|
||||
void countdown_app_run(CountDownTimerApp* app);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
App(
|
||||
appid="Count_Down_Timer",
|
||||
name="Count Down Timer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="app_main",
|
||||
cdefines=["APP_COUNT_DOWN_TIMER"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
fap_icon="cntdown_timer.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 306 B |
@@ -0,0 +1,41 @@
|
||||
#include "utils.h"
|
||||
|
||||
static const NotificationSequence sequence_finish = {
|
||||
&message_display_backlight_on,
|
||||
&message_blue_255,
|
||||
&message_vibro_on,
|
||||
&message_note_c5,
|
||||
&message_delay_250,
|
||||
&message_note_c5,
|
||||
&message_delay_500,
|
||||
&message_note_c5,
|
||||
&message_delay_100,
|
||||
&message_note_c5,
|
||||
&message_delay_500,
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
&message_delay_250,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence sequence_beep = {
|
||||
&message_blue_255,
|
||||
&message_note_d5,
|
||||
&message_delay_100,
|
||||
&message_sound_off,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
void notification_beep_once() {
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_beep);
|
||||
notification_off();
|
||||
}
|
||||
|
||||
void notification_off() {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
}
|
||||
|
||||
void notification_timeup() {
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
#include <furi.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
void notification_beep_once();
|
||||
void notification_off();
|
||||
void notification_timeup();
|
||||
|
||||
#endif // __UTILS_H__
|
||||
@@ -0,0 +1,323 @@
|
||||
#include "countdown_view.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
// internal
|
||||
static void handle_cmd(CountDownTimView* hw, CountDownViewCmd cmd);
|
||||
static void handle_time_setting_updown(CountDownTimView* hwv, CountDownViewCmd cmd);
|
||||
static void handle_time_setting_select(InputKey key, CountDownTimView* hwv);
|
||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection);
|
||||
|
||||
// callbacks
|
||||
static void countdown_timer_view_on_enter(void* ctx);
|
||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx);
|
||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx);
|
||||
static void timer_cb(void* ctx);
|
||||
|
||||
CountDownTimView* helloworld_view_new() {
|
||||
CountDownTimView* hwv = (CountDownTimView*)(malloc(sizeof(CountDownTimView)));
|
||||
|
||||
hwv->view = view_alloc();
|
||||
|
||||
hwv->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, hwv);
|
||||
|
||||
hwv->counting = false;
|
||||
|
||||
view_set_context(hwv->view, hwv);
|
||||
|
||||
view_allocate_model(hwv->view, ViewModelTypeLocking, sizeof(CountDownModel));
|
||||
|
||||
view_set_draw_callback(hwv->view, countdown_timer_view_on_draw);
|
||||
view_set_input_callback(hwv->view, countdown_timer_view_on_input);
|
||||
view_set_enter_callback(hwv->view, countdown_timer_view_on_enter);
|
||||
|
||||
return hwv;
|
||||
}
|
||||
|
||||
void countdown_timer_view_delete(CountDownTimView* hwv) {
|
||||
furi_assert(hwv);
|
||||
|
||||
view_free(hwv->view);
|
||||
furi_timer_stop(hwv->timer);
|
||||
furi_timer_free(hwv->timer);
|
||||
|
||||
free(hwv);
|
||||
}
|
||||
|
||||
View* countdown_timer_view_get_view(CountDownTimView* hwv) {
|
||||
return hwv->view;
|
||||
}
|
||||
|
||||
void countdown_timer_view_state_reset(CountDownTimView* hwv) {
|
||||
hwv->counting = false;
|
||||
with_view_model(
|
||||
hwv->view, CountDownModel * model, { model->sec_expected = 10; }, true);
|
||||
}
|
||||
|
||||
void countdown_timer_state_toggle(CountDownTimView* hwv) {
|
||||
bool on = hwv->counting;
|
||||
if(!on) {
|
||||
furi_timer_start(hwv->timer, furi_kernel_get_tick_frequency() * 1); // 1s
|
||||
} else {
|
||||
furi_timer_stop(hwv->timer);
|
||||
notification_off();
|
||||
}
|
||||
|
||||
hwv->counting = !on;
|
||||
}
|
||||
|
||||
// on enter callback, CountDownTimView as ctx
|
||||
static void countdown_timer_view_on_enter(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* hwv = (CountDownTimView*)ctx;
|
||||
countdown_timer_view_state_reset(hwv);
|
||||
}
|
||||
|
||||
// view draw callback, CountDownModel as ctx
|
||||
static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
CountDownModel* model = (CountDownModel*)ctx;
|
||||
|
||||
char buffer[64];
|
||||
|
||||
int32_t sec = model->sec_expected;
|
||||
CountDownViewSelect select = model->select;
|
||||
|
||||
elements_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
draw_selection(canvas, select);
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%02ld:%02ld:%02ld",
|
||||
(sec % (60 * 60 * 24)) / (60 * 60), // hour
|
||||
(sec % (60 * 60)) / 60, // minute
|
||||
sec % 60); // second
|
||||
canvas_draw_str_aligned(
|
||||
canvas, SCREEN_CENTER_X, SCREEN_CENTER_Y, AlignCenter, AlignCenter, buffer);
|
||||
}
|
||||
|
||||
// keys input event callback, CountDownTimView as ctx
|
||||
static bool countdown_timer_view_on_input(InputEvent* event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* hw = (CountDownTimView*)ctx;
|
||||
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
switch(event->key) {
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
case InputKeyRight:
|
||||
case InputKeyLeft:
|
||||
handle_time_setting_select(event->key, hw);
|
||||
break;
|
||||
|
||||
case InputKeyOk:
|
||||
if(event->type == InputTypeShort) {
|
||||
handle_cmd(hw, CountDownTimerToggleCounting);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(event->type == InputTypeLong) {
|
||||
switch(event->key) {
|
||||
case InputKeyOk:
|
||||
handle_cmd(hw, CountDownTimerReset);
|
||||
break;
|
||||
|
||||
case InputKeyBack:
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void timer_cb(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
CountDownTimView* hwv = (CountDownTimView*)ctx;
|
||||
|
||||
int32_t sec;
|
||||
bool timeup = false;
|
||||
|
||||
// decrement counter
|
||||
with_view_model(
|
||||
hwv->view,
|
||||
CountDownModel * model,
|
||||
{
|
||||
sec = model->sec_expected;
|
||||
sec--;
|
||||
|
||||
// check timeup
|
||||
if(sec <= 0) {
|
||||
sec = 0;
|
||||
timeup = true;
|
||||
}
|
||||
|
||||
model->sec_expected = sec;
|
||||
},
|
||||
true);
|
||||
|
||||
if(timeup) {
|
||||
handle_cmd(hwv, CountDownTimerTimeUp);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_time_setting_updown(CountDownTimView* hwv, CountDownViewCmd cmd) {
|
||||
int32_t sec_expected;
|
||||
|
||||
with_view_model(
|
||||
hwv->view,
|
||||
CountDownModel * model,
|
||||
{
|
||||
sec_expected = model->sec_expected;
|
||||
switch(cmd) {
|
||||
case CountDownTimerMinuteUp:
|
||||
sec_expected += 60;
|
||||
break;
|
||||
case CountDownTimerMinuteDown:
|
||||
sec_expected -= 60;
|
||||
break;
|
||||
case CountDownTimerHourDown:
|
||||
sec_expected -= 3600;
|
||||
break;
|
||||
case CountDownTimerHourUp:
|
||||
sec_expected += 3600;
|
||||
break;
|
||||
case CountDownTimerSecUp:
|
||||
sec_expected++;
|
||||
break;
|
||||
case CountDownTimerSecDown:
|
||||
sec_expected--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(sec_expected < 0) {
|
||||
sec_expected = 0;
|
||||
}
|
||||
|
||||
model->sec_expected = sec_expected;
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
static void handle_cmd(CountDownTimView* hw, CountDownViewCmd cmd) {
|
||||
switch(cmd) {
|
||||
case CountDownTimerTimeUp:
|
||||
notification_timeup();
|
||||
break;
|
||||
|
||||
case CountDownTimerReset:
|
||||
furi_timer_stop(hw->timer);
|
||||
countdown_timer_view_state_reset(hw);
|
||||
notification_off();
|
||||
|
||||
break;
|
||||
|
||||
case CountDownTimerToggleCounting:
|
||||
countdown_timer_state_toggle(hw);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_time_setting_select(InputKey key, CountDownTimView* hwv) {
|
||||
bool counting = hwv->counting;
|
||||
CountDownViewCmd setting_cmd = CountDownTimerSecUp;
|
||||
CountDownViewSelect selection;
|
||||
|
||||
if(counting) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load current selection from model context
|
||||
with_view_model(
|
||||
hwv->view, CountDownModel * model, { selection = model->select; }, false);
|
||||
|
||||
// select
|
||||
switch(key) {
|
||||
case InputKeyUp:
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
setting_cmd = CountDownTimerSecUp;
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
setting_cmd = CountDownTimerMinuteUp;
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
setting_cmd = CountDownTimerHourUp;
|
||||
break;
|
||||
}
|
||||
|
||||
handle_time_setting_updown(hwv, setting_cmd);
|
||||
break;
|
||||
|
||||
case InputKeyDown:
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
setting_cmd = CountDownTimerSecDown;
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
setting_cmd = CountDownTimerMinuteDown;
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
setting_cmd = CountDownTimerHourDown;
|
||||
break;
|
||||
}
|
||||
|
||||
handle_time_setting_updown(hwv, setting_cmd);
|
||||
break;
|
||||
|
||||
case InputKeyRight:
|
||||
selection--;
|
||||
selection = selection % 3;
|
||||
break;
|
||||
|
||||
case InputKeyLeft:
|
||||
selection++;
|
||||
selection = selection % 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// save selection to model context
|
||||
with_view_model(
|
||||
hwv->view, CountDownModel * model, { model->select = selection; }, false);
|
||||
}
|
||||
|
||||
static void draw_selection(Canvas* canvas, CountDownViewSelect selection) {
|
||||
switch(selection) {
|
||||
case CountDownTimerSelectSec:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X + 25, SCREEN_CENTER_Y + 11, 21, 2);
|
||||
break;
|
||||
case CountDownTimerSelectMinute:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 10, SCREEN_CENTER_Y + 11, 21, 2);
|
||||
break;
|
||||
case CountDownTimerSelectHour:
|
||||
elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 47, SCREEN_CENTER_Y + 11, 21, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef __COUNTDOWN_VIEW_H__
|
||||
#define __COUNTDOWN_VIEW_H__
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define SCREEN_CENTER_X (SCREEN_WIDTH / 2)
|
||||
#define SCREEN_CENTER_Y (SCREEN_HEIGHT / 2)
|
||||
|
||||
#define MAX_SCALE 80
|
||||
#define MAX_MOVE_STEP 16
|
||||
#define MIN_MOVE_STEP 2
|
||||
|
||||
typedef enum {
|
||||
CountDownTimerMinuteUp,
|
||||
CountDownTimerMinuteDown,
|
||||
CountDownTimerSecDown,
|
||||
CountDownTimerSecUp,
|
||||
CountDownTimerHourUp,
|
||||
CountDownTimerHourDown,
|
||||
CountDownTimerReset,
|
||||
CountDownTimerTimeUp,
|
||||
CountDownTimerToggleCounting,
|
||||
} CountDownViewCmd;
|
||||
|
||||
typedef enum {
|
||||
CountDownTimerSelectSec,
|
||||
CountDownTimerSelectMinute,
|
||||
CountDownTimerSelectHour,
|
||||
} CountDownViewSelect;
|
||||
|
||||
typedef struct {
|
||||
int32_t sec_expected;
|
||||
uint8_t select; // setting
|
||||
} CountDownModel;
|
||||
|
||||
typedef struct {
|
||||
View* view;
|
||||
FuriTimer* timer; // 1Hz tick timer
|
||||
bool counting;
|
||||
|
||||
} CountDownTimView;
|
||||
|
||||
// functions
|
||||
// allocate helloworld view
|
||||
CountDownTimView* helloworld_view_new();
|
||||
|
||||
// delete helloworld view
|
||||
void countdown_timer_view_delete(CountDownTimView* hwv);
|
||||
|
||||
// return view
|
||||
View* countdown_timer_view_get_view(CountDownTimView* hwv);
|
||||
|
||||
void countdown_timer_view_state_reset(CountDownTimView* hwv); // set initial state
|
||||
void countdown_timer_state_toggle(CountDownTimView* hwv);
|
||||
#endif // __COUNTDOWN_VIEW_H__
|
||||
Reference in New Issue
Block a user