It is currently Wed Jul 06, 2011 6:33 pm

All times are UTC - 8 hours




 Page 1 of 1 [ 3 posts ] 
Author Message
 Post subject: Analog Notification
PostPosted: Thu Mar 03, 2011 6:36 pm 

Joined: Sun Feb 27, 2011 8:32 pm
Posts: 138
Location: Where ever the USAF sends me.
Ok so here's my first attempt at a program. All I did was change the digital clock on the notification app, to the Analog Watch App. The only thing I still need to do is reduce the resource file size as it's just abit over 280kB.

Attached are the screen shots from the simulator, as I haven't gotten my watch yet to run a video on.


/**
* Analog Notifications App
*
* Description:
* This app demonstrates notification management via 3 screens: a analog clock screen,
* a screen listing the most recent notifications and screen displaying a new or
* selected notification. Also featured is button management to toggle between screens
* and power management using sleep mode.
*
* Copyright (C) 2011, Allerta Inc.
* Author: Ryan Young ([email protected])
* Modified by: Jeremy Leesmann ([email protected]) Killer Turtle Software
*
* Permission to use, copy, modify, and/or distribute this software for 
* any purpose with or without fee is hereby granted, provided that the 
* above copyright notice and this permission notice appear in all copies. 

* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
* SOFTWARE.
**/

#include <pulse_os.h>
#include <pulse_types.h>
#include <app_resources.h>

#include <stddef.h>


// Time required to be considered a hold in ms
#define BUTTON_HOLD_TIME 500

// Processor will go to sleep in 15 sec if no activity on the clock screen
#define TIME_BEFORE_SLEEP 15000

// This is a text box widget that will define the bounds of where to render the text
struct PWTextBox text_box;

// Text widget that stores required information about rendering the text
// It must be initialized before it can be used
struct PWidgetTextDynamic text_widget;

// Text buffer to store text to be rendered
char text_buffer[16];

// Timer used to put processor to sleep
int32_t sleep_timer_id;

// Timer used to determine button holds
int32_t button_timer_id;

// Clock font color
color24_t clock_color = {199, 125, 243, 0};

// Current system minute
uint8_t current_min;

uint8_t current_hour;

// Current system time
struct pulse_time_tm current_time;

// If bluetooth is connected
bool bt_connected;

// Index of selected item
uint8_t notification_selected;

// Number of notifications in list
uint8_t notification_list_size;

// Draws all notifications when screen is initialized
bool notification_list_first_draw;

//ID of selected notification in list
PulseNotificationId notification_id;

// Types of screens
typedef enum Screen
{
    SCREEN_CLOCK,
    SCREEN_NOTIFICATION_LIST,
    SCREEN_NOTIFICATION_READ
} Screen;

// Current screen being displayed
Screen current_screen;



    color24_t background_color = {255, 255, 255, 0};
    struct PWTextBox text_box;
    //char text_buffer[3];
    struct PWidgetTextDynamic text_widget;

    //Function that draws the background
void watch_background() {

    //setting text box for the text
    text_box.top = 0;
    text_box.left = SCREEN_WIDTH / 2 - 10;
    text_box.right = SCREEN_WIDTH / 2 + 20;
    text_box.bottom = 24;

    //Prints the text to the screen
    sprintf(text_buffer, "12");
    enum PWTextStyle style = (PWTS_TRUNCATE | PWTS_VERTICAL_CENTER);
    pulse_init_dynamic_text_widget(&text_widget, text_buffer, FONT_LCD_DIGI_DS, background_color, style);
    text_widget.font.resource_id = FONT_LCD_DIGI_DS;
    text_widget.font.color = background_color;
    pulse_render_text(&text_box, &text_widget);

    //Draws the left bar
    pulse_set_draw_window(0, 61, 15, 64);
    for (int i = 0; i < 64; i++) {
        pulse_draw_point24(background_color);
    }

    //Draws the bottom bar
    pulse_set_draw_window(43, 112, 46, 127);
    for (int i = 0; i < 64; i++) {
        pulse_draw_point24(background_color);
    }

    //Draws the right bar
    pulse_set_draw_window(80, 61, 95, 64);
    for (int i = 0; i < 64; i++) {
        pulse_draw_point24(background_color);
    }


}

