wtorek, 30 września 2014

Windows … 10, hurra!

Tak naprawdę dopiero jutro będzie wielki dzień związany z instalowaniem z preview.windows.com  i byciem w WIP (Windows Insider Program) , dziś tylko “sucha forpoczta” w oparciu o oficjalne już informacje, z eventu targetowanego głównie do korporacji. Przeskok numeracyjny do wersji 10 wydaje mi się sztuczką marketingową mającą na celu większe zdystansowanie się od marki Windows 8.x i podkreślenie przełomowości produktu.  Nie ma Windows 9, nie ma Windows Phone, jest za to Windows 10 obsługujący wszystko od małych smartfonów po duże, panoramiczne ekrany. Dziś był publicznie pokazywany build 9841. Ciekaw jestem unifikacji platformy do app-ek mających być także w jednym Store. Celowałbym tu w stosunkowo niewielką ewolucję XAML/HTML5 na WinRT, tym bardziej że w ekosystemie Windows/Windows Phone 8.1 mamy już ponad 90 kilka procent wspólnego WinRT API, współdzielone kontrolki, binarną zgodność XAML i komponentów WinRT, więc stosunkowo nietrudno byłoby to już przerobić na 100%.  Dodatkowo mamy info że dotychczasowe app-ki mają być obsługiwane. Może niedługo dowiemy się odrobinę więcej, bo najwięcej się raczej dowiemy na BUILD w kwietniu 2015 (wcześniej w 2015 będzie event dla konsumentów, gdzie poznamy więcej nowości samego Windows 10 i nowe urządzenia z nim związane, a później - niż BUILD - wydawanie finalnego systemu). Z dzisiejszych informacji warto wspomnieć o Snap Assistant (dwa ostatnie screenshoty, możemy dzielić ekran na części, asystent pomaga nam wypełnić danym oknem pozostałą przestrzeń) i  koncepcji “Continuum” (dwa w jednym,  płynna obsługa hybrydowych urządzeń mogących być laptopem i dotykowym tabletem). W “Continuum” po odłączeniu klawiatury zostaniemy zapytani o zmianę trybu. Po przejściu w tryb bardziej mobilny aplikacja Modern zacznie wypełniać cały pulpit, a na systemowym TaskBar pojawi się przycisk Back.  Cofając się otwieramy ekran startowy, który wygląda jak rozciągnięcie menu Start na całym ekranie… Po ponownym podłączeniu klawiatury i przejściu w tryb desktopowy ekran startowy zamienia się w menu Start. Widać odejście od forsowania jednego dotykowego UI za wszelką cenę, niech użytkownicy urządzeń dotykowych mają zoptymalizowane dla siebie rozwiązania, a użytkownicy desktopu bardziej tradycyjne, pasujące do klawiatury i myszki.  Udoskonaleń doczekała się  poczciwa konsola cmd (m.in obsługa CRTL+V czy lepsza obsługa tekstów wieloliniowych). Istotna jest też customizacja i inne udoskonalenia w store dla korporacji. Pomimo, że znaczna część przekazywanych dzisiaj informacji nie jest zaskoczeniem, a jedynie powtórzeniem tego, co już było nieoficjalnie wiadome w Internecie  od kilku tygodni czy nawet miesięcy, udało się stworzyć interesujący event, ponieważ zostały pokazane szczegóły, które wcześniej nie wyciekły.

onestorewindows_r1_c1

screen_shot_2014-09-30_at_2_23_12_pm

snap1

snap2

 

Linki

sobota, 27 września 2014

Pojedynek z Androidem - odc. 10 layout once again

Witam. Dziś pozwolę sobie powrócić do rozważań na temat layoutu na podstawie kolejnych filmów. Obecność TableLayout przy braku GridView wskazuje, że nie najnowszych.

W pierwszej części mamy podstawowe, znane od dawna layouty. Czy mi się podobają?  Jakie mam nowe uwagi czy porównania?

  • LinearLayout - jak już może kiedyś wspominałem dla mnie coś takiego ala StackPanel z XAML, ale z możliwością rozciągania dzieci do wypełnienia dostępnej przestrzeni, czyli jakby i część możliwości Grid-a (może coś ala flexbox z CSS). Zejdźmy na szczegóły. Layout_gravity to dla mnie jakby Horizontal/VerticalAlignment, gravity - Horizontal/VerticalContentAlignment z ContentControl (przy czym możemy mieć w Android wiele dzieci), a weight – odpowiednik podziału proporcjonalnego wyrażanego * w Grid.  WeightSum to może dobre wyjście, by nie definiować dodatkowych pustych bytów (choć trzeba o nim wiedzieć).
  • RelativeLayout - po przyjrzeniu się kilka razy może ujść, chociaż wydaje się to przegadane (w XAML z reguły najczęściej wystarczy operować sprawnie na StackPanel i Grid, w Android mamy więcej jak w CSS wyborów i możemy tą samą rzecz zrobić na wiele sposobów). Na pewno warto stosować, bo jest bardziej wydajne od LinearLayout, zwłaszcza zagnieżdżonych. Interesujące jest też wyrównanie do dolnej linii tekstu (nie ma czegoś takiego gotowego w XAML). Słabizną jest IMHO przeskakiwanie elementów przy ukrywaniu ich sąsiadów. Trzeba stosować dodatkowy parametr layout_alignWithParentIfMissing, aby temu zapobiec.
  • FrameLayout - jak się tak przyjrzeć, to ma pewne cechy Grid-a (elementy wstawione jako dalsze przysłaniają te wstawione wcześniej, mamy rozciąganie do wypełnienia przestrzeni, obsługuje wyrównania deklarowane przez dzieci, choć nie mamy wierszy i kolumn). Spójność i uniwersalność kontenera burzą moim zdaniem jego właściwości takie jak foreground czy foregroundGravity (może, gdy mamy mniej elementów w środku, to jest lepiej, ale kontener bardzo wyraźnie wiąże się z dzieckiem na najwyższej warstwie, a co z innymi dziećmi? )
  • TableLayout - hm… właściwie po co? Jakby wzorowanie się na CSS, gdzie też table średnio mi się podoba i w zasadzie to tylko do data gridów pasuje najlepiej. Ale rozumiem, przez ileś wersji nie było w Android odpowiednika Grid-a (obecnie mamy GridView), więc trzeba było sobie radzić przy użyciu TableLayout. Jak coś wrzucimy poza wierszami, to się rozciągnie na całą szerokość. Może i super, ale burzy spójność komponentu (hm… może znowu chodzi o wydajność). Co do rozciągania i ściskania wskazanych kolumn lub wszystkich mam mieszane uczucia, z jednej strony niby mogę precyzyjnie tym sterować, z drugiej strony to mocniejszy autorytaryzm rodzica i dodatkowe parametry, a podobne rzeczy mogłyby określać same kolumny (np. w DataGrid można określić minimalną, maksymalną szerokość lub podział proporcjonalny *)

