czwartek, 30 października 2014

Microsoft Band - wreszcie coś do założenia na rękę !

No i wreszcie ujrzała światło dzienne opaska na rękę, mogąca również być zegarkiem. Ma jednak mocno zaakcentowane funkcje zdrowotne. Mierzy ilość kroków, spalonych kalorii, rytm serca, jakość snu (cokolwiek by to znaczyło). Na pewno taka opaska zachęcałaby mnie do ćwiczeń. Poza tym może odbierać wszelkiego rodzaju notyfikacje, wiadomości, maile z telefonu, powiadomienia np. z Facebook’a, informować o pogodzie czy finansach. Może wykrywać stres. Ma “Cortanę”, która nam odpowie. Można rzadziej wyjmować telefon z kieszeni… Cześć zadań możemy wykonywać bez telefonu np. sprawdzić godzinę czy swoją aktywność.

screen_shot_2014-10-29_at_10_20_35_pm

Współpracuje z urządzeniami na wszystkie popularne platformy mobilne jak Android, iOS oraz oczywiście Windows Phone / Windows.  Wytrzymuje dwie doby bez podładowania baterii. W sprzedaży tylko w USA i to w limitowanych ilościach, przynajmniej na ten moment niestety…

Linki

niedziela, 26 października 2014

Moje trzy grosze o “Angular 2.0” -;)

Na początek trochę linków:

Twórcy Angular 2.0 postawili się jakby odciąć od tego co robią i postawili że pogrzebią wszystkie używane obecnie często byty takie jak (mamy pięć grobowców anno domini 2014):

  • kontrolery
  • dyrektywy
  • $scope
  • moduły
  • jQuery lite

Stawiają na przyszłościowy ECMA Script 6 i dalsze, przyszłościowe standardy Web, zajawili się TypeScript-em i stworzyli jego nadzbiór AtScript. Każdy TypeScript ma być AtScriptem. Cały przyszły Angular ma być napisany w AtScript, który kompilowany będzie do JavaScript ECMA 5 albo Dart.  Pisanie aplikacji w Angular też ma być możliwe w AtScript, choć tradycjonaliści będą nadal mogli pisać też w JavaScript.  Kto uczył się i zna TypeScript zobaczy, że AtScript dodatkowo wprowadza metadane, kontrolę typów w czasie runtime (TypeScript ma tylko w czasie pisania).  Tylko, co teraz powiedzą wszyscy hejterzy TypeScript, skoro oto proszę Google postanowił wykorzystać pomysł Microsoft i go jeszcze rozwinąć, napisać w tym cały swój framework i dać programistom aplikacji taką możliwość?  Twórca TypeScript zapowiedział swój roadmap tak, by dało się go używać w Angular 2.0, z kolei Google “wyrówna” swój AtScript, by zgadzał się z TypeScript-em. Jakiś deal pod stołem? Aha, i ten AtScript ma być zgłoszony jako jeszcze bardziej przyszłościowa wersja ECMA Script (nie wiem, chyba przynajmniej 8, bo 7 już mamy, chociaż 6 od kilku lat nie może być ukończona i nie ma jej w żadnej przeglądarce)

Priorytetem ma być wydajność (wszyscy, którzy uważali że obecny Angular był idealny zobaczą jak wysoce nie był), postawienie na Mobile-First, nowy dynamiczny router 2.0, nowe podejście do dependecy injection, długo by wymieniać…

Nie wiadomo, kiedy ukaże się prototypowa nawet wersja Angular 2.0 . Na razie najnowszą produkcyjną stabilną wersją jest Angular 1.3, który będzie utrzymywany pewnie przez odpowiednio długi czas zanim przełomowy Angular 2.0 nie zadomowi się na dobre.

sobota, 25 października 2014

Pojedynek z Androidem - odc. 16 ActionBar (uzupełnienia)

Tym razem kilka uzupełnień związanych z ActionBar. Moje uwagi:

  • podobać się może kompatybilność wstecz i możliwość używania nawet na dość starych urządzeniach.
  • ukrywanie i zarządzenie widokiem trochę toporne jak dla mnie, ale da się przeżyć
  • nawigacje zakładkowa i listowa - przyzwoite, ale nic nadzwyczajnego nie widzę, w Windows możemy wstawić dowolną zawartość do paska aplikacyjnego, a poza tym mamy nowoczesne kontrolki Hub i Pivot do nawigacji
  • interaktywne widoki akcji na pasku - własne lub predefiniowane kontrolki pokazywane na stałe lub rozwijane, spełnia to swoją rolę. W Windows 8.x oraz Windows Phone 8.1 (XAML WinRT, HTML5) każdy może ogólnodostępnymi metodami dla kontrolek modyfikować zawartość paska aplikacyjnego (jest to normalna kontrolka). W Windows Phone nie nowszym niż 8.0 i w Silverlight na WP 8.1 mamy komponent systemowy o dość ograniczonej konfiguracji, który nie jest kontrolką XAML. W Android nie mamy do końca uniwersalnych mechanizmów dla całej platformy w zakresie modyfikacji zawartości komponentów wizualnych mimo wprowadzenia uniwersalnych fragmentów (może wynika to z tego, że dołożono je później do platformy, w Windows uniwersalne kontrolki były od początku)

Szczegóły poniżej:

 

Poprawa interakcji dzięki ActionBar

Dostępny na wszystkie wersje Android

Sterowanie wyglądem

  • obrazki
    • setIcon - inna ikona niż domyślna ikona aktywności
    • setLogo - ustawienie większego obrazka niż ikona po lewej stronie
    • setBackgroundDrawable - użycie bitmapy lub czegoś podobnego do tła
  • tekst
    • setTitle
    • setSubtitle
    • setDisplayShowTitleEnabled - pokazywanie/ukrywanie tytułu i podtytułu

Ukrywanie

  • programowo
    • ActionBar.hide / ActionBar.show
    • ActionBar.isShowing - informacja, czy pasek jest widoczny
  • statycznie
    • aktywność z motywem Theme.Holo.NoActionBar

Overlaying

Lepszy user experience przy ukrywaniu/pokazywaniu

  • Overlay pozwala ukrywać/pokazywać ActionBar bez resizowania aktywności
  • Włączanie overlay przy pomocy stylu
    • zdefiniowanie nowego stylu dziedziczącego po Theme.Holo
    • ustawienie windowActionBarOverlay na true
  • Włączanie overlay programowo
    • pobieramy referencję na okno za pomocą Activity.getWindow
    • używamy requestFeature do zatwierdzenia Window.FEATURE_ACTION_BAR_OVERLAY
  • aplikacja musi określić, co ukrywa/pokazuje ActionBar
    • zwykle zdarzenia dotykowe
    • często ukrywamy po wyborze akcji

public class XActivity extends Activity  {

        …

        @Override

        public boolean onTouchEvent(MotionEvent event)  {

                if (event.getAction() == MotionEvent.ACTION_DOWN)

                        toggleActionBar();

                return true;

        }

        private void toggleActionBar()  {

                ActionBar actionBar = getActionBar();

                if (actionBar.isShowing())

                        actionBar.hide();

                else

                        actionBar.show();

        }

}

values/styles.xml

<resources>

          <style name=”Theme.Holo.ActionBarOverlay” parent=”android:style/Theme.Holo”>

                   <item name=”android:windowActionBarOverlay”>true</item>

          </style>

</resources>

manifest

<activity …

                    android.theme=”style/Theme.Holo.ActionBarOverlay”

                    …>

            …

</activity>

Nawigacja