//Draws a black square over the previous image
void erase_hand(int16_t x0, int16_t y0, int16_t width, int16_t height) {
    pulse_set_draw_window(x0, y0, x0 + width + 2, y0 + height + 2);
    color24_t pixel = {0, 0, 0};
    for (int i = 0; i < ((width + 3)*(height + 3)); i++) {
        pulse_draw_point24(pixel);
    }
}

//Deletes the hand. For each hand (minute/hour) there are four subcases split per quadrants
//As the rotation of the image isn't perfect, the numbers 12,6,9,etc are fudge factors.
//If other hands are used, they might have to be modified to make it look better.
void delete_hand(int t) {
    PulseResource image = t;
    int16_t width, height;

    pulse_dimensions_t dimensions_t = pulse_get_image_info(image);

    width=dimensions_t.width;
    height=dimensions_t.height;

    //Q1
    if (image >= IMAGE_HOUR_A && image <= IMAGE_HOUR_D) {
        erase_hand(48 - 12, 64 - height + 6, width, height);
    }

    //Q2
    if (image >= IMAGE_HOUR_E && image <= IMAGE_HOUR_G) {
        erase_hand(48 - 12, 64 - 9, width, height);
    }

    //Q3
    if (image >= IMAGE_HOUR_H && image <= IMAGE_HOUR_J) {
        erase_hand(48 - width + 1, 64 - 9, width, height);
    }

    //Q4
    if (image >= IMAGE_HOUR_K && image <= IMAGE_HOUR_L) {
        erase_hand(48 - width + 2, 64 - height + 6, width, height);
    }

    //Q1
    if (image >= IMAGE_MINUTE_A0 && image <= IMAGE_MINUTE_B5) {
        erase_hand(48 - 12, 64 - height + 6, width, height);
    }
    //Q2
    if (image >= IMAGE_MINUTE_B6 && image <= IMAGE_MINUTE_D0) {
        erase_hand(48 - 12, 64 - 10, width, height);
    }
    //Q3
    if (image >= IMAGE_MINUTE_D1 && image <= IMAGE_MINUTE_E5) {
        erase_hand(48 - width + 3, 64 - 10, width, height);
    }
    //Q4
    if (image >= IMAGE_MINUTE_E6 && image <= IMAGE_MINUTE_F9) {
        erase_hand(48 - width + 3, 64 - height + 6, width, height);
    }

}

//Draws the hand, same principle as above, with the same problem regarding fudge factors.
void draw_hand(int t) {
    PulseResource image = t;
    int16_t width, height;

    pulse_dimensions_t dimensions_t = pulse_get_image_info(image);

    width=dimensions_t.width;
    height=dimensions_t.height;

    //Q1
    if (image >= IMAGE_HOUR_A && image <= IMAGE_HOUR_D) {
        pulse_draw_image_with_transparency(image, 48 - 12, 64 - height + 6, COLOR_BLACK24);
    }

    //Q2
    if (image >= IMAGE_HOUR_E && image <= IMAGE_HOUR_G) {
        pulse_draw_image_with_transparency(image, 48 - 12, 64 - 9, COLOR_BLACK24);
    }

    //Q3
    if (image >= IMAGE_HOUR_H && image <= IMAGE_HOUR_J) {
        pulse_draw_image_with_transparency(image, 48 - width + 1, 64 - 9, COLOR_BLACK24);
    }

    //Q4
    if (image >= IMAGE_HOUR_K && image <= IMAGE_HOUR_L) {
        pulse_draw_image_with_transparency(image, 48 - width + 2, 64 - height + 6, COLOR_BLACK24);
    }

    //Q1
    if (image >= IMAGE_MINUTE_A0 && image <= IMAGE_MINUTE_B5) {
        pulse_draw_image_with_transparency(image, 48 - 12, 64 - height + 6, COLOR_BLACK24);
    }

    //Q2
    if (image >= IMAGE_MINUTE_B6 && image <= IMAGE_MINUTE_D0) {
        pulse_draw_image_with_transparency(image, 48 - 12, 64 - 10, COLOR_BLACK24);
    }

    //Q3
    if (image >= IMAGE_MINUTE_D1 && image <= IMAGE_MINUTE_E5) {
        pulse_draw_image_with_transparency(image, 48 - width + 3, 64 - 10, COLOR_BLACK24);
    }

    //Q4
    if (image >= IMAGE_MINUTE_E6 && image <= IMAGE_MINUTE_F9) {
        pulse_draw_image_with_transparency(image, 48 - width + 3, 64 - height + 6, COLOR_BLACK24);
    }

}