Druga część to pokaz narzędzi do podglądania tworzonych elementów w drzewie wizualnym i wskazówki co do wydajności. Podgląd zbudowanych elementów w runtime kojarzy mi się z HTML pisanym komponentowo, gdzie też czasami z jakiegoś tagu może powstać więcej tagów (w XAML dla uczciwości też każda kontrolka user czy template jest wizualizowana za pomocą fragmentu XAML widzianego wyżej jako jeden element, ale nie ma w standardzie narzędzi do podglądu zbudowanego w ostatecznym rezultacie drzewa). Jak się okazuje podobnie może czasami się dziać w Androidzie  i to bez pisania własnych kontrolek!  Być może na małą skalę, ale jednak. W ograniczeniu liczby rodziców pomocny może być tag merge (tworzy jednego rodzica automatycznie). Include to jakaś forma reużytkowania powtarzalnych fragmentów interfejsu użytkownika (odpowiednik jakby wstrzyknięcia szablonu w Angularze, czy bardziej ogólnie jakby szablonu lub kontrolki XAML/HTML5). Może prosty prekursor bardziej rozbudowanych fragmentów?  Podobać się może ViewStub przewidziany do lazy loadingu, czyli to my decydujemy kiedy chcemy doładować jakiś fragment drzewa.  W XAML nie potrzebujemy jednak do tego dodatkowych tworów, ot doładowujemy kawałek drzewa, ale imperatywnie.  W Android mimo deklaratywności ViewStub i tak ładujemy go imperatywnie w kodzie, więc nie widzę tu dużego pożytku poza tym, że trzymamy cały XML w jednym miejscu.  W XAML albo robimy takie rzeczy sami w całości ręcznie albo dostajemy gotowe w niektórych kontrolkach np. Hub (Windows/Windows Phone 8.1). Androida można pochwalić za dobre narzędzia analityczne, wskazujące jak można zoptymalizować ułożony przez siebie layout w XML oraz narzędzia pokazujące czas mierzenia, układania i rysowania poszczególnych elementów drzewa. W mobilnych aplikacjach XAML/HTML5 również mamy narzędzia związane z mierzeniem wydajności, chociaż wygląda to trochę inaczej. Przykładowo mierząc responsywność aplikacji XAML możemy się dowiedzieć, ile czasu było potrzebne na sparsowanie, ułożenie danego elementu, ile zajął kod aplikacji czy inne operacje XAML. Wracając do komponentów w Android ImageView ma pewne cechy Image z XAML, gdzie też można zadecydować o sposobie rozciągania obrazka wewnątrz przestrzeni czy ustawić choćby tło. Jeśli chodzi o TextView to już taki atrybut jak choćby drawableLeft to IMHO zaburzenie czystości formy pola tekstowego, ale może to być wygodne i bardziej wydajne niż budowanie samemu takiego szablonu.

Poniżej jak zawsze szczegóły.

 

Podstawowe layouty

  • LinearLayout
  • RelativeLayout
  • FrameLayout
  • TableLayout

Podstawowe atrybuty

  • size
    • match-parent - cała dostępna przestrzeń
    • wrap-content – tyle, ile zajmuje element (auto)
  • margin vs padding
  • gravity
    • layout_gravity:  na zewnątrz np. center_horizontal,  center_horizontal | bottom (centralnie na dole)
    • gravity:  wewnątrz elementu, może oddziaływać na więcej niż jeden element w środku

Przeliczanie z dp na px

px = dp * density

density = bucket(dpi / 160);

LinearLayout

  • layout_gravity - nie działa w kierunku orientacji danego LinearLayout
  • layout_weight + rozmiar = 0dp - podział proporcjonalny (*), jeśli jeden element będzie na wrap-content to reszta podzieli pozostałą przestrzeń proporcjonalnie

Wycentrowana 1/3

<LinearLayout  android:layout_width=”match_parent”

      android:layout_height=”match_parent”

      android: orientation=”horizontal” >

       <View android:layout_width=”0dp”

               android:layout_height=”1dp”

               android:layout_weight=”1” />

       <TextView android:layout_width=”0dp”

               android:layout_height=”wrap_content”

               android:layout_weight=”1”

               android:text=”@string/text1”

               android:background=”#cf9” />

       <View android:layout_width=”0dp”

               android:layout_height=”1dp”

               android:layout_weight=”1”  />

</LinearLayout>

Lepiej z sumą wag

<LinearLayout …

         android:gravity=”center_horizontal”

         android:weightSum=”3” >

         <TextView …

                 android:layout_weight=”1” />

</LinearLayout>

RelativeLayout

Względne

  • pozycja  np. layout_below,  layout_toLeftOf
  • wyrównanie  np. layout_alignParentRight

do

  • rodzica
    • layout_alignParentTop
    • layout_alignParentBottom
    • layout_alignParentLeft
    • layout_alignParentRight
    • layout_centerHorizontal
    • layout_centerVertical
    • layout_centerInParent
  • sąsiada
    • layout_above
    • layout_below
    • layout_toLeftOf
    • layout_toRightOf

Domyślnie lewy górny róg

Czasami trzeba więcej niż jeden np. layout_below + layout_toLeftOf

Czasami potrzebne dodatkowe wyrównanie np. w elemencie nad danym elementem, tak by zaczynały się oba równo np. layout_above + layout_alignLeft

Dodatkowe parametry do wyrównania:

  • layout_alignLeft
  • layout_alignTop
  • layout_alignRight
  • layout_alignBottom
  • layout_alignBaseline (wyrównanie do dolnej linii tekstu we wskazanym elemencie)

android:visibility

  • ”invisible”  - niewidoczny, ale zajmuje przestrzeń (jak hidden)
  • “gone”  - niewidoczny, nie zajmuje przestrzeni (jak collapsed)

Jak element, do którego się odnosimy w innym elemencie jest “gone” to layout tego drugiego elementu ignoruje położenie względem niego

Dodatkowy parametr, by zapobiec przeskakiwaniu elementów przy ukrywaniu innych:  layout_alignWithParentIfMissing = “true”

FrameLayout

<FrameLayout

        android:layout_width=”match_parent”

        android:layout_height=”match_parent”>

        <View

                android:layout_width=”240dp”

                android:layout_height=”180dp”

                android:layout_gravity=”center”

                android:background=”#9cf” />

         <View

                android:layout_width=”180dp”

                android:layout_height=”240dp”

                -- android:layout_gravity=”right|center_vertical”

                android:layout_margin=”16dp”

                android:background=”#c9f” />

  </FrameLayout>

Drugi element przysłania pierwszy

<FrameLayout

         android:layout_width=”wrap_content”

         android:layout_height=”wrap_content”  >

        <ImageView

                android:layout_width=”wrap_content”

                android:layout_height=”wrap_content”

                android:src=”@drawable/image1” />

         <ImageView

                android:layout_width=”wrap_content”

                android:layout_height=”wrap_content”

                android:src=”@drawable/image2” />

</FrameLayout>

<FrameLayout

         android:layout_width=”wrap_content”

         android:layout_height=”wrap_content”

         android:foreground=”@drawable/image2”

         android:foregroundGravity=”top|left” >

         <ImageView

                android:layout_width=”wrap_content”

                android:layout_height=”wrap_content”

                android:src=”@drawable/image1” />