Tryby nawigacji

  • standardowa
  • zakładki
  • lista - wybór widoków z listy drop-down

Kontrolowanie umieszczenia zakładek

  • W zależności od dostępnej przestrzeni
    • stos zakładek pod głównym ActionBar, jeśli zachodzi taka potrzeba
    • na głównym ActionBar, jeśli pozwala przestrzeń
    • możliwość robienia miejsca przez umieszczenie listy akcji na dole i ukrywanie ikony/tytułu

Pokazywanie na ActionBar:

ActionBar actionBar = getActionBar();

 

int currentOptions = actionBar.getDisplayOptions();

boolean currentVisibleValue = (currentOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;

boolean newVisibleValue = !currentVisibleValue;

 

actionBar.setDisplayShowHomeEnabled(newVisibleValue);

actionBar.setDisplayShowTitleEnabled(newVisibleValue);

Nawigacja listowa

  • lista jest tworzona jako SpinnerAdapter
    • w większości przypadków ukrywamy tytuł
  • każda pozycja reprezentuje ekran
  • każdy ekran zaimplementowany jako Fragment

ActionBar.OnNavigationListener

  • pojedyncza instancja obsługuje wszystkie ekrany
  • onNavigationItemSelected wołany przy każdej zmianie wyboru
    • indeks wyboru
    • ID wyboru, jeśli dotyczy
  • każdy wybór wiąże się z pokazaniem odpowiedniego Fragment-u
    • pełnoekranowy fragment dołączamy do android.R.id.content
    • niepełnoekranowy fragment dołączamy do odpowiedniej ViewGroup

public class XActivity extends Activity  {

          ActionBar.OnNavigationListener _navigationListener;

          …

}

public class SimpleNavigationListener  implements ActionBar.OnNavigationListener  {

          …

          public boolean onNavigationItemSelected(int i, long l)  {

                   String fragmentClassName = _fragmentList[i];

                   Fragment theFragment = Fragment.instantiate(_containingActivity, fragmentClassName);

 

                   FragmentTransaction fragmentTransaction = _containigActivity.getFragmentManager(). beginTransaction();

                   fragmentTransaction.replace(android.R.id.content, theFragment);  //lub viewGroupId

                   fragmentTransaction.commit();

 

                   return true;

          }

          public static SimpleNavigationListener SetupListNavigation(Activity containingActivity, …)  {

                   …

                   SimpleNavigationListener listener = new SimpleNavigationListener(containingActivity, …);

 

                   ActionBar actionBar = containingActivity.getActionBar();

 

                   actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

                   actionBar.setListNavigationCallbacks(displayNameList, listener);

                  

                   actionBar.setDisplayShowTitleEnabled(false);

          }

}

Interaktywność

Widoki akcji - interaktywne widgety, które pokazują się w ActionBar

  • pozwalają pokazywać okazjonalnie używane UI w ActionBar i zaoszczędzić miejsce na ekranie
  • zastępuje pojedynczy element z akcją (może być stale widoczny lub automatycznie być ukrywany i pokazywany)

Definicja w XML

  • specyfikujemy klasę widoku dla elementów menuItem atrybutem actionViewClass
  • można używać większość klas dziedziczących po View (wbudowanych lub własnych)
  • najczęściej używaną klasą jest SearchView (ma wbudowaną świadomość ActionBar)

<menu …>

         <item …

                    android:actionViewClass=”android.widget.SearchView”

</menu>

Własne widoki akcji 

Poprzez zasób z layoutem

  • tak jak każdy zasób dla layoutu
  • wiążemy z menuItem stosując atrybut actionLayout

<menu …>

         <item …

                    android:actionLayout=”@layout/action_view_layout”

</menu>

Pokazywanie i ukrywanie widoków akcji

  • domyślnie widok akcji jest widoczny cały czas
  • możliwość dynamicznego pokazywania i ukrywania
    • w atrybucie showAsAction zawieramy collapseActionView (po naciśnięciu przycisku następuje rozwinięcie do kontrolki)
    • programowo
      • pobieramy referencję na pozycję przy użyciu menu.findItem
      • menuItem.isActionViewExpanded / menuItem.expandActionView / menuItem.collapseActionView
      • odpowiadanie na pokazywanie / ukrywanie
        • implementujemy interfejs onActionExpandListener (możliwość anulowania akcji przez zwrócenie false)
          • boolean onMenuItemActionExpand(MenuItem menuItem)
          • boolean onMenuItemActionCollapse(MenuItem menuItem)
        • wiążemy listener z menuItem metodą setOnActionExpandListener

Dostępność zawartości widoku akcji z poziomu kodu

  • metoda getActionView
    • przy actionViewClass - zwraca referencję na tą klasę
    • przy actionLayout - zwraca główny node layoutu
  • zawartość z widoku akcji jest dostępna poprzez metodę findViewById

TextView

  • setOnEditorActionListener
  • TextView.OnEditorActionListener
    • boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent)

public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent)  {

        if  (keyEvent != null)  {

                 if  (keyEvent.getAction() == KeyEvent.ACTION_DOWN  && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER)  {

                          CharSequence enterText = textView.getText();

                          …

                 }

        }

        return false;

}

czwartek, 23 października 2014

Pojedynek z Androidem - odc. 15 asynchroniczne programowanie i serwisy (mała retrospekcja)

Po wchłonięciu gorących nowości z ostatniego buildu Windows 10 przyszła pora na wydanie kolejnego odcinka poświęconemu systemowi Android. Dziś zrobię trochę rozważań opartych… na tym, co już było. Taka mała retrospekcja.

Jeśli chodzi o responsywność to filozofia przypomina w dużym stopniu tę znaną z Windows, przy czym Microsoft ma tylko asynchroniczne WinRT API i programista - choćby nawet chciał - to nie przyblokuje interfejsu np. operacją na pliku. W Androidzie trzeba być świadomym AsyncTask.  Co do sposobu jego wykonywania, to mamy historyczne zróżnicowanie (sekwencyjnie, równolegle, potem znów sekwencyjnie z możliwością jawnego wybrania). W Windows TPL / WinJS pozwalają wygodnie sterować ciągami operacji.

Serwis widziany z poziomu różnych aplikacji, procesów może być udogodnieniem dla twórców aplikacji, ale - IMHO - też i jakąś furtką dla osób o niezbyt dobrych zamiarach.

Wątki w serwisie… cóż jak dla mnie trochę przesyt różnych tworów takich jak zagnieżdżone klasy, implementacje interfejsu Runnable.  A wszystko to okraszone różnymi zmiennymi.  Zarządzanie cyklem życia serwisu obejmuje potrzebę pamiętania wyrafinowanych opcji przy wznawianiu jego pracy.

Wyświetlanie wiadomości toast z poziomu serwisu wymaga stworzenia wątku z pomocą klasy Handler i loopera. Uff… W Windows nie widzę, żeby były jakieś różnice między korzystaniem z notyfikacji toast w aplikacji i serwisie. W Android przy wysyłania notyfikacji z serwisu (serwisy foreground) też musimy postąpić nieco inaczej niż w UI.  W Windows znów nie ma różnic dla notyfikacji tile.  Android nie jest tutaj mistrzem prostoty.

Wyzwalanie serwisów na zdarzenia systemowe…  to jedna z głównych koncepcji tasków w tle na platformie Windows.  W Android implementujemy odpowiednią klasę do przechwycenia systemowego zdarzenia, a potem w jej metodzie onReceive jawnie wywołujemy serwis przez startService.  Microsoft zaenkapsulował nam taki przypadek w postaci predefiniowanych triggerów, w Androidzie możemy sobie sami to oprogramować.

 

 

