Programowanie urządzeń mobilnych BARTŁOMIEJ ZASS Developer Evangelist Microsoft
Przegląd rozwiązań Wstęp do Windows Phone Nawigacja Launchers and Choosers Sensory Networking Lokalna baza SQL CE Fast Application Switching Multitasking Live Tiles Notyfikacje Push Podsumowanie
Krótki przegląd rozwiązań iphone Android Blackberry Symbian Windows Phone
11 Października 2010 To jest oś czasu!
Metro ale czemu?
Windows Phone
Metro podstawowe elementy
People vs. Icons
11 Października 2010 Jesień 2011
11 Października 2010 Jesień 2011 Dziś (16) + 19 nowych (35)
Sprzęt - aktualizacja Capacitive touch 4 or more contact points Sensors Motion Sensor A-GPS, Accelerometer, Compass, Light, Proximity, Camera 5 mega pixels or more Multimedia Common detailed specs, Codec acceleration Memory 256MB RAM or more, 8GB Flash or more GPU DirectX 9 acceleration Gyro Improved capability detection APIs CPU Qualcomm MSM8x55 800Mhz or higher MSM7x30 Hardware buttons Back, Start, Search
11 Października 2010 Jesień 2011 The Future
Platformy
Narzędzia Windows Toolkits Phone SDK 7.1 http://watwp.codeplex.com/ http://silverlight.codeplex.com/
application.xap
Demo HTML5
Demo XNA
Silverlight
Demo Silverlight Hello World / Tools
Frame i Page Frame Kontrolka kontenera Top-level PhoneApplicationFrame class Zawiera kontrolkę page i elementy systemowe jak system tray i application bar Page Zajmuje całą przestrzeń contentent dla frame PhoneApplicationPage - derived class Zawiera tytuł Może zawierać swój własny application bar
Nawigacja pomiędzy stronami Silverlight w Windows Phone używa modelu nawigacji opartej na stronach Podobne do nawigacji na stronach Internetowych Każda strona identyfikowalna jest po adresie URI Każda strona jest w zasadzie bezstanowa private void hyperlinkbutton1_click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/SecondPage.xaml", UriKind.RelativeOrAbsolute)); }
Nawigacja pomiędzy stronami Aplikacja może zapewnić przyciski do nawigacji wstec Fizyczny przycisk Wstecz także nawiguje to wcześniejszej strony Nie wymaga to żadnego kodu wbudowane zachowanie private void button1_click(object sender, RoutedEventArgs e) { NavigationService.GoBack(); }
Nadpisywanie przycisku Wstecz <phone:phoneapplicationpage x:class="phoneapp2.mainpage" BackKeyPress="PhoneApplicationPage_BackKeyPress" shell:systemtray.isvisible="true"> private void PhoneApplicationPage_BackKeyPress(object sender, CancelEventArgs e) { e.cancel = true; //Informacja, ze zdarzenie jest obsłużone }
Nawigacja - zdarzenia OnNavigatedTo System.Windows.Navigation.NavigationEventArgs Wywoływany podczas każdej nawigacji na stronę OnNavigatedFrom System.Windows.Navigation.NavigationEventArgs Wywołany podczas każdej nawigacji ze strony
Nawigacja przekazywanie danych private void passparam_click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/SecondPage.xaml?msg=" + textbox1.text, UriKind.Relative)); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.onnavigatedto(e); string msg = ""; if (NavigationContext.QueryString.TryGetValue("msg", out msg)) textblock1.text = msg; }
Demo Nawigacja
Launchers & Choosers Sandbox Super aplikacja
Launchers & Choosers PhotoChooserTask L C PhoneCallTask Sandbox
Launchers Choosers PhoneCallTask SearchTask SMSComposeTask WebBrowserTask EmailComposeTask MarketplaceDetailTask MarketplaceHubTask MarketplaceReviewTask MarketplaceSearchTask MediaPlayerLauncher BingMapsTask BingMapsDirectionsTask CameraCaptureTask EmailAddressChooserTask PhoneNumberChooserTask PhotoChooserTask SaveEmailAddressTask SavePhoneNumberTask AddressChooseTask GameInviteTask SaveRingtoneTask Nowe w Mango Zaktualizowane w Mango
Launcher - przykład EmailComposeTask emailcomposetask = new EmailComposeTask(); emailcomposetask.to = "user@example.com"; emailcomposetask.body = "Email message body"; emailcomposetask.cc = "user2@example.com"; emailcomposetask.subject = "Email subject"; emailcomposetask.show();
Chooser - przykład public partial class MainPage : PhoneApplicationPage { PhotoChooserTask photochoosertask; public MainPage() { InitializeComponent(); photochoosertask = new PhotoChooserTask(); photochoosertask.completed += new EventHandler<PhotoResult>(photoChooserTask_Completed); } private void button1_click(object sender, RoutedEventArgs e) { photochoosertask.show(); } void photochoosertask_completed(object sender, PhotoResult e) { if (e.taskresult == TaskResult.OK) { BitmapImage bmp = new BitmapImage(); bmp.setsource(e.chosenphoto); } } }
Demo Launchers & Choosers
Sensory +Y -X -Z +X
+Y -X -Z +X cos γ. cos α cos β. sin α + sin β. sin γ. cos α sin β. sin α + cos β. sin γ. cos α cos γ. sin α cos β. cos α + sin β. sin γ. sin α sin β. cos α + cos β. sin γ. sin α sin γ sin β. cos γ cos β. sin γ
Motion Sensor Wirtualny sensor łączący pozostałe: gyro + compass + accelerometer Motion Sensor vs. gyro / compass / accelerometer Dokładniejszy Szybszy czas odpowiedzi Porównywalnie mniejsze przesunięcie Potrafi usunąć niepewność w typach ruchu Potrafi sobie poradzić z brakiem żyroskopu Używaj kiedy tylko się da!
Motion Sensor - API if (Motion.IsSupported) { _sensor = new Motion(); _sensor.currentvaluechanged += new EventHandler<SensorReadingEventArgs<MotionReading>> (_sensor_currentvaluechanged); _sensor.start(); } void _sensor_currentvaluechanged(object sender, SensorReadingEventArgs<MotionReading> e) { Vector3 v = new Vector3( e.sensorreading.deviceacceleration.x, e.sensorreading.deviceacceleration.y, e.sensorreading.deviceacceleration.z); }
Kamera Dostęp do strumienia kamery PhotoCamera Silverlight 4 Webcam Wyświetlanie w aplikacji Video Brush
Kamera kiedy co? PhotoCamera Robienie zdjęć Obsługa przycisku na telefonie Obsługa Flash i Focus Webcam Nagrywanie Video Nagrywanie Audio Współdzielenie z komputerem
Demo Sensory
Networking Sockets TCP UDP unicast, Multicast (Wi-Fi) Connection Manager Control Nadpisuje kontroluje ustawienia HTTP Full header access WebClient wraca na oryginalnym wątku
Demo Networking
Dane w Windows Phone Isolated Storage Wyizolowana przestrzeń dla aplikacji Brak ograniczeń ilości danych Usuwana podczas deinstalacji Nie czyszczony w trakcie aktualizacji Baza danych SQL CE Duże ilości danych relacyjnych
Isolated Storage Własne pliki, katalogi Dowolny strumień danych Serializacja do XML i binarna IsolatedStorageSettings Ustawienia globalne Serializowane do XML
Demo Isolated Storage
LINQ dla wszystkich
Przykład: sklep Relacje, powiązania Koszyk zakupów 7 tabel Setki rekordów 5 kluczy obcych PK ItemReferenceData FK1 PK FK1 ItemId ItemName ItemDescription CategoryId PK Lists ListId ListName Favorites FavoriteItemId FavoriteItemName FavoriteItemCategory FavoriteItemQuantity FavoriteItemDescription FavoriteItemListId FavoriteItemPhoto PK PK FK1 FK2 Categories CategoryId CategoryName ListItems ListItemId ListItemName ListId Quantity Category Description StoreId PK FK1 History HistoryItemId PK HistoryItemName HistoryItemCategory HistoryItemQuantity HistoryItemDescriptioin HistoryItemDateAdded HistoryItemListId HistoryItemPhoto StoreId Stores StoreName StoreLocationLat StoreLocationLong StoreAddressLine1 StoreAddressLine2 StoreAddressCity StoreAddressState StoreAddressCountry StoryAddressZip
Przykład: słownik Words Ogromne ilości danych referencyjnych Słownik 3 tabele PK WordId Word Pronunciation Definition AlternateSpellings Origin PK FK1 PK FK1 Favorites FavoriteId WordId History HistoryItemId WordId AddedDate 1 tabela z 500k rekordów
SQL CE API analogiczne jak w EF Code First 4.1 Nie jest to pełne Entity Framework Wewnątrz procesu aplikacji Bazy w pliku XAP lub Isolated Storage Transakcje obsługiwane automatycznie
Demo Baza danych SQL CE
SQL CE i Isolated Storage Ustawienia - ApplicationSettings Dane niestrukturalne w plikach Isolated Storage Dane strukturalne w SQL CE Package Manager Katalog główny aplikacji Katalog główny DB Pliki instalacyjne Baza danych (r/o) App Zarządza plikami i ustawieniami Katalog z danymi WP7 Isolated Storage APIs Ustawienia aplikacji DB Baza danych Pliki aplikacji
Architektura var query = from w in db.wines where w.country == USA" select w.name; Custom Data Context App Objects.Call System.Linq.Queryable.Select(.Call System.Linq.Queryable.Where(.Constant(Table(Wines)), '(.Lambda #Lambda1)), '(.Lambda #Lambda2)).Lambda #Lambda1(db.Wines $w) { $w.country == USA" }.Lambda #Lambda2(w.Country $w) { $w.name } Object Materialization Identity Management Change Tracking Update Processing select Name from Wines where Country = USA Core ADO.NET (System.Data) SQLCE ADO.NET Provider (System.Data.SqlServerCe) SQL CE DB
Tworzenie bazy - przykład // Definiujemy Data Context public partial class WineDataContext : DataContext { public Table<Wine> Wines; public Table<Vineyard> Vineyards; public WineDataContext(string connection) : base(connection) { } } // Definiujemy tabele [Table] public class Wine { [Column(IsPrimaryKey=true] public string WineID { get; set; } [Column] public string Name { get; set; } } // Tworzymy bazę danych na podstawie kontekstu oraz connection string DataContext db = new WineDataContext("isostore:/wineDB.sdf"); if (!db.databaseexists()) db.createdatabase();
Tworzenie bazy c.d. Dowolny klient desktopowego SQL CE Oficjalnie nie obsługiwane Dane referencyjne Pomocnicza aplikacja Windows Phone Skopiowanie bazy do docelowego projektu Ew. skopiowanie do ISO Storage przy starcie SqlMetal generacja klas z bazy lub modelu
Zapytanie - przykład // Tworzymy Data Context DataContext db = new WineDataContext("isostore:/wineDB.sdf"); // Znajdź wszystkie wina danego szczepu, które są w domu var q = from w in db.wines where w.varietal.name == Shiraz && w.isathome == true orderby w.dateacquired select w;
Insert/Update/Delete Dodawanie rekordów Wine newwine = new Wine { WineID = 1768", Name = Windows Phone Syrah", Description = Bold and spicy" }; db.wines.insertonsubmit(newwine); db.submitchanges(); Aktualizacja Wine wine = (from w in db.wines where w.wineid == 1768" select w).first(); wine.description = Hints of plum and melon"; db.submitchanges();
Synchronizacja zmian DataContext Zmiany najpierw w DataContext Zapis zmian przez SubmitChanges() SubmitChanges LINQ to SQL tworzy change set i zgłasza do bazy Name Varietal AtHome Yellow Tail Pinot Noir True Name Varietal AtHome DB Little Penguin Pinot Noir True False
Aktualizacja schematu DatabaseSchemaUpdater proste modyfikacje Dodawanie Tabel Kolumn Indeksów Kluczy obcych Wszystkie zmiany są transakcyjne Bardziej zaawansowane scenariusze - osobny kontekst i ręczne przeniesienie danych
Aktualizacja schematu - przykład Tworzymy nowy DatabaseSchemaUpdater MyDerivedDataContext context = new MyDerivedDataContext("foo.sdf"); DatabaseSchemaUpdater dbupdater = context.createdatabaseschemaupdater(); Dodajemy nową tabelę dbupdater.addtable<product>(); Dodajemy nową kolumnę dbupdater.addcolumn<product>( EANCode"); Rozpoczynamy proces aktualizacji dbupdater.execute();
Synchronizacja Brak replikacji jak w WM 6 Sync Framework Obecnie tylko Isolated Storage Własne rozwiązanie (OData)
Nowe API w WP 7.5 Choosery udostępniające dane użytkowników EmailAddressChooserTask PhoneNumberChooserTask AddressChooserTask Microsoft.Phone.UserData dostęp bezpośredni Kontakty Kalendarz
Kalendarz i kontakty Dostęp tylko do odczytu Dane sieci firm trzecich nie są udostępniane SIM, Windows Live, Exchange, Google - wszystko Facebook tylko nazwa i zdjęcie Twitter i inne nic
Kontakty: Hello, World! Contacts contacts = new Contacts(); contacts.searchcompleted += new EventHandler<ContactsSearchEventArgs>((sender, e) => {... = e.results; }); // np. wybierz wszystkie kontakty contacts.searchasync(string.empty, FilterKind.None, null); Stan Wyrażenie filtrujące (nie regex) Rodzaj filtra: imię, email, telefon, czy pinned // np. szukaj kontaktów zawierających Ba" contacts.searchasync( Ba", FilterKind.DisplayName, null);
Kalendarz: Hello, World! Appointments appointments = new Appointments(); appointments.searchcompleted += new EventHandler<AppointmentsSearchEventArgs>((sender, e) => {... = e.results; }); Data początkowa // E.g. get next appointment (up to 1 week away) appointments.searchasync(datetime.now, DateTime.Now + TimeSpan.FromDays(7), 1, null); Data koocowa Maksymalna liczba elementów stan
Demo Kontakty i kalendarz
Harmonia Windows Phone Unikalny i responsywny UX Nigdy nie żałuj zainstalowania aplikacji Zintegrowany interfejs Health UX Bateria Transfer danych Przetestowane usługi OS
Tombstoning w WP 7 Aplikacje domyślnie nie pracują w tle Zamykane po przejściu do głównego menu Konieczny zapis stanu PhoneApplicationService.Current.State[] PhoneApplicationPage.State[] App.xaml.cs Activated, Deactivated, Launching, Closing
Demo Tombstoning
Fast Application Switching Szybkie wznowienie Wznawianie Wczytywanie Przywracanie stanu! IsAppInstancePreserved == true == false running Zapis stanu! activated deactivated Najstarsza usuwana Tombstoned dormant Zasoby zwalniane Wątki i timery zawieszane
FAS w kodzie private void Application_Activated(object sender, ActivatedEventArgs e) { if (!e.isapplicationinstancepreserved) { // Załaduj stan tylko, kiedy jest to konieczne... } } private void Application_Deactivated(object sender, DeactivatedEventArgs e) { // Zapisuj stan zawsze (na wszelki wypadek) }
Fast Application Switching Ładowanie stanu tylko, kiedy jest to konieczne Kompatybilne z WP7 Automatyczne wznowienie Stan pamięci, storyboardy, pętla XNA, sensory, audio Ręczne wznowienie MediaElement, komunikacja sieciowa, kamera
Multitasking w Windows Phone Usługi Agenci Zarządzanie zasobami
Background Notification Service Remindery Alarmy using Microsoft.Phone.Scheduler; private void AddReminder(object AddAlarm(object sender, sender, RoutedEventArgs e) e) { Reminder Alarm alarm reminder = new = Alarm("Long new Reminder("CompanyMeeting"); Day"); reminder.begintime alarm.begintime = DateTime.Now.AddSeconds(15); = reminder.content alarm.content = "It's = "Soccer been Fields a long by day. The Go Commons"; to bed."; reminder.title alarm.title = "Alarm"; = "Microsoft Annual Company Product Fair 2009"; reminder.recurrencetype = RecurrenceInterval.Yearly; reminder.navigationuri = new Uri("/Reminder.xaml", UriKind.Relative); } ScheduledActionService.Add(reminder); ScheduledActionService.Add(alarm);
Demo Bg Notification Service
Background Transfer Service using Microsoft.Phone.BackgroundTransfer; My WP Book App downloads Cheese & Win completed Great Mysterie WP Tips & Tric void DownloadWithBTS(Uri BtsProgressChanged(object GetCurrentProgress() sourceuri, sender, Uri BackgroundTransferEventArgs destinationpath) e) { DrawProgressBar(btr.BytesReceived); = new BackgroundTransferRequest(sourceUri, destinationuri); } btr.transferstatuschanged DrawProgressBar(e.Request.BytesReceived); += BtsStatusChanged; btr.transferprogresschanged += BtsProgressChanged; } BackgroundTransferService.Add(btr); } Nasza aplikacja ISO Store Background Transfer Service POST GET Chmura
Background Transfer Service - limity 1 2 3 4 5 20 MB 100 MB 20 MB Bez ograniczeo
Demo Bg Transfer Service
Background Audio HTML5 <audio id="audio_tag" controls="controls" src="http://html5audio.com/demo1.mp3" onplay="startplayback()" onended="nexttrack()" </audio> function NextTrack() { musicplayer = document.getelementbyid("audio_tag"); musicplayer.src = 'http://html5audio.com/demo2.mp3'; } www.html5audio. Przeglądarka Zune Service Chmura ++
Background Audio Streaming Background Audio current 0:09 0:10 0:11 0:12 fav Player void PlayStateChanged(object = BackgroundAudioPlayer.Instance; sender, EventArgs e) player.playstatechanged { += new eventhandler(playstatechanged); switch (player.playerstate) AudioTrack { track = new AudioTrack(new Uri("/audiofile.mp3"), ); case PlayState.FastForwarding: player.play(); // Move to next track break; } } Ode to WP By MSFTMan My Music App Zune Service ++ Buffer Cloud ISO Store ++ Buffer
Architektura Programista Inne procesy Aplikacja myapp.dll UX Logika OS Pojedynczaa plikacja Cloud ISO Store Usługi systemowe Logika myappagent.dll
Demo Bg Audio
Generic Background Agents Typy agentów Okresowy (Perodic) On-Idle (Resource-intensive) Może być jeden lub obydwa Inicjalizowane w UI, działanie w tle Wznawiane po restarcie Kontrola użytkownika Maksymalnie 18 działających agentów Synchronizacja z UI przez Mutex Działanie do 14 dni (może być odnowione)
Generic Background Agents Periodic Agent Uruchamiane Co 30 min Czas działania 25 sekund Scenariusze Synchronizacja częściowa Lokalizacja Inne Resource-Intensive Agent Uruchamiane Zewnętrzne zasilanie, wifi/lan Czas działania 10 minut Scenariusze Backup Początkowa inicjalizacja Inne
Funkcjonalność Background Agents Dozwolone Tiles Toast Lokalizacja Sied R/W ISO store Structured storage Sockety Większośd API Zabronione Interfejs użytkownika Biblioteki XNA Mikrofon i aparat Sensory Odtwarzanie audio
Demo Background Agent
Live Tiles lokalne API Lokalne zmiany (nie przez notyfikacje) Pełna kontrola nad wszystkimi właściwościami (foreground oraz background) Wiele kafelków per aplikacja Dodawnie/Zmienianie/Usuwanie Bezpośrednie przekierowanie do strony/stanu
Live Tiles lokalne API, c.d. Aktualizacja drugiej strony kafelka Content, tytuł, tło Content string is bigger Content Title Title Background Automatycznie, asynchronicznie obraca się na drugą stronę
Demo Live Tiles API
Aktualizacja kafelków Agent / aplikacja lokalne API ShellTileSchedule grafika co min. 1h (URL) Notyfikacje PUSH Tile Toast Raw
Architektura notyfikacji push Cloud Service Windows Phone 7 MPNS
try { channel = HttpNotificationChannel.Find( mychannel ); } catch { txtstatus.text = "Status : Failed"; } if (channel!= null) { Debug.WriteLine(channel.ChannelUri.ToString()); txtstatus.text = "Status : Connected"; isconnected = true; } else { channel = new HttpNotificationChannel(channelName); channel.channeluriupdated +=... channel.shelltoastnotificationreceived +=... channel.httpnotificationreceived +=...; channel.erroroccurred +=... } try { channel.open(); } catch { channel = null; txtstatus.text = "Status: Failed"; }
Notyfikacje push - nowości Obsługa wielu kafelków / drugiej strony Wiele lokalizacji pogody, kategorii wiadomości, itp. Mogą aktualizować wszystkie nasze kafelki Brak zmian w API! BindToShellTile binduje do wszystkich kafelków Wysyłamy Tile ID do usługi 3 nowe elementy z drugiej strony kafelka <wp:notification xmlns:wp="wpnotification"> <wp:tile Id= /WorldNews.xaml?how=start > <wp:backgroundimage>http://www.contoso.com/worldtile.png</wp:backgroundimage> <wp:count>4</wp:count> <wp:title>world News Updates</wp:Title> <wp:backbackgroundimage>http://www.contoso.com/worldback.png</wp:backbackgroundimage> <wp:backcontent>peace talks resume</wp:backcontent> <wp:backtitle>middle East News</wp:BackTitle> </wp:tile> </wp:notification>
Notyfikacje push nowości, c.d. Deep Toast Bezpośrednie przekierowanie np. do odpowiedniej strony Brak zmian w API BindToShellToast 1 nowy element z Navigation URI <wp:notification xmlns:wp="wpnotification"> <wp:toast> <wp:text1>world News Updates</wp:Text1> <wp:text2>egypt braces for march in palace square</wp:text2> <wp:param>/detailspage.xaml?storyid=186435</wp:param> </wp:toast> </wp:notification>
Materiały Strona główna dla programistów Windows Phone (App Hub) UX guidelines Bezpłatny e-book Ch. Petzolda (WP 7) WP 7 Training Kit WP 7.5 Training Kit (nowości) Sesje z konferencji MIX Polskie Centrum WP 7 na MSDN Polskojęzyczny kurs (webcasty) Zbiór materiałów dla programistów Przykłady kodu How do I videos Sesje z konferencji BUILD
Q&A