</FrameLayout>

foreground:  obrazek jest rozciągany, by wypełnić całą otaczającą go przestrzeń

TableLayout

Domyślnie TableRow

  • layout_width=”match_parent”
  • layout_height=”wrap_content”

Domyślnie dzieci TableRow

  • layout_width=” wrap_content”
  • layout_height=”wrap_content”

<TableLayout

         android:layout_width=”wrap_content”

         android:layout_height=”wrap_content”>

         <TableRow>

                    <TextView …

                             android:layout_column=”1”

                             android:layout_span=”2” />

                   <TextView …/>

                   <TextView …/>

         </TableRow>

        <TableRow>

                    <TextView …/>

                   <TextView …/>

                   <TextView …/>

        </TableRow>

</TableLayout>

Pojedyncza komórka w wierszu – tam, gdzie powinna być, reszta wiersza pusta

Liczenie wierszy i kolumn od zera

Jak wrzucimy coś bezpośrednio do TableLayout, to się rozciągnie na całą szerokość

<TableLayout

         android:layout_width=”wrap_content”

         android:layout_height=”wrap_content”>

        <TextView … />

         <TableRow>

                    <TextView …

                             android:layout_column=”1”

                             android:layout_span=”2” />

                   <TextView …/>

                   <TextView …/>

         </TableRow>

        <TableRow>

                    <TextView …/>

                   <TextView …/>

                   <TextView …/>

        </TableRow>

</TableLayout>

 

<TableLayout

          android: shrinkColumns=”2”  …>

          …

</TableLayout>

Wyrównanie elementu do wysokości wiersza  - android:layout=”match_parent”

TableLayout z szerokością match_parent nie rozciąga się

Rozciąganie kolumn

<TableLayout

          android: stretchColumns=”0,1,2”  …>  //rozciągaj kolumnę 2,  * – wszystkie

          …

</TableLayout>

Ukrywanie kolumn

<TableLayout

          android: collapseColumns=”3”  …>  //ukryj kolumnę 3,  pozostaje zarezerwowana przestrzeń przy stretchColumns=”*”

          …

</TableLayout>

Wybór layoutu

1 wymiar

  • LinearLayout  (oś x lub y)
  • FrameLayout (oś z)

2 wymiary

  • RelativeLayout
  • TableLayout

Etapy:

  1. Measure - każdy element: width, height
  2. Layout - każdy element: left, top (right, bottom wynikają z rozmiarów elementu)

Ilość przebiegów przy pomiarze:

  • LinearLayout - 1  (2 z weight)
  • RelativeLayout  - 2
  • FrameLayout  - 1
  • TableLayout - 2 (3 przy ściskaniu i rozciąganiu kolumn)

Hierarchy Viewer

  • narzędzie do pokazywania struktury layoutu z właściwościami
  • możliwość sprawdzenia każdej aplikacji na emulatorze czy urządzeniu
  • pobieramy ViewServer.java z https://github.com/romainguy/ViewServer
  • dodajemy pozwolenie INTERNET
  • włączamy ViewServer w naszej aktywności (przykład: ViewServerActivity.java)

Android Studio

  • Android Device Monitor –> Hierarchy View Perspective
    • podgląd i nawigacja po hierarchii
    • pokazanie dla każdego elementu, jak kosztowne są:
      • pomiar
      • layout
      • rysowanie

Merge  - wstawienie bezpośrednie wszystkich elementów do parenta

      Jeden FrameLayout zamiast dwóch.

<merge …>

       <View …/>

       <View …/>

</merge>

       Znalezienie automatycznie tworzonego parenta

View parent = findViewById(android.R.id.content);

Include

Android Studio

  • res –> New Resource File:  typ - Layout, root – ImageView

       heart.xml:    <ImageView …/>     

<LinearLayout …>

        <include layout=”@layout/heart” />

        <include layout=”@layout/heart”

                android:layout_width=”wrap_content”

                android:layout_height=”wrap_content”

                android:layout_marginTop=”16dp”/>

       <include layout=”@layout/heart” />

</LinearLayout>

ViewStub

  • podobny do include
  • element nie jest ładowany do drzewa, dopóki nie jest potrzebny (domyślnie elementy są tworzone w drzewie nawet przy visibility=”gone”)

<RelativeLayout …>

         <TextView …/>

         <Button …/>

         <ViewStub

                  android:id=”@+id/desc”

                  android:layout=”@layout/checkerboard”

                  android:layout_width=”wrap_content”

                  android:layout_height=”wrap_content”

                  android:layout_below=”@id/title”

                  android:visibility=”gone”  />

</RelativeLayout>

       Załadowanie/pokazanie lub ukrycie:

if (mDescView == null)  {

         mDescView = ((ViewStub) findViewById(R.id.desc)).inflate();

}

boolean visible = (mDescView.getVisibility() == View.VISIBLE);

mDescView.setVisibility(visible ? View.GONE : View.VISIBLE);

Optymalizacja

Android Studio

  • Analyse –> Inspect Code…  (Android Lint)

Obrazek z tekstem - wersja odchudzona

<TextView

          android:layout_width=”wrap_content”

          android:layout_height=”wrap_content”

          android:drawableLeft=”@drawable/heart”

          android:textSize=”20sp”

          android:text=”@string/hello” />

Pole edytowalne wypełniające całą przestrzeń, a za nim przycisk - wydajniej w RelativeLayout niż LinearLayout

Flaga Wietnamu – czerwony prostokąt z żółtą gwiazdą w środku

<ImageView

           android:layout_width=”240dp”

           android:layout_height=”160dp”

           android:layout_gravity=”center”

           android:background=”@color/red”

           android:src=”@drawable/yellow_star”

           android:scaleType=”center” />   //obrazek nie jest rozciągany, tylko wycentrowany

wtorek, 23 września 2014

Pojedynek z Androidem - odc.9 notyfikacje (jeszcze o nich), widgety, sny

Przyszła pora na interakcję użytkownika z aplikacją, kiedy nie jest uruchomiona. Hm… rzecz dość typowa choćby w kafelkowym Windows. Przyznam się. Po krótkiej przerwie miałem ochotę trochę pohejtować na Adroida.  Niemniej jednak po obejrzeniu filmów z dzisiejszej działki dochodzę - także i w tym odcinku - do wniosku, że widzę pewne plusy i minusy. Spróbuję jak poprzednio omawiane pojęcia tłumaczyć równoważnikami z ekosystemu Windows, co pozwoli mi czynić porównania. A więc do rzeczy:

