initial push

This commit is contained in:
fkuepper
2016-12-15 15:13:37 +01:00
parent 33a9f9e5b1
commit 473fe88bc1
30 changed files with 2632 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
{
"author": "mybahn@kueppernet.de",
"dependencies": {
"pebble-clay": "^1.0.0"
},
"keywords": [],
"name": "mybahn",
"pebble": {
"capabilities": [
"configurable"
],
"displayName": "myBahn",
"enableMultiJS": true,
"messageKeys": [
"status",
"message",
"StatTitle[10]",
"StatIBNR[10]",
"ConnStartIBNR[10]",
"ConnDestIBNR[10]",
"ConnTitle[10]",
"ConnSubTitle[10]",
"BahnReqId",
"BahnReqType",
"BahnReqUrl",
"BahnReqStartStation",
"BahnReqDestStation",
"BahnReqProducts",
"BahnRespPartNum",
"BahnRespParts",
"BahnReqTypeBuildConnResults",
"BahnReqTypeGetConnDetails",
"BahnConnDetails",
"BahnConnTitle",
"BahnConnSubtitle",
"BahnConnDetailsUrl",
"BahnMaxLength",
"BahnFrueherUrl",
"BahnSpaeterUrl",
"BahnReqTypeGetConnResult",
"BahnRespTypeLoadFailed",
"DefICE",
"DefIC",
"DefIR",
"DefRE",
"DefS",
"DefSchiff",
"DefU",
"DefBus",
"DefTram",
"DefAST",
"ConnUseDefProd[10]",
"ConnICE[10]",
"ConnIC[10]",
"ConnIR[10]",
"ConnRE[10]",
"ConnS[10]",
"ConnSchiff[10]",
"ConnU[10]",
"ConnBus[10]",
"ConnTram[10]",
"ConnAST[10]",
"StatSubTitle[10]",
"StatUseDefProd[10]",
"StatICE[10]",
"StatIC[10]",
"StatIR[10]",
"StatRE[10]",
"StatS[10]",
"StatSchiff[10]",
"StatU[10]",
"StatBus[10]",
"StatTram[10]",
"StatAST[10]",
"DefAllProd",
"StatAllProd[10]",
"ConnAllProd[10]",
"ConnEnabled[10]",
"StatEnabled[10]"
],
"projectType": "native",
"resources": {
"media": [
{
"file": "images/mybahn25x25.png",
"menuIcon": true,
"name": "IMAGE_ICON_MAIN",
"targetPlatforms": [
"aplite",
"basalt",
"chalk",
"diorite",
"emery"
],
"type": "bitmap"
}
]
},
"sdkVersion": "3",
"targetPlatforms": [
"aplite",
"basalt",
"chalk",
"diorite",
"emery"
],
"uuid": "46824e47-14bd-422d-b993-258a62a92151",
"watchapp": {
"watchface": false
}
},
"version": "1.0.0"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

+245
View File
@@ -0,0 +1,245 @@
#include <pebble.h>
#include "bahn_interface.h"
#include "loading_window.h"
#include "load_failed_window.h"
static int request_id = 0;
static ConnectionResults connectionResultBuffer;
static char detailsResultBuffer[CONNECTION_DETAILS_LENGTH + 1];
static void (*requestCompleteCallback)();
static void request_cancelled() {
request_id++;
}
static void display_wait_screen() {
LoadingWindow_show(request_cancelled);
}
static void hide_wait_screen() {
LoadingWindow_hide();
}
static DictionaryIterator* prepare_request(int request_type) {
display_wait_screen();
DictionaryIterator* out_iter;
AppMessageResult result = app_message_outbox_begin(&out_iter);
if (result != APP_MSG_OK) {
hide_wait_screen();
LoadFailedWindow_show();
return NULL;
}
dict_write_int(out_iter, MESSAGE_KEY_BahnReqId, &request_id, sizeof(int), true);
dict_write_int(out_iter, MESSAGE_KEY_BahnReqType, &request_type, sizeof(int), true);
return out_iter;
}
static void send_request(DictionaryIterator* out_iter) {
dict_write_end(out_iter);
app_message_outbox_send();
}
static void send_request_for_url(int request_type, char* request_url) {
request_id++;
DictionaryIterator* out_iter = prepare_request(request_type);
if (out_iter == NULL) {
return;
}
dict_write_int(out_iter, MESSAGE_KEY_BahnMaxLength, &request_type, sizeof(int), true);
dict_write_tuplet(out_iter, &TupletCString(MESSAGE_KEY_BahnReqUrl, request_url));
send_request(out_iter);
}
static void send_request_for_connections(char* start_station, char* dest_station, int products) {
request_id++;
DictionaryIterator* out_iter = prepare_request(MESSAGE_KEY_BahnReqTypeBuildConnResults);
if (out_iter == NULL) {
return;
}
dict_write_tuplet(out_iter, &TupletCString(MESSAGE_KEY_BahnReqStartStation, start_station));
dict_write_tuplet(out_iter, &TupletCString(MESSAGE_KEY_BahnReqDestStation, dest_station));
dict_write_int(out_iter, MESSAGE_KEY_BahnReqProducts, &products, sizeof(int), true);
send_request(out_iter);
}
static void connection_result_received(DictionaryIterator *iter) {
Tuple* t = dict_find(iter, MESSAGE_KEY_BahnRespPartNum);
if (!t) {
return;
}
int partNum = t->value->int32;
if (partNum == 1) {
connectionResultBuffer.number_of_entries = 0;
connectionResultBuffer.has_frueher = false;
connectionResultBuffer.has_spaeter = false;
}
t = dict_find(iter, MESSAGE_KEY_BahnFrueherUrl);
if (t) {
connectionResultBuffer.has_frueher = true;
strncpy(connectionResultBuffer.frueher_link, t->value->cstring, CONNECTION_RESULT_DETAIL_LINK_LENGTH);
connectionResultBuffer.frueher_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH] = '\0';
return;
}
t = dict_find(iter, MESSAGE_KEY_BahnSpaeterUrl);
if (t) {
connectionResultBuffer.has_spaeter = true;
strncpy(connectionResultBuffer.spaeter_link, t->value->cstring, CONNECTION_RESULT_DETAIL_LINK_LENGTH);
connectionResultBuffer.spaeter_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH] = '\0';
return;
}
int result_index = connectionResultBuffer.number_of_entries++;
ConnectionResult* resultEntry = &connectionResultBuffer.entries[result_index];
resultEntry->title[0] = '\0';
resultEntry->subtitle[0] = '\0';
resultEntry->detail_link[0] = '\0';
t = dict_find(iter, MESSAGE_KEY_BahnConnTitle);
if (t) {
strncpy(resultEntry->title, t->value->cstring, CONNECTION_RESULT_TITLE_LENGTH);
resultEntry->title[CONNECTION_RESULT_TITLE_LENGTH] = '\0';
}
t = dict_find(iter, MESSAGE_KEY_BahnConnSubtitle);
if (t) {
strncpy(resultEntry->subtitle, t->value->cstring, CONNECTION_RESULT_SUBTITLE_LENGTH);
resultEntry->subtitle[CONNECTION_RESULT_SUBTITLE_LENGTH] = '\0';
}
t = dict_find(iter, MESSAGE_KEY_BahnConnDetailsUrl);
if (t) {
strncpy(resultEntry->detail_link, t->value->cstring, CONNECTION_RESULT_DETAIL_LINK_LENGTH);
resultEntry->detail_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH] = '\0';
}
}
static void connection_details_received(DictionaryIterator *iter) {
strncpy(detailsResultBuffer, dict_find(iter, MESSAGE_KEY_BahnConnDetails)->value->cstring, CONNECTION_DETAILS_LENGTH);
detailsResultBuffer[CONNECTION_DETAILS_LENGTH] = '\0';
}
bool BahnInterface_handle_app_message(DictionaryIterator *iter, void *context) {
Tuple *tuple = dict_find(iter, MESSAGE_KEY_BahnReqId);
if (!tuple) {
return false;
}
// This value was stored as JS Number, which is stored here as int32_t
int32_t msg_request_id = tuple->value->int32;
if (msg_request_id != request_id) {
int m = msg_request_id;
return true;
}
int partNum = dict_find(iter, MESSAGE_KEY_BahnRespPartNum)->value->int32;
int parts = dict_find(iter, MESSAGE_KEY_BahnRespParts)->value->int32;
unsigned int request_type = dict_find(iter, MESSAGE_KEY_BahnReqType)->value->int32;
if (request_type == MESSAGE_KEY_BahnReqTypeGetConnResult) {
connection_result_received(iter);
} else if (request_type == MESSAGE_KEY_BahnReqTypeGetConnDetails) {
connection_details_received(iter);
} else if (request_type == MESSAGE_KEY_BahnRespTypeLoadFailed) {
hide_wait_screen();
LoadFailedWindow_show();
}
if (partNum == parts) {
hide_wait_screen();
if (requestCompleteCallback) {
requestCompleteCallback();
}
}
return true;
}
static int prv_combine_product_code(AllowedProducts* p1, AllowedProducts* p2) {
int result = 0;
if (p1->ice || p2->ice || p1->allow_all || p2->allow_all) {
result +=1;
}
if (p1->ic || p2->ic || p1->allow_all || p2->allow_all) {
result +=2;
}
if (p1->ir || p2->ir || p1->allow_all || p2->allow_all) {
result +=4;
}
if (p1->zug || p2->zug || p1->allow_all || p2->allow_all) {
result +=8;
}
if (p1->sbahn || p2->sbahn || p1->allow_all || p2->allow_all) {
result +=16;
}
if (p1->bus || p2->bus || p1->allow_all || p2->allow_all) {
result +=32;
}
if (p1->schiff || p2->schiff || p1->allow_all || p2->allow_all) {
result +=64;
}
if (p1->ubahn || p2->ubahn || p1->allow_all || p2->allow_all) {
result +=128;
}
if (p1->tram || p2->tram || p1->allow_all || p2->allow_all) {
result +=256;
}
if (p1->ast || p2->ast || p1->allow_all || p2->allow_all) {
result +=512;
}
return result;
}
static int prv_get_product_code(AllowedProducts* products) {
return prv_combine_product_code(products, products);
}
int get_products_for_connection(int valid_connection_index) {
return prv_get_product_code(MyBahnSettings_get_allowed_products_for_connection(valid_connection_index));
}
int get_products_for_station_to_station(int from_station_index, int to_station_index) {
StationConfig* s1 = MyBahnSettings_get_valid_station(from_station_index);
StationConfig* s2 = MyBahnSettings_get_valid_station(to_station_index);
AllowedProducts* p1 = s1->use_default_products ? MyBahnSettings_get_default_products() : &s1->allowed_products;
AllowedProducts* p2 = s2->use_default_products ? MyBahnSettings_get_default_products() : &s2->allowed_products;
return prv_combine_product_code(p1, p2);
}
void BahnInterface_init() {
request_id = 0;
}
void BahnInterface_read_connection_results(int connection_index, void (*completeCallback)()) {
ConnectionConfig* conn = MyBahnSettings_get_valid_connection(connection_index);
requestCompleteCallback = completeCallback;
send_request_for_connections(conn->start_ibnr, conn->dest_ibnr, get_products_for_connection(connection_index));
}
void BahnInterface_read_station_to_station_results(int from_station_index, int to_station_index, void (*completeCallback)()) {
StationConfig* start = MyBahnSettings_get_valid_station(from_station_index);
StationConfig* dest = MyBahnSettings_get_valid_station(to_station_index);
requestCompleteCallback = completeCallback;
send_request_for_connections(start->ibnr, dest->ibnr, get_products_for_station_to_station(from_station_index, to_station_index));
}
void BahnInterface_read_query_link_results(char* query_link, void (*completeCallback)()) {
requestCompleteCallback = completeCallback;
send_request_for_url(MESSAGE_KEY_BahnReqTypeBuildConnResults, query_link);
}
void BahnInterface_read_connection_details(ConnectionResult* result, void (*completeCallback)()) {
requestCompleteCallback = completeCallback;
send_request_for_url(MESSAGE_KEY_BahnReqTypeGetConnDetails, result->detail_link);
}
ConnectionResults* BahnInterface_get_current_connection_results() {
return &connectionResultBuffer;
}
char* BahnInterface_get_current_connection_details() {
return detailsResultBuffer;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include "mybahn_settings.h"
#define CONNECTION_RESULT_TITLE_LENGTH 20
#define CONNECTION_RESULT_SUBTITLE_LENGTH 25
#define CONNECTION_RESULT_DETAIL_LINK_LENGTH 172
#define CONNECTION_DETAILS_LENGTH 1024
#define MAX_CONNECTION_RESULTS 20
typedef struct {
char title[CONNECTION_RESULT_TITLE_LENGTH + 1];
char subtitle[CONNECTION_RESULT_SUBTITLE_LENGTH + 1];
char detail_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH + 1];
} ConnectionResult;
typedef struct {
ConnectionResult entries[MAX_CONNECTION_RESULTS];
int number_of_entries;
bool has_frueher;
bool has_spaeter;
char frueher_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH + 1];
char spaeter_link[CONNECTION_RESULT_DETAIL_LINK_LENGTH + 1];
} ConnectionResults;
void BahnInterface_init();
void BahnInterface_read_connection_results(int connection_index, void (*completeCallback)());
void BahnInterface_read_station_to_station_results(int from_station_index, int to_station_index, void (*completeCallback)());
void BahnInterface_read_query_link_results(char* query_link, void (*completeCallback)());
void BahnInterface_read_connection_details(ConnectionResult* result, void (*completeCallback)());
bool BahnInterface_handle_app_message(DictionaryIterator *iter, void *context);
ConnectionResults* BahnInterface_get_current_connection_results();
char* BahnInterface_get_current_connection_details();
+76
View File
@@ -0,0 +1,76 @@
#include <pebble.h>
#include "scroll_text_layer.h"
#include "bahn_interface.h"
#include "connection_details_window.h"
static Window* s_window;
static ScrollTextLayer* s_scroll_layer;
#define NUM_FONTS 6
static char* fonts[6] = {
FONT_KEY_GOTHIC_18,
FONT_KEY_GOTHIC_18_BOLD,
FONT_KEY_GOTHIC_24,
FONT_KEY_GOTHIC_24_BOLD,
FONT_KEY_GOTHIC_28,
FONT_KEY_GOTHIC_28_BOLD,
};
static uint8_t font = 3;
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
font += 1;
if (font >= NUM_FONTS) {
font = 0;
}
scroll_text_layer_set_text(s_scroll_layer, "");
scroll_text_layer_set_system_font(s_scroll_layer, fonts[font]);
scroll_text_layer_set_text(s_scroll_layer, BahnInterface_get_current_connection_details());
}
static void click_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
}
static void window_load(Window* window) {
if (!s_scroll_layer) {
s_scroll_layer = scroll_text_layer_create_fullscreen(window);
}
scroll_text_layer_set_system_font(s_scroll_layer, fonts[font]);
scroll_text_layer_set_text(s_scroll_layer, BahnInterface_get_current_connection_details());
scroll_text_layer_add_to_window(s_scroll_layer, window);
scroll_text_layer_set_callbacks(s_scroll_layer, (ScrollLayerCallbacks) {
.click_config_provider = click_config_provider
});
}
static void window_unload(Window *window) {
scroll_text_layer_destroy(s_scroll_layer);
s_scroll_layer = NULL;
}
static void show_window() {
s_window = window_create();
window_set_window_handlers(s_window, (WindowHandlers) {
.load = window_load,
.unload = window_unload,
});
window_stack_push(s_window, true);
}
void ConnectionDetailsWindow_show(ConnectionResult* result) {
ConnectionDetailsWindow_destroy();
BahnInterface_read_connection_details(result, show_window);
}
void ConnectionDetailsWindow_destroy() {
if (!s_window) {
return;
}
window_destroy(s_window);
s_window = NULL;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "bahn_interface.h"
void ConnectionDetailsWindow_show(ConnectionResult* result);
void ConnectionDetailsWindow_destroy();
+158
View File
@@ -0,0 +1,158 @@
#include <pebble.h>
#include "resources.h"
#include "mybahn_settings.h"
#include "bahn_interface.h"
#include "connection_details_window.h"
#include "connection_results_menu.h"
static MenuLayer* s_menu_layer;
static Window* s_window;
static char s_header[2 * CONNECTION_SUBTITLE_LENGTH + 10];
static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) {
return 1;
}
static uint16_t menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
int result = results->number_of_entries;
if (results->has_frueher) {
result ++;
}
if (results->has_spaeter) {
result ++;
}
return result;
}
static bool is_frueher_entry(int menu_index) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
if (menu_index == 0 && results->has_frueher) {
return true;
}
return false;
}
static bool is_spaeter_entry(int menu_index) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
int result_index = menu_index;
if (results->has_frueher) {
result_index--;
}
if (result_index >= results->number_of_entries && results->has_spaeter) {
return true;
}
return false;
}
static int compute_result_index(int menu_index) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
if (results->has_frueher) {
return menu_index-1;
}
return menu_index;
}
static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
if (is_frueher_entry(cell_index->row)) {
menu_cell_basic_draw(ctx, cell_layer, FRUEHER, NULL, NULL);
} else if (is_spaeter_entry(cell_index->row)) {
menu_cell_basic_draw(ctx, cell_layer, SPAETER, NULL, NULL);
} else {
int result_index = compute_result_index(cell_index->row);
menu_cell_basic_draw(ctx, cell_layer, results->entries[result_index].title, results->entries[result_index].subtitle, NULL);
}
}
static int16_t menu_get_header_height_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return MENU_CELL_BASIC_HEADER_HEIGHT;
}
static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, uint16_t section_index, void *data) {
menu_cell_basic_header_draw(ctx, cell_layer, s_header);
}
static void reload_menu() {
menu_layer_reload_data(s_menu_layer);
}
static void update_connections(char* connection_query_link) {
BahnInterface_read_query_link_results(connection_query_link, reload_menu);
}
static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) {
ConnectionResults* results = BahnInterface_get_current_connection_results();
if (is_frueher_entry(cell_index->row)) {
update_connections(results->frueher_link);
} else if (is_spaeter_entry(cell_index->row)) {
update_connections(results->spaeter_link);
} else {
ConnectionDetailsWindow_show(&results->entries[compute_result_index(cell_index->row)]);
}
}
static void build_connection_results_window() {
ConnectionResults* results = BahnInterface_get_current_connection_results();
if (results->number_of_entries == 0) {
return;
}
s_window = window_create();
Layer *window_layer = window_get_root_layer(s_window);
GRect bounds = layer_get_frame(window_layer);
s_menu_layer = menu_layer_create(bounds);
menu_layer_set_click_config_onto_window(s_menu_layer, s_window);
menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks){
.get_num_sections = menu_get_num_sections_callback,
.get_num_rows = menu_get_num_rows_callback,
.get_header_height = menu_get_header_height_callback,
.draw_header = menu_draw_header_callback,
.draw_row = menu_draw_row_callback,
.select_click = menu_select_callback,
});
layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer));
window_stack_push(s_window, true);
}
void ConnectionResultsMenu_show_for_connection(int connection_index) {
ConnectionResultsMenu_destroy();
ConnectionConfig* connection = MyBahnSettings_get_valid_connection(connection_index);
s_header[0] = '\0';
strcat(s_header, connection->title);
strcat(s_header, " ");
strcat(s_header, connection->subtitle);
BahnInterface_read_connection_results(connection_index, build_connection_results_window);
}
void ConnectionResultsMenu_show_station_to_station(int from_station_index, int to_station_index) {
ConnectionResultsMenu_destroy();
StationConfig* from_station = MyBahnSettings_get_valid_station(from_station_index);
StationConfig* to_station = MyBahnSettings_get_valid_station(to_station_index);
s_header[0] = '\0';
strcat(s_header, from_station->title);
strcat(s_header, TO_MARKER);
strcat(s_header, to_station->title);
BahnInterface_read_station_to_station_results(from_station_index, to_station_index, build_connection_results_window);
}
void ConnectionResultsMenu_destroy() {
if (!s_window) {
return;
}
menu_layer_destroy(s_menu_layer);
window_destroy(s_window);
s_menu_layer = NULL;
s_window = NULL;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
void ConnectionResultsMenu_show_for_connection(int connection_index);
void ConnectionResultsMenu_show_station_to_station(int from_station_index, int to_station_index);
void ConnectionResultsMenu_destroy();
+24
View File
@@ -0,0 +1,24 @@
#include <pebble.h>
#include "resources.h"
#include "message_window.h"
#include "load_failed_window.h"
static Window *s_main_window;
static bool initialized = false;
static void window_load(Window *window) {
if (!initialized) {
MessageWindow_build(window, LOAD_FAILED_MESSAGE, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD));
initialized = true;
}
}
void LoadFailedWindow_show() {
if(!s_main_window) {
s_main_window = window_create();
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = window_load
});
}
window_stack_push(s_main_window, true);
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include <pebble.h>
void LoadFailedWindow_show();
+47
View File
@@ -0,0 +1,47 @@
#include <pebble.h>
#include "resources.h"
#include "message_window.h"
#include "loading_window.h"
static Window *s_main_window;
static void (*s_back_callback)();
static bool initialized = false;
static void back_single_click_handler(ClickRecognizerRef recognizer, void *context) {
window_stack_remove(s_main_window, true);
if (s_back_callback) {
s_back_callback();
}
}
static void config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_BACK, back_single_click_handler);
}
static void window_load(Window *window) {
if (!initialized) {
MessageWindow_build(s_main_window, LOADING_MESSAGE, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
window_set_click_config_provider(s_main_window, config_provider);
initialized = true;
}
}
static void window_unload(Window *window) {
s_back_callback = NULL;
}
void LoadingWindow_show(void (*back_callback)()) {
if(!s_main_window) {
s_main_window = window_create();
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = window_load,
.unload = window_unload
});
}
s_back_callback = back_callback;
window_stack_push(s_main_window, true);
}
void LoadingWindow_hide() {
window_stack_remove(s_main_window, true);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <pebble.h>
void LoadingWindow_show();
void LoadingWindow_hide();
+68
View File
@@ -0,0 +1,68 @@
#include <pebble.h>
#include "mybahn_settings.h"
#include "bahn_interface.h"
#include "main_menu.h"
static Window *s_main_window;
static void main_window_load(Window *window) {
// create main menu layer
MainMenu_create(window);
}
static void main_window_unload(Window *window) {
// Destroy main menu layer
MainMenu_destroy();
}
static void inbox_received_callback(DictionaryIterator *iter, void *context) {
if (BahnInterface_handle_app_message(iter, context)) {
return;
}
if (MyBahnSettings_handle_app_message(iter, context)) {
MainMenu_refresh();
return;
}
}
static void inbox_dropped_callback(AppMessageResult reason, void *context) {
}
static void outbox_failed_callback(DictionaryIterator *iterator, AppMessageResult reason, void *context) {
}
static void outbox_sent_callback(DictionaryIterator *iterator, void *context) {
}
static void init() {
const int inbox_size = 2048;
const int outbox_size = 512;
MyBahnSettings_init();
BahnInterface_init();
app_message_register_inbox_received(inbox_received_callback);
app_message_register_inbox_dropped(inbox_dropped_callback);
app_message_register_outbox_failed(outbox_failed_callback);
app_message_register_outbox_sent(outbox_sent_callback);
app_message_open(inbox_size, outbox_size);
s_main_window = window_create();
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = main_window_load,
.unload = main_window_unload,
});
window_stack_push(s_main_window, true);
}
static void deinit() {
window_destroy(s_main_window);
}
int main(void) {
init();
app_event_loop();
deinit();
}
+136
View File
@@ -0,0 +1,136 @@
#include <pebble.h>
#include "resources.h"
#include "mybahn_settings.h"
#include "to_stations_menu.h"
#include "connection_results_menu.h"
#include "connection_details_window.h"
#include "main_menu.h"
static MenuLayer *s_menu_layer;
static bool is_connections_section(uint16_t section_index) {
if (section_index > 0) {
return false;
}
if (MyBahnSettings_get_number_of_valid_connections() == 0) {
return false;
}
return true;
}
static bool is_stations_section(uint16_t section_index) {
if (section_index == 1) {
return true;
}
if (section_index == 0
&& MyBahnSettings_get_number_of_valid_connections() == 0
&& MyBahnSettings_get_number_of_valid_stations() > 0) {
return true;
}
return false;
}
static void select_connection(int connection_index) {
ConnectionResultsMenu_show_for_connection(connection_index);
}
static void select_start_station(int station_index) {
ToStationsMenu_display(station_index);
}
static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) {
uint16_t result = 0;
if (MyBahnSettings_get_number_of_valid_connections() > 0) {
result++;
}
if (MyBahnSettings_get_number_of_valid_stations() > 0) {
result++;
}
if (result == 0) {
result = 1;
}
return result;
}
static uint16_t menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
if (is_connections_section(section_index)) {
return MyBahnSettings_get_number_of_valid_connections();
} else if (is_stations_section(section_index)) {
return MyBahnSettings_get_number_of_valid_stations();
}
return 1;
}
static int16_t menu_get_header_height_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return MENU_CELL_BASIC_HEADER_HEIGHT;
}
static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, uint16_t section_index, void *data) {
// Determine which section we're working with
if (is_connections_section(section_index)) {
menu_cell_basic_header_draw(ctx, cell_layer, CONNECTIONS);
} else if (is_stations_section(section_index)) {
menu_cell_basic_header_draw(ctx, cell_layer, START_STATIONS);
}
}
static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) {
// Determine which section we're going to draw in
if (is_connections_section(cell_index->section)) {
ConnectionConfig* connection = MyBahnSettings_get_valid_connection(cell_index->row);
menu_cell_basic_draw(ctx, cell_layer, connection->title, connection->subtitle, NULL);
} else if (is_stations_section(cell_index->section)) {
StationConfig* station = MyBahnSettings_get_valid_station(cell_index->row);
menu_cell_basic_draw(ctx, cell_layer, station->title, station->subtitle, NULL);
} else {
menu_cell_basic_draw(ctx, cell_layer, APP_NAME, PLEASE_CONFIGURE, APP_ICON);
}
}
static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) {
if (is_connections_section(cell_index->section)) {
select_connection(cell_index->row);
} else if (is_stations_section(cell_index->section)) {
if (MyBahnSettings_get_number_of_valid_stations() > 1) {
select_start_station(cell_index->row);
}
}
}
void MainMenu_create(Window *window) {
MainMenu_destroy();
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_frame(window_layer);
s_menu_layer = menu_layer_create(bounds);
menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks){
.get_num_sections = menu_get_num_sections_callback,
.get_num_rows = menu_get_num_rows_callback,
.get_header_height = menu_get_header_height_callback,
.draw_header = menu_draw_header_callback,
.draw_row = menu_draw_row_callback,
.select_click = menu_select_callback,
});
// Bind the menu layer's click config provider to the window for interactivity
menu_layer_set_click_config_onto_window(s_menu_layer, window);
layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer));
}
void MainMenu_destroy() {
if (!s_menu_layer) {
return;
}
menu_layer_destroy(s_menu_layer);
s_menu_layer = NULL;
ToStationsMenu_destroy();
ConnectionResultsMenu_destroy();
ConnectionDetailsWindow_destroy();
}
void MainMenu_refresh() {
menu_layer_reload_data(s_menu_layer);
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <pebble.h>
void MainMenu_create(Window *window);
void MainMenu_destroy();
void MainMenu_refresh();
+41
View File
@@ -0,0 +1,41 @@
#include <pebble.h>
#include "resources.h"
#include "message_window.h"
#define DIALOG_MESSAGE_WINDOW_MARGIN 10
static void icon_update_proc(Layer *layer, GContext *ctx) {
GRect bounds = layer_get_bounds(layer);
GRect bitmap_bounds = gbitmap_get_bounds(APP_ICON);
graphics_context_set_compositing_mode(ctx, GCompOpSet);
graphics_draw_bitmap_in_rect(ctx, APP_ICON, (GRect){.origin = bounds.origin, .size = bitmap_bounds.size});
}
void MessageWindow_build(Window *window, char* message, GFont message_font) {
TextLayer *s_label_layer;
Layer *s_icon_layer;
TextLayer *s_message_layer;
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
GRect bitmap_bounds = gbitmap_get_bounds(APP_ICON);
s_icon_layer = layer_create(GRect(DIALOG_MESSAGE_WINDOW_MARGIN, DIALOG_MESSAGE_WINDOW_MARGIN, bitmap_bounds.size.w, bitmap_bounds.size.h));
layer_set_update_proc(s_icon_layer, icon_update_proc);
layer_add_child(window_layer, s_icon_layer);
s_label_layer = text_layer_create(GRect(DIALOG_MESSAGE_WINDOW_MARGIN + bitmap_bounds.size.w + 5, DIALOG_MESSAGE_WINDOW_MARGIN / 2, bounds.size.w - bitmap_bounds.size.w - 5 - (2 * DIALOG_MESSAGE_WINDOW_MARGIN), bounds.size.h));
text_layer_set_text(s_label_layer, APP_NAME);
text_layer_set_background_color(s_label_layer, GColorClear);
text_layer_set_text_alignment(s_label_layer, GTextAlignmentLeft);
text_layer_set_font(s_label_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD));
layer_add_child(window_layer, text_layer_get_layer(s_label_layer));
s_message_layer = text_layer_create(GRect(DIALOG_MESSAGE_WINDOW_MARGIN, DIALOG_MESSAGE_WINDOW_MARGIN + bitmap_bounds.size.h, bounds.size.w - (2 * DIALOG_MESSAGE_WINDOW_MARGIN), bounds.size.h));
text_layer_set_text(s_message_layer, message);
text_layer_set_background_color(s_message_layer, GColorClear);
text_layer_set_text_alignment(s_message_layer, PBL_IF_ROUND_ELSE(GTextAlignmentCenter, GTextAlignmentLeft));
text_layer_set_font(s_message_layer, message_font);
layer_add_child(window_layer, text_layer_get_layer(s_message_layer));
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void MessageWindow_build(Window *window, char* message, GFont message_font);
+339
View File
@@ -0,0 +1,339 @@
#include <pebble.h>
#include "resources.h"
#include "mybahn_settings.h"
//#define CREATE_DUMMY_SETTINGS
static MyBahnSettings settings;
static void prv_validate_station(StationConfig* station) {
station->is_valid = true;
if (!station->is_enabled) {
station->is_valid = false;
} else if (strlen(station->ibnr) == 0) {
station->is_valid = false;
} else if (strlen(station->title) == 0) {
strncpy(station->title, station->ibnr, IBNR_LENGTH);
station->title[IBNR_LENGTH+1] = '\0';
}
}
static void prv_validate_connection(ConnectionConfig* connection) {
connection->is_valid = true;
if (!connection->is_enabled) {
connection->is_valid = false;
} else if (strlen(connection->start_ibnr) == 0) {
connection->is_valid = false;
} else if (strlen(connection->dest_ibnr) == 0) {
connection->is_valid = false;
} else if (strlen(connection->title) == 0) {
strcpy(connection->title, connection->start_ibnr);
strcat(connection->subtitle, TO_MARKER);
strcat(connection->subtitle, connection->dest_ibnr);
}
}
static void prv_validate_settings() {
settings.number_of_valid_stations = 0;
for (int i = 0; i < NUMBER_OF_STATIONS; i++) {
prv_validate_station(&settings.stations[i]);
if (settings.stations[i].is_valid) {
settings.valid_stations[settings.number_of_valid_stations++] = i;
}
}
settings.number_of_valid_connections = 0;
for (int i = 0; i < NUMBER_OF_CONNECTIONS; i++) {
prv_validate_connection(&settings.connections[i]);
if (settings.connections[i].is_valid) {
settings.valid_connections[settings.number_of_valid_connections++] = i;
}
}
}
static void prv_create_default_settings() {
#ifdef CREATE_DUMMY_SETTINGS
settings.default_products.allow_all = true;
settings.default_products.ice = true;
settings.default_products.ic = true;
settings.default_products.ir = true;
settings.default_products.zug = true;
settings.default_products.sbahn = true;
settings.default_products.bus = true;
settings.default_products.schiff = true;
settings.default_products.ubahn = true;
settings.default_products.tram = true;
settings.default_products.ast = true;
int i = -1;
i++;
strcpy(settings.stations[i].ibnr, "8006596");
strcpy(settings.stations[i].title, "Wpt. Barmen");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8003368");
strcpy(settings.stations[i].title, "Köln Messe/Deutz");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8000266");
strcpy(settings.stations[i].title, "Wpt. Hbf");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8006719");
strcpy(settings.stations[i].title, "Wpt. Oberbarmen");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8006620");
strcpy(settings.stations[i].title, "Wpt. Unterbarmen");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8000152");
strcpy(settings.stations[i].title, "Hannover Hbf");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8000142");
strcpy(settings.stations[i].title, "Hagen Hbf");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8006226");
strcpy(settings.stations[i].title, "Wattenscheid");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i++;
strcpy(settings.stations[i].ibnr, "8000085");
strcpy(settings.stations[i].title, "D'dorf Hbf");
settings.stations[i].is_enabled = true;
settings.stations[i].use_default_products = true;
i = -1;
i++;
strcpy(settings.connections[i].start_ibnr, "8003368");
strcpy(settings.connections[i].dest_ibnr, "8000266");
strcpy(settings.connections[i].title, "Köln Messe/Deutz");
strcpy(settings.connections[i].subtitle, "->Wpt. Hbf");
settings.connections[i].is_enabled = true;
settings.connections[i].use_default_products = false;
settings.connections[i].allowed_products.allow_all = false;
settings.connections[i].allowed_products.ice = false;
settings.connections[i].allowed_products.ic = false;
settings.connections[i].allowed_products.ir = false;
settings.connections[i].allowed_products.zug = true;
settings.connections[i].allowed_products.sbahn = true;
settings.connections[i].allowed_products.bus = true;
settings.connections[i].allowed_products.schiff = true;
settings.connections[i].allowed_products.ubahn = true;
settings.connections[i].allowed_products.tram = true;
settings.connections[i].allowed_products.ast = true;
i++;
strcpy(settings.connections[i].start_ibnr, "8000266");
strcpy(settings.connections[i].dest_ibnr, "8006596");
strcpy(settings.connections[i].title, "Wpt. Hbf");
strcpy(settings.connections[i].subtitle, "->Wpt. Barmen");
settings.connections[i].is_enabled = true;
settings.connections[i].use_default_products = false;
settings.connections[i].allowed_products.allow_all = false;
settings.connections[i].allowed_products.ice = false;
settings.connections[i].allowed_products.ic = false;
settings.connections[i].allowed_products.ir = false;
settings.connections[i].allowed_products.zug = true;
settings.connections[i].allowed_products.sbahn = true;
settings.connections[i].allowed_products.bus = true;
settings.connections[i].allowed_products.schiff = true;
settings.connections[i].allowed_products.ubahn = true;
settings.connections[i].allowed_products.tram = true;
settings.connections[i].allowed_products.ast = true;
i++;
strcpy(settings.connections[i].start_ibnr, "8006596");
strcpy(settings.connections[i].dest_ibnr, "8003368");
strcpy(settings.connections[i].title, "Wpt. Barmen");
strcpy(settings.connections[i].subtitle, "->Köln Messe/Deutz");
settings.connections[i].is_enabled = true;
settings.connections[i].use_default_products = false;
settings.connections[i].allowed_products.allow_all = false;
settings.connections[i].allowed_products.ice = false;
settings.connections[i].allowed_products.ic = false;
settings.connections[i].allowed_products.ir = false;
settings.connections[i].allowed_products.zug = true;
settings.connections[i].allowed_products.sbahn = true;
settings.connections[i].allowed_products.bus = true;
settings.connections[i].allowed_products.schiff = true;
settings.connections[i].allowed_products.ubahn = true;
settings.connections[i].allowed_products.tram = true;
settings.connections[i].allowed_products.ast = true;
#endif
}
// Read settings from persistent storage
void MyBahnSettings_load() {
// Read settings from persistent storage, if they exist
if (persist_exists(MESSAGE_KEY_DefAllProd)) {
persist_read_data(MESSAGE_KEY_DefAllProd, &settings.default_products, sizeof(settings.default_products));
for (int i = 0; i < NUMBER_OF_STATIONS; i++) {
persist_read_data(MESSAGE_KEY_StatEnabled + i, &settings.stations[i], sizeof(StationConfig));
}
for (int i = 0; i < NUMBER_OF_CONNECTIONS; i++) {
persist_read_data(MESSAGE_KEY_ConnEnabled + i, &settings.connections[i], sizeof(ConnectionConfig));
}
} else {
prv_create_default_settings();
}
prv_validate_settings();
}
// Save the settings to persistent storage
void MyBahnSettings_save() {
persist_write_data(MESSAGE_KEY_DefAllProd, &settings.default_products, sizeof(settings.default_products));
for (int i = 0; i < NUMBER_OF_STATIONS; i++) {
persist_write_data(MESSAGE_KEY_StatEnabled + i, &settings.stations[i], sizeof(StationConfig));
}
for (int i = 0; i < NUMBER_OF_CONNECTIONS; i++) {
persist_write_data(MESSAGE_KEY_ConnEnabled + i, &settings.connections[i], sizeof(ConnectionConfig));
}
}
static bool prv_receive_settings_string(char* dest, DictionaryIterator *iter, int msg_key, int length) {
Tuple *config_tuple = dict_find(iter, msg_key);
if (config_tuple) {
strncpy(dest, config_tuple->value->cstring, length);
dest[length] = '\0';
return true;
}
return false;
}
#define RECEIVE_BOOL(received_flag, iter, msg_key, target) {\
Tuple* t = dict_find(iter, msg_key); \
if (t) { \
received_flag = true; \
target = t->value->int32 == 1; \
} \
}
bool MyBahnSettings_handle_app_message(DictionaryIterator *iter, void *context) {
bool has_received_settings = false;
// Standard Verkehrsmittel
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefAllProd, settings.default_products.allow_all);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefICE, settings.default_products.ice);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefIC, settings.default_products.ic);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefIR, settings.default_products.ir);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefRE, settings.default_products.zug);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefS, settings.default_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefS, settings.default_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefBus, settings.default_products.bus);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefSchiff, settings.default_products.schiff);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefU, settings.default_products.ubahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefTram, settings.default_products.tram);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_DefAST, settings.default_products.ast);
// Station-Settings
for (int i = 0; i < NUMBER_OF_STATIONS; i++) {
has_received_settings |= prv_receive_settings_string(settings.stations[i].title, iter, MESSAGE_KEY_StatTitle + i, STATION_TITLE_LENGTH);
has_received_settings |= prv_receive_settings_string(settings.stations[i].subtitle, iter, MESSAGE_KEY_StatSubTitle + i, STATION_SUBTITLE_LENGTH);
has_received_settings |= prv_receive_settings_string(settings.stations[i].ibnr, iter, MESSAGE_KEY_StatIBNR + i, IBNR_LENGTH);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatEnabled + i, settings.stations[i].is_enabled);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatUseDefProd + i, settings.stations[i].use_default_products);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatAllProd + i, settings.stations[i].allowed_products.allow_all);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatICE + i, settings.stations[i].allowed_products.ice);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatIC + i, settings.stations[i].allowed_products.ic);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatIR + i, settings.stations[i].allowed_products.ir);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatRE + i, settings.stations[i].allowed_products.zug);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatS + i, settings.stations[i].allowed_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatS + i, settings.stations[i].allowed_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatBus + i, settings.stations[i].allowed_products.bus);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatSchiff + i, settings.stations[i].allowed_products.schiff);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatU + i, settings.stations[i].allowed_products.ubahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatTram + i, settings.stations[i].allowed_products.tram);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_StatAST + i, settings.stations[i].allowed_products.ast);
}
// Connection-Settings
for (int i = 0; i < NUMBER_OF_CONNECTIONS; i++) {
has_received_settings |= prv_receive_settings_string(settings.connections[i].title, iter, MESSAGE_KEY_ConnTitle + i, CONNECTION_TITLE_LENGTH);
has_received_settings |= prv_receive_settings_string(settings.connections[i].subtitle, iter, MESSAGE_KEY_ConnSubTitle + i, CONNECTION_SUBTITLE_LENGTH);
has_received_settings |= prv_receive_settings_string(settings.connections[i].start_ibnr, iter, MESSAGE_KEY_ConnStartIBNR + i, IBNR_LENGTH);
has_received_settings |= prv_receive_settings_string(settings.connections[i].dest_ibnr, iter, MESSAGE_KEY_ConnDestIBNR + i, IBNR_LENGTH);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnEnabled + i, settings.connections[i].is_enabled);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnUseDefProd + i, settings.connections[i].use_default_products);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnAllProd + i, settings.connections[i].allowed_products.allow_all);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnICE + i, settings.connections[i].allowed_products.ice);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnIC + i, settings.connections[i].allowed_products.ic);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnIR + i, settings.connections[i].allowed_products.ir);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnRE + i, settings.connections[i].allowed_products.zug);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnS + i, settings.connections[i].allowed_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnS + i, settings.connections[i].allowed_products.sbahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnBus + i, settings.connections[i].allowed_products.bus);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnSchiff + i, settings.connections[i].allowed_products.schiff);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnU + i, settings.connections[i].allowed_products.ubahn);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnTram + i, settings.connections[i].allowed_products.tram);
RECEIVE_BOOL(has_received_settings, iter, MESSAGE_KEY_ConnAST + i, settings.connections[i].allowed_products.ast);
}
if (has_received_settings) {
prv_validate_settings();
MyBahnSettings_save();
}
return has_received_settings;
}
void MyBahnSettings_init() {
MyBahnSettings_load();
}
ConnectionConfig* MyBahnSettings_get_valid_connection(int valid_connection_index) {
if (valid_connection_index >= settings.number_of_valid_connections) {
return NULL;
}
return &settings.connections[settings.valid_connections[valid_connection_index]];
}
StationConfig* MyBahnSettings_get_valid_station(int valid_station_index) {
if (valid_station_index >= settings.number_of_valid_stations) {
return NULL;
}
return &settings.stations[settings.valid_stations[valid_station_index]];
}
AllowedProducts* MyBahnSettings_get_default_products() {
return &settings.default_products;
}
int MyBahnSettings_get_number_of_valid_connections() {
return settings.number_of_valid_connections;
}
int MyBahnSettings_get_number_of_valid_stations() {
return settings.number_of_valid_stations;
}
AllowedProducts* MyBahnSettings_get_allowed_products_for_connection(int connection_index) {
ConnectionConfig* conn = MyBahnSettings_get_valid_connection(connection_index);
if (&conn->allowed_products == NULL || conn->use_default_products) {
return &settings.default_products;
}
return &conn->allowed_products;
}
AllowedProducts* MyBahnSettings_get_allowed_products_for_station(int station_index) {
StationConfig* s = MyBahnSettings_get_valid_station(station_index);
if (&s->allowed_products == NULL || s->use_default_products) {
return &settings.default_products;
}
return &s->allowed_products;
}
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include <pebble.h>
#define SETTINGS_KEY 1
#define IBNR_LENGTH 8
#define STATION_TITLE_LENGTH 20
#define STATION_SUBTITLE_LENGTH 25
#define CONNECTION_TITLE_LENGTH 20
#define CONNECTION_SUBTITLE_LENGTH 25
#define NUMBER_OF_STATIONS 10
#define NUMBER_OF_CONNECTIONS 10
typedef struct {
bool allow_all;
bool ice;
bool ic;
bool ir;
bool zug;
bool sbahn;
bool bus;
bool schiff;
bool ubahn;
bool tram;
bool ast;
} AllowedProducts;
typedef struct {
char ibnr[IBNR_LENGTH+1];
char title[STATION_TITLE_LENGTH+1];
char subtitle[STATION_SUBTITLE_LENGTH+1];
bool use_default_products;
AllowedProducts allowed_products;
bool is_enabled;
bool is_valid;
} StationConfig;
typedef struct {
char title[CONNECTION_TITLE_LENGTH+1];
char subtitle[CONNECTION_SUBTITLE_LENGTH+1];
char start_ibnr[IBNR_LENGTH+1];
char dest_ibnr[IBNR_LENGTH+1];
bool use_default_products;
AllowedProducts allowed_products;
bool is_enabled;
bool is_valid;
} ConnectionConfig;
// A structure containing our settings
typedef struct {
AllowedProducts default_products;
StationConfig stations[NUMBER_OF_STATIONS];
ConnectionConfig connections[NUMBER_OF_CONNECTIONS];
int number_of_valid_stations;
int number_of_valid_connections;
int valid_stations[NUMBER_OF_STATIONS];
int valid_connections[NUMBER_OF_STATIONS];
} MyBahnSettings;
bool MyBahnSettings_handle_app_message(DictionaryIterator *iter, void *context);
void MyBahnSettings_init();
void MyBahnSettings_load();
void MyBahnSettings_save();
AllowedProducts* MyBahnSettings_get_default_products();
ConnectionConfig* MyBahnSettings_get_valid_connection(int valid_connection_index);
StationConfig* MyBahnSettings_get_valid_station(int valid_station_index);
int MyBahnSettings_get_number_of_valid_connections();
int MyBahnSettings_get_number_of_valid_stations();
AllowedProducts* MyBahnSettings_get_allowed_products_for_connection(int connection_index);
AllowedProducts* MyBahnSettings_get_allowed_products_for_station(int station_index);
+11
View File
@@ -0,0 +1,11 @@
#include <pebble.h>
#include "resources.h"
static GBitmap *s_app_icon_bitmap;
GBitmap* getAppIcon() {
if (!s_app_icon_bitmap) {
s_app_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ICON_MAIN);
}
return s_app_icon_bitmap;
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <pebble.h>
#define APP_NAME "myBahn"
#define NOT_CONFIGURED "Nicht konfiguriert!"
#define PLEASE_CONFIGURE "Bitte konfigurieren!"
#define CONNECTIONS "Verbindungen"
#define START_STATIONS "Von Station"
#define TO_MARKER "->"
#define FRUEHER "Früher"
#define SPAETER "Später"
#define LOAD_FAILED_MESSAGE "Abfrage fehlgeschlagen.\nIBNR korrekt?\nKeine Verbindung zu Internet oder Phone?"
#define LOADING_MESSAGE "Abfrage läuft..."
#define APP_ICON getAppIcon()
GBitmap* getAppIcon();
+78
View File
@@ -0,0 +1,78 @@
#include <pebble.h>
#include "scroll_text_layer.h"
#define MAX_HEIGHT 2000
#define PADDING_X 4
#define PADDING_Y 4
struct ScrollTextLayer {
TextLayer* text_layer;
ScrollLayer* scroll_layer;
};
static void scroll_text_layer_update(ScrollTextLayer* layer);
ScrollTextLayer* scroll_text_layer_create(GRect rect) {
ScrollTextLayer* stl = malloc(sizeof(ScrollTextLayer));
stl->scroll_layer = scroll_layer_create(rect);
GRect max_text_bounds = GRect(PADDING_X, PADDING_Y, rect.size.w - (PADDING_X * 2), MAX_HEIGHT);
stl->text_layer = text_layer_create(max_text_bounds);
scroll_layer_add_child(stl->scroll_layer, text_layer_get_layer(stl->text_layer));
return stl;
}
void scroll_text_layer_destroy(ScrollTextLayer* layer) {
if (NULL == layer) {
return;
}
text_layer_destroy(scroll_text_layer_get_text_layer(layer));
scroll_layer_destroy(scroll_text_layer_get_scroll_layer(layer));
free(layer);
}
void scroll_text_layer_add_to_window(ScrollTextLayer* layer, Window* window) {
if (NULL == layer || NULL == window) {
return;
}
scroll_layer_set_click_config_onto_window(layer->scroll_layer, window);
layer_add_child(window_get_root_layer(window), scroll_layer_get_layer(layer->scroll_layer));
}
void scroll_text_layer_set_text(ScrollTextLayer* layer, char* text) {
if (NULL == layer) {
return;
}
text_layer_set_size(layer->text_layer, GSize(layer_get_bounds(scroll_layer_get_layer(layer->scroll_layer)).size.w - (PADDING_X * 2), MAX_HEIGHT));
text_layer_set_text(layer->text_layer, text);
scroll_text_layer_update(layer);
}
TextLayer* scroll_text_layer_get_text_layer(ScrollTextLayer* layer) {
if (NULL == layer) {
return NULL;
}
return layer->text_layer;
}
ScrollLayer* scroll_text_layer_get_scroll_layer(ScrollTextLayer* layer) {
if (NULL == layer) {
return NULL;
}
return layer->scroll_layer;
}
void scroll_text_layer_set_font(ScrollTextLayer* layer, GFont font) {
text_layer_set_font(scroll_text_layer_get_text_layer(layer), font);
scroll_text_layer_update(layer);
}
static void scroll_text_layer_update(ScrollTextLayer* layer) {
GSize max_size = text_layer_get_content_size(layer->text_layer);
text_layer_set_size(layer->text_layer, max_size);
GRect bounds = layer_get_bounds(scroll_layer_get_layer(layer->scroll_layer));
scroll_layer_set_content_size(layer->scroll_layer, GSize(bounds.size.w, max_size.h + (PADDING_Y * 3)));
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <pebble.h>
struct ScrollTextLayer;
typedef struct ScrollTextLayer ScrollTextLayer;
ScrollTextLayer* scroll_text_layer_create(GRect rect);
void scroll_text_layer_destroy(ScrollTextLayer* layer);
TextLayer* scroll_text_layer_get_text_layer(ScrollTextLayer* layer);
ScrollLayer* scroll_text_layer_get_scroll_layer(ScrollTextLayer* layer);
void scroll_text_layer_add_to_window(ScrollTextLayer* layer, Window* window);
void scroll_text_layer_set_text(ScrollTextLayer* layer, char* text);
void scroll_text_layer_set_font(ScrollTextLayer*, GFont font);
#define scroll_text_layer_create_fullscreen(window) scroll_text_layer_create(layer_get_bounds(window_get_root_layer(window)));
#define scroll_text_layer_set_text_color(layer, color) text_layer_set_text_color(scroll_text_layer_get_text_layer(layer), color)
#define scroll_text_layer_set_background_color(layer, color) text_layer_set_background_color(scroll_text_layer_get_text_layer(layer), color)
#define scroll_text_layer_set_text_alignment(layer, alignment) text_layer_set_text_alignment(scroll_text_layer_get_text_layer(layer), alignment)
#define scroll_text_layer_set_system_font(layer, font) scroll_text_layer_set_font(layer, fonts_get_system_font(font))
#define scroll_text_layer_set_callbacks(layer, callbacks) scroll_layer_set_callbacks(scroll_text_layer_get_scroll_layer(layer), callbacks)
+90
View File
@@ -0,0 +1,90 @@
#include <pebble.h>
#include "mybahn_settings.h"
#include "resources.h"
#include "connection_results_menu.h"
#include "to_stations_menu.h"
static MenuLayer* s_menu_layer;
static Window* s_window;
static char s_header[STATION_TITLE_LENGTH + 10];
static int from_station_index;
static void select_to_station(int to_station_index) {
ConnectionResultsMenu_show_station_to_station(from_station_index, to_station_index);
}
static int compute_to_station_index(int station_index) {
if (station_index < from_station_index) {
return station_index;
}
return station_index + 1;
}
static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) {
return 1;
}
static uint16_t menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return MyBahnSettings_get_number_of_valid_stations() - 1;
}
static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) {
int to_station_index = compute_to_station_index(cell_index->row);
StationConfig* station = MyBahnSettings_get_valid_station(to_station_index);
menu_cell_basic_draw(ctx, cell_layer, station->title, station->subtitle, NULL);
}
static int16_t menu_get_header_height_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return MENU_CELL_BASIC_HEADER_HEIGHT;
}
static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, uint16_t section_index, void *data) {
menu_cell_basic_header_draw(ctx, cell_layer, s_header);
}
static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) {
int to_station_index = compute_to_station_index(cell_index->row);
select_to_station(to_station_index);
}
void ToStationsMenu_display(int _from_station_index) {
ToStationsMenu_destroy();
from_station_index = _from_station_index;
StationConfig* start_station = MyBahnSettings_get_valid_station(from_station_index);
s_header[0] = '\0';
strcat(s_header, start_station->title);
strcat(s_header, TO_MARKER);
s_window = window_create();
Layer *window_layer = window_get_root_layer(s_window);
GRect bounds = layer_get_frame(window_layer);
s_menu_layer = menu_layer_create(bounds);
menu_layer_set_click_config_onto_window(s_menu_layer, s_window);
menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks){
.get_num_sections = menu_get_num_sections_callback,
.get_num_rows = menu_get_num_rows_callback,
.get_header_height = menu_get_header_height_callback,
.draw_header = menu_draw_header_callback,
.draw_row = menu_draw_row_callback,
.select_click = menu_select_callback,
});
layer_add_child(window_layer, menu_layer_get_layer(s_menu_layer));
window_stack_push(s_window, true);
}
void ToStationsMenu_destroy() {
if (!s_window) {
return;
}
menu_layer_destroy(s_menu_layer);
window_destroy(s_window);
s_menu_layer = NULL;
s_window = NULL;
}
+4
View File
@@ -0,0 +1,4 @@
#pragma once
void ToStationsMenu_display(int from_station_index);
void ToStationsMenu_destroy();
+265
View File
@@ -0,0 +1,265 @@
var baseUrl = 'http://mobil.bahn.de/bin/query.exe/dox?start=1&rt=1';
function xhrRequest(url, type, callback) {
var xhr = new XMLHttpRequest();
var async = callback ? true : false;
if (async) {
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status >= 200 && this.status < 300) {
callback(this.responseText);
} else {
callback(null);
}
}
};
}
try {
xhr.open(type, url, async);
xhr.send();
} catch(error) {
if (callback) {
callback(null);
}
return null;
}
if (!async) {
if (xhr.status >= 200 && xhr.status < 300) {
return xhr.responseText;
} else {
return null;
}
}
}
function extractConnectionResultInfos(text) {
if (!text) {
return null;
}
var detailLinkExp = /a href="([^"]*)"/;
var zeitExp = /span class="bold">(\d*:\d*)</g;
var vmExp = /<td class="overview iphonepfeil">([^<]*)</;
var umstiegeUndDauerExp = /<td class="overview">(\d*) *<br *.>(\d*:\d*)/;
var delaysExp = /<td class="overview tprt"> *<span[^>]*>(.\d*)<.*<span[^>]*>(.\d*)</;
var nurStartDelayExp = /<td class="overview tprt"> *<span[^>]*>(.\d*)</;
var nurZielDelayExp = /<td class="overview tprt"> *&nbsp.*<span[^>]*>(.\d*)</;
var achtungExp = /achtung_17x19_mitschatten.png/;
var ausfallExp = /Icon_Zug_faellt_aus_mit_Schatten_17x19.png/;
var frueherExp = /(http:\/\/reiseauskunft\.bahn\.de\/bin\/query2\.exe\/dox\?[^"]*amp;e=2[^"]*)"/;
var spaeterExp = /(http:\/\/reiseauskunft\.bahn\.de\/bin\/query2\.exe\/dox\?[^"]*&amp;e=1[^"]*)"/;
var ampExp = /&amp;/gm;
var results = [];
var resultStrings = text.replace(/(\r\n)|(\n|\r)|\t/gm,"").split('<td class="overview timelink">');
var frueherLink = text.match(frueherExp);
if (frueherLink) {
frueherLink = frueherLink[1].replace(ampExp, "&");
results.push({moreLink: frueherLink, earlier: true});
}
for (var i = 1; i < resultStrings.length; i++) {
var connectionInfo = resultStrings[i].trim();
var result = {};
var detailLink = connectionInfo.match(detailLinkExp);
if (detailLink) {
result.detailLink = detailLink[1].replace(ampExp, "&");
}
zeitExp.lastIndex = 0;
result.von = zeitExp.exec(connectionInfo)[1];
result.bis = zeitExp.exec(connectionInfo)[1];
result.vm = connectionInfo.match(vmExp)[1].trim();
var umstiegeUndDauer = connectionInfo.match(umstiegeUndDauerExp);
if (umstiegeUndDauer) {
result.umstiege = umstiegeUndDauer[1];
result.dauer = umstiegeUndDauer[2];
}
var delays = connectionInfo.match(delaysExp);
if (delays) {
result.delayStart = delays[1];
result.delayZiel = delays[2];
} else {
var nurZielDelay = connectionInfo.match(nurZielDelayExp);
if (nurZielDelay) {
result.delayZiel = nurZielDelay[1];
} else {
var nurStartDelay = connectionInfo.match(nurStartDelayExp);
if (nurStartDelay) {
result.delayStart = nurStartDelay[1];
}
}
}
if (connectionInfo.match(achtungExp)) {
result.achtung = true;
} else {
result.achtung = false;
}
if (connectionInfo.match(ausfallExp)) {
result.ausfall = true;
} else {
result.ausfall = false;
}
results.push(result);
}
var spaeterLink = text.match(spaeterExp);
if (spaeterLink) {
spaeterLink = spaeterLink[1].replace(ampExp, "&");
results.push({moreLink: spaeterLink, later: true});
}
return results;
}
function extractConnectionDetailInfos(text) {
if (!text) {
return null;
}
var achtungExp = /<[^<]*achtung_17x19_mitschatten.png[^>]*>/gm;
var ausfallExp = /<[^<]*Icon_Zug_faellt_aus_mit_Schatten_17x19.png[^>]*>/gm;
var warnungExp = /<div class="red bold haupt" *>([^<]*)<\/div>/;
var spanExp = /<\/?span[^>]*>/gm;
var himDetailsLinkExp = /"(http:\/\/reiseauskunft\.bahn\.de\/bin\/query2\.exe\/dox\?[^"]*&amp;him=[^"]*)"/;
var htmlTagExp = /<[^<>]*>/gm;
var brSurrogateExp = /&#br;/gm;
var results = '';
text = text
.replace(/(\r\n)|(\n|\r)|\t/gm, '')
.replace(/<br *\/?>/gm, '&#br;')
.replace(spanExp, '');
var resultLines = text
.replace(/<div class="rlinebottom">.*/, '')
.replace(achtungExp, ' !')
.replace(ausfallExp, ' X')
.replace(/Umstiegszeit/, '')
.replace(/<div class="motSection">/gm, '> ')
.replace(/<div class="interSection">/gm, '> ')
.split('<div class="rline');
var warnung = text.match(warnungExp);
if (warnung) {
warnung = warnung[1]
.replace(brSurrogateExp, '\n')
.replace(/^\s*/, '')
.replace(/\s*$/, '');
results += '! ' + warnung + '\n';
}
for (var i = 1; i < resultLines.length; i++) {
var detailLine = '<div class="rline' + resultLines[i];
detailLine = detailLine
.replace(htmlTagExp, '')
.replace(brSurrogateExp, '\n')
.replace(/^\s*/, '')
.replace(/\s*$/, '');
if (results.length > 0) {
results += '\n';
}
results += detailLine;
}
results = results
.replace(/ Gl. /gm, "\nGl. ");
// Hinweise extrahieren
resultLines = text
.replace(/(\r\n)|(\n|\r)|\t/gm,"")
.replace(/<form action=.*/, '')
.split('<div class="him">');
for (i = 1; i < resultLines.length; i++) {
var himLine = resultLines[i];
himDetailsLinkExp.lastIndex = 0;
var himDetailsLink = himLine.match(himDetailsLinkExp);
if (himDetailsLink) {
himLine = extractHimDetails(himDetailsLink[1]);
}
if (!himLine) {
continue;
}
himLine = himLine
.replace(/<\/div>.*/, '')
.replace(brSurrogateExp, '\n')
.replace(/^\s*/, '')
.replace(/\s*$/, '');
if (results.length > 0) {
results += '\n';
}
results += himLine;
}
results = results
.replace(/&nbsp;/gm, ' ')
.replace(/&#196;/gm, "Ä")
.replace(/&#214;/gm, "Ö")
.replace(/&#220;/gm, "Ü")
.replace(/&#223;/gm, "ß")
.replace(/&#228;/gm, "ä")
.replace(/&#246;/gm, "ö")
.replace(/&#252;/gm, "ü");
return results;
}
function extractHimDetails(himDetailsUrl) {
var himLine = '';
var himDetailsResponseText = xhrRequest(himDetailsUrl, 'GET');
if (himDetailsResponseText) {
var himDetailsLines = himDetailsResponseText
.replace(/(\r\n)|(\n|\r)|\t/gm,"")
.split('<div class="him">');
himLine = himDetailsLines[1];
}
return himLine;
}
function getConnectionsFromUrl(url, resultCallback) {
xhrRequest(url, 'GET', function(responseText) {
var results = extractConnectionResultInfos(responseText);
resultCallback(results);
});
}
function getConnections(startKey, zielKey, journeyProducts, resultCallback) {
var productParam = '';
if (journeyProducts) {
productParam = '&journeyProducts=' + journeyProducts;
}
var url = baseUrl + '&S=' + startKey + '&Z=' + zielKey + productParam;
return getConnectionsFromUrl(url, resultCallback);
}
function getConnectionDetailInfos(detailLink, resultCallback) {
xhrRequest(detailLink, 'GET', function(responseText){
var result = extractConnectionDetailInfos(responseText);
resultCallback(result);
});
}
module.exports = {
getConnections : getConnections,
getConnectionsFromUrl : getConnectionsFromUrl,
getConnectionDetailInfos : getConnectionDetailInfos
};
+355
View File
@@ -0,0 +1,355 @@
var numberOfConnections = 10;
var numberOfStations = 10;
var exports = [
{
"type": "heading",
"defaultValue": "myBahn Konfiguration"
},
{
"type": "toggle",
"id": "HowtoToggle",
"label": "Anleitung anzeigen",
"group": "Howto",
"defaultValue": false
},
{
"type": "text",
"group": "Howto",
"defaultValue":
'Im ersten Abschnitt stellst Du die Standard-Verkehrsmittel ein, die Du für Deine Abfragen verwenden willst. Diese kannst Du für einzelne Verbindungen oder Bahnhöfe bei Bedarf gezielt übersteuern.'
},
{
"type": "text",
"group": "Howto",
"defaultValue":
'Für die Konfiguration von Bahnhöfen bzw. Verbindungen brauchst Du die jeweilige, 8-stellige IBNR bzw. EVA-Nr der Haltestellen.<br/>' +
'Ein Verzeichnis findest Du z.B. bei der Deutschen Bahn unter <a href="http://data.deutschebahn.com/dataset/data-haltestellen">http://data.deutschebahn.com/dataset/data-haltestellen</a>.<br/>' +
'Oder Du benutzt die Online-Suche von Michael Dittrich unter <a href="http://www.michaeldittrich.de/ibnr/online.php">http://www.michaeldittrich.de/ibnr/online.php</a>.<br/>' +
'Ungültige oder falsche IBNR führen dazu, dass die Abfragen fehlschlagen oder falsche Daten liefern.'
},
{
"type": "text",
"group": "Howto",
"defaultValue":
'Im Abschnitt Verbindungen konfigurierst Du maximal 10 Fahrtstrecken zwischen je zwei festgelegten Bahnhöfen, die Du oft brauchst und daher schnell und einfach abfragen willst.<br/>' +
'Nicht benötigte Einträge kannst Du über den Schalter neben der Verbingungsüberschrift ausschalten; d.h. nur die eingeschalteten Verbindungen erscheinen in der Verbindungsliste auf der Pebble.</br>' +
'Du musst Start- und Ziel-Bahnhof durch die zugehörige IBNR/EVA-Nr sowie einen Titel zur Anzeige in der Liste auf der Pebble spezifizieren. Optional kannst Du noch einen Untertitel für die Anzeige angeben.<br/>' +
'Fehlt eine der IBNR/EVA-Nr oder der Titel, ist die Verbindung ungültig und erscheint nicht in der Liste auf der Pebble!</br>' +
'Titel werden automatisch auf 20 Zeichen gekürzt, Untertitel auf 25.</br>' +
'Zusätzlich kannst Du abweichend von den konfigurierten Standard-Verkehrsmitteln eigene Einstellungen für die bei dieser Verbindung erlaubten Verkehrsmittel vornehmen.'
},
{
"type": "text",
"group": "Howto",
"defaultValue":
'Im Abschnitt Bahnhöfe konfigurierst Du maximal 10 Bahnhöfe, zwischen denen Du beliebige Abfragen machen kannst. D.h. Du wählst auf der Pebble einen der Bahnhöfe als Start und einen als Ziel aus und bekommst eine Auskunft für die Fahrtstrecke.<br/>' +
'Nicht benötigte Einträge kannst Du über den Schalter neben der Bahnhofsüberschrift ausschalten; d.h. nur die eingeschalteten Bahnhöfe erscheinen in der Bahnhofsliste auf der Pebble.</br>' +
'Du musst den Bahnhof durch die zugehörige IBNR/EVA-Nr sowie einen Titel zur Anzeige in der Liste auf der Pebble spezifizieren. Optional kannst Du noch einen Untertitel für die Anzeige angeben.<br/>' +
'Fehlt die IBNR/EVA-Nr oder der Titel, ist der Bahnhof ungültig und erscheint nicht in der Liste auf der Pebble!</br>' +
'Titel werden automatisch auf 20 Zeichen gekürzt, Untertitel auf 25.</br>' +
'Zusätzlich kannst Du abweichend von den konfigurierten Standard-Verkehrsmitteln eigene Einstellungen für die an diesem Bahnhof erlaubten Verkehrsmittel vornehmen. ' +
'Bei der Abfrage von Bahnhof zu Bahnhof wird die Summe aller an beiden Bahnhöfen erlaubten Verkehrsmittel verwendet.'
},
{
"type": "text",
"group": "Howto",
"defaultValue":
'Innerhalb der Bahnhöfe und Verbindungen kannst Du jeweils über den Button "Nach oben" die Einträge umsortieren.'
}
];
function createProductSection(withUseDefaultToggle, prefix, suffix) {
if (!prefix) {
prefix = '';
}
if (!suffix) {
suffix = '';
}
var result = [];
if (withUseDefaultToggle) {
result.push(
{
"type": "toggle",
"messageKey": prefix + "UseDefProd" + suffix,
"label": "Standard-Verkehrsmittel verwenden",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
}
result.push(
{
"type": "toggle",
"messageKey": prefix + "AllProd" + suffix,
"label": "Alle",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "ICE" + suffix,
"label": "ICE",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "IC" + suffix,
"label": "IC/EC",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "IR" + suffix,
"label": "Interregio und Schnellzug",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "RE" + suffix,
"label": "Regio- und Nahverkehr",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "S" + suffix,
"label": "S-Bahn",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "Bus" + suffix,
"label": "Bus",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "Schiff" + suffix,
"label": "Schiff",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "U" + suffix,
"label": "U-Bahn",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "Tram" + suffix,
"label": "Straßenbahn",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
result.push(
{
"type": "toggle",
"messageKey": prefix + "AST" + suffix,
"label": "Anruf Sammeltaxi (AST)",
"group": prefix + "Products" + suffix,
"defaultValue": true
});
return result;
}
var sectionDefaultJourneyProducts = createProductSection(false, "Def");
sectionDefaultJourneyProducts.unshift(
{
"type": "text",
"defaultValue":
"Stelle hier die Verkehrsmittel ein, die Du standardmäßig für Abfragen erlauben willst."
}
);
sectionDefaultJourneyProducts.unshift(
{
"type": "heading",
"defaultValue": "Standard-Verkehrsmittel"
}
);
var sectionStationsItems = [
{
"type": "heading",
"defaultValue": "Bahnhöfe"
},
{
"type": "text",
"defaultValue":
"Trage hier die Bahnhöfe ein, die Du in Bahnhof-zu-Bahnhof-Abfragen verwenden willst."
}
];
for (var i = 0; i < numberOfStations; i++) {
var statIndex = "[" + i +"]";
var items = [
{
"type": "toggle",
"messageKey": "StatEnabled"+statIndex,
"group": "StatEnabled"+statIndex,
"label": "Bahnhof " + (i+1)
},
{
"type": "input",
"messageKey": "StatTitle"+statIndex,
"group": "StatEnabled"+statIndex,
"label": "Titel",
"attributes": {
"limit": 20,
}
},
{
"type": "input",
"messageKey": "StatSubTitle"+statIndex,
"group": "StatEnabled"+statIndex,
"label": "Untertitel (optional)",
"attributes": {
"limit": 25,
}
},
{
"type": "input",
"messageKey": "StatIBNR"+statIndex,
"group": "StatEnabled"+statIndex,
"label": "IBNR/EVA-Nr",
"attributes": {
"limit": 8,
}
}
];
var stationProducts = createProductSection(true, "Stat", statIndex);
items.push(stationProducts);
if (i > 0) {
items.push(
{
"type": "button",
"id" : "StatUp"+i,
"primary": false,
"group": "StatEnabled"+statIndex,
"defaultValue": "Nach oben"
}
);
}
sectionStationsItems.push(
{
"type": "section",
"items": items
});
}
var sectionConnectionsItems = [
{
"type": "heading",
"defaultValue": "Verbindungen"
},
{
"type": "text",
"defaultValue":
"Konfiguriere hier die Verbindungen, die Du häufig benutzt."
}
];
for (var i = 0; i < numberOfConnections; i++) {
var connIndex = "[" + i +"]";
var items = [
{
"type": "toggle",
"messageKey": "ConnEnabled"+connIndex,
"group": "ConnEnabled"+connIndex,
"label": "Verbindung " + (i+1)
},
{
"type": "input",
"messageKey": "ConnTitle"+connIndex,
"group": "ConnEnabled"+connIndex,
"label": "Titel",
"attributes": {
"limit": 20,
}
},
{
"type": "input",
"messageKey": "ConnSubTitle"+connIndex,
"group": "ConnEnabled"+connIndex,
"label": "Untertitel (optional)",
"attributes": {
"limit": 25,
}
},
{
"type": "input",
"messageKey": "ConnStartIBNR"+connIndex,
"group": "ConnEnabled"+connIndex,
"label": "Start IBNR/EVA-Nr",
"attributes": {
"limit": 8,
}
},
{
"type": "input",
"messageKey": "ConnDestIBNR"+connIndex,
"group": "ConnEnabled"+connIndex,
"label": "Ziel IBNR/EVA-Nr",
"attributes": {
"limit": 8,
}
}
];
var connectionProducts = createProductSection(true, "Conn", connIndex);
items.push(connectionProducts);
if (i > 0) {
items.push(
{
"type": "button",
"id" : "ConnUp"+i,
"primary": false,
"group": "ConnEnabled"+connIndex,
"defaultValue": "Nach oben"
}
);
}
sectionConnectionsItems.push(
{
"type": "section",
"items": items
});
}
exports.push({
"type" : "section",
"items": sectionDefaultJourneyProducts
});
exports.push({
"type" : "section",
"items": sectionConnectionsItems
});
exports.push({
"type" : "section",
"items": sectionStationsItems
});
exports.push(
{
"type": "submit",
"defaultValue": "Speichern"
});
module.exports = exports;
+199
View File
@@ -0,0 +1,199 @@
module.exports = function(minified) {
var numberOfConnections = 10;
var numberOfStations = 10;
var clayConfig = this;
function toggleAllProducts() {
var allowAllProducts = this.get();
var groupMembers = clayConfig.getItemsByGroup(this.config.group);
var useDefaultProductsMessageKey = this.messageKey.replace('All', 'UseDef');
for (var i = 0; i < groupMembers.length; i++) {
var item = groupMembers[i];
if (item.messageKey == this.messageKey || item.messageKey == useDefaultProductsMessageKey) {
continue;
}
if (allowAllProducts) {
item.hide();
} else {
item.show();
}
}
}
function toggleUseDefaultProducts() {
var useDefaultProducts = this.get();
if (useDefaultProducts) {
hideOtherGroupMembersOf.call(this);
} else {
var allProdToggle = clayConfig.getItemByMessageKey(this.messageKey.replace('UseDef', 'All'));
allProdToggle.show();
toggleAllProducts.call(allProdToggle);
}
}
function toggleGroupEnabled() {
var enabled = this.get();
var groupMembers = clayConfig.getItemsByGroup(this.config.group);
for (var i = 0; i < groupMembers.length; i++) {
var item = groupMembers[i];
if (item.messageKey == this.messageKey) {
continue;
}
if (enabled) {
item.show();
} else {
item.hide();
}
}
var useDefaultProductsToggle = clayConfig.getItemByMessageKey(this.messageKey.replace('Enabled', 'UseDefProd'));
if (useDefaultProductsToggle) {
if (enabled) {
useDefaultProductsToggle.show();
} else {
useDefaultProductsToggle.hide();
}
}
}
function toggleHowto() {
if (this.get()) {
showOtherGroupMembersOf.call(this);
} else {
hideOtherGroupMembersOf.call(this);
}
}
function hideOtherGroupMembersOf() {
var groupMembers = clayConfig.getItemsByGroup(this.config.group);
for (var i = 0; i < groupMembers.length; i++) {
var item = groupMembers[i];
if (item.id && this.id && item.id == this.id) {
continue;
} else if (item.messageKey && this.messageKey && item.messageKey == this.messageKey) {
continue;
}
item.hide();
}
}
function showOtherGroupMembersOf() {
var groupMembers = clayConfig.getItemsByGroup(this.config.group);
for (var i = 0; i < groupMembers.length; i++) {
var item = groupMembers[i];
if (item.id && this.id && item.id == this.id) {
continue;
} else if (item.messageKey && this.messageKey && item.messageKey == this.messageKey) {
continue;
}
item.show();
}
}
function swapItems(prefix, lhsIndex, rhsIndex) {
var lhsSuffix = "[" + lhsIndex + "]";
var rhsSuffix = "[" + rhsIndex + "]";
var allItems = clayConfig.getAllItems();
for (var i = 0; i < allItems.length; i++) {
var rhsItem = allItems[i];
if (!rhsItem.messageKey) {
continue;
}
if (!rhsItem.messageKey.startsWith(prefix)) {
continue;
}
if (!rhsItem.messageKey.endsWith(rhsSuffix)) {
continue;
}
var lhsItem = clayConfig.getItemByMessageKey(rhsItem.messageKey.replace(rhsSuffix, lhsSuffix));
if (!lhsItem) {
continue;
}
var temp = lhsItem.get();
lhsItem.set(rhsItem.get());
rhsItem.set(temp);
}
}
function truncateInput() {
if (!this.config.attributes && !this.config.attributes.limit) {
return;
}
var maxLength = this.config.attributes.limit;
var value = this.get();
if (value && value.length <= maxLength) {
return;
}
this.set(value.substring(0, maxLength));
}
function handleUpButton() {
var idParts = this.id.split("Up");
var prefix = idParts[0];
var sourceIndex = parseInt(idParts[1], 10);
var destIndex = sourceIndex - 1;
swapItems(prefix, destIndex, sourceIndex);
}
function installAllProductsToggle(prefix, suffix) {
var allProdToggle = clayConfig.getItemByMessageKey(prefix + 'AllProd' + (suffix ? suffix : ''));
toggleAllProducts.call(allProdToggle);
allProdToggle.on('change', toggleAllProducts);
}
function installUseDefaultProductsToggle(prefix, suffix) {
var useDefaultProdToggle = clayConfig.getItemByMessageKey(prefix + 'UseDefProd' + (suffix ? suffix : ''));
toggleUseDefaultProducts.call(useDefaultProdToggle);
useDefaultProdToggle.on('change', toggleUseDefaultProducts);
useDefaultProdToggle.on('show', toggleUseDefaultProducts);
useDefaultProdToggle.on('hide', hideOtherGroupMembersOf);
}
function installEnabledToggle(prefix, suffix) {
var groupEnableToggle = clayConfig.getItemByMessageKey(prefix + 'Enabled' + (suffix ? suffix : ''));
toggleGroupEnabled.call(groupEnableToggle);
groupEnableToggle.on('change', toggleGroupEnabled);
}
function installUpButtonHandler(prefix, index) {
var upButton = clayConfig.getItemById(prefix + 'Up' + index);
if (upButton) {
upButton.on('click', handleUpButton);
}
}
function installShowHowtoHandler() {
var howtoToggle = clayConfig.getItemById('HowtoToggle');
toggleHowto.call(howtoToggle);
howtoToggle.on('change', toggleHowto);
}
function installTruncationHandlers() {
var allInputItems = clayConfig.getItemsByType('input');
for (var i = 0; i < allInputItems.length; i++) {
var inputItem = allInputItems[i];
inputItem.on('change', truncateInput);
}
}
clayConfig.on(clayConfig.EVENTS.AFTER_BUILD, function() {
installAllProductsToggle('Def');
var indexSuffix, i;
for (i = 0; i < numberOfStations; i++) {
indexSuffix = "[" + i + "]";
installAllProductsToggle('Stat', indexSuffix);
installUseDefaultProductsToggle('Stat', indexSuffix);
installEnabledToggle('Stat', indexSuffix);
installUpButtonHandler('Stat', i);
}
for (i = 0; i < numberOfConnections; i++) {
indexSuffix = "[" + i + "]";
installAllProductsToggle('Conn', indexSuffix);
installUseDefaultProductsToggle('Conn', indexSuffix);
installEnabledToggle('Conn', indexSuffix);
installUpButtonHandler('Conn', i);
}
installShowHowtoHandler();
installTruncationHandlers();
});
};
+160
View File
@@ -0,0 +1,160 @@
var BahnInterface = require('./bahn-interface');
var keys = require('message_keys');
var Clay = require('pebble-clay');
var clayConfig = require('./config');
var customClay = require('./custom-clay');
var clay = new Clay(clayConfig, customClay, { autoHandleEvents: false });
var maxConfigKeysPerMessage = 100;
var msgBuffer = [];
Pebble.addEventListener('showConfiguration', function(e) {
Pebble.openURL(clay.generateUrl());
});
// Overridden to fragment the config dictionary
Pebble.addEventListener('webviewclosed', function(e) {
if (e && !e.response) {
return;
}
// Get the keys and values from each config item
var dict = clay.getSettings(e.response);
var dictToSend = {};
var count = 0;
for (var key in dict) {
if (dict.hasOwnProperty(key)) {
count++;
dictToSend[key] = dict[key];
if (count >= maxConfigKeysPerMessage) {
msgBuffer.push(dictToSend);
count = 0;
dictToSend = {};
}
}
}
if (count > 0) {
msgBuffer.push(dictToSend);
}
flushMsgBuffer();
});
function flushMsgBuffer() {
if (msgBuffer.length === 0) {
return;
}
var dictToSend = msgBuffer.shift();
Pebble.sendAppMessage(dictToSend, appMessageSent, appMessageFailed);
}
function appMessageSent(e) {
flushMsgBuffer();
}
function appMessageFailed(e) {
console.log('Failed to send data!');
console.log(JSON.stringify(e));
}
// Listen for when the watchface is opened
Pebble.addEventListener('ready',
function(e) {
}
);
// Listen for when an AppMessage is received
Pebble.addEventListener('appmessage',
function(e) {
var dict = e.payload;
if (dict.BahnReqType) {
handleBahninterfaceRequest(dict);
}
}
);
function handleBahninterfaceRequest(dict) {
var requestType = dict.BahnReqType;
var requestId = dict.BahnReqId;
if (requestType == keys.BahnReqTypeBuildConnResults) {
if (dict.BahnReqUrl) {
var url = dict.BahnReqUrl;
BahnInterface.getConnectionsFromUrl(url, function(results) {
sendConnectionResults(requestId, results);
flushMsgBuffer();
});
} else {
var startStation = dict.BahnReqStartStation;
var destStation = dict.BahnReqDestStation;
var products = dict.BahnReqProducts;
BahnInterface.getConnections(startStation, destStation, products, function(results) {
sendConnectionResults(requestId, results);
flushMsgBuffer();
});
}
} else if (requestType == keys.BahnReqTypeGetConnDetails) {
var detailLink = dict.BahnReqUrl;
var maxLength = dict.BahnReqMaxLength;
BahnInterface.getConnectionDetailInfos(detailLink, function(details) {
sendResponse(requestId, keys.BahnReqTypeGetConnDetails, 1, 1, function(dict) {
dict.BahnConnDetails = details ? details.substring(0, maxLength) : 'Keine Details gefunden!';
});
flushMsgBuffer();
});
}
}
function sendConnectionResults(requestId, results) {
if (!results || results.length === 0) {
sendResponse(requestId, keys.BahnRespTypeLoadFailed, 0, 0, function(dict) {
});
return;
}
var parts = results.length;
for (var i = 0; i < parts; i++) {
sendConnectionResult(requestId, results[i], i+1, parts);
}
}
function sendConnectionResult(requestId, result, partNum, parts) {
sendResponse(requestId, keys.BahnReqTypeGetConnResult, partNum, parts, function(dict) {
if (result.moreLink) {
if (result.earlier) {
dict.BahnFrueherUrl = result.moreLink;
} else {
dict.BahnSpaeterUrl = result.moreLink;
}
return;
}
var von = result.von + (result.delayStart ? ' '+result.delayStart : '');
var bis = result.bis + (result.delayZiel ? ' '+result.delayZiel : '');
var achtung = result.achtung ? ' !' : '';
var ausfall = result.ausfall ? ' X' : '';
var dauer = result.dauer ? ' ' + result.dauer : '';
var umstiege = result.umstiege ? ' ' + result.umstiege : '';
var vm = result.vm ? ' ' + result.vm : '';
var resultTitle = von + achtung + ausfall;
var resultSubtitle = bis + dauer + umstiege + vm;
dict.BahnConnTitle = resultTitle;
dict.BahnConnSubtitle = resultSubtitle;
dict.BahnConnDetailsUrl = result.detailLink;
});
}
function sendResponse(requestId, requestType, partNum, numParts, responseDataBuilder) {
var dict = {};
dict.BahnReqId = requestId;
dict.BahnReqType = requestType;
dict.BahnRespParts = numParts;
dict.BahnRespPartNum = partNum;
responseDataBuilder(dict);
msgBuffer.push(dict);
}
+52
View File
@@ -0,0 +1,52 @@
#
# This file is the default set of rules to compile a Pebble project.
#
# Feel free to customize this to your needs.
#
import os.path
try:
from sh import CommandNotFound, jshint, cat, ErrorReturnCode_2
hint = jshint
except (ImportError, CommandNotFound):
hint = None
top = '.'
out = 'build'
def options(ctx):
ctx.load('pebble_sdk')
def configure(ctx):
ctx.load('pebble_sdk')
def build(ctx):
if False and hint is not None:
try:
hint([node.abspath() for node in ctx.path.ant_glob("src/**/*.js")], _tty_out=False) # no tty because there are none in the cloudpebble sandbox.
except ErrorReturnCode_2 as e:
ctx.fatal("\nJavaScript linting failed (you can disable this in Project Settings):\n" + e.stdout)
ctx.load('pebble_sdk')
build_worker = os.path.exists('worker_src')
binaries = []
for p in ctx.env.TARGET_PLATFORMS:
ctx.set_env(ctx.all_envs[p])
ctx.set_group(ctx.env.PLATFORM_NAME)
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
ctx.pbl_program(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf)
if build_worker:
worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})
ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf)
else:
binaries.append({'platform': p, 'app_elf': app_elf})
ctx.set_group('bundle')
ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json']), js_entry_file='src/pkjs/index.js')