Wyzwania dla responsywnego “user experience”

Responsywność

  • Musimy utrzymywać dostępność głównego wątku by przechwytywać zdarzenia
  • Przewodniki dla Android zalecają by handlery zajmowały mniej niż 200ms (preferowane jest mniej niż 100ms)
  • Komunikaty ANR pojawiają się, jeśli wątek główny zajęty będzie dłużej niż 5 sekund

Trzy strategie na podtrzymywanie responsywności

  • upewnienie się, że operacje potencjalnie blokujące nie występują w głównym wątku
    • klasa Strictmode
      • od Android 2.3 / API Level 9
      • rodzaje przechwytywanych operacji - setThreadPolicy(…)
      • sposób przechwytywania - setVmPolicy(…)
      • najczęściej używane - enableDefaults
      • tylko na czas developmentu
  • używanie dodatkowych wątków
    • użyteczne dla nie za długich, długo trwających operacji
      • od 100 ms do kilku sekund
      • wątki trwające więcej niż kilka sekund mogą być zabite przez system operacyjny
    • międzywątkowy dostęp do UI
      • Activity.runOnUiThread
      • View.post
    • stosowanie dedykowanych wątków zwykle nie jest najlepszym pomysłem na poprawę UI
      • tworzenie/niszczenie wątku jest kosztowne
      • dość złożony kod
    • lepiej stosować asynchroniczne taski specjalne dedykowane do tego rodzaju zadań – AsyncTask
      • fazy pracy
        • onPreExecute - odczytanie informacji z UI w głównym wątku
        • doBackground
        • publishProgress / onProgressUpdate
        • onPostExecute
      • lepsza wydajność niż dedykowane wątki (wykorzystanie puli wątków)
      • taski wykonujące się dłużej niż kilka sekund mogą być zabite przez OS
      • duże różnice w wykonywaniu - domyślne zachowanie puli wątków zależy od wersji systemu
        • taski wykonywane są zawsze sekwencyjnie ( Android 1.0 / API 1  -  Android 1.5 / API 3)
        • taski mogą wykonywać się równolegle (Android 1.6 / API 4  -  Android 2.2 / API 8)
        • taski wykonywane są zawsze sekwencyjnie (Android 2.3  / API 9  i nowszy)
      • od Android 3.0 / API 11 można określić sposób wykonywania taska
        • AsyncTask.executeOnExecutor
        • AsyncTask.SERIAL_EXECUTOR - sekwencyjne wykonywanie tasków
        • AsyncTask.THREAD_POOL_EXECUTOR - równoległe wykonywanie tasków
      • więcej:  odc 4
  • używanie serwisów

 

public class XActivity extends Activity  {          

       …

       private void updateDisplay(String message)  {

               _defaultTextViewTemp = message;

              _defaultTextView.post(new Runnable()  {

                      public void run()  {

                              _defaultTextView.setText(_defaultTextViewTemp);

                      }

              }

       }

}

 

_asyncTaskWorker = new AsyncTaskWorker();

_asyncTaskWorker.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, _param);

 

Implementacja długotrwających operacji jako serwisów

Operacja “długotrwająca” - na tyle długa, że użytkownik może przełączyć się na inną aplikację

W serwisie mogą być określone preferencje co do wykonywania

  • może być długo trwającą operacją w tle
  • może mieć taką samą ważność jak UI aktualnie używanej aplikacji

Kategorie serwisów

  • uruchomione (started services)
    • duża kontrola nad swoim czasem życia
    • ograniczona interakcja z innymi komponentami
    • większość serwisów
  • związane (bound services)
    • czas życia dopasowany do komponentów, które mają do niego dostęp
    • duża interakcja z komponentami, które mają do niego dostęp

IntentService

  • najprostsza implementacja (wystarczy konstruktor i nadpisanie onHandleIntent)
  • uruchamiany na żądanie (na czas przetwarzania przychodzących żądań)
  • obsługa scenariusza kolejkowania długo trwającej operacji w tle
  • tworzy pojedynczy wątek roboczy po wywołaniu startService
    • przy dodatkowych wywołaniach startService podczas wykonywania żądania są serializowane
  • wątek roboczy jest kończony po przetworzeniu wszystkich żądań
  • tworzony jest nowy wątek roboczy, jeśli pojawią się nowe żądania po zamknięciu
  • więcej: odc 5

<manifest … package=”xxx.yyy”>

         <application>

                    <service android:name=”.serviceClass” android:label=”nazwa używana w widokach dla użytkownika” />

         </application>

</manifest>

Używanie serwisów między procesami

Serwis

  • wykonuje się w procesie, w którym jest zdefiniowany
  • może być wywoływany z innych komponentów będących w jego procesie
  • może być wywoływany przez komponenty z innych procesów (z innej aplikacji)
  • może obsługiwać równocześnie żądania z różnych procesów
  • uzupełnienie do odc 5
  • aby być widoczny dla innych procesów musi definiować filtr intencji
    • zwykle test akcji, ale można także umieścić kategorię i/lub dane
    • jeśli zachodzi potrzeba proces serwisu jest uruchamiany
    • jeśli proces serwis już jest uruchomiony wykorzystywana jest jego istniejąca instancja

<service …>

         <intent-filter>

                  …

        </intent-filter>

</service>

 

Zarządzanie cyklem życia serwisu

Serwisy uruchomione (started services) mogą zarządzać swoim cyklem życia

Cykl życia w trzech metodach klasy Service

  • onCreate (każda instancja klasy dokładnie jedno wywołanie, po Context.startService jeśli nie było instancji serwisu)
  • onStartCommand  (przy wywoływaniu Context.startService)
  • onDestroy (po wywołaniu Context.stopService, stopSelf (przez sam serwis) lub przez system operacyjny ze względu na zasoby)
  • więcej:  odc 5

Started services nie używają metody onBind (w nadpisaniu zwracamy null)

Wątki w serwisach

  • wszystkie metody callback serwisu wykonują się w głównym wątku aplikacji (tym samym co UI)
  • jesteśmy odpowiedzialni za przekazanie pracy do innego wątku
    • można użyć jawnie tworzonego wątku
    • można użyć jedną z klas, która implementuje ExecutorService
      • implementacjami ExecutorService są pule wątków
      • używamy klasy Executors to tworzenia instancji ExecutorService
      • przykład: odc 13

Executors

  • newFixedThreadPool(…)
  • newScheduledThreadPool(…)
  • newSingleThreadExecutor()

public class WorkerService extends Service  {

        …

        ExecutorService _executorService;

       

        @Override

        public void onCreate()  {

                 …

                 _executorService = Executors.newSingleThreadExecutor();

        }   

 

        @Override

        public int onStartCommand(Intent intent, int flags, int startId)  {

                  _executorService.execute(new Runnable()  {

                          public void run()  {

                                  …

                          }

                 });

                 return Service.START_NOT_STICKY;

        }  

 

         …

}