Notyfikacje - ostatnio byłem świadomy jedynie podstaw z podstaw, dziś jest lepiej, aczkolwiek cały czas nie rzucił mi się przed oczy odpowiednik notyfikacji push, ciągle jesteśmy w obszarze notyfikacji lokalnych.  Kolejne wersje Androida coś tam dokładały do tematu. Opcja zapobiegania przed zamknięciem przez użytkownika, IMHO furtka dla natrętów (w Windows póki co to takiej opcji nie ma, choć pewne notyfikacje np. takie ala Skype mogą trwać dłużej niż inne). Z kolei możliwość pozycjonowania przekładające się na miejsce na liście centrum notyfikacji wydaje się niezłym pomysłem. Numer?  Jakieś echo licznika z kafelka? Podobieństwo tylko częściowe, w notyfikacjach Android numer oznacza raczej większą niż 1 liczbę wiadomości, które wszystkie możemy wrzucić do jednej notyfikacji. Wiadomość powitalna niczym górny toast notification - nie jest to już pewien przerost formy nad treścią, by do właściwej notyfikacji z chwilą jej pierwszego pojawienia wyświetlać drugi komunikat?  Więcej informacji po rozwinięciu?  Może OK, ale w sumie niewiele wnosi większy obrazek czy dłuższy tekst… A notyfikacja powinna być krótka, treściwa IMHO.  Dorabianie na sztywno stosu Back przy wejściu przez notyfikację?  Tu się zastanawiam, czy to fajne, że zawsze ze szczegółów Back wrócimy (czy też wejdziemy) na listę… Ale jest to opcja, więc to nasz wybór (w Windows też można modyfikować stos back). Notyfikacje wywoływane przez serwis,  przez taska w tle to często spotykana rzecz w rodzinie Windows. W Androidzie mamy podobną możliwość, jedynie sztywna metoda startforeground przyjmująca notyfikację podoba mi się już mniej, w Windows nie ma żadnych różnic w wywołaniu notyfikacji w aplikacji i tasku. Interaktywność i akcje na notyfikacjach?  No tu czuję, że taka funkcjonalność mogłaby się przydać w Windows… Choć pewnym bardziej odległym skojarzeniem jest w WP 8.1 kontrolka ContentDialog służąca do robienia okien dialogowych z własną dowolną zawartością XAML.  W Android podobać się może kompatybilność wstecz, czyli korzystanie z nowych funkcjonalności na starszych platformach dzięki dołączanej bibliotece.  Można używać nowszych np. parametrów konfigurujących notyfikację, a automatycznie na niektórych starszych  platformach w najgorszym przypadku otrzymamy zachowanie standardowe, jeśli tego nie obsługują.  W Windows target app-ki determinuje zawsze dostępne API,  nie ma oglądanie się za czy przed siebie (nielicznym wyjątkiem była zdaje się kiedyś możliwość korzystania przez refleksję w runtime z dll-ki nowszego WP w starszej app-ce).  Własny layout w notyfikacji?  Hm, mogłoby się to przydać w Windows.  Kojarzy mi się tutaj komunikat o wyjęciu USB z Windows 9, który jakoś wygląda mniej typowo niż reszta, bo ma dwie ikony…  Kto wie, może się kiedyś doczekamy.

Widgety - słowo widget brzmi dla mnie jakoś starodawnie, kojarzy mi się z Windows Vista, którego był jedną ze sztandarowych nowości i przestał być eksponowany w Windows 7. Świat praktycznie o nich zapomniał w Windows.  Niemniej jednak mniej interaktywne i bardziej jednolite kafelki stały się głośne. A MS Research pracuje nad interaktywnymi kafelkami, czyli jakby nie patrząc nad bardziej nowoczesną formą widgetów. Ale wróćmy do Androida. Podobnie jak w Windows z kafelkami pewne rzeczy możemy zdefiniować w manifeście, który dodatkowo wskazuje tutaj na osobny plik XML oraz klasę wywodzącą się z AppWidgetProvider. Obrazek do zasobnika widgetów, początkowy layout trochę jakby przerost formy nad treścią wynikający z OS, w Windows wprost z listy aplikacji możemy przypiąć app-kę w postaci kafla na ekran startowy lub dodatkowe kafle z poziomu samej już aplikacji. W Android widgety nie muszą być tylko na ekranie startowym, mogą być w każdym AppWidgetHost.  W rodzinie Windows “kafelki” możemy sobie sami narysować w kontrolce Hub (pamiętam, że kiedyś w Windows Phone SDK pojawiła się kontrolka imitująca kafelki niemal jak z ekranu startowego). Deklaratywne definiowanie kafelków oraz ich modyfikacja z poziomu kodu jest prostsza w Windows,  ale i same kafelki są prostsze. Mają narzucony duży wybór szablonów, jedynie w WP 8.1 pojawiała się możliwość wrzucania dowolnej zawartości XAML do kafelka (dzięki XamlRenderingBackgroundTask). W kafelkach Windows z reguły mamy standardowe animacje, podmiany zawartości, nie mamy interaktywności z własnymi przyciskami powiązanymi z akcjami.  Aczkolwiek prostota i spójność wizualna systemu na ekranie startowym mogą się podobać, choć są też i pewnym ograniczeniem.  W Windows 9 w menu aplikacji Modern pojawiło się miejsce na własne komendy aplikacji, ale to bardzo odległe skojarzenie.  W Android mamy menadżer wywodzący z AppWidgetProvider zarządzający wszystkimi instancjami widgetu, dostajemy informacje co się z nimi dzieje, w Windows nie ma takiego centralnego sterowania.  Aktualizacja widgetów w Android z poziomu aplikacji jest dość przekombinowana. W Windows jest bardzo intuicyjna i prosta, aczkolwiek nie mówimy tam o podmianie fragmentów dowolnej zawartości, tylko o podmianie kilku pól określonych w API  lub o wyrenderowaniu od nowa całej zawartości.  Możliwości prostego i okresowego odświeżania jest jednak kilka, możemy nawet tylko deklaracją w manifeście wymusić okresowe odświeżanie obrazka. W Android musi być wszystko bardziej skomplikowane, choćby wariant ze sterowaniem alarmem bedący jakimś kolejnym odpowiednikiem okresowego odświeżania (w Windows notyfikacje powiązane z alarmem nie są na kaflach, są to interaktywne specjalizowane odpowiedniki toast czy też raczej message boxów posiadające przyciski z akcjami).  Android ma opcję okresowego odświeżania określoną w manifeście, natomiast odświeżanie sterowane alarmem może być bardzo precyzyjne czasowo (nawet możemy różnicować okresy, czy decydować czy to nam może obudzić urządzenie, btw budzić urządzenie mogą niektóre triggery do tasków w Windows). Powstaje pytanie czy takie precyzyjne aktualizowanie widgetów jest bardzo potrzebne?  Wyskakujące okna alarmu np. z budzącym mnie co dzień dzwonkiem z telefonu bardziej mnie przekonują.  Konfiguracja widgetu?  Dla mnie przerost formy nad treścią, nie licząc już przekombinowanej i złożonej implementacji. Podobać się może podmienialność zawartości, ale upuszczania, otwierania okna z konfiguracją po upuszczeniu, zmiany wyglądu widgetu po zamknięciu go czy nie są zbyt przekombinowane?  W Windows konfiguracja aplikacji jest jedna, na jej podstawie możemy z poziomu kodu aplikacji  wpływać w jakimś stopniu na kafelki, przy czym główny kafelek może jest mniej modyfikowalny, ale wiadomo że reprezentuje wejście na stronę startową aplikacji, a kafelki secondary są odsyłaczami do innych adresów w aplikacji.  Rozumiem, że użytkownik Androida sam upuszcza sobie kilka razy ten sam widget i konfiguruje, co do czego ma mu służyć i jak wyglądać…

