SlideShare a Scribd company logo
1 of 68
Download to read offline
SMARTWATCH - Jednak coś więcej
niż dodatkowy ekran
na notyfikacje?
by Tomek Czerw, Marcin Nycz
● Krakowski Software House
● Ruby on Rails, Android i iOS
● Specjalizujemy się w tworzeniu aplikacji webowych i mobilnych
● Współpracujemy z wieloma firmami i startupami z całego świata
O NAS:
2009 - założenie firmy
50 zrealizowanych projektów
40 pracowników
Wyróżnienia:
HISTORIA:
Top Web & Software Developers
in Poland 2015
Top Tens Ruby on Rails
Development Companies
NASZE PROJEKTY:
HOMEAHEAD
PROEST
WYŻSZA JAKOŚĆ OBSŁUGI KLIENTA
Punkty mobilne
i catering
Jeden program pozwala niezawodnie zarządzać
całym biznesem. Dane przechowywane są w
chmurze, co umożliwia dostęp do nich z
dowolnego komputera. Przejrzyste raporty
i statystyki aktualizowane są na bieżąco.
Innowacyjne oprogramowanie
mobilne
Menadżer oraz kelner są w stanie kontrolować cały proces
sprzedaży, krok po kroku - wykorzystując przejrzyste wykresy
oraz raporty dostępne w czasie rzeczywistym.
Dostęp do aktualnych danych
POSbistro umożliwia przyjmowanie zamówień bezpośrednio od
klienta przy stoliku. Wraz z pierwszym kliknięciem kuchnia jest
informowana jaki roszaj potrawy musi zostać przygotowany.
Gdy zamówienie jest gotowe kelner otrzymuje informację na
swoim zegarku
Mobilność sprzedaży
POSbistro jest mobilnym systemem dla restauracji oraz cateringu.
Współpracuje ze wszystkimi urządzeniami Samsung - tabletami,
telewizorami oraz zegarkami. Jest wykorzystywane przez setki
restauracji w Polsce i Europie
Kelner przyjmuje zamówienie
za pomocą tabletu
Wydanie potrawy
Informacje są
przechowywane
w chmurze
Właściciel ma pełną
kontrolę nad sytuacją
w lokalach
Zamówienie pojawia się
w kuchni na ekranie SMART TV
Potrawa jest gotowa
do wydania
Kelner jest informowany za pośrednictwem
POSpager na zegarku Samsung Gear
Szybkie i wydajne zarządzanie zamówieniami
w restauracji
● Aplikacja Kuchnia - Android telefon/tablet
● Aplikacja Kelner - Tizen smartwatch
● Komunikacja z działającym REST API
ZAŁOŻENIA:
API
KOMUNIKACJA:
KOMUNIKACJA:
Pusher client Pusher client
API
Pusher trigger
Pusher.com
● Tablet/telefon tworzy kuchnię
● Kuchni maksymalnie może być 20
● Kelner łączy się z API i pobiera listę kuchni
● Śledzenie wydanych posiłków
ZAŁOŻENIA FUNKCJONALNE:
POTRZEBNE ELEMENTY:
● https://pusher.com/
● https://pusher.com/
● Endpointy udostępnione przez wcześniej
przygotowane API
POTRZEBNE ELEMENTY:
POTRZEBNE ELEMENTY:
● https://pusher.com/
● Endpointy udostępnione przez wcześniej
przygotowane API
● kawa :)
Pytania przed
rozpoczęciem?
KUCHNIA
- aplikacja na Androida
WYKORZYSTYWANE BIBLIOTEKI:
● Pusher
● RecyclerView
● Retrofit
● Gson
DZIAŁANIE APLIKACJI KUCHNIA:
● Stwórz kuchnię i zarejestruj kuchnię
● Wyświetl menu kuchni
● Wydawaj potrawy
● Czekaj na informację od kelnerów
Podstawowe obiekty?
KLASY:
public class Kitchen {
private UUID id;
private String name;
private List<MenuItem> queue = new ArrayList<>();
private List<MenuItem> menu = new ArrayList<>();
public Kitchen(UUID id, String name, List<MenuItem> queue) {
this.id = id;
this.name = name;
this.queue = queue;
}
/** gettery i settery */
}
PODSTAWOWE OBIEKTY:
public class MenuItem {
private UUID id;
private String name;
private boolean selected;
private Date takeDate;
public MenuItem(UUID id, String name, boolean selection) {
this.id = id;
this.name = name;
this.selected = selection;
this.takeDate = null;
}
/** gettery i settery */
}
PODSTAWOWE OBIEKTY:
public class Waiter {
private String id;
private String name;
public Waiter(String id, String name) {
this.id = id;
this.name = name;
}
/** gettery i settery */
}
PODSTAWOWE OBIEKTY:
KLASY:
Komunikacja REST API
HOST: https://kuchnia-api-railwaymen.herokuapp.com/api/
// CREATE LOCATION AND GET PUSHER CHANNEL
POST host/kitchens
Body:
{
name : "name",
deviceId : "deviceId"
}
// DELETE KITCHEN
DELETE host/kitchens/{kitchen_id}
// COMPLETE MEAL
POST host/kitchens/{kitchen_id}/menu_items/{menu_item_id}
Body:
{
id : "uuid",
name : "name",
date : "date"
}
ENDPOINTY KUCHNI:
// CREATE LOCATION AND GET PUSHER CHANNEL
@POST("kitchens")
Call<Kitchen> register(@Body Map<String, Object> map);
// DELETE KITCHEN
@DELETE("kitchens/{kitchen_id}")
Call<Void> removeKitchen(@Path("kitchen_id") UUID kitchenId);
// COMPLETE MEAL
@POST("kitchens/{kitchen_id}/menu_items/{menu_item_id}")
Call<Void> completeMeal(@Path("kitchen_id") UUID kitchenId,
@Path("menu_item_id") UUID menuItemId, @Body Map<String, Object> map);
KOMUNIKACJA REST API - RETROFIT:
public static Endpoints buildRetrofit() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY)).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.API_URL).client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create()).build();
return retrofit.create(Endpoints.class);
}
KOMUNIKACJA REST API - RETROFIT:
try {
Map<String, Object> map = new HashMap<>();
map.put(Constants.Keys.NAME, name);
map.put(Constants.Keys.DEVICE_ID, Utils.getDeviceId(this));
Call<Kitchen> call = getEndpoints().register(map);
call.enqueue(callback);
} catch (Exception e) {
Log.d(MainActivity.class.getSimpleName(), e.toString());
}
/** ....
private Callback<Kitchen> callback = new Callback<Kitchen>() {
@Override
public void onResponse(Call<Kitchen> call, Response<Kitchen> response) {
if (response != null) {
case 200:
kitchen = response.body();
if (kitchen != null) {
buildMenu();
syncPusher = new SyncPusher(MainActivity.this, kitchen.getId().toString());}
}
}
@Override
public void onFailure(Call<Kitchen> call, Throwable t) {
}
};
KOMUNIKACJA - POBIERANIE MENU KUCHNI:
Wyświetlanie menu
KLASY:
recyclerView.setVisibility(View.VISIBLE);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new SpaceItemDecorator(10));
layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
adapter = new MenuRecyclerAdapter(this);
recyclerView.setAdapter(adapter);
WYŚWIETLANIE MENU:
@Override
public MenuHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(hostActivity).inflate(R.layout.recycler_menu_item,
parent, false);
MenuHolder holder = new MenuHolder(view);
view.setOnClickListener(new OnMenuClickListener(holder, hostActivity));
return holder;
}
@Override
public int getItemCount() {
return hostActivity.getKitchen().getMenu().size();
}
public class MenuHolder extends RecyclerView.ViewHolder {
@Bind(R.id.text)
TextView text;
@Bind(R.id.date)
TextView dateText;
@Bind(R.id.card_view)
CardView cardView;
public MenuHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
WYŚWIETLANIE MENU - ADAPTER:
private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("HH:mm");
@Override
public void onBindViewHolder(MenuHolder holder, int position) {
MenuItem menuElement = hostActivity.getKitchen().getMenu().get(position);
holder.text.setText(menuElement.getName());
if (menuElement.isSelected()) {
holder.text.setTextColor(hostActivity.getResources().getColor(android.R.color.white));
holder.cardView
.setBackgroundColor(hostActivity.getResources().getColor(R.color.colorPrimary));
if (menuElement.getTakeDate() != null) {
holder.dateText.setText(SIMPLE_DATE_FORMAT.format(menuElement.getTakeDate()));
holder.dateText.setVisibility(View.VISIBLE);
}
} else {
holder.text.setTextColor(hostActivity.getResources().getColor(R.color.colorPrimary));
holder.cardView.setBackgroundColor(
hostActivity.getResources().getColor(android.R.color.white));
holder.dateText.setVisibility(View.GONE);
}
}
WYŚWIETLANIE MENU - ADAPTER:
Wydanie przedmiotu
z kuchni
KLASY:
@Override
public void onClick(View view) {
if (Utils.isDeviceConnected(hostActivity)) {
int position = holder.getAdapterPosition();
MenuItem menuElement = hostActivity.getKitchen().getMenu().get(position);
Date date = new Date();
menuElement.setSelected(true);
menuElement.setTakeDate(date);
hostActivity.getAdapter().notifyItemChanged(position);
Map<String, Object> map = new HashMap<>();
map.put(Constants.Keys.ID, menuElement.getId());
map.put(Constants.Keys.NAME, menuElement.getName());
map.put(Constants.Keys.DATE, date);
Call<Void> call = hostActivity.getEndpoints()
.completeMeal(hostActivity.getKitchen().getId(), menuElement.getId(), map);
call.enqueue(callback);
}
}
private Callback<Void> callback = new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
}
};
WYDANIE PRZEDMIOTU Z KUCHNI:
Komunikacja Pusher
KLASY:
private void connectPusher() {
pusher = new Pusher(Constants.Pusher.APP_KEY, new PusherOptions().setCluster("eu"));
pusher.connect(connectionEventListener, ConnectionState.ALL);
subscribeChannel(channelName);
}
private void subscribeChannel(String channelName) {
channel = pusher.subscribe(channelName);
for (String eventName : Constants.WISHLIST_EVENT_LIST) {
channel.bind(eventName, subscriptionEventListener);
}
}
private ConnectionEventListener connectionEventListener = new ConnectionEventListener() {
@Override
public void onConnectionStateChange(ConnectionStateChange change) {
Log.e(SyncPusher.class.getSimpleName(), change.getCurrentState().toString());
}
@Override
public void onError(String message, String code, Exception e) {
Log.e(SyncPusher.class.getSimpleName(), message);
}
};
KOMUNIKACJA PUSHER:
private SubscriptionEventListener subscriptionEventListener = new
SubscriptionEventListener() {
@Override
public void onEvent(final String channelName, final String eventName, final String data)
{
Runnable eventRunnable = new Runnable() {
@Override
public void run() {
AbstractEvent event = EventFactory.getEvent(eventName, data, mainActivity);
if (event != null) {
event.handleEvent();
}
}
};
mainActivity.getExecutor().submit(eventRunnable);
}
};
PUSHER:
public class EventFactory {
private EventFactory() {
};
public static AbstractEvent getEvent(String eventName, String data, MainActivity
mainActivity) {
if (eventName.equals(Constants.Event.CONNECTED)) {
return new ConnectedEvent(data, mainActivity);
} else if (eventName.equals(Constants.Event.DISCONNECTED)) {
return new DisconnectedEvent(data, mainActivity);
} else if (eventName.equals(Constants.Event.TAKED)) {
return new TakedEvent(data, mainActivity);
}
return null;
}
}
UPROSZCZONA FABRYKA EVENTÓW:
Pusher - eventy
KLASY:
public abstract class AbstractEvent {
protected MainActivity mainActivity;
public AbstractEvent(String data, MainActivity mainActivity) {
this.mainActivity = mainActivity;
parseData(data);
}
public abstract void handleEvent();
abstract void parseData(String data);
}
PUSHER - EVENTY:
public class TakedEvent extends AbstractEvent {
private MenuItem menuItem;
private Waiter waiter;
@Override
public void handleEvent() {
if (menuItem.getId() != null) {
int position = Utils.findMenuItemPosition(mainActivity.getKitchen().getMenu(),
menuItem.getId());
MenuItem menuItem = mainActivity.getKitchen().getMenu().get(position);
menuItem.setSelected(false);
menuItem.setTakeDate(null);
mainActivity.getAdapter().notifyItemChanged(position);
}}
@Override
protected void parseData(String data) {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(data);
menuItem = mainActivity.gson.fromJson(
jsonObject.get(Constants.Keys.MENU_ITEM).toString(), MenuItem.class);
waiter = mainActivity.gson
.fromJson(jsonObject.get(Constants.Keys.WAITER).toString(), Waiter.class);
} catch (JSONException e) {
Log.e(TakedEvent.class.getSimpleName(), e.toString());
}}
}
PUSHER - ODEBRANIE POTRAWY:
public class ConnectedEvent extends AbstractEvent {
private Waiter waiter;
public ConnectedEvent(String data, MainActivity mainActivity) {
super(data, mainActivity);
}
@Override
public void handleEvent() {
mainActivity.showSnackbar(waiter == null ? "Kelner"
: waiter.getName() +" " +mainActivity.getString(R.string.
waiter_connected));
}
@Override
protected void parseData(String data) {
if (data != null) {
waiter = MainActivity.gson.fromJson(data, Waiter.class);
}
}
}
PUSHER - DOŁĄCZENIE KELNERA:
public class DisconnectedEvent extends AbstractEvent {
private Waiter waiter;
public DisconnectedEvent(String data, MainActivity mainActivity) {
super(data, mainActivity);
}
@Override
public void handleEvent() {
mainActivity.showSnackbar(waiter == null ? "Kelner"
: waiter.getName() + " " + mainActivity.getString(R.string.
waiter_disconnected));
}
@Override
protected void parseData(String data) {
if (data != null) {
waiter = MainActivity.gson.fromJson(data, Waiter.class);
}
}
}
PUSHER - ROZŁĄCZENIE KELNERA:
KELNER - aplikacja na
zegarek z systemem Tizen
WYKORZYSTYWANE BIBLIOTEKI:
● Underscore
● jQuery
● Chance
● Pusher
● TAU
STRUKTURA APLIKACJI TIZEN:
window.onload = function() {
document.addEventListener('tizenhwkey', function(e) {
if (e.keyName === "back") {
var currentPage = $(".ui-page-active").attr('id');
if (currentPage === 'no_kitchens') {
try {
tizen.application.getCurrentApplication().exit();
} catch (ignore) {
}
} else if (currentPage === 'kitchens') {
tau.changePage("#no_kitchens");
stopServices()
}
// ....
}
});
$("#no-kitchens").click(function() {
getKitchens();
});
tau.changePage("#no_kitchens");
Pusher.log = function(message) {
if (window.console && window.console.log) {
window.console.log(message);
}
};
};
main.js:
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.
org/ns/widgets" id="http://railwaymen.org/Kelner" version="1.0.0" viewmodes="
maximized">
<access origin="*" subdomains="true"></access>
<tizen:application id="JRp9qBI8KW.Kelner" package="JRp9qBI8KW"
required_version="2.2"/>
<content src="index.html"/>
<feature name="http://tizen.org/feature/screen.size.all"/>
<feature name="http://tizen.org/api/tizen" required="true"/>
<feature name="http://tizen.org/feature/download"/>
<feature name="http://tizen.org/feature/network.internet"/>
<feature name="http://tizen.org/feature/screen.size.all"/>
<icon src="icon.png"/>
<name>Kelner</name>
<tizen:privilege name="http://tizen.org/privilege/application.launch"/>
<tizen:privilege name="http://tizen.org/privilege/download"/>
<tizen:privilege name="http://tizen.org/privilege/power"/>
<tizen:privilege name="http://tizen.org/privilege/push"/>
<tizen:privilege name="http://tizen.org/privilege/internet"/>
<tizen:profile name="wearable"/>
</widget>
config.xml:
DZIAŁANIE APLIKACJI KELNER:
● Odśwież kuchnie
● Lista kuchni
● Pobierz wydane potrawy
● Zgłoś gotowość odebrania potrawy
● Odbierz potrawę
<!--...-->
<div id="no_kitchens" class="ui-page">
<div class="ui-content">
<div class="center-block">
<span id="no-kitchens" class="center-text">Odśwież kuchnie.</span>
</div>
</div>
</div>
<div id="kitchens" class="ui-page">
<header class="ui-header ui-has-more">
<h2 class="ui-title">Kuchnie</h2>
</header>
<div class="ui-content" id="mainPage">
<ul id="kitchenList" data-role="listview" class="ui-listview"></ul>
</div>
</div>
<div id="menu_items" class="ui-page">
<header class="ui-header">
<h2 class="ui-title waiter_name">Stefan</h2>
</header>
<div class="ui-content" id="mainPage" data-add-back-btn="true">
<ul id="menuItemsList" class="ui-listview"></ul>
</div>
</div>
<!--...-->
WIDOK - WIDOCZNE EKRANY:
HOST: https://kuchnia-api-railwaymen.herokuapp.com/api/
// GET KITCHENS
GET host/kitchens
// GET MENU ITEMS
POST host/kitchens/{kitchen_id}/menu_items
Body:
{
id : "deviceId",
name : "deviceName"
}
// LOGOUT WAITER
DELETE host/kitchens/{kitchen_id}/waiters/{waiter_id}
// TAKE MENU ITEM
DELETE host/kitchens/{kitchen_id}/menu_items/{menu_item_id}
Body:
{
id : "deviceId",
name : "deviceName"
}
ENDPOINTY ZEGARKA:
var getKitchens = function() {
sendRequest('GET', buildUrl('/kitchens', ''), function(request, data) {}, function(data)
{});
}
var logoutWaiter = function(kitchenId, waiterId) {
sendRequest('DELETE', buildUrl('/kitchens/' + kitchenId + '/waiters/'
+ waiterId), function(request, data) {}, function(data) {});
}
var getMenuItems = function(kitchenId, waiter) {
sendRequestWithBody('POST', buildUrl('/kitchens/' + kitchenId
+ '/menu_items'), waiter, function(request, data) {}, function(data) {});
}
var takeMenuItem = function(menuItem, waiter) {
sendRequestWithBody('DELETE', buildUrl('/kitchens/'
+ window.currentKitchen.id + '/menu_items/' + menuItem.id, ''),
waiter, function(request, data) {}, function(request, data) {});
}
KOMUNIKACJA REST API:
var formatTime = function(date) {
var tmp = new Date(date);
return tmp.getHours() + ":" + (tmp.getMinutes() < 10 ? "0" : "")
+ tmp.getMinutes();
}
var sortQueue = function() {
window.currentKitchen.queue.sort(function(menuItem1, menuItem2) {
return menuItem1.date ? -1 : menuItem2.date ? 1 : 0;
});
}
var removeFromQueue = function(menuItemId) {
window.currentKitchen.queue = _.reject(window.currentKitchen.queue,
function(menuItem) {
return menuItem.id === menuItemId;
});
}
FUNKCJE POMOCNICZE:
Wyświetlanie elementów
i interakcja z nimi
MAIN.JS:
var renderKitchens = function() {
if (window.kitchens.length != 0) {
if (window.kitchens.length)
tau.changePage("#kitchens");
$("#kitchenList").empty();
window.kitchens.sort(function compare(lok1, lok2) {
if (lok1.name.toLowerCase() < lok2.name.toLowerCase())
return -1;
if (lok1.name.toLowerCase() > lok2.name.toLowerCase())
return 1;
return 0;});
window.kitchens.forEach(function(kitchen) {
$("#kitchenList").append(renderKitchen(kitchen));
});
$("#kitchenList li").click(kitchenClickListener);
} else {
$("#no-kitchens").text('Brak zarejestrowanych kuchni');
}}
var renderKitchen = function(kitchen) {
return "<li data-id="" + kitchen.id
+ ""><a href="#" class="ui-li-text-sub">" + kitchen.name
+ "</a></li>";
}
WYŚWIETLANIE KUCHNI:
var kitchenClickListener = function(e) {
var kitchenId = $(e.currentTarget).data("id");
var deviceId = tizen.systeminfo.getCapabilities().duid
if (deviceId) {
window.waiter = {
id : deviceId,
name : chance.name()
}
} else {
window.waiter = {
id : uuid(),
name : chance.name()
}
}
getMenuItems(kitchenId, JSON.stringify(window.waiter))
}
LOGOWANIE SIĘ DO WYBRANEJ KUCHNI:
var checkQueue = function() {
$(".waiter_name").text(window.waiter.name);
if (window.currentKitchen.queue.length === 0) {
tau.changePage("#no_menu_items");
} else if (window.currentKitchen.queue.length === 1) {
tau.changePage("#single_menu_item_container");
renderSinglePageMenuItem(window.currentKitchen.queue[0])
} else {
tau.changePage("#menu_items");
renderMenuItems();
}
}
SPRAWDZANIE WYDANYCH POTRAW:
var renderSinglePageMenuItem = function(menuItem) {
$("#single_menu_item").text(menuItem.name);
$("#single_menu_item_date").text(formatTime(menuItem.date));
$('#single_menu_item_container').unbind("click");
$('#single_menu_item_container').click(
function() {
takeMenuItem(window.currentKitchen.queue[0], JSON
.stringify(window.waiter));
});
}
var renderMenuItems = function() {
$("#menuItemsList").empty();
if (window.currentKitchen.queue.length != 1) {
tau.changePage("#menu_items");
sortQueue()
window.currentKitchen.queue.forEach(function(menuItem) {
$("#menuItemsList").append(renderMenuItem(menuItem));
});
$("#menuItemsList li").click(menuItemClickListener);
} else {
tau.changePage("#single_menu_item_container");
renderSinglePageMenuItem(window.currentKitchen.queue[0])
}
}
WYŚWIETLANIE POTRAW:
sync.js:
Odbieranie potrawy
var menuItemClickListener = function(e) {
var menuItemId = $(e.currentTarget).data("id");
var menuItem = _.findWhere(window.currentKitchen.queue, {
id : menuItemId
});
takeMenuItem(menuItem, JSON.stringify(window.waiter))
}
ODBIERANIE POTRAWY:
var bindResponsibility = function(menuItem) {
tau.changePage("#well_done");
$("#menu_item").text(menuItem.name);
$("#menu_item_date").text(formatTime(menuItem.date));
$('#well_done').unbind("click");
var timeTask = window.setTimeout(function() {
checkQueue();
}, 3000);
$('#well_done').click(function() {
window.clearTimeout(timeTask)
checkQueue();
});
}
removeFromQueue(menuItem.id)
if (request.status === 204) {
tau.changePage("#to_slow");
}
else {
bindResponsibility(menuItem)
}
REZULTATY:
syncPusher.js:
Komunikacja Pusher
var startServices = function(kitchenId) {
window.pusher = new Pusher('be378fc6b685348ae04c', {
cluster : 'eu',
encrypted : true
});
window.channel = window.pusher.subscribe(kitchenId);
}
KOMUNIKACJA PUSHER:
Pusher - eventy
KOMUNIKACJA PUSHER:
window.channel.bind('event_complete', function(menuItemJson) {});
window.channel.bind('event_take',function(eventJson) {});
window.channel.bind('event_kitchen_disconnected', function() {});
PUSHER - EVENTY:
TESTUJEMY!
Na zjeździe 11
30-527 Krakow, Poland
tel: +48 12 391 60 76
Silicon Valley
Acceleration Center.
180 Sansome Street
San Francisco, CA 94104
tel: 1-415-449-4791
info@railwaymen.org
www.railwaymen.org
@Railwaymen_org
railwaymen.software.development
/company/railwaymen

More Related Content

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Smartwatch - jednak coś więcej niż dodatkowy ekran na notyfikacje?

  • 1. SMARTWATCH - Jednak coś więcej niż dodatkowy ekran na notyfikacje? by Tomek Czerw, Marcin Nycz
  • 2. ● Krakowski Software House ● Ruby on Rails, Android i iOS ● Specjalizujemy się w tworzeniu aplikacji webowych i mobilnych ● Współpracujemy z wieloma firmami i startupami z całego świata O NAS:
  • 3. 2009 - założenie firmy 50 zrealizowanych projektów 40 pracowników Wyróżnienia: HISTORIA: Top Web & Software Developers in Poland 2015 Top Tens Ruby on Rails Development Companies
  • 7. WYŻSZA JAKOŚĆ OBSŁUGI KLIENTA Punkty mobilne i catering Jeden program pozwala niezawodnie zarządzać całym biznesem. Dane przechowywane są w chmurze, co umożliwia dostęp do nich z dowolnego komputera. Przejrzyste raporty i statystyki aktualizowane są na bieżąco.
  • 8. Innowacyjne oprogramowanie mobilne Menadżer oraz kelner są w stanie kontrolować cały proces sprzedaży, krok po kroku - wykorzystując przejrzyste wykresy oraz raporty dostępne w czasie rzeczywistym. Dostęp do aktualnych danych POSbistro umożliwia przyjmowanie zamówień bezpośrednio od klienta przy stoliku. Wraz z pierwszym kliknięciem kuchnia jest informowana jaki roszaj potrawy musi zostać przygotowany. Gdy zamówienie jest gotowe kelner otrzymuje informację na swoim zegarku Mobilność sprzedaży POSbistro jest mobilnym systemem dla restauracji oraz cateringu. Współpracuje ze wszystkimi urządzeniami Samsung - tabletami, telewizorami oraz zegarkami. Jest wykorzystywane przez setki restauracji w Polsce i Europie
  • 9. Kelner przyjmuje zamówienie za pomocą tabletu Wydanie potrawy Informacje są przechowywane w chmurze Właściciel ma pełną kontrolę nad sytuacją w lokalach Zamówienie pojawia się w kuchni na ekranie SMART TV Potrawa jest gotowa do wydania Kelner jest informowany za pośrednictwem POSpager na zegarku Samsung Gear Szybkie i wydajne zarządzanie zamówieniami w restauracji
  • 10. ● Aplikacja Kuchnia - Android telefon/tablet ● Aplikacja Kelner - Tizen smartwatch ● Komunikacja z działającym REST API ZAŁOŻENIA:
  • 12. KOMUNIKACJA: Pusher client Pusher client API Pusher trigger Pusher.com
  • 13. ● Tablet/telefon tworzy kuchnię ● Kuchni maksymalnie może być 20 ● Kelner łączy się z API i pobiera listę kuchni ● Śledzenie wydanych posiłków ZAŁOŻENIA FUNKCJONALNE:
  • 15. ● https://pusher.com/ ● Endpointy udostępnione przez wcześniej przygotowane API POTRZEBNE ELEMENTY:
  • 16. POTRZEBNE ELEMENTY: ● https://pusher.com/ ● Endpointy udostępnione przez wcześniej przygotowane API ● kawa :)
  • 19. WYKORZYSTYWANE BIBLIOTEKI: ● Pusher ● RecyclerView ● Retrofit ● Gson
  • 20. DZIAŁANIE APLIKACJI KUCHNIA: ● Stwórz kuchnię i zarejestruj kuchnię ● Wyświetl menu kuchni ● Wydawaj potrawy ● Czekaj na informację od kelnerów
  • 22. public class Kitchen { private UUID id; private String name; private List<MenuItem> queue = new ArrayList<>(); private List<MenuItem> menu = new ArrayList<>(); public Kitchen(UUID id, String name, List<MenuItem> queue) { this.id = id; this.name = name; this.queue = queue; } /** gettery i settery */ } PODSTAWOWE OBIEKTY:
  • 23. public class MenuItem { private UUID id; private String name; private boolean selected; private Date takeDate; public MenuItem(UUID id, String name, boolean selection) { this.id = id; this.name = name; this.selected = selection; this.takeDate = null; } /** gettery i settery */ } PODSTAWOWE OBIEKTY:
  • 24. public class Waiter { private String id; private String name; public Waiter(String id, String name) { this.id = id; this.name = name; } /** gettery i settery */ } PODSTAWOWE OBIEKTY:
  • 26. HOST: https://kuchnia-api-railwaymen.herokuapp.com/api/ // CREATE LOCATION AND GET PUSHER CHANNEL POST host/kitchens Body: { name : "name", deviceId : "deviceId" } // DELETE KITCHEN DELETE host/kitchens/{kitchen_id} // COMPLETE MEAL POST host/kitchens/{kitchen_id}/menu_items/{menu_item_id} Body: { id : "uuid", name : "name", date : "date" } ENDPOINTY KUCHNI:
  • 27. // CREATE LOCATION AND GET PUSHER CHANNEL @POST("kitchens") Call<Kitchen> register(@Body Map<String, Object> map); // DELETE KITCHEN @DELETE("kitchens/{kitchen_id}") Call<Void> removeKitchen(@Path("kitchen_id") UUID kitchenId); // COMPLETE MEAL @POST("kitchens/{kitchen_id}/menu_items/{menu_item_id}") Call<Void> completeMeal(@Path("kitchen_id") UUID kitchenId, @Path("menu_item_id") UUID menuItemId, @Body Map<String, Object> map); KOMUNIKACJA REST API - RETROFIT:
  • 28. public static Endpoints buildRetrofit() { OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor() .setLevel(HttpLoggingInterceptor.Level.BODY)).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Constants.API_URL).client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()).build(); return retrofit.create(Endpoints.class); } KOMUNIKACJA REST API - RETROFIT:
  • 29. try { Map<String, Object> map = new HashMap<>(); map.put(Constants.Keys.NAME, name); map.put(Constants.Keys.DEVICE_ID, Utils.getDeviceId(this)); Call<Kitchen> call = getEndpoints().register(map); call.enqueue(callback); } catch (Exception e) { Log.d(MainActivity.class.getSimpleName(), e.toString()); } /** .... private Callback<Kitchen> callback = new Callback<Kitchen>() { @Override public void onResponse(Call<Kitchen> call, Response<Kitchen> response) { if (response != null) { case 200: kitchen = response.body(); if (kitchen != null) { buildMenu(); syncPusher = new SyncPusher(MainActivity.this, kitchen.getId().toString());} } } @Override public void onFailure(Call<Kitchen> call, Throwable t) { } }; KOMUNIKACJA - POBIERANIE MENU KUCHNI:
  • 31. recyclerView.setVisibility(View.VISIBLE); recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new SpaceItemDecorator(10)); layoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(layoutManager); adapter = new MenuRecyclerAdapter(this); recyclerView.setAdapter(adapter); WYŚWIETLANIE MENU:
  • 32. @Override public MenuHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(hostActivity).inflate(R.layout.recycler_menu_item, parent, false); MenuHolder holder = new MenuHolder(view); view.setOnClickListener(new OnMenuClickListener(holder, hostActivity)); return holder; } @Override public int getItemCount() { return hostActivity.getKitchen().getMenu().size(); } public class MenuHolder extends RecyclerView.ViewHolder { @Bind(R.id.text) TextView text; @Bind(R.id.date) TextView dateText; @Bind(R.id.card_view) CardView cardView; public MenuHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } WYŚWIETLANIE MENU - ADAPTER:
  • 33. private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("HH:mm"); @Override public void onBindViewHolder(MenuHolder holder, int position) { MenuItem menuElement = hostActivity.getKitchen().getMenu().get(position); holder.text.setText(menuElement.getName()); if (menuElement.isSelected()) { holder.text.setTextColor(hostActivity.getResources().getColor(android.R.color.white)); holder.cardView .setBackgroundColor(hostActivity.getResources().getColor(R.color.colorPrimary)); if (menuElement.getTakeDate() != null) { holder.dateText.setText(SIMPLE_DATE_FORMAT.format(menuElement.getTakeDate())); holder.dateText.setVisibility(View.VISIBLE); } } else { holder.text.setTextColor(hostActivity.getResources().getColor(R.color.colorPrimary)); holder.cardView.setBackgroundColor( hostActivity.getResources().getColor(android.R.color.white)); holder.dateText.setVisibility(View.GONE); } } WYŚWIETLANIE MENU - ADAPTER:
  • 35. @Override public void onClick(View view) { if (Utils.isDeviceConnected(hostActivity)) { int position = holder.getAdapterPosition(); MenuItem menuElement = hostActivity.getKitchen().getMenu().get(position); Date date = new Date(); menuElement.setSelected(true); menuElement.setTakeDate(date); hostActivity.getAdapter().notifyItemChanged(position); Map<String, Object> map = new HashMap<>(); map.put(Constants.Keys.ID, menuElement.getId()); map.put(Constants.Keys.NAME, menuElement.getName()); map.put(Constants.Keys.DATE, date); Call<Void> call = hostActivity.getEndpoints() .completeMeal(hostActivity.getKitchen().getId(), menuElement.getId(), map); call.enqueue(callback); } } private Callback<Void> callback = new Callback<Void>() { @Override public void onResponse(Call<Void> call, Response<Void> response) { } @Override public void onFailure(Call<Void> call, Throwable t) { } }; WYDANIE PRZEDMIOTU Z KUCHNI:
  • 37. private void connectPusher() { pusher = new Pusher(Constants.Pusher.APP_KEY, new PusherOptions().setCluster("eu")); pusher.connect(connectionEventListener, ConnectionState.ALL); subscribeChannel(channelName); } private void subscribeChannel(String channelName) { channel = pusher.subscribe(channelName); for (String eventName : Constants.WISHLIST_EVENT_LIST) { channel.bind(eventName, subscriptionEventListener); } } private ConnectionEventListener connectionEventListener = new ConnectionEventListener() { @Override public void onConnectionStateChange(ConnectionStateChange change) { Log.e(SyncPusher.class.getSimpleName(), change.getCurrentState().toString()); } @Override public void onError(String message, String code, Exception e) { Log.e(SyncPusher.class.getSimpleName(), message); } }; KOMUNIKACJA PUSHER:
  • 38. private SubscriptionEventListener subscriptionEventListener = new SubscriptionEventListener() { @Override public void onEvent(final String channelName, final String eventName, final String data) { Runnable eventRunnable = new Runnable() { @Override public void run() { AbstractEvent event = EventFactory.getEvent(eventName, data, mainActivity); if (event != null) { event.handleEvent(); } } }; mainActivity.getExecutor().submit(eventRunnable); } }; PUSHER:
  • 39. public class EventFactory { private EventFactory() { }; public static AbstractEvent getEvent(String eventName, String data, MainActivity mainActivity) { if (eventName.equals(Constants.Event.CONNECTED)) { return new ConnectedEvent(data, mainActivity); } else if (eventName.equals(Constants.Event.DISCONNECTED)) { return new DisconnectedEvent(data, mainActivity); } else if (eventName.equals(Constants.Event.TAKED)) { return new TakedEvent(data, mainActivity); } return null; } } UPROSZCZONA FABRYKA EVENTÓW:
  • 41. public abstract class AbstractEvent { protected MainActivity mainActivity; public AbstractEvent(String data, MainActivity mainActivity) { this.mainActivity = mainActivity; parseData(data); } public abstract void handleEvent(); abstract void parseData(String data); } PUSHER - EVENTY:
  • 42. public class TakedEvent extends AbstractEvent { private MenuItem menuItem; private Waiter waiter; @Override public void handleEvent() { if (menuItem.getId() != null) { int position = Utils.findMenuItemPosition(mainActivity.getKitchen().getMenu(), menuItem.getId()); MenuItem menuItem = mainActivity.getKitchen().getMenu().get(position); menuItem.setSelected(false); menuItem.setTakeDate(null); mainActivity.getAdapter().notifyItemChanged(position); }} @Override protected void parseData(String data) { JSONObject jsonObject = null; try { jsonObject = new JSONObject(data); menuItem = mainActivity.gson.fromJson( jsonObject.get(Constants.Keys.MENU_ITEM).toString(), MenuItem.class); waiter = mainActivity.gson .fromJson(jsonObject.get(Constants.Keys.WAITER).toString(), Waiter.class); } catch (JSONException e) { Log.e(TakedEvent.class.getSimpleName(), e.toString()); }} } PUSHER - ODEBRANIE POTRAWY:
  • 43. public class ConnectedEvent extends AbstractEvent { private Waiter waiter; public ConnectedEvent(String data, MainActivity mainActivity) { super(data, mainActivity); } @Override public void handleEvent() { mainActivity.showSnackbar(waiter == null ? "Kelner" : waiter.getName() +" " +mainActivity.getString(R.string. waiter_connected)); } @Override protected void parseData(String data) { if (data != null) { waiter = MainActivity.gson.fromJson(data, Waiter.class); } } } PUSHER - DOŁĄCZENIE KELNERA:
  • 44. public class DisconnectedEvent extends AbstractEvent { private Waiter waiter; public DisconnectedEvent(String data, MainActivity mainActivity) { super(data, mainActivity); } @Override public void handleEvent() { mainActivity.showSnackbar(waiter == null ? "Kelner" : waiter.getName() + " " + mainActivity.getString(R.string. waiter_disconnected)); } @Override protected void parseData(String data) { if (data != null) { waiter = MainActivity.gson.fromJson(data, Waiter.class); } } } PUSHER - ROZŁĄCZENIE KELNERA:
  • 45. KELNER - aplikacja na zegarek z systemem Tizen
  • 46. WYKORZYSTYWANE BIBLIOTEKI: ● Underscore ● jQuery ● Chance ● Pusher ● TAU
  • 48. window.onload = function() { document.addEventListener('tizenhwkey', function(e) { if (e.keyName === "back") { var currentPage = $(".ui-page-active").attr('id'); if (currentPage === 'no_kitchens') { try { tizen.application.getCurrentApplication().exit(); } catch (ignore) { } } else if (currentPage === 'kitchens') { tau.changePage("#no_kitchens"); stopServices() } // .... } }); $("#no-kitchens").click(function() { getKitchens(); }); tau.changePage("#no_kitchens"); Pusher.log = function(message) { if (window.console && window.console.log) { window.console.log(message); } }; }; main.js:
  • 49. <?xml version="1.0" encoding="UTF-8"?> <widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen. org/ns/widgets" id="http://railwaymen.org/Kelner" version="1.0.0" viewmodes=" maximized"> <access origin="*" subdomains="true"></access> <tizen:application id="JRp9qBI8KW.Kelner" package="JRp9qBI8KW" required_version="2.2"/> <content src="index.html"/> <feature name="http://tizen.org/feature/screen.size.all"/> <feature name="http://tizen.org/api/tizen" required="true"/> <feature name="http://tizen.org/feature/download"/> <feature name="http://tizen.org/feature/network.internet"/> <feature name="http://tizen.org/feature/screen.size.all"/> <icon src="icon.png"/> <name>Kelner</name> <tizen:privilege name="http://tizen.org/privilege/application.launch"/> <tizen:privilege name="http://tizen.org/privilege/download"/> <tizen:privilege name="http://tizen.org/privilege/power"/> <tizen:privilege name="http://tizen.org/privilege/push"/> <tizen:privilege name="http://tizen.org/privilege/internet"/> <tizen:profile name="wearable"/> </widget> config.xml:
  • 50. DZIAŁANIE APLIKACJI KELNER: ● Odśwież kuchnie ● Lista kuchni ● Pobierz wydane potrawy ● Zgłoś gotowość odebrania potrawy ● Odbierz potrawę
  • 51. <!--...--> <div id="no_kitchens" class="ui-page"> <div class="ui-content"> <div class="center-block"> <span id="no-kitchens" class="center-text">Odśwież kuchnie.</span> </div> </div> </div> <div id="kitchens" class="ui-page"> <header class="ui-header ui-has-more"> <h2 class="ui-title">Kuchnie</h2> </header> <div class="ui-content" id="mainPage"> <ul id="kitchenList" data-role="listview" class="ui-listview"></ul> </div> </div> <div id="menu_items" class="ui-page"> <header class="ui-header"> <h2 class="ui-title waiter_name">Stefan</h2> </header> <div class="ui-content" id="mainPage" data-add-back-btn="true"> <ul id="menuItemsList" class="ui-listview"></ul> </div> </div> <!--...--> WIDOK - WIDOCZNE EKRANY:
  • 52. HOST: https://kuchnia-api-railwaymen.herokuapp.com/api/ // GET KITCHENS GET host/kitchens // GET MENU ITEMS POST host/kitchens/{kitchen_id}/menu_items Body: { id : "deviceId", name : "deviceName" } // LOGOUT WAITER DELETE host/kitchens/{kitchen_id}/waiters/{waiter_id} // TAKE MENU ITEM DELETE host/kitchens/{kitchen_id}/menu_items/{menu_item_id} Body: { id : "deviceId", name : "deviceName" } ENDPOINTY ZEGARKA:
  • 53. var getKitchens = function() { sendRequest('GET', buildUrl('/kitchens', ''), function(request, data) {}, function(data) {}); } var logoutWaiter = function(kitchenId, waiterId) { sendRequest('DELETE', buildUrl('/kitchens/' + kitchenId + '/waiters/' + waiterId), function(request, data) {}, function(data) {}); } var getMenuItems = function(kitchenId, waiter) { sendRequestWithBody('POST', buildUrl('/kitchens/' + kitchenId + '/menu_items'), waiter, function(request, data) {}, function(data) {}); } var takeMenuItem = function(menuItem, waiter) { sendRequestWithBody('DELETE', buildUrl('/kitchens/' + window.currentKitchen.id + '/menu_items/' + menuItem.id, ''), waiter, function(request, data) {}, function(request, data) {}); } KOMUNIKACJA REST API:
  • 54. var formatTime = function(date) { var tmp = new Date(date); return tmp.getHours() + ":" + (tmp.getMinutes() < 10 ? "0" : "") + tmp.getMinutes(); } var sortQueue = function() { window.currentKitchen.queue.sort(function(menuItem1, menuItem2) { return menuItem1.date ? -1 : menuItem2.date ? 1 : 0; }); } var removeFromQueue = function(menuItemId) { window.currentKitchen.queue = _.reject(window.currentKitchen.queue, function(menuItem) { return menuItem.id === menuItemId; }); } FUNKCJE POMOCNICZE:
  • 56. var renderKitchens = function() { if (window.kitchens.length != 0) { if (window.kitchens.length) tau.changePage("#kitchens"); $("#kitchenList").empty(); window.kitchens.sort(function compare(lok1, lok2) { if (lok1.name.toLowerCase() < lok2.name.toLowerCase()) return -1; if (lok1.name.toLowerCase() > lok2.name.toLowerCase()) return 1; return 0;}); window.kitchens.forEach(function(kitchen) { $("#kitchenList").append(renderKitchen(kitchen)); }); $("#kitchenList li").click(kitchenClickListener); } else { $("#no-kitchens").text('Brak zarejestrowanych kuchni'); }} var renderKitchen = function(kitchen) { return "<li data-id="" + kitchen.id + ""><a href="#" class="ui-li-text-sub">" + kitchen.name + "</a></li>"; } WYŚWIETLANIE KUCHNI:
  • 57. var kitchenClickListener = function(e) { var kitchenId = $(e.currentTarget).data("id"); var deviceId = tizen.systeminfo.getCapabilities().duid if (deviceId) { window.waiter = { id : deviceId, name : chance.name() } } else { window.waiter = { id : uuid(), name : chance.name() } } getMenuItems(kitchenId, JSON.stringify(window.waiter)) } LOGOWANIE SIĘ DO WYBRANEJ KUCHNI:
  • 58. var checkQueue = function() { $(".waiter_name").text(window.waiter.name); if (window.currentKitchen.queue.length === 0) { tau.changePage("#no_menu_items"); } else if (window.currentKitchen.queue.length === 1) { tau.changePage("#single_menu_item_container"); renderSinglePageMenuItem(window.currentKitchen.queue[0]) } else { tau.changePage("#menu_items"); renderMenuItems(); } } SPRAWDZANIE WYDANYCH POTRAW:
  • 59. var renderSinglePageMenuItem = function(menuItem) { $("#single_menu_item").text(menuItem.name); $("#single_menu_item_date").text(formatTime(menuItem.date)); $('#single_menu_item_container').unbind("click"); $('#single_menu_item_container').click( function() { takeMenuItem(window.currentKitchen.queue[0], JSON .stringify(window.waiter)); }); } var renderMenuItems = function() { $("#menuItemsList").empty(); if (window.currentKitchen.queue.length != 1) { tau.changePage("#menu_items"); sortQueue() window.currentKitchen.queue.forEach(function(menuItem) { $("#menuItemsList").append(renderMenuItem(menuItem)); }); $("#menuItemsList li").click(menuItemClickListener); } else { tau.changePage("#single_menu_item_container"); renderSinglePageMenuItem(window.currentKitchen.queue[0]) } } WYŚWIETLANIE POTRAW:
  • 61. var menuItemClickListener = function(e) { var menuItemId = $(e.currentTarget).data("id"); var menuItem = _.findWhere(window.currentKitchen.queue, { id : menuItemId }); takeMenuItem(menuItem, JSON.stringify(window.waiter)) } ODBIERANIE POTRAWY:
  • 62. var bindResponsibility = function(menuItem) { tau.changePage("#well_done"); $("#menu_item").text(menuItem.name); $("#menu_item_date").text(formatTime(menuItem.date)); $('#well_done').unbind("click"); var timeTask = window.setTimeout(function() { checkQueue(); }, 3000); $('#well_done').click(function() { window.clearTimeout(timeTask) checkQueue(); }); } removeFromQueue(menuItem.id) if (request.status === 204) { tau.changePage("#to_slow"); } else { bindResponsibility(menuItem) } REZULTATY:
  • 64. var startServices = function(kitchenId) { window.pusher = new Pusher('be378fc6b685348ae04c', { cluster : 'eu', encrypted : true }); window.channel = window.pusher.subscribe(kitchenId); } KOMUNIKACJA PUSHER:
  • 66. window.channel.bind('event_complete', function(menuItemJson) {}); window.channel.bind('event_take',function(eventJson) {}); window.channel.bind('event_kitchen_disconnected', function() {}); PUSHER - EVENTY:
  • 68. Na zjeździe 11 30-527 Krakow, Poland tel: +48 12 391 60 76 Silicon Valley Acceleration Center. 180 Sansome Street San Francisco, CA 94104 tel: 1-415-449-4791 info@railwaymen.org www.railwaymen.org @Railwaymen_org railwaymen.software.development /company/railwaymen