Serwis ma dwa mechanizmy na zarządzanie swoim cyklem życia:

  • żądanie zatrzymania siebie
    • jawne
      • serwis wywołuje stopSelf
      • system automatycznie wywołuje onDestroy i zatrzymuje serwis
    • warunkowe
      • serwis może wskazać, że chciałby się zatrzymać jeśli nie ma oczekującej pracy
      • serwis wywołuje stopSelfResult przyjmujące numer żądania z onStartCommand
        • serwis się zatrzyma tylko wtedy, jeśli żadne żądania nie pojawią się po danym żądaniu
  • onStartCommand - zwracana wartość określa, co system powinien zrobić, jeśli proces serwisu zostanie zabity (z powodu zasobów)
    • START_NOT_STICKY - jeśli serwis został zabity, nie jest restartowany (przy większych zasobach) aż do wywołania startService
    • START_STICKY - jeśli serwis został zabity, automatycznie jest restartowany, gdy zasoby są dostępne, intencja przekazywana w onStartCommand ma wartość null
    • START_REDELIVER_INTENT - jeśli serwis został zabity przed wywołaniem stopSelfResult dla ostatniego żądania, automatycznie jest restartowany, jeśli zasoby są dostępne, przekazywana jest intencja ostatnio przekazana do serwisu zanim został zabity
    • START_STICKY_COMPATIBILITY - kompatybilność z zachowaniem serwisu z Android 1.6 / API 4 lub wcześniejszym, automatyczny restart serwisu po zabiciu, jeśli zasoby są dostępne, podczas restartu metoda onStartCommand może zostać wywołana, konieczność nadpisania przestarzałej metody onStart
  • patrz: odc 5 

public class WorkerService extends Service  {

        …   

        ExecutorService _executorService;

        ScheduledExecutorService _scheduledStopService;

       

        @Override

        public void onCreate()  {

                 …

                 _executorService = Executors.newSingleThreadExecutor();

                 _scheduledStopService = Executors.newSingleThreadScheduledExecutor();

        }   

 

        @Override

        public int onStartCommand(Intent intent, int flags, int startId)  {

                 serviceRunnable runnable = new serviceRunnable(this, startId);

                  _executorService.execute(runnable);

                 return Service.START_NOT_STICKY;

        }

 

        …

 

        class serviceRunnable implements Runnable  {

                 WorkerService _theService;

                 int _startId;

                 public serviceRunnable(WorkerService theService, int startId)  {

                          _theService = theService;

                          _startId = startId;

                 }

                 public void run()  {

                           …

                          //_theService.stopSelf();

                         // _theService.stopSelfResult(_startId);

                         // opóźnione zatrzymanie

                         delayedStopRequest stopRequest = new delayedStopRequest(_theService, _startId);

                         _theService._scheduledStopService.schedule(stopRequest, 5, TimeUnit.MINUTES);

                 }

        }

 

        class delayedStopRequest implements Runnable  {

                 WorkerService _theService;

                 int _startId;

                 public delayedStopRequest(WorkerService theService, int startId)  {

                          _theService = theService;

                          _startId = startId;

                 }

                 public void run()  {

                           _theService.stopSelfResult(_startId);

                 }

        }      

}

 

Interakcja z serwisami

Ogólnie, serwisy powinny wchodzić w minimalną interakcję z użytkownikami

  • nie należy próbować oddziaływać bezpośrednio na instancję aktywności
    • przechowywanie referencji nie jest bezpieczne
    • instancje aktywności są często odtwarzane na nowo
  • należy używać technik będących poza interakcją z użytkownikiem
    • wiadomości toast
    • notyfikacje

Wyświetlanie wiadomości toast z poziomu serwisu wymaga specjalnej obsługi

  • musi być wywoływana z wątku, który ma looper (HandlerThread)
  • pomocna jest tu klasa Handler
    • pozwala jawnie tworzyć HandlerThread
    • najłatwiejsze w użyciu Context.getMainLooper by dostać się do głównego HandlerThread z UI

public class WorkerService extends Service  {

        …

        class serviceRunnable implements Runnable  {

                WorkerService _service;                

                …

                Handler _handler;

                String _statusMessage;

                …

                public void run()  {

                      setupHandler();                    

                      …

                      updateStatus(“xxx”);

                      …

                }

                void setupHandler()  {

                       _handler = new Handler(_service.getMainLooper());

                }

                void updateStatus(String message)  {

                        _statusMessage = message;

                        _handler.post(new Runnable() {

                                public void run()  {

                                      Toast toast = Toast.makeText(_service, _statusMessage , Toast.LENGTH_LONG);

                                       toast.show();

                                }

                        });

                }

         }

}

Serwisy, które wpływają bezpośrednio na user experience

  • serwisy foreground 
    • serwis, którego użytkownik jest bezpośrednio świadomy
      • np. odtwarzacz muzyki
    • informacje serwisu pojawiają się w status bar (wyświetlanie notyfikacji przez serwis)
      • wyświetlenie ikony
      • wyświetlenie tekstu (dodatkowa informacja po rozwinięciu; napis podczas pierwszego pojawienia)
      • możliwość wyzwolenia akcji, kiedy użytkownik wchodzi w interakcję z paskiem statusu
    • serwis może się wchodzić i wychodzić z przebywania w stanie foreground
      • sam może wywołać na sobie startForeground / stopForeground
      • wywołanie stopForeground powoduje usunięcie notyfikacji z paska statusu (serwis nadal kontynuuje swoje działanie)
      • jeśli zostanie zamknięty z jakiegokolwiek powodu, system automatycznie wywołuje stopForeground
    • notyfikacja jest głównym sposobem na interakcję z użytkownikiem
      • jak wszystkie serwisy, serwis foreground nie ma interfejsu użytkownika
      • możliwość modyfikowania notyfikacji w dowolnym czasie za pomocą NotificationManager
    • więcej: odc 9

public class WorkerService extends Service  {

         …

         final int _notificationId = 1;

         Notification _foregroundNotification;

         NotificationManager _notificationManager;

         …

         @Override

         public void onCreate() {

                …

               _notificationManager = (NotificationManager)  this.getSystemService(Context.NOTIFICATION_SERVICE);

 

                startInForeground();

         }

         …

         void startInForeground()  {

                 int notificationIcon = R.drawable.icon24x24;

                 String notificationTickerText = “xxx”;

                 long notificationTimestamp = System.currentTimeMillis();

                 _foregroundNotification = new Notification(notificationIcon, notificationTickerText, notificationTimestamp);

 

                 String notificationTitleText = “yyy”;

                 String notificationBodyText = “zzz”;

                 Intent intent = new Intent(this, XActivity.class);

                 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

                 _foregroundNotification.setLatestEventInfo(this, notificationTitleText, notificationBodyText, pendingIntent);

 

                 startForeground(_notificationId, _foregroundNotification);

         }

         void setStatusIcon(int iconId)  {

                 _foregroundNotification.icon = iconId;

                 _notificationManager.notify(_notificationId, _foregroundNotification);

          }

         class serviceRunnable implements Runnable  {

                  WorkerService _service;

                  …

                  public void run()  {

                         _service.setStatusIcon(R.drawable.green24x24);

                         …

                         _service.setStatusIcon(R.drawable.icon24x24);

                  }

         }

}

Serwisy i systemowa aktywność

  • zamykanie przy wyłączaniu urządzenia
  • automatyczny start przy bootowaniu urządzenia
  • zatrzymywanie/uruchamianie korzystania z zasobów sieciowych jeśli urządzenie włącza/wyłącza tryb samolotowy
  • niski stan baterii
  • użytkownik podłącza/odłącza urządzenie do/z zasilania sieciowego

Sygnalizowanie zazwyczaj za pomocą broadcast intents.

Implementujemy broadcast receiver. W metodzie onReceive wywołujemy startService.

Przykład: odc 5

środa, 22 października 2014

Windows 10 Build 9860 oficjalnie już jest!