Dreams – “sny”, oszczędzacze ekranu,  tak porównując z Windows kojarzy mi się to z aplikacją “Live Lock Screen BETA” w WP 8.1, która pozwala dowolnie modyfikować zawartość lock-screen oraz już bardziej odlegle z modyfikacją tapety lock-screen oraz wyświetlaniem na nim notyfikacji pochodzących od niektórych aplikacji.  Przy czym w Androidzie dreams są wywoływane podczas ładowania lub dokowania urządzenia, a w Windows mamy po prostu modyfikację lock-screen w każdych warunkach.  Nie mamy też przynajmniej na razie otwartego uniwersalnego API do definiowania dowolnej zawartości, ale niedługo to się może zmienić choćby za sprawą osłony API używanego przez  “Live Lock Screen”.

 

Notyfikacje

Ilość informacji w wiadomości oraz poziom interaktywności zależy od wersji systemu

Preferujmy użycie NotificationCompat.Builder

Android Studio

  • Menu kontekstowe na folderze z aplikacją w projekcie -> Open Module Settings –> support-v4-18.0.0

4 podstawowe właściwości wspierane przez wszystkie wersje Android

  • 3 podstawowe związane z wyświetlaniem
    • mała ikona
    • tytuł
    • tekst
  • Akcja obsługująca zdarzenie click w notyfikacji
    • opcjonalne w Android 3.0 (API Level 11) i nowszym
    • wymagana we wszystkich wcześniejszych wersjach

http://developer.android.com/design/style/iconography.html

Android 3.0 (API 11) daje większą kontrolę nad notyfikacjami

  • możliwość wpływania na zachowanie notyfikacji na liście
  • możliwość umieszczania większych ilości informacji na notyfikacji
  • zachowania NotificationCompat.Builder są ignorowane, jeśli nie są wspierane

http://developer.android.com/about/dashboards/index.html

Kontrolowanie sposobu zamknięcia notyfikacji i pozycjonowania

  • opcja automatycznego czyszczenia notyfikacji po zaznaczeniu przez użytkownika
    • Builder.setAutoCancel
    • w większości przypadków powinna być ustawiona na true
  • zapobiegnięcie pozbycia się notyfikacji przez użytkownika
    • Builder.setOngoing
    • ustawiać na true, tylko jeśli jest to absolutnie konieczne
  • kontrola pozycjonowania (które wyżej)
    • Builder.setPriority
    • używajmy z rozwagą ze względu na różnice w specyfice zachowania

Więcej informacji

  • Builder.setLargeIcon - duża ikona różna od tej ikony ze statusu
  • Builder.setNumber - liczba na notyfikacji (zwielokrotnione notyfikacje)
  • Builder.setTicker - specjalna wiadomość wyświetlana podczas pierwszego pojawienia się notyfikacji, u góry ekranu przysłaniając na jakiś czas pasek systemowy

builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),  R.drawable.notification_xxx))

 

ArrayList<String> textValues = new ArrayList<String>();

textValues.add(detailText1);

textValues.add(detailsText2);

 

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

intent.setAction(“NotifyMulti”);

intent.putExtra(XListActivity.TITLE_EXTRA, title);

intent.putExtra(XListActivity.TEXT_VALUES_EXTRA, textValues);

builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),  R.drawable.notification_yyy))

.setNumber(count);

.setTicker(“aaa”); 

Rozszerzalne notyfikacje

Android 4.1 (API 16) wprowadził rozszerzalne notyfikacje

  • mają standardowy widok z możliwością rozwinięcia by pokazać więcej informacji
  • tworzone za pomocą klas styli za pomocą klasy NotificationCompat
    • Builder.setStyle
    • na wcześniejszych platformach zostanie użyty standardowy widok
  • BigTextStyle - pole tekstowe, które może przenosić tekst do następnych linii
  • BigPictureStyle - obrazek rozciągający się do wysokości 240dp
  • InboxStyle -  wiele pól tekstowych (jedno w każdej linii)

NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();

bigTextStyle.setBigContentTitle(bigTitle)

        .setSummaryText(bigSummary)

        .bigText(notificationText);

builder.setStyle(bigTextStyle);

 

NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();

bigPictureStyle.setBigContentTitle(bigTitle)

         .setSummaryText(bigSummary)

         .bigPicture(BitmapFactory.decodeResource(getResources(),  R.drawable.xyz));

Interaktywne notyfikacje

Otwarcie aktywności z poziomu notyfikacji często burzy nawigację

  • domyślnie, notyfikacje nie tworzą przycisku back z historią (następuje powrót do ekranu startowego)

Tworzenie spójności w nawigacji

Potrzebujemy dołączać informacje o stosie back do notyfikacji

  • aplikacja z docelową aktywnością potrzebuje przechowywać hierarchię aktywności w manifeście
  • musimy przechowywać hierarchię aktywności w instancji TaskStackBuilder
  • z android.app.TaskStackBuilder tworzymy PendingIntent z odpowiednim stosem back
  • tylko na urządzeniach z Android 4.1 lub nowszym
  • android.support.v4.app.TaskStackBuilder - wsparcie dla starszych urządzeń

Jeśli otworzymy notyfikację prowadzącą do maila, to dzięki deklaracji stosu przyciskiem Back możemy “powrócić” (czytaj wejść) na listę maili

Context context = getActivity();

 

Intent intent = new Intent(“xxx”);

intent.putExtra(abc_index, 0);

 

TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);

taskStackBuilder.addNextIntentWithParentStack(intent);

PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

 

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

builder.setSmallIcon(R.drawable.efg)

.setAutoCancel(true)

.setContentTitle(“aaa”)

.setContentText(“bbb”)

.setContentIntent(pendingIntent);

Notification notification = builder.build();

 

<activity

          android:name=”.XXX”

          android:label=”Abc”

          android:parentActivityName=”YYY”>

          <meta-data   //dla wcześniejszych od 4.1

                  android:name=”android.support.PARENT_ACTIVITY”

                  android:value=”YYY” />

           <intent-filter>

                   …

           </intent-filter>

</activity>

Android  Studio

  • New  -> Activity –> Blank  Activity –> uzupełniamy Hierarchical Parent

Notyfikacje i serwisy

Notyfikacje mogą dostarczyć UI dla serwisów

  • same jako lekkie UI
  • mogą odpalać aktywności sterujące serwisem
  • serwisy polegające na kontroli notyfikacji często są serwisami foreground

Serwisy foreground

Foreground serwis to taki, który bezpośrednio wpływa na user experience

  • wykonywanie jako foreground service podnosi priorytet serwisu (priorytet czasu życia bardzo zbliżony do aktywności)
  • wyświetla notyfikacje podczas wykonywania (brak specjalnych wymagań dla notyfikacji)
  • serwis staje się foreground serwisem przez wywołanie startForeground (przekazujemy id notyfikacji i instancje Notification)
  • serwis pozostaje w foreground dopóki nie zostanie zamknięty lub nie wywołamy stopForeground