//Sends the proper paramter to the erase hand function. the offset is due to
//the ordering of the images in the pulse_types.h file
void eraseMinute(int min) {
    if (min == 0) {
        delete_hand(59 + 13);
    } else {
        delete_hand(min + 13 - 1);
    }
}

//Sends the proper paramter to the draw hand function. the offset is due to
//the ordering of the images in the pulse_types.h file
void drawMinute(int min) {
    draw_hand(min + 13);
}

//Erases the previous minute, repaints the background, draws the minute and then the hour hand
void setMinute(int hrs, int min) {
    eraseMinute(min);
    watch_background();
    drawMinute(min);
    drawHour(hrs);
}

//Sends the proper paramter to the erase hand function. the offset is due to
//the ordering of the images in the pulse_types.h file
void eraseHour(int hrs) {
    if (hrs == 0 || hrs == 12) {
        delete_hand(12);
    } else if (hrs < 12) {
        delete_hand(hrs + 1);
    } else {
        delete_hand(hrs - 12 + 1);
    }
}

//Sends the proper paramter to the draw hand function. the offset is due to
//the ordering of the images in the pulse_types.h file
void drawHour(int hrs) {
    if (hrs < 12) {
        draw_hand(hrs + 1);
    } else {
        draw_hand(hrs - 12 + 1);
    }
}

//Erases the previous minute, repaints the background, draws the minute and then the hour hand
void setHour(int hrs, int min) {
    eraseHour(hrs);
    watch_background();
    drawMinute(min);
    drawHour(hrs);
}

// Gets the notification based on the id and prints the messages to the screen
void handle_pulse_protocol_notification(PulseNotificationId id)
{
    current_screen = SCREEN_NOTIFICATION_READ;
    cancel_sleep();
    pulse_blank_canvas();
    struct PulseNotification *notification = pulse_get_notification(id);
    char body_text[150];

    // Set font and color
    text_widget.font.resource_id = FONT_GEORGIA_10;
    text_widget.font.color = COLOR_WHITE24;

    // Render and draw header
    pulse_draw_image(IMAGE_FULLSCREEN_EMAIL, 0, 0);
    text_box.top = 5;
    text_box.right = SCREEN_WIDTH;
    text_box.left = 28;
    text_box.bottom = 20;
    enum PWTextStyle style = PWTS_TRUNCATE;
    pulse_init_dynamic_text_widget(&text_widget, text_buffer, FONT_GEORGIA_10, COLOR_WHITE24, style);
    sprintf(text_buffer, "%s", notification->sender);
    pulse_render_text(&text_box, &text_widget);

    // Render and draw body
    text_box.top = 28;
    text_box.right = SCREEN_WIDTH;
    text_box.left = 0;
    text_box.bottom = SCREEN_HEIGHT;
    style = PWTS_WRAP_AT_SPACE;
    pulse_init_dynamic_text_widget(&text_widget, body_text, FONT_GEORGIA_10, COLOR_WHITE24, style);
    sprintf(body_text, "%s", notification->body);
    pulse_render_text(&text_box, &text_widget);
}