Każdy, kto ma zainstalowany Windows 10 Technical Preview, może sobie w aplikacji Settings w “Preview Builds” zlecić pobranie nowego buildu. Możliwość ta pojawiła się wczoraj w nocy. Update z chmury to jedyna opcja, plik iso nie został udostępniony.

Aktualizacja przebiegła mi dziś sprawnie.  Najpierw pobierał się build z sieci… Następnie otrzymałem modernistyczny komunikat o potrzebie restartu systemu. Potem nastąpiło właściwe wgrywanie w %, przygotowanie urządzeń w %,  konfiguracja & pc settings w % i… już zacząłem działać na 9860.  Mam wszystkie dotychczasowe skróty na biurku, dane, aplikacje, pełna wygoda. Całość zajęła kilkanaście, no może około dwudziestu paru minut łącznie z downloadem. Tego posta piszę już z nowego systemu.

 

W sieci zdążyły się pojawić filmiki o 9860. Bardzo dobry jest ten poniżej:

Informacje zbiorcze o nowym buildzie

 

NOWOŚCI

Wybór częstotliwości pobierania nowych buildów

Albo otrzymujemy częściej buildy mniej stabilne (Fast),  albo rzadziej lepiej dopracowane (Slow). Oczywiście entuzjaści, insiderzy powinni wybrać sobie opcję Fast, co też i ja uczyniłem.

Linki

Centrum notyfikacyjne

Ikonkę centrum (kilka poziomych kresek na białym prostokącie) znajdziemy w taskbar nieopodal miejsca, gdzie w Windows zazwyczaj pojawiały się “stare dymki”. Po jej kliknięciu otwiera się to, co w przeciekach, czyli proste białe okienko w stylu modernistycznym, w którym można przeglądać nieotworzone notyfikacje. W moim przypadku “na dzień dobry” czekało tam powiadomienie od Windows Feedback, które po kliknięciu wyświetliło zapytanie, jak podobają mi się aplikacje Modern na pulpicie.

Animacja podczas otwierania i zamykania okien

Okno przy otwieraniu  lekko “wybucha” swoimi rozmiarami, przy zamykaniu płynnie się natomiast “kurczy”. Tego efektu nie zauważyłem w oknach z aplikacjami Modern (które uzyskały czarne paski tytułowe!). Może lekko nawiązuje do animacji okien z OS X, ale IMHO naprawdę lekko.

Linki

Nowa animacja przy przełączaniu się między wirtualnymi pulpitami

Linki

Nowości w dotyku

Linki

Battery Saver

Kojarzy się z Windows Phone. Funkcjonalność nieukończona.

Linki

Data Sense

Podobnie jak w przypadku Battery Sense, adaptacja kolejnej funkcjonalności z Windows Phone. Również nieukończona.

Linki

Customizacja lock-screen

Linki

Przeniesienie połączeń Wi-Fi z Charms Bar do aplikacji Settings

Teraz po naciśnięciu ikonki sieciowej otwiera się nowe okno z app-ką Settings na zakładce konfiguracji sieci Ethernet / Wi-Fi.

 

Dodatkowe informacje o Windows 10

 

To na razie tyle, jeśli chodzi o nowy Windows.  Na zakończenie bonusowo w tematyce Microsoft dodam, że są coraz bardziej wyraźne pogłoski o zegarku mającym współpracować z wszystkimi mobilnymi platformami.  Komunikacja jakby tylko przez Bluetooth LE …

piątek, 17 października 2014

Pojedynek z Androidem - odc. 14 jeszcze o intencjach

Intencja to Androida esencja. Zachęcony takowym hasłem zdobyłem się na zdobycie kolejnego levelu systemu z zielonym robotem. W sumie jakiegoś dużego zaskoczenia nie było, wiele zostało już powiedziane wcześniej, ale trochę nowych informacji się pojawiło - czy to porządkujących jakieś tam zagadnienie czy odsłaniające więcej szczegółów. A więc, podsumujmy:

Intencja - twór dość ogólny, pełni funkcję zarówno jakby tasków, kontraktów, funkcjonalności (czy czasami zadeklarowanych w manifeście wręcz GUID-ów) oraz zdarzeń i triggerów. Fajnie, że wymyślono coś w miarę uniwersalnego do różnych zastosowań, stosowanego zarówno przez system operacyjny, jak i każdego dostawcę oprogramowania, a każdy może definiować sobie nowe takie byty.  W Windows/Windows Phone mamy wielopostaciowość, narzucone rodzaje komunikacji, które czasami możemy dostosowywać do swoich potrzeb np. przesyłać dane w swoim formacie, obsłużyć swój typ pliku czy protokół.  Czy to oznacza, że to gorzej? Niekoniecznie, możliwości są w miarę podobne, Windows obsługuje wszystko co powinien, a w wielu przypadkach API dzięki choćby async czy zwykłym nawet zdarzeniom wydaje się łatwiejsze i bardziej intuicyjne. 

Aktywacja komponentów - pochwalić można kontrolę nad tworzeniem instancji.  API pokazujące sposób “myślenia systemu” przy wyszukiwaniu komponentów pod dane kryteria może i niezłe, ale czy aż tak potrzebne?  

PendingIntent - jak już wiemy służy do delegowania wykonania czegoś na jakimś komponencie. Jakby taka pewna forma callbacku, w którym wywoływana jest właściwa intencja. Może się sprawdzić w takiej sytuacji jak chociażby oprogramowanie własnych przycisków na notyfikacji czy widgecie, czego jeszcze nie mamy w rodzinie Windows. Natomiast domyślne zwracanie tej samej referencji dla równych intencji (ale np. różniących się danymi) jest pułapką, na którą trudno znaleźć proste wytłumaczenie. Trzeba o tym po prostu wiedzieć, a jeśli nie o to akurat nam chodzi, to używamy jawnie wybranej flagi.

Obsługa funkcji systemowych

  • zmiany w systemie - odpowiednik jakby nawet niektórych triggerów w Windows, zdarzenia śledzące szczegółowo co się dzieje z baterią (np. jej temperaturą) czy podłączenie / odłączenie słuchawek póki co wydają się być domeną Androida
  • przechwytywanie notyfikacji po czasie ich wystąpienia - rzecz dotyczy takich specyficznych przypadków jak dokowanie czy brak miejsca w storage, wydaje się przydatne, na razie w mobilnej platformie Windows czegoś takiego nie mamy (choć są dłużej trwające notyfikacje toast przydatne np. dla aplikacji typu VoIP jak choćby Skype), ale mam tu ciekawe skojarzenia z notyfikacjami Windows 10, w którym postanowiono połączyć w jedną całość notyfikacje z WinRT z notyfikacjami pokazywanymi do tej pory na task barze. W efekcie czego jak np. odłączam sobie USB pokazuje mi się “modernistyczny” prostokąt z informacją, że mogę go już bezpiecznie odłączyć. Może idąc tym tropem doczekamy się podobnych notyfikacji np. o braku miejsca na dysku ?  A wtedy zasadne byłoby odbieranie takich informacji po czasie? Równie dobrze jednak możnaby to załatwić odczytaniem jakiegoś stanu systemowego np. przy starcie aplikacji.
  • kolejkowanie w czasie akcji do wykonania, wyświetlanie strony Web, telefonowanie, robienie zdjęć, wybieranie kontaktu z listy kontaktów - wszystko to mamy także w Windows, przy czym kilka uwag. Kolejkowanie może nie jest aż tak konfigurowalne jak w Android. Chodzi mi o kontrolowanie precyzji co do odstępów czasowych. Jeśli chodzi o budzenie urządzenia, to w Windows również jest to możliwe (Connected Standby), taką ideę spotykamy w niektórych triggerach czy alarmach (w aplikacji alarmowej). Wyświetlanie strony Web jest tak samo proste (Launcher w WinRT API albo z dawniejszych czasów task w WP). Jeśli chodzi o telefonowanie, to w Windows Phone nie ma bezpośredniego dzwonienia. Robienie zdjęć z wykorzystaniem aplikacji systemowej w obu systemach jest dość proste (bardziej nowoczesną formę i prostotę ma WinRT API Windows, w WP możemy odnaleźć jeszcze taska z dawniejszych czasów, który również jest prosty w obsłudze, choć już mniej nowoczesny, bo oparty o zdarzenie, a nie o async).  Jeśli chodzi o wybór kontaktów to mamy analogię w postaci contact pickera w WinRT (możemy jeszcze w starszym API do WP odnaleźć contact choosera).  Co ciekawe Android używa słowa “contract” w klasie zwracającej dane do intencji, a sama akcja zawiera słowo “pick” !  Na tym jednak podobieństwa się kończą, bo wyciąganie danych o kontaktach pokazane przez autora nagrania jest o niebo trudniejsze niż w API Windows, gdzie mamy obiekty klas.  W Androidzie trzeba odpytywać w niskopoziomowym stylu bazodanowym tabelki content providera, bo wyciągnąć e-mail czy numer telefonu wybranego kontaktu!  Trochę słabo.

 