start w serwisie:

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

PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0, activityIntent);

 

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

builder.setSmallIcon(R.drawable.xxx)

       .setContentTitle(“YYY”)

       .setContentText(“…”)

       .setContentIntent(activityPendingIntent);

 

Notification notification = builder.build();

startForeground(SERVICE_NOTIFY_ID, notification);

stop w serwisie

stopForeground(true);

Akcje notyfikacji

Akcje dostarczają przyciski bezpośrednio na notyfikacjach

  • wspierane od Androida 3.0 (API 11)
  • do 3 akcji na notyfikacji  (renderowane jako ikona + tekst)
  • każdy przycisk odpala osobny PendingIntent
  • unikajmy stosowania w aplikacjach z docelowym API niższym niż 11 (na platformach wspierających akcje przyciski są renderowane na biało)

public class XService extends Service  {

        …

        public static Intent getStopIntent(Context context)  {

                 Intent intent = new Intent(context,  XService.class);

                 intent.setAction(“stop”);

                 return intent;

        }

        …

        private void handleStart()  {

                …

                Intent stopServiceIntent = getStopIntent(this);

                PendingIntent  stopServicePendingIntent = PendingIntent.getActivity(this, 0, activityIntent, 0);

 

                NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

                builder.

                        …

                        .addAction(R.drawable.ic_action_cancel,  “Stop”,  stopServicePendingIntent);

                …

        }

}

Niestandardowe layouty

Notyfikacje mogą wyświetlać layout zdefiniowany przez aplikację

  • własne hierarchie widoków
    • notyfikacje wykonują się w procesie zarządzanym przez system
    • hierarchia widoków jest zawarta w procesie naszej aplikacji
    • normalnie nie można przekazywać hierarchii widoków pomiędzy procesami
  • klasa RemoteViews pozwala przekazywać hierarchię widoków między procesami
    • używamy NotificationCompat.Builder.setContent
  • obiekty PendingIntent związane z elementami wizualnymi layoutu

Context context = getActivity();

 

RemoteViews notificationViews =

          new RemoteViews(context.getPackageName(),  R.layout.xxx);

 

Intent stopIntent = XService.getStopIntent(context);

PendingIntent stopPendingIntent = PendingIntent.getService(context, 0, stopIntent, 0);

notificationViews.setOnClickPendingIntent(R.id.btnStop, stopPendingIntent);

 

 

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

 

builder.

        …

        .setContent(notificationViews);

 

Widgety

Interaktywne komponenty, które żyją na ekranie startowym

  • może być w każdej implementacji AppWidgetHost (najczęściej na ekranie startowym)
  • może dostarczać regularnie odświeżaną zawartość
  • może inicjować akcje w oparciu o interakcję z użytkownikiem

Zachowanie dostarczone przez kombinację systemu i aplikacji

  • początkowy user experience dostarcza system
  • wygląd widgetu i interakacja jest po stronie aplikacji

System

  • AppWidgetManager

Aplikacja

  • Zasoby:  AppWidgetProviderInfo (obrazek preview, layout),  Layout
  • Kod:  AppWidgetProvider

Tworzenie widgetu

  • implementacja widget providera
    • tworzenie jako broadcast receiver
    • zwykle dziedziczymy po AppWidgetProvider
  • informacje o widget providerze w pliku zasobów XML
    • podgląd obrazka
    • layout do początkowego renderowania na ekranie startowym
    • minimalna wysokość i szerokość (w dp) na ekranie startowym
  • powiązanie odpowiednich informacji z widget providerem w manifeście aplikacji
    • filtr intencji dla akcji APPWIDGET_UPDATE
    • wpis w metadanych z informacjami o widget providerze
      • nazwa:  android.appwidget.provider
      • zasób: nazwa zasobu z informacjami o widget providerze

public class XWidget extends AppWidgetProvider  {

       public void onReceive(Context context, Intent intent)  {

               super.onReceive(context, intent);

       }

}

 

<appwidget-provider …

        android:minWidth=”180dp”

        android:minHeight=”110dp”

        android:previewImage=”@drawable/xxx”

        android:initialLayout=”@layout/yyy” >

</appwidget-provider>

 

<manifest>

      <application  …>

                …

                <receiver

                          android:name=”.XWidget”

                          android:label=”Aaa”>

                          <intent-filter>

                                  <action android:name=”android.appwidget.action.APPWIDGET_UPDATE”/>

                          </intent-filter>

                          <meta-data

                                   android:name=”android.appwidget.provider”

                                   android:resource=”@xml/zyz”/>

                </receiver>

        </application>

</manifest>

Z listy widgetów przeciągamy zrobiony przez nas na ekran startowy.

Provider jest notyfikowany, kiedy widget został umieszczony na ekranie startowym

  • otrzymuje APPWIDGET_UPDATE broadcast
    • mapuje na AppWidgetProvider.onUpdate
    • zawiera unikalny identyfikator dla instancji widgetu
  • zwykle zastępuje początkowy layout nowym layoutem
    • pozwala wiązać akcje z layoutem
  • nie może bezpośrednio załadować layoutu ponieważ widget istnieje w innym procesie, procesie ekranu startowego
  • konstruuje nowy layout przy użyciu RemoteViews
    • wiąże akcje używając PendingIntents
  • używa AppWidgetManager’a do zastąpienia początkowego layoutu nowym
    • AppWidgetManager.updateAppWidget

public class XWidget extends XWidgetProvider  {

        public void onReceive(Context context,  Intent intent)  {

                super.onReceive(context,  intent);

        }

        @Override

        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)  {

                …

                for (int index = 0; index < appWidgetIds.length; index++)  {

                        …

                        RemoteViews appWidgetViews = getWidgetRemoteViews(context);

                        appWidgetManager.updateAppWidget(appWidgetIds[index],  appWidgetViews);

                }

        }

        public static RemoteViews getWidgetRemoteViews(Context context)  {

                Intent button1Intent = new Intent(context, YActivity.class);

                button1Intent.putExtra(“image_resource_id”, R.drawable.zzz);

                PendingIntent button1PendingIntent = PendingIntent.getActivity(context,  0, button1Intent, PendingIntent.FLAG_UPDATE_CURRENT);

 

                RemoteViews appWidgetViews = new RemoteViews(context.getPackageName(), R.layout.xxx);

                appWidgetsViews.setOnClickPendingIntent(R.id.btn1, button1PendingIntent);

 

                return appWidgetViews;

        }

}

Aktywność wyświetlająca podany w parametrze obrazek

public class YActivity {

        @Override

         protected void onCreate(Bundle savedInstanceState) {

                 …

                 if (savedInstanceState == null) {

                         Intent startIntent = getIntent();

                         int imageViewResourceId = startIntent.getIntExtra(“image_resource_id”, R.drawable.default_image);

 

                         PlaceholderFragment fragment = new PlaceholderFragment();

                         Bundle args = new Bundle(1);

                         args.putInt(“image_resource_id”,  imageViewResourceId);

                         fragment.setArguments(args);

                         getFragmentManager().beginTransaction()

                                .add(R.id.container, fragment)

                                .commit();

                 }

         }