// Shows the most recent notifications
void latest_notifications_screen(uint8_t index)
{   
    // Create an array to hold the PulseNotificationIds
    PulseNotificationId latest_notifications[MAX_NOTIFICATIONS_PER_LIST];

    // Call a function to populate the array
    pulse_get_notification_list_by_type(PNT_ALL, latest_notifications);

    // Get size of the list
    for(int i = 0; i < MAX_NOTIFICATIONS_PER_LIST; i++)
    {
        if (latest_notifications[i] == NULL)
        {
            break;
        }
        notification_list_size = i + 1;
    }

    // Draw screen
    if(notification_list_size != 0)
    {
        // Screen initialized for first time
        if(notification_list_first_draw)
        {
            pulse_blank_canvas();
            notification_list_first_draw = false;
            for(int i = 0; i < notification_list_size; i++)
            {
                struct PulseNotification * list_item = pulse_get_notification(latest_notifications[i]);
                // Draw item icon
                if(i == index)
                {
                    pulse_draw_image(IMAGE_MSGLIST_EMAIL_GLOW, 0, i * 16);
                    notification_id = latest_notifications[i];
                }
                else
                {
                    pulse_draw_image(IMAGE_MSGLIST_EMAIL_NOGLOW, 0, i * 16);
                }
               
                // Print item sender
                draw_notification_list_item_header(i, list_item->sender);
            }
        }
        else
        {
            // Only redraw current selected and previously selected notifications
            if(notification_list_size > 1)
            {
                struct PulseNotification * list_item;
                uint8_t notification_previously_selected = notification_selected - 1;
                if(notification_previously_selected == 0xFF)
                {
                    notification_previously_selected = notification_list_size - 1;
                }

                // Highlight and redraw new selected item
                list_item = pulse_get_notification(latest_notifications[notification_selected]);
                pulse_draw_image(IMAGE_MSGLIST_EMAIL_GLOW, 0, notification_selected * 16);
                draw_notification_list_item_header(notification_selected, list_item->sender);
                notification_id = latest_notifications[notification_selected];

                // Unhighlight and redraw previously selected item
                list_item = pulse_get_notification(latest_notifications[notification_previously_selected]);
                pulse_draw_image(IMAGE_MSGLIST_EMAIL_NOGLOW, 0, notification_previously_selected * 16);
                draw_notification_list_item_header(notification_previously_selected, list_item->sender);
            }
        }
    }
    else
    {
        pulse_blank_canvas();
        printf("No notifications");
    }
}

// Prints the sender of a notification list item
void draw_notification_list_item_header(uint8_t index, char item_header[])
{
    text_box.top = index * 16 + 3;
    text_box.right = SCREEN_WIDTH;
    text_box.left = 18;
    text_box.bottom = (index + 1) * 16;
    enum PWTextStyle style = PWTS_TRUNCATE;
    pulse_init_dynamic_text_widget(&text_widget, text_buffer, FONT_GEORGIA_10, COLOR_WHITE24, style);
    sprintf(text_buffer, "%s", item_header);
    pulse_render_text(&text_box, &text_widget);
}

// Redraw bluetooth icon
void update_bluetooth_icon(bool connected)
{
    if(connected)
    {
        pulse_set_draw_window(0, 0, 19, 19);
        for(int i = 0; i < 400; i++)
        {
            pulse_draw_point24(COLOR_BLACK24);
        }
    }
    else
    {
        pulse_draw_image(IMAGE_BLUETOOTH_NO, 0, 0);
    }
}

void return_to_clock_screen()
{
    pulse_blank_canvas();
    current_screen = SCREEN_CLOCK;


    //Gets the current time from the Real Time Clock and stores the current time in two variables
    pulse_get_time_date(¤t_time);
    current_min = current_time.tm_min;
    current_hour = current_time.tm_hour;
   
    //Draw Background
    watch_background();

    //Draw Minute and Hour hands
    drawMinute(current_min);
    drawHour(current_hour);

    // Draw bluetooth status icon
    update_bluetooth_icon(bt_connected);
    prepare_to_sleep();
}

// Will put processor to sleep in predetermined number of ms
void prepare_to_sleep()
{
    sleep_timer_id = pulse_register_timer(TIME_BEFORE_SLEEP, &pulse_update_power_down_timer, 0);
}