Mała powtórka

Intencja

  • struktura komunikatu specjalnego przeznaczenia
    • aktywacja komponentu
    • opisuje operację lub zdarzenie
  • używana w dwóch celach
    • opis operacji do wykonania
    • opis czegoś co się zdarzyło

Opis operacji bez używania jej typu

  • Action - opis akcji lub operacji do wykonania
  • Extras - dodatkowe informacje (słownik klucz-wartość)
  • Data - URI lub typ mime danych przetwarzanych przez akcję
  • Category - identyfikuje podgrupę typu komponentu, która powinna przechwycić intencję

Android routuje intencje w oparciu o typ docelowy

  • 3 typy docelowe
    • aktywności lub serwisy
      • żądanie wykonania operacji
      • wybierany jest dokładnie jeden serwis lub aktywność
      • użytkownik może być poproszony o dokonanie wyboru
    • broadcast receivery
      • zdarzenie, które zaszło
      • wszystkie pasujące broadcast receivery otrzymują intencję
    • celem każdej intencji jest tylko jeden rodzaj komponentu
  • komponent nie musi się wykonywać by otrzymać intencję
    • package manager sprawdza komponent intencji
    • system automatycznie tworzy nową instancję komponentu, jeśli jest to potrzebne

Komponenty używają filtrów intencji do ogłaszania swoich funkcjonalności i swoich “zainteresowań”

  • IntentFilter
    • wartości składowych opisują testy na podzbiór wartości składowych intencji
    • komponent może zdefiniować wiele instancji filtrów
    • normalnie jest opisywany w manifeście
    • niektóre filtry mogą być dynamicznie rejestrowane w kodzie
  • 2 zasadnicze cele
    • określenie, które komponenty powinny otrzymać daną intencję
    • opisanie możliwości komponentu lub pożądanych przez niego funkcjonalności

Testy filtrów intencji

  • akcja – jedna lub więcej akcji, które komponent może przechwycić
  • dane – jedno lub więcej Uri/rodzaj danych  - // -
  • kategoria – jedna lub więcej podgrup, do których należy komponent

PendingIntent  opakowuje intencję w akcję, która ma być z nią wykonana

  • enkapsuluje tożsamość nadawcy i dane uwierzytelniające
  • używany do delegowania akcji na inny komponent
  • pozwala wykonywać odbiorcy akcję, jakby była pochodziła od nadawcy
  • pozwala przywrócić kontrolę nad danym zadaniem
    • odpala aktywność, kiedy użytkownik zaznacza notyfikację
    • zwraca sterowanie z powrotem do aplikacji, która przekazała sterowanie reużytkowalnemu widgetowi
    • otrzymuje wyniki z serwisu

Linki

 

Aktywacja komponentów przez intencje

Komponenty są aktywowane przez wysyłanie intencji

  • aktywności aktywowane przez startActivity
  • serwisy  - // -  przez startService
  • broadcast receivery  - // -  przez sendBroadcast
  • intencje mogą być wysyłane przez aplikacje lub system

Jawne intencje  - najprostsze to stworzenia

  • zawierają informacje o komponencie
  • zawsze ukierunkowane na instancję typu
  • cała pozostała zawartość intencji ignorowana, kiedy komponent jest wybierany

Intencje oferują dużo więcej niż tylko proste tworzenie obiektów

  • dostarczają kontrolę nad zarządzaniem komponentem
  • wysyłanie intencji może tworzyć nową instancję komponentu
  • -  // - przekierowywać intencję na istniejącą instancję komponentu
  • flagi kontrolujące zachowanie podczas aktywacji komponentu

Każdy rodzaj komponentu ma inny dostęp do intencji

  • serwisy
    • Intent automatycznie jest przekazywany do metody onStartCommand
  • broadcast receivery
    • Intent automatycznie jest przekazywany do metody onReceive
  • aktywności
    • intencja, która utworzyła aktywność jest dostępna przez metodę getIntent
    • każda inna intencja wysłana do aktywności jest przekazywana do metody onNewIntent

Najprostsze wywołania

Intent intent1 = new Intent(this, YActivity.class);

startActivity(intent1);

 

Intent intent2 = new Intent(this, ZService.class);

startActivity(intent2);

Flagi

intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

Niejawne intencje

  • znoszą ograniczenia na znajomość nazwy komponentu (wywołania między różnymi aplikacjami i procesami)
  • dopasowywane do komponentów przy użyciu akcji, kategorii i/lub danych
  • znalezienie komponentu oparte jest na filtrach intencji dostarczanych przez komponenty
    • aktywacja działa tak samo jak przy intencjach jawnych
    • jedyna różnica jest w sposobie wybierania komponentu
  • nie zawierają nazwy komponentu
    • jeśli występuje, intencja nie jest porównywana z żadnymi filtrami intencji

Filtry intencji dostarczane przez komponent

  • definiowane w manifeście aplikacji
  • pasuje, jeśli wszystko w nim pasuje (akcja, kategoria, dane)
  • komponent pasuje, jeśli jeden lub więcej filtrów pasuje
  • komponent bez filtra może być aktywowany tylko przez jawną intencję

Akcja

  • nazwa w stylu pakietowym (często “action” na końcu nazwy pakietu)  np. com.xxx.action.DO_WORK
  • dla wielu standardowych akcji w kodzie używamy stałych Intent.ACTION_xxx (w manifeście używamy zawsze jawnej wartości string)
  • intencja może posiadać najwyżej jedną akcję
  • filtr intencji zawiera zero lub więcej akcji (w praktyce jedna lub więcej)

Intent intent = new Intent(“com.xxx.action.LOG_TIME”);

startService(intent);

 

<intent-filter>

          <action android:name=”com.xxx.action.LOG_TIME” />

          <action android:name=”com.xxx.action.LOG_DATE” />

</intent-filter>

Dopasowanie akcji

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli akcja intencji zawiera się w liście akcji filtra
  • filtr z pustą listą akcji nie pasuje do żadnych intencji
  • intencja bez akcji pasuje do wszystkich filtrów, które mają co najmniej jedną akcję