         …

}

Akcje broadcast głównego widgetu

Providery widgetów mogą otrzymywać wiele akcji broadcast

  • jawny filtr intencji APPWIDGET_UPDATE (wszystkie inne akcje są automatycznie routowane do providera)
  • APPWIDGET_ENABLED / AppWidgetProvider.onEnabled - pierwsza instancja widgetu została dodana do ekranu startowego
  • APPWIDGET_UPDATE / AppWidgetProvider.onUpdate - widget wymaga odświeżenia, zawiera informację, kiedy instancja widgetu została dodana do ekranu startowego
  • APPWIDGET_DELETED / AppWidgetProvider.onDeleted  - instancja widgetu została usunięta z ekranu startowego
  • APPWIDGET_DISABLED / AppWidgetProvider.onDisabled - ostatnia instancja widgetu została usunięta z ekranu startowego

Aktualizacja i konfiguracja widgetów

Aktualizacja widgetów

Okresowa aktualizacja zawartości widgetu

  • 3 podejścia (można używać kombinacji)
  • Okres aktualizacji widget providera
  • Aktualizacja wyzwalana przez aplikację
  • Okres aktualizacji wyzwalanej przez AlarmManager

Widget Provider Update Period

  • najprostszy do zaimplementowania
  • atrybut updatePeriodMillis w provider info  (w ms)
  • provider otrzymuje APPWIDGET_UPDATE broadcast Intent
  • budzi urządzenie, jeśli jest uśpione (potencjalnie może wyczerpywać baterie)
  • rekomendowany okresem jest 1 godzina lub więcej
  • wszystkie okresy poniżej 30 minut są traktowane jak 30 minut

<appwidget-provider …

       android:updatePeriodMillis=”3600000”>

Aktualizacja wyzwalana przez aplikację

Aplikacja może aktualizować widget w dowolnym czasie

  • do otrzymania instancji menadżera widgetów używamy AppWidgetManager.getInstance
  • podmieniamy RemoteViews tak jak robimy to w metodzie onUpdate providera - używamy AppWidgetManager.updateAppWidget
  • modyfikacja zawartości RemoteViews stanowi wyzwanie
    • hierarchia kontrolek nie jest jeszcze pobrana i dlatego nie jest dostępna programistycznie
    • musimy zapamiętać słownik elementów wizualnych, które chcemy zmieniać

public class MainActivity extends Activity {

        Context context = getActivity();

 

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

        ComponentName appWidgetComponentName = new ComponentName(context, XWidget.class);

        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(appWidgetComponentName);

 

        for(int index = 0; index < appWidgetIds.length;  index++)  {

                int appWidgetId = appWidgetIds[index];

 

                RemoteViews appWidgetViews = XWidget.getWidgetRemoteViews(context);

                appWidgetViews.setCharSequence(R.id.txtTitleText, “setText”, “YYY”);

                appWidgetManager.updateAppWidget(appWidgetId,  appWidgetViews);                                 

        }       

}

Aktualizacja wyzwalana przez alarm

AlarmManager dostarcza szczegółową kontrolę nad czasem

  • większa kontrola nad czasem niż provider update period
    • możliwość ustawiania krótszych przedziałów
    • możliwość różnicowania przedziałów czasowych
    • możliwość kontroli, czy urządzenie się obudzi, kiedy jest uśpione
  • AlarmManager wyśle broadcast Intent, jeśli okres się zakończy
    • ogólnie dobrze jest definiować własny Intent
    • można łatwo dodać Intent do tych przechwyconych przez widget provider’a

public class XWidget extends AppWidgetProvider  {

        …

        static PendingIntent getExplicitUpdatePendingIntent(Context context)  {

               Intent intent = getExplicitUpdateIntent(context);

               PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,  0);

               return pendingIntent;

        }

        static Intent getExplicitUpdateIntent(Context context)  {

               Intent intent = new Intent(context,  XWidget.class);

               intent.setAction(“xxx”);

               return intent;

        }

        public void onReceive(Context context, Intent intent)  {

              String action = intent.getAction();

              if  (“xxx”.equalsIgnoreCase(action))  {

                     doExplicitUpdate(context,  intent);

              }  else  {

                      super.onReceive(context,  intent);

              }            

        }

        private void doExplicitUpdate(Context context,  Intent intent)  {

               AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

               ComponentName appWidgetComponentName = new ComponentName(context, XWidget.class);

               int[] appWidgetIds = appWidgetManager.getAppWidgetIds(appWidgetComponentName);

             

               if (appWidgetIds != null && appWidgetIds.lendth > 0)

                      onUpdate(context,  appWidgetManager,  appWidgetIds);

        }

}

 

public class MainActivity extends Activity  {

        …

        private void btnStartAlarmManagerOnClick(Button v)  {

                Context context = getActivity();

                AlarmManager am = (AlarmManager)  context.getSystemService(Context.ALARM_SERVICE);

                PendingIntent pendingIntent = XWidget.getExplicitUpdatePendingIntent(context);

 

                long currentTimeMillis = System.currentTimeMillis();

                long intervalMillis = 5000;

                //setInexactRepeating w odróżnieniu do setRepeating dostosowuje się do innych czynności w systemie i przyczynia się do większej oszczędności baterii

                am.setInexactRepeating(AlarmManager.RTC, currentTimeMillis + intervalMillis, intervalMillis, pendingIntent);

        }

        private void btnStopAlarmManagerOnClick(Button v)  {

                Context context = getActivity();

                AlarmManager am = (AlarmManager)  context.getSystemService(Context.ALARM_SERVICE);

                PendingIntent pendingIntent = XWidget.getExplicitUpdatePendingIntent(context);

                am.cancel(pendingIntent);

        }

        private static PendingIntent getAlarmPendingIntent(Context context)  {

                PendingIntent pendingIntent = null;

 

                return pendingIntent;

        }

}

 

<application …>

        <receiver  …>

                 <intent-filter>

                         <action android:name=”android.appwidget.action.APPWIDGET_UPDATE”/>

                         <action android:name=”xxx”/>

                 </intent-filter>

                 …

        </receiver>

</application>

 

Konfiguracja aktywności

Widgety mogą mieć związaną ze sobą aktywność konfiguracyjną

  • używana do ustawienia zachowania danej instancji widgetu
  • specyfikujemy nazwę aktywności w atrybucie configure w widget provider info
  • automatycznie wyświetlana po upuszczeniu widgetu na ekran startowy (aktywność musi zawierać filtr intencji dla akcji APPWIDGET_CONFIGURE)
  1. Widget na ekranie startowym
  2. AppWidgetManager –> startActivityForResult (Intent z appWidgetId)
  3. Wybór opcji przez użytkownika
  4. Wykonanie zmian na widgecie (bezpośrednio lub poprzez broadcast intent)
  5. setResult do AppWidgetManager (Intent z appWidgetId)