// Cancel call to put processor to sleep
void cancel_sleep()
{
    pulse_cancel_timer(&sleep_timer_id);
}

// This function is called once after the watch has booted up
// and the OS has loaded
void main_app_init()
{
    bt_connected = false;
    notification_list_size = 0;
   
    return_to_clock_screen();

    // Callback to handles new notifications
    pulse_register_callback(ACTION_NEW_PULSE_PROTOCOL_NOTIFICATION, &handle_pulse_protocol_notification);

    // Callback to handle button waking up processor
    pulse_register_callback(ACTION_WOKE_FROM_BUTTON, &return_to_clock_screen);
}

void main_app_handle_button_down()
{
    if(current_screen == SCREEN_CLOCK)
    {
        current_screen = SCREEN_NOTIFICATION_LIST;
        cancel_sleep();
        notification_list_first_draw = true;
        notification_selected = 0xFF;
    }
    else if(current_screen == SCREEN_NOTIFICATION_LIST)
    {
        if(notification_list_size == 0)
        {
            return_to_clock_screen();
        }
        else
        {           
            // Set timer for button hold
            button_timer_id = pulse_register_timer(BUTTON_HOLD_TIME, &handle_pulse_protocol_notification,
                                            notification_id);
        }
    }
    else
    {
        return_to_clock_screen();
    }
}

void main_app_handle_button_up()
{
    if(current_screen == SCREEN_NOTIFICATION_LIST)
    {
        // Button was not a hold
        pulse_cancel_timer(&button_timer_id);

        // Select next message in list
        notification_selected++;
        if(notification_selected == notification_list_size)
        {
            notification_selected = 0;
        }

        // Update screen
        latest_notifications_screen(notification_selected);
    }
}

// Main loop. This function is called frequently.
// No blocking calls are allowed in this function or else the watch will reset.
// The inPulse watchdog timer will kick in after 5 seconds if a blocking
// call is made.
void main_app_loop()
{
    pulse_get_time_date(¤t_time);
    if(current_screen == SCREEN_CLOCK)
    {
            pulse_get_time_date(¤t_time);

    //if the minutes have changed, then redraw
    if (current_min != current_time.tm_min) {

        setMinute(current_hour, current_min);
        current_min = current_time.tm_min;
    }
   
    //if the hours have changed, then redraw
    if (current_hour != current_time.tm_hour) {

        setHour(current_hour, current_min);
        current_hour = current_time.tm_hour;
    }

            update_bluetooth_icon(bt_connected);
        }
    }

// This function is called whenever the processor is about to sleep (to conserve power)
// The sleep functionality is scheduled with pulse_update_power_down_timer(uint32_t)
void main_app_handle_doz()
{

}

void main_app_handle_hardware_update(enum PulseHardwareEvent event)
{
    switch(event) {
        case BLUETOOTH_CONNECTED:
            bt_connected = true;
            break;
        case BLUETOOTH_DISCONNECTED:
            bt_connected = false;
            break;       
    }
    if(current_screen == SCREEN_CLOCK)
    {
        update_bluetooth_icon(bt_connected);
    }
}


Attachments:

Message.jpg [ 4.06 KiB | Viewed 303 times ]

Analog.jpg [ 4.55 KiB | Viewed 303 times ]

_________________
Page ID: KTS
Offline
 Profile  
 
 Post subject: Re: Analog Notification
PostPosted: Thu Mar 03, 2011 8:19 pm 
User avatar

Joined: Mon Feb 14, 2011 7:07 pm
Posts: 172
That looks awesome, great mashup!



_________________
---

Lead designer of inPulse
Offline
 Profile  
 
 Post subject: Re: Analog Notification
PostPosted: Sun Mar 06, 2011 10:05 am 

Joined: Sun Feb 27, 2011 8:32 pm
Posts: 138
Location: Where ever the USAF sends me.
I just have to figure out how to get the compiled app smaller, cause it's like 42k right now.



_________________
Page ID: KTS
Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 3 posts ] 

All times are UTC - 8 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  

cron