Extras

  • informacje mogą być silnie typowane
    • string i większość typów prymitywnych
    • tablice typów
    • typy Serializable (Java) i Parceable (Android)
  • nie biorą udziału przy dopasowywaniu komponentów (nie ma ich w filtrach)

Kategorie

  • nazwa w stylu pakietowym (często na końcu “category”)
  • dla wielu standardowych kategorii w kodzie używamy stałych Intent.CATEGORY_xxx (w manifeście używamy zawsze jawnej wartości string)
  • intencja może zawierać 0 lub więcej kategorii (kategorie najczęściej pojawiają się w generowanych przez system intencjach)
  • filtr intencji zawiera 0 lub więcej kategorii

Dopasowywanie kategorii

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli wszystkie kategorie w intencji są obecne w filtrze
  • intencja z pustą listą kategorii przechodzi test kategorii dla wszystkich filtrów (także filtry z pustą listą kategorii)
  • czasami kategorie są wymagane w filtrze, nawet jeśli ich nie ma w intencji
    • CATEGORY_DEFAULT - wymagana dla aktywności, która chce być dostępna przez startActivity przez intencję niejawną
    • CATEGORY_LAUNCHER - wymagane dla aktywności, która ma się pojawiać na ekranie Android Launcher

Dane

  • URI / mime (np. audio/mp4)
  • intencja zawiera zero lub więcej elementów określających dane
  • filtr zawiera zero lub więcej testów na dane
  • typy mime / uri mogą zawierać *
  • test URI składa się z kilku części: schemat(protokokół), host, port, ścieżka

Dopasowywanie danych

  • wielkość liter ma znaczenie
  • dopasowanie, jeśli dane intencji przechodzą przynajmniej jeden test danych z filtra
  • komplikacje
  • scenariusze dopasowania
    • intencja bez typu lub URI + filtr bez typu i URI
    • intencja z typem bez URI + filtr z pasującym typem bez URI
    • intencja z URI bez typu + filtr z pasującym URI bez typu
    • intencja z URI i typem + filtr z pasującym typem bez URI lub z pasującymi typem i URI

Konieczność

<activity …>

         <intent-filter>

                   <action android:name=”com.xxx.yyy.ABC_DEF” />

                   <category android:name=”android.intent.category.DEFAULT”/>

         </intent-filter>

</activity>

Intent intent = new Intent(”com.xxx.yyy.ABC_DEF”);

startActivity(intent);

Jeśli przygotujemy dwie aktywności z takimi filtrami jak powyżej, to przy takim samym wywołaniu Android wyświetli okno dialogowe pozwalające wybrać użytkownikowi aktywność (z opcją zapamiętania domyślnego wyboru).

Proces dopasowywania intencji do filtrów jest łatwo dostępny

  • możliwość logowania i uzyskania wyników dopasowania
  • logowanie
    • dodajemy do intencji Intent.FLAG_DEBUG_LOG_RESOLUTION
    • zapisywanie do Logcat
      • zawartość intencji
      • wszystkie pasujące komponenty
      • wybrany komponent
  • PackageManager
    • lista pasujących komponentów dla intencji
      • queryIntentActivities, queryIntentServices, queryBroadcastReceivers
    • komponent, który będzie wybrany dla intencji
      • resolveActivity, resolveService
    • wyniki zawierają opis komponentów
      • obiekty klasy ResolveInfo
      • flaga GET_RESOLVED_FILTER w zapytaniu pozwala uzyskać filtr, który został dopasowany do intencji
    • aktywności: dla naśladowania zachowania startActivity w wywołaniu należy użyć flagi MATCH_DEFAULT_ONLY

 

Delegacje i callbacki w intencjach PendingIntent

Bezpieczny sposób delegacji akcji na komponentach

Często stosowane w notyfikacjach, widgetach, serwisach, aktywnościach.

notification.setLatestEventInfo(this, “aaa”, “aaa”, pi);

PendingIntent enkapsuluje

  • Intent
  • akcję powiązaną z Intent
  • dane uwierzytelniające do komponentu
  • tworzenie pakietu komponentu

Zawartość PendingIntent  nie jest widzialna dla innych aplikacji

  • nie jest wystawiana poza tworzący ją komponent
  • przechowywana jako wpis w systemie Android

Współdzielenie PendingIntent jako referencji

Zduplikowane requesty o PendingIntent otrzymują referencję na tą samą pozycję

  • requesty muszą pochodzić z tej samej aplikacji
  • przekazane intencje muszą być równe jak w definicji Intent.filterEquals

Flagi PendingIntent

Flagi do metod getXXX pozwalają kontrolować zachowanie referencji

  • FLAG_UPDATE_CURRENT - jeśli pozycja już istnieje, zaktualizowanie jej extras z nowego Intent
  • FLAG_NO_CREATE - nowa pozycja nie jest tworzona, zwracana jest referencja do istniejącej pozycji, w przeciwnym razie null
  • FLAG_CANCEL_CURRENT - tworzona jest nowa pozycja, jeśli istnieje zgodna z nią pozycja oznaczona zostanie jako anulowana
  • FLAG_ONE_SHOT - pozycja może zostać wysłana tylko raz (możliwe jest wiele referencji do pozycji), jeśli istnieje zgodna z nią pozycja i nie zawiera tej flagi, tworzona jest nowa pozycja (obie pozycje są ważne)

W każdym przypadku ten sam PendingIntent bez extras:

Intent intent1 = new Intent(“com.xxx.ABC_DEF”);

PendingIntent pi1 = PendingIntent.getActivity(this, 0, intent1, 0);

 

Intent intent2 = new Intent(“com.xxx.ABC_DEF”);

intent2.putExtra(“KLM”, “xyz”);

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, 0);

Ta sama pozycja z extras:

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT);

Jak naprawić (pierwsza bez extras, druga z extras) ?

PendingIntent pi2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_ONE_SHOT);

Wysyłanie PendingIntent

Wykonywanie zawartości

  • wywołanie metody send wysyła Intent  (kilka przeładowań)
  • możliwość robienia ograniczonych modyfikacji z Intent
    • możliwość wysyłania wartości Intent nie ustawionych już w Intent w PendingIntent
    • Intent.fillIn - wykonanie modyfikacji
  • możliwość wysłania kodu rezultatu do celu PendingIntent
    • ograniczone scenariusze, gdzie PendingIntent jest dostarczycielem wyniku
    • często używane w połączeniu z Activity.createPendingResult

class XActivity extends Activity {

        …

        private ComponentName getWallPaperServiceComponentName()  {

                final String serviceClassName = “com.android.internal.service.wallpaper.ImageWallPaper”;

                ComponentName wallpaperService = null;

               

                ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

                List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(Integer.MAX_VALUE);

               

                for(ActivityManager.RunningServiceInfo theService: services)  {

                       if  (serviceClassName.equalsIgnoreCase(theService.service.getClassName())) {

                               wallpaperService = theService.service;

                               break;

                       }

                }

 

                return wallpaperService;

        }

        public void showWallpaperControlPanel() {

                ComponentName wallpaperComponentName = getWallPaperServiceComponentName();

 

                ActivityManager am = (ActivityManager)  getSystemService(Context.ACTIVITY_SERVICE);

                PendingIntent pi = am.getRunningServiceControlPanel(wallpaperComponentName);

 

                try {

                       pi.send(); 

                }  catch (PendingIntent.CanceledException e)  {

                }             

        }

}

 

Testy danych w filtrze intencji