<appwidget-provider …

        android:configure=”com.abc.xyz.XWidgetConfigActivity”

</appwidget-provider>

 

<manifest>

        <activity 

               android:name=”com.abc.xyz.XWidgetConfigActivity”

               … >

               <intent-filter>

                       <action android:name=”android.appwidget.action.APPWIDGET_CONFIGURE”/>

              </intent-filter>

        </activity>

</manifest>

 

public class XWidgetConfigActivity extends Activity  {

        …

        @Override

        protected void onCreate(Bundle savedInstanceState)  {

               …

               //widget nie zostanie umieszczony na ekranie startowym, jeśli użytkownik zignoruje ekran konfiguracyjny

               setResult(RESULT_CANCELED);

               …

        }

        private void btnDoneOnClick(Button v)  {

               Activity activity = getActivity();

               …

               int appWidgetId = resolveAppWidgetId(activity);

               AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(activity);

               Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);

               options.putBoolean(“option1”, flag);

               appWidgetManager.updateAppWidgetOptions(appWidgetId,  options);

 

               Intent intent = XWidget.getExplicitUpdateIntent(activity);

               intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

               activity.sendBroadcast(intent);

 

               Intent resultValue = new Intent();

               resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,  appWidgetId);

               activity.setResult(RESULT_OK,  resultValue);

               activity.finish();

        }

        int resolveAppWidgetId(Activity activity)  {

                int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

                Intent createIntent = getActivity().getIntent();

                Bundle extras = createIntent.getExtras();

                if (extras != null)  {

                        appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,

                                     AppWidgetManager.INVALID_APPWIDGET_ID);

                }

                return appWidgetId;

        }

}

 

public class XWidget  {

         …

         public void onReceive(Context context, Intent intent)  {

              

         }

         private void doExplicitUpdate(Context context,  Intent intent)  {

               …

               int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);

               int[] appWidgetIds = appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID  ? 

                         appWidgetManager.getAppWidgetIds(appWidgetComponentName) : new int[] { appWidgetId };

               …

         }

         …

         public static RemoteViews getWidgetRemoteViews(Context context, int appWidgetId)  {

                …

                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

                Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);

                boolean use2Buttons = options.getBoolean(“use2Buttons”, true);

                int resourceId = use2Buttons ? R.layout.xxx : R.layout.yyy;

                RemoteViews appWidgetViews = new RemoteViews(context.getPackageName(), resourceId);

                …

         }

}

 

Dreams

UI experience, kiedy urządzenie nie jest aktywnie używane

  • = oszczędzacze ekranów
  • zorientowane na oglądanie, możliwa interaktywnosć, ale zwykle jest bardzo ograniczona
  • wyzwalane kiedy urządzenie jest ładowane lub dokowane
    • może wpływać na stopień naładowania
    • zarządzamy przez Settings –> Display –> Daydream (on/off,  włączanie/wyłączanie aplikacji, na dole - when to daydream: while docked, while charging, either)
  • używają te same layouty i widoki jak aktywności
    • praktycznie nie mają więcej nic wspólnego z aktywnościami
    • wykonywane są przez specjalny typ serwisu
  • implementacje dziedziczą po klasie DreamService
  • zostały dodane w Android 4.2 (API Level 17)

Cykl życiowy

  • onAttachedToWindow - powiązanie z oknem, uruchamianie i ładowanie UI, przypięcie handlerów do zdarzeń
  • onDreamingStarted - wejście w stan wykonywania, inicjalizacja animacji lub innych form pokazujących wykonywanie
  • onDreamingStopped - zakończenie stanu wykonywania, zatrzymanie wszystkiego, co zostało uruchomione w onDreamingStarted
  • onDetachedFromWindow - okno jest czyszczone, czyścimy każdy event handler zapięty w onAttachedToWindow

Tworzenie

  • napisanie serwisu dziedziczącego po DreamService
  • nadpisanie onAttachedToWindow - metodą setContentView ładujemy zasób layoutu
  • dodajemy filtr intencji by uczynić dream widzialnym dla systemu

public class XDreamService extends DreamService  {

       @Override

        public void onAttachedToWindow()  {

                super.onAttachedToWindow();

                setContentView(R.layout.xxx);

        }

}

<service

          android:name=”.XDreamService”

          android:label=”Xxx”>

          <intent-filter>

                    <action android:name=”android.service.dreams.DreamService”/>

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

          </intent-filter>

</service>

sobota, 20 września 2014

Windows 9 Build 9841 - Technical Preview ?

Do sieci wyciekły screenshoty i informacje związane z nowszą kompilacją nr. 9841. Uważa się, że to ona może się stać wydaniem Technical Preview.

Menu Start – można je skalować, ma skórkę zależną od tła pulpitu, podobnie taskbar ma jakby odcień z tła pulpitu (bardziej spójny wygląd całego systemu). Screenhoty z różnymi kompozycjami kolorystycznymi można zobaczyć na stronie http://www.windowsbleu.com/2014/09/24-new-windows-9-start-menu-screenshots.html. Osobiście najbardziej podoba mi się zielona kompozycja, ta którą zamieszczam poniżej.

startscreennew

Windows Insider Preview Program - program do zgłaszania feedbacku przez użytkownika, do dyspozycji będziemy mieć aplikację Windows Feedback, po wykonywaniu niektórych czynności będziemy proszeni o feedback, pojawiać się mogą specjalne notyfikacje z pytaniami, a dzięki chmurze build będzie mógł być często aktualizowany, modyfikowany czy naprawiany.

not

Aplikacje Modern UI - te z 8.1 działają także na nowym Windows, do przełączania się między nimi można używać przycisku “Task view” w taskbar (widok tasków jest używany do tworzenia wirtualnych pulpitów i przełączania się między nimi po ich utworzeniu, można też go użyć do snapowania aplikacji poprzez przeciąganie podglądu aplikacji do pożądanej przez nas części okna)

wtp-hero

Charms Bar - w trybie desktopowym zamiast paska Charms Bar na górnym pasku tytułowym okna jest dodatkowy przycisk “…” komend aplikacji (komendy różniące się pomiędzy różnymi aplikacjami plus systemowe opcje z paska Charms Bar tj. Search, Share, Play, Print, Projects, Settings oraz opcja Full Screen przełączająca między oknem a widokiem pełnoekranowym)

Share w File Explorer – przycisk w desktopowym File Explorer umożliwiający współdzielenie plików z aplikacjami Modern UI

share-fx

Data pierwszej publicznej odsłony została podobno przesunięta na pierwszy tydzień października.

Dla uzupełnienia spływających informacji o Windows 9 warto dodać, że będzie wspierał ekrany 8K UltraHD (obsługa nowych rozmiarów pozwalających na lepszą skalowalność ekranu oraz nowe ikony 768 x 768 pikseli).

Jeszcze jedno uzupełnienie dla Windows 9:  notyfikacje od aplikacji Modern UI i desktopowych pokazują się w tym samym miejscu i trafiają do centrum notyfikacji np. komunikat o bezpiecznym odłączeniu urządzenia USB

not-02

Linki