Uri

  • w intencjach jako pojedyncza wartość
  • testy w filtrze intencji jako części Uri

Testy danych

  • Uri - nie wszystkie części są wymagane
  • mimeType
  • case sensitive

<data android:scheme=”http”/>

<data android:host=”www.xyz.com”/>

<data android:port=”80”/>

<data android:path=”/xxx/yyy/zzz”/>

<data android:mimeType=”audio/mp4”/>

Test Uri

  • musi zawierać przynajmniej schemat (inne części testu są ignorowane, jeśli nie ma schematu)
  • przy podaniu portu trzeba podać hosta
  • przy podaniu path, pathPrefix, pathPattern należy podać hosta

Relacje między atrybutami danych

  • wiele atrybutów może być umieszczonych na jednym elemencie data
  • każdy atrybut może być umieszczony na osobnym elemencie data
  • ten sam atrybut dla data może pojawiać się kilka razy w filtrze -warunek “OR”
    • jeden atrybut na osobnych elementach data - prosta alternatywa
    • gdy chcemy mieć alternatywne kombinacje np. protokołów z hostami robimy to w osobnych filtrach

public class XActivity extends Activity {

        …

        private List<String> getMatchingActivityNames(Intent intent)  {

                List<String> activityNames = new LinkedList<~>();

 

                PackageManager pm = getPackageManager();

                List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(intent, PackageManager.GET_INTENT_FILTERS | …);

 

                for(ResolveInfo resolveInfo : resolveInfoList)

                        activityNames.add(resolveInfo.activityInfo.name);

 

                return activityNames;

        }

}

pathPattern

  • x* - 0 lub więcej wystąpień znaku x
  • .* - 0 lub więcej dowolnego znaku

typy mime

  • każdy standardowy
  • własne wartości (często w połączeniu z własnym content providerem)
  • * - pasuje do wszystkich podtypów np. image/*
  • test zawierający tylko typ mime zakłada komunikację lokalną
    • pliki lokalne (schemat “file”)
    • dane trzymane w content providerach (schemat “content”)

 

Systemowe funkcje dostępne przez intencje

Wykrywanie zmian w stanie systemu

  • bootowanie / zamykanie systemu
    • Intent.ACTION_BOOT_COMPLETED
    • Intent.ACTION_SHUTDOWN
  • podłączenie / odłączenie słuchawek
    • Intent.ACTION_HEADSET_PLUG
    • extras: stan, typ, mic
  • zmiany w zasilaniu
    • Intent.ACTION_POWER_CONNECTED
    • Intent.ACTION_POWER_DISCONNECTED
    • Intent.ACTION_BATTERY_LOW
    • Intent.ACTION_BATTERY_OKAY

Przechwytywanie przegapionych notyfikacji

  • do odebrania większości notyfikacji systemowych potrzebujemy wcześniej zarejestrować broadcast receivery
  • niektóre notyfikacje mają trwałą ważność, nowi odbiorcy mogą je otrzymywać po fakcie
    • dokowanie urządzenia
      • Intent.ACTION_DOCK_EVENT
    • mało miejsca w storage
      • Intent.ACTION_DEVICE_STORAGE_LOW

Rejestrowanie się na systemowe zdarzenia

  • większość wspiera rejestrację w manifeście
  • wszystkie wspierają rejestrację w run-time za pomocą context.registerReceiver
  • niektóre mogą być rejestrowane tylko w run-time
    • Intent.ACTION_BATTERY_CHANGED - szczegółowe zmiany w stanie zasilania systemu (także zmiana temperatury, podłączenie, odłączenie), spore różnice pomiędzy urządzeniami (np. co ile % zmiany w naładowaniu odpalane jest zdarzenie)
    • Intent.ACTION_TIME_TICK - upływ każdej minuty czasu systemowego

AlarmManager

  • kolejkowanie akcji w przyszłości
  • używamy intencji PendingIntent
  • akcje mogą być wykonywane nawet jeśli program nie wykonuje się
  • opcjonalnie może budzić urządzenie
  • nie jest przewidziany do zdarzeń czasowych w działającej aplikacji (zbyt duży narzut)
  • trzy opcje do odpalania PendingIntent
    • AlarmManager.set - jednorazowo o podanym czasie
    • AlarmManager.setRepeating - o podanym czasie i dokładnie równych odstępach
    • AlarmManager.setInexactRepeating - mniej więcej o podanym czasie i aproksymowanych odstępach (większa oszczędność energii)
  • specyfikacja czasu
    • relatywny do czasu systemowego (SystemClock.elapsedRealtime)
    • relatywny do czasu UTC (System.currentTimeMillis)
    • opcjonalna możliwość budzenia urządzenia
    • flagi
      • ELAPSED_REALTIME
      • ELAPSED_REALTIME_WAKEUP
      • RTC
      • RTC_WAKEUP

long alarmTime = SystemClock.elapsedRealtime() + 5000;

AlarmManager alarmManager = (AlarmManager)  getSystemService(Context.ALARM_SERVICE);

alarmManager.set(AlarmManager.ELAPSED_REALTIME, alarmTime, pendingIntent);

Wiele funkcjonalności platformy Android jest zwykłymi aktywnościami

  • w większości przypadków używamy startActivity  np. przeglądarka web i telefon
  • dla otrzymywania rezultatów odbieramy startActivityForResult  np. kamera & kontakty

Wyświetlanie zawartości Web

  • data intencji: “http”/”https”
  • akcja intencji:  Intent.ACTION_VIEW

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(“http://kult.art.pl”));

startActivity(intent);

Telefon

  • dane intencji: “tel” uri
  • Intent.ACTION_DIAL - wyświetlenie ekranu do zadzwonienia
  • Intent.ACTION_CALL - od razu zadzwonienie (wymagane pozwolenie w manifeście permission.CALL_PHONE)

Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(“tel:0123456789”));

startActivity(intent);

Robienie zdjęć (już było)

Lista kontaktów

  • dane intencji:  ContactsContract.Contacts.CONTENT_URI
  • akcja intencji:  Intent.ACTION_PICK
  • po wyborze użytkownika wywoływane jest onActivityResult
    • dane intencji zawierają uri do kontakty w content providerze
    • Intent.EXTRA_SHORTCUT_NAME zawiera wyświetlaną nazwę kontaktu

Intent intent = new Intent(Intent.ACTION_PICK,  ContactsContract.Contacts.CONTENT_URI);

startActivityForResult(intent, 200);

 

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data)  {

         if (resultCode == Activity.RESULT_OK)  {

                 switch (requestCode)  {

                         case 200:

                                 String displayName = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);

                                 Uri contactUri = data.getData();

                                 String email = getContactEmail(contactUri);

                                 String phoneNumber = getContractPhoneNumber(contactUri);

                                 break;

                 }

         }

}

 

private String getContactEmail(Uri contactUri)  {

          return getContactCommonDataItem(contactUri, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,

          ContactsContract.CommonDataKinds.Email.CONTACT_ID, ContactsContract.CommonDataKinds.Email.DATA);

}

 

private String getContactPhoneNumber(Uri contactUri)  {

          return getContactCommonDataItem(contactUri, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,

          ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DATA);

}

 

private String getContactCommonDataItem(Uri contactUri,  Uri dataStoreUri,  String idColumnName)  {

          String contactId = contactUri.getLastPathSegment();

          String resultValue = null;

 

          Cursor cursor = managedQuery(dataStoreUri,  null, idColumnName + “=?”, new String[] { contactId }, null);

          if (cursor.moveToFirst()) {

                  …

          }

}