Apka w Google Play – przygotowanie

Po długich tygodniach (czy miesiącach) projektowania, kodowania i testowania nadchodzi taki moment, kiedy nasza aplikacja z bezużytecznej, paskudnej larwy przekształca się w pięknego motyla, szykującego się do pokazania światu. A nam zaczyna coraz częściej chodzić po głowie udostępnienie aplikacji szerszemu gronu odbiorców. Najsensowniejszą opcją jest opublikowanie jej w sklepie Google Play (dawnym Android Market – bardzo mi się podobała ta nazwa). Można również założyć wątek na jakimś forum androida np. forum.android.com.pl , szczególnie jeśli nasza aplikacja nie może zostać opublikowana w sklepie z powodu niezgodności z regulaminem. Zakładamy, że naszej aplikacji ten problem nie dotyczy, więc…

Konto developerskie

googleDev

Aby opublikować apkę musimy założyć konto developerskie na stronie Google Developers, które zostanie powiązane z naszym zwykłym kontem Google. Założenie go wymaga jednorazowej opłaty 25$ wniesionej za pomocą karty kredytowej. Nie ma opcji przelewu czy PayPal, więc jeśli nie posiadamy odpowiedniej karty, to dobrym rozwiązaniem jest wirtualna karta kredytowa (większość kont bankowych ma taką opcję). Ostatnim punktem jest uzupełnienie naszych danych o:

  • nazwę programisty (widoczna dla klientów sklepu)
  • mail kontaktowy
  • nr telefonu (do użytku tylko dla firmy Google na wypadek problemów)opcje google dev

Od tego momentu można już rozpocząć dodawanie pierwszej apki.

Samo konto oferuje dostęp do bardzo dużej ilości usług i platform. Naturalnie obiektem naszego zainteresowania jest zakładka Android, a dokładniej Google Play Console, z której możemy dodawać aplikacje. Sama konsola ma sporo dodatkowych funkcjonalności jak np. połączenie z usługami Firebase, AdWords, DoubleClick czy ustawienie konta sprzedawcy. Ja poprzestałem na ustawieniach podstawowych.

Dodanie aplikacji – przygotowanie

Konto developerskie już śmiga, możemy rozpocząć dodawanie pierwszej aplikacji. Rozpoczynamy od określenia domyślnej wersji językowej oraz tytułu aplikacji (do 30 znaków). Kolejnym krokiem jest przygotowanie elementów:

  • krótkiego opisu do 80 znaków
  • rozszerzonego opisu do 4000 znaków
  • min. 2 screenów z aplikacji (JPG lub PNG)
  • ikony apki 512x512px (PNG z kanałem alfa)
  • grafiki nagłówka do sekcji Polecane 1024x500px (JPG lub PNG bez kanału alfa)

Jest to lista minimum. Poza tym możemy dodać link do filmu promocyjnego na YouTube, grafiki promocyjne itp. Dla każdej wybranej przez nas obsługiwanej wersji językowej możemy również dodać osobne tłumaczenie opisów.

Po uzbrojeniu się w w/w treści wrzucamy je w odpowiednie pola zakładki Informacje o aplikacji. Kolejnym krokiem jest wybranie typu aplikacji (Gra lub Aplikacja), jej kategorii oraz dokonanie oceny treści. google play consoleDzwonnik trafił do typu Aplikacje w kategorii Narzędzia. Oceny można dokonać dopiero po uploadzie pliku .apk z samą aplikacją, a polega to na wypełnieniu kwestionariusza na temat występowania w apce przemocy, golizny, nazistowskich symboli, narkotyków itp. Na jej podstawie nadawane są kategorie wiekowe różnych organizacji. Dzwonnik dostał m.in. PEGI 3 😉

Ostatnimi dwom krokami jest uzupełnienie informacji kontaktowych widocznych dla klientów sklepu: strona internetowa, mail i numer telefonu. Wymagany bezwzględnie jest tylko mail. Pozostaje jeszcze dołączyć link do polityki prywatności, jednak można tą opcję pominąć (z czego skorzystałem). Voila!

aplikacja bez apk

W następnym wpisie poruszę kwestię podpisywania aplikacji,  gdyż uploadować można tylko podpisane apki 🙂

Reklamy

Dzwonnik 1.0 w Google Play!

Kamień milowy został osiągnięty!

Dzwonnik google play phonezagościł wśród aplikacji dostępnych w sklepie Google Play 🙂

Samo wykupienie dostępu do konta developerskiego (25$ za dożywotni dostęp) jest zaledwie preludium do publikacji aplikacji. Po uszczupleniu kiesy czeka nas przygotowanie różnej maści wymaganych opisów, grafik, podpisanie aplikacji i wypełnienie tony wymaganych przez Google formularzy zanim będziemy mogli nacisnąć finalny przycisk, za którym kryje się udostępnienie publiczne aplikacji. A nawet po jego naciśnięciu należy uzbroić się w cierpliwość, gdyż czeka nas oglądanie poniższego widoku oznajmiającego mielenie apki w trybach sklepu. Teoretycznie może to trwać kilka godzin, w przypadku mojej aplikacji było to około godziny.

przetwarzenie Google play

Sam Dzwonnik udostępnia obecnie swoje podstawowe funkcjonalności, bez żadnych dodatkowych bajerów i jest dostępny całkowicie za darmo i bez reklam, dla telefonów od wersji systemu Android 5.0 (API 21). W miarę możliwości postaram się dodać w kolejnych wersjach kilka opcjonalnych funkcjonalności:

  • regulacja głośności dla każdego z kanałów osobno (dzwonek, wiadomości, media, system)
  • parę skórek do wyboru
  • wsparcie dla starszych wersji Androida

Ponadto  w kilku krótkich postach przybliżę cały proces publikacji, ze szczególnym uwzględnieniem momentów problematycznych i… nieco dziwnych.

Tymczasem jeśli masz pomysł na jakieś rozwinięcie możliwości Dzwonnika zapraszam do komentowania 😉

Wersje językowe

Nie wiem jakie jest Wasze zdanie, ale osobiście uważam, że przed opublikowaniem apki w Google Play dobrym pomysłem jest zaopatrzenie jej w angielską wersję językową. W końcu w dzisiejszych czasach jest to lingua franca, współczesna łacina. Ewentualnie spolszczenie, jeśli całość jest od początku po angielsku 😉

Środowisko Android udostępnia bardzo przyjemny mechanizm ułatwiający tłumaczenie wszystkich tekstów występujących w aplikacji dzięki możliwości przechowywania ciągów znaków jako resources  typu String w pliku strings.xml.

strings

W ten sposób zamiast zabitego na sztywno tekstu gdzieś w kodzie, wstawiane jest odniesienie do konkretnej wartości tekstowej za pomocą jej nazwy, w pliku ze stringami.

notHard

notHardR

Daje to programiście dwie zalety:

  • cały tekst występujący w aplikacji znajduje się w jednym miejscu
  • możliwość wykorzystania tego samego Stringa w wielu miejscach

Android Studio umożliwia prosty mechanizm stworzenia analogicznych plików, w różnych wersjach językowych. Wystarczy stworzyć nowy plik i wybrać język, który chcemy użyć, a odpowiednio nazwane pliki i foldery zostaną utworzone automatycznie. Pozostaje jeszcze skopiować wartości z pliku strings.xml i wiersz po wierszu przetłumaczyć. Wszystko elegancko znajduje się w jednym miejscu.

1

W zależności od języka ustawionego w systemie telefonu aplikacja wybiera odpowiednią wersję językową, a w przypadku jej braku,  jako domyślna jest traktowana wersji z pliku strings.xml. Trzeba jedynie pamiętać aby żaden fragment tekstu przeznaczony do przetłumaczenia nie schował się gdzieś wewnątrz kodu, wpisany na sztywno.

extract string

Na szczęście IDE przypomina nam o tym i umożliwia bardzo prostą ekstrakcję Stringa. Również po wykonaniu inspekcji kodu (Analyze -> Inspect Code) jedną z informacji, które otrzymamy są miejsca z występującym hardcodowanym tekstem.

international

W analogiczny sposób można regionalizować również inne zasoby np. obrazy. Nie znam rozwiązań z innych środowisk, ale ta metoda bardzo przypadła mi do gustu.

Java 9 – co nowego?

Powoli zbliża się data pojawienia się najnowszej wersji Java SE, czyli wersji 9 (właściwie to 1.9). Była ona już wielokrotnie przesuwana, a ostatnia obsuwa była spowodowana problemami projektu Jigsaw, który należy do najważniejszych nowości.
Jakie najistotniejsze zmiany przyniesie Java 9?

  • zapewnienie modułowości aplikacji Java w ramach projektu Jigsaw. Dzięki temu będzie można wydzielać i kompilować moduły aplikacji wraz z określeniem wymaganych oraz dostarczanych przez moduł zależności. Zastąpi to konieczność dołączania wszystkich niezbędnych JARów do każdej z aplikacji z osobna i będzie odpowiedzią na problem JAR hell
  • JShell -wbudowane środowisko REPL (read-eval-print JShellloop), czyli konsoli wraz z interaktywnym interpreterem poleceń w trybie rzeczywistym. Takie narzędzie o nazwie BeanShell jest rozwijane od 1999r, jednak nie należało nigdy do pakietu Java
  • pełne wsparcie dla protokołu HTTP 2.0
  • wbudowane środowisko do tworzenia mikro-benchmarków dzięki wchłonięciu Java Microbenchmarking Harness (JMH). Dzięki temu sposób testowania będzie ustandaryzowany, co umożliwi porównywanie wyników testów
  • zastosowanie Garbage Collectora typu G1 jako domyślnego

Jestem ciekaw jak w praktyce sprawdzi się modułowość aplikacji i w jaki sposób wpłynie na narzędzia typu Maven czy Gradle. Pierwotnie miała zostać wprowadzona już w wersji Java 7, jednak była wielokrotnie przekładana.

Odliczanie do wydania nowej wersji Javy można śledzić na stronie: http://www.java9countdown.xyz/

 

New design!

Aplikacja została przetestowana, kilka drobnostek udałobell_icon się wychwycić i poprawić, zatem… czas zadbać o wygląd! Moim celem było stworzenie interfejsu intuicyjnego, ukazującego na pierwszy rzut oka co do czego służy. Ponadto Dzwonnik zyskał nową ikonkę 🙂

 

Na liście pozostaje jeszcze założenie konta developerskiego w Google Play i umieszczenie aplikacji w sklepie. A czy Waszym zdaniem nowy wygląd jest czytelny?

Drobnostka

Przez ostatnie dwa tygodnie nie mam ani chwili żeby zabrać się za dalszy rozwój Dzwonnika. Nowa praca, ilość pochłanianej w niej wiedzy oraz pozostałe obowiązki są wyczerpujące… dlatego na dziś będzie…

drobnostka!

Link do bardzo ciekawego oraz obrazowego tutoriala po możliwościach oraz komendach GITa http://learngitbranching.js.org/ . Warto się zapoznać jeśli ktoś nie czuje się z gitem pewnie lub chciałby odświeżyć wiedzę 🙂

Nowy rozdział

Okres wypowiedzenia w poprzedniej pracy minął, zaległy nexturlop się skończył…  nadszedł długo wyczekiwany tydzień. Tydzień, w którym zaczynam pierwszy dzień pracy jako etatowy programista! Wyczekiwany niecierpliwie oraz z ogromną ciekawością, jak również pewną obawą przed nieznanym. Pierwsze trzy dni spędziłem nad bhp, konfiguracją sprzętu (ale póki co jeszcze nie całego środowiska) oraz szkoleniami na temat wszystkiego czym zajmuje się firma. Jest bardzo ciekawie, a czeka mnie jeszcze kilka(naście) dni takiego chłonięcia wiedzy 🙂

A co tam słychać u naszego Dzwonnika? Trochę wyewoluował i w tej chwili mamy już dostępną opcję usunięcia całej bazy danych, a przy dodawaniu nowego stanu jako domyśle wartości – czas oraz głośność – ładowane są aktualne ustawienia. Ponadto kilkudniowe testy użytkowe samej aplikacji nie ujawniły póki co żadnych niespodzianek. Jest dobrze 😉

Refleksyjne testowanie

Słyszałem o istnieniu mechanizmu refleksji w Javie, który w magiczny sposób pozwala grzebać „w bebechach” klas, pomimo deklaracji prywatności owych „bebechów”. Dzisiaj trafiła się doskonała okazja aby poznać bliżej ów mechanizm, ponieważ zechciałem przetestować ukrytą w głębi klasy, prywatną metodę wyliczającą nową wartość głośności dzwonka.

Metoda, o której mowa, przedstawia się na listingu poniżej. Posiada dwa parametry: wartość głośności wybraną przez użytkownika (zakres 0 do 7) oraz maksymalną wartość głośności danego strumienia audio, pobieraną wcześniej z AudioManager’a.

private int calculateNewVolume(int volume, int realMaxVolume){
    float multiply = (float)realMaxVolume / USER_VOLUME_CHANGE_RANGE;
    return (int)(volume*multiply);
}

Jak widać są to proste obliczenia na liczbach zmiennoprzecinowych zakończone zaokrągleniem w dół do wartości typu int. Ponieważ chciałem sprawdzić czy wyniki są poprawne, a jednocześnie nie zwiększać dostępności metody (bo po co ma być widoczna, skoro nie jest wykorzystywana „na zewnątrz”?) padło na wykorzystanie refleksji. A zatem, mechanizm refleksji pozwala na dostęp do metod, pól oraz konstruktorów wybranej klasy w sytuacji, kiedy ich modyfikatory dostępu są inne niż public. Zobaczmy jak wygląda ścieżka dobrania się do metody private z poziomu innej klasy.

Pierwszym krokiem jest utworzenie obiektu typu Class. Możemy skorzystać z trzech możliwości jego utworzenia: przy użyciu forName(), pola .class lub getClass(), jeśli posiadamy istniejącą instancję danej klasy.

Class clazz = Class.forName("com.wordpress.gatarblog.dzwonnik.Receivers.RingtoneSwitcher");
Class clazz = RingtoneSwitcher.class;
Class clazz = ringtoneSwitcherObject.getClass();

Kolejnym krokiem jest utworzenie obiektu Method, do którego przypiszemy wybraną metodę na podstawie jej nazwy oraz podanie typów jej parametrów jako pól .class. Istnieją dwie metody: getMethod() oraz getDeclaredMethod(). Różnica polega na tym, że tylko druga z nich daje pełen dostęp do metod, niezależnie od ich modyfikatora: public, protected, private lub jego braku.

Method method = clazz.getDeclaredMethod("calculateNewVolume",int.class, int.class);

Ponieważ metoda calculateNewVolume() jest prywatna przed jej użyciem musimy umożliwić dostęp do niej.

method.setAccessible(true);

Aby użyć takiej metody musimy również stworzyć instancję jej macierzystego obiektu.

RingtoneSwitcher ringtoneSwitcherInstance = clazz.newInstance();

Teraz nareszcie możemy użyć naszej metody method za pomocą invoke(). W jej parametrach podajemy najpierw instancję RingtoneSwitcher’a, a następnie po kolei wartości/referencje parametrów metody, którą trzymamy w obiekcie method.

Object result = method.invoke(ringtoneSwitcherInstance,volume,maxVol);

Obiekt result przechowuje nasz wynik działania metody calculateNewVolume(). Cały kod testujący ma postać:

public class RingtoneSwitcherTest {

    private static Class clazz;
    private static Method method;
    private static Object ringtoneSwitcherInstance;

    @BeforeClass
    public static void beforeAll() throws Exception {
        clazz = RingtoneSwitcher.class;
        method = clazz.getDeclaredMethod("calculateNewVolume",int.class, int.class);
        method.setAccessible(true);
        ringtoneSwitcherInstance = clazz.newInstance();
    }

    @Test
    public void maximumVolValueSeven() throws Exception {
        int volume = 2;
        int maxVol = 7;
        int expect = 2; // 2/7 * 7 = 2
        Object result = method.invoke(ringtoneSwitcherInstance,volume,maxVol);
        Assert.assertEquals(expect,result);
    }

...

W podobny sposób możemy uzyskiwać dostęp do innych elementów danej klasy. Jeśli nie wiemy co wchodzi w jej skład, a bardzo chcemy się dowiedzieć to możemy wydobyć tablicę obiektów np. Method[] za pomocą getDeclaredMethods() zawierającą wszystkie metody.

Uwaga! Pobieranie klas, pól, metod etc na podstawie ich nazwy niesie ze sobą konieczność obsłużenia wyjątków rzucanych na wypadek braku danego elementu (ClassNotFoundException, NoSuchMethodException…)! Niestety istnienie podanych nazw jest sprawdzane dopiero na etapie uruchomienia kodu. Ponadto w przypadku zmiany nazwy np. podczas refaktoryzacji, łatwo zapomnieć o zmianie nazwy podanej jako String.

Spotkałem się z opiniami, że metody prywatne nie powinny być testowane bezpośrednio, a jedynie jako składowa część metod publicznych, a architektura całego kodu powinna wykluczać konieczność ich osobnego testowania. Generalnie brzmi to pięknie, jednak jak zwykle diabeł tkwi w szczegółach. W sytuacji takiej klasy jak RingtoneSwitcher, gdzie cała klasa jest standardowo sterowana czasem, testy są niemożliwe lub mocno utrudnione. W takiej sytuacji test poprzez użycie refleksji jest strzałem w dziesiątkę. W mojej metodzie znalazłem dzięki niemu poważny błąd 🙂

Za zły pomysł uważam zmianę modyfikatorów dostępu na publiczne tylko po to aby coś przetestować. A jakie jest Wasze zdanie na ten temat?

Czas na testy

etsty

Dzwonnik jest już gotowy w swojej wersji zawierającej minimalne funkcjonalności. Można stworzyć zdarzenie zmiany głośności dzwonka lub trybu na dowolny dzień tygodnia oraz godzinę. Wraz ze zmianą głośności dzwonka zmieniają się również głośności powiadomień, multimediów oraz alarmów. Rejestracja zdarzeń na dany dzień następuje raz na dobę dzięki DailyReceiver. W przypadku restartu telefonu DailyReceiver również zostanie uruchomiony dzięki zdarzeniu sterowanym rebootem – OnBootReceiver. Niestety ze względu na trudności w testowaniu zdarzeń wywoływanych czasem muszę ograniczyć się do testów manualnych. Jedynie klasa obsługi bazy danych ma obecnie napisane testy jednostkowe z wykorzystaniem frameworka Robolectric.

W dalszych planach pozostaje:

  • przetestowanie działania aplikacji w praktyce
  • dodanie kilku funkcjonalności (np. usunięcie całej bazy, wczytanie aktualnych ustawień dzwonków przy tworzeniu nowego zdarzenia)
  • poprawienie interfejsu graficznego
  • wrzucenie aplikacji do sklepu Google Play (naturalnie darmowej)

Ponieważ nigdy nie korzystałem se sklepu Google jako developer, jak również nie korzystałem z Google Analytics jestem bardzo ciekawy tej części projektu 🙂

Dzyń Dzyń

Doszedłem do momentu, w którym aplikacja otrzymała swoją najbardziej rzucającą się w oczy funkcjonalność, czyli możliwość modyfikowania głośności dzwonka. Alternatywnie przejścia w tryb wibracji lub ciszy. Dostęp do modyfikacji tych parametrów zapewnia klasa AudioManager, której instancję otrzymujemy z kontekstu aplikacji.

AudioManager audioManager = (AudioManager)
context.getSystemService(Context.AUDIO_SERVICE);

Za pomocą AudioManagera możemy ustawić wybrany profil dźwiękowy:

audioManager.setRingerMode(profileType);

Dzwonnik umożliwia wybór trzech podstawowych profili:

AudioManager.RINGER_MODE_SILENT

AudioManager.RINGER_MODE_VIBRATE

AudioManager.RINGER_MODE_NORMAL

Ponieważ XX w. skończył się jakiś czas temu i obecnie telefon potrafi wydawać z siebie wiele różnych dźwięków ponad samym poinformowaniem o przychodzącym połączeniu. Dźwięki są  skategoryzowane w ramach strumieni. Przykładami są m.in. strumienie odpowiadające za dzwonek, powiadomienia, alarmy, dźwięk muzyki/filmów:

AudioManager.STREAM_RING

AudioManager.STREAM_NOTIFICATION

AudioManager.STREAM_ALARM

AudioManager.STREAM_MUSIC

Aby zmienić wartość głośności środowisko Android udostępnia metody oparte o podanie typu strumienia audio oraz operacji jaką chcemy na nim wykonać:

audioManager.adjustStreamVolume(streamType, operationType, flag); 

Mamy kilka operacji do wyboru. Podstawowe służą do zwiększenia/zmniejszenia głośności o jedną jednostkę:

AudioManager.ADJUST_RAISE

AudioManager.ADJUST_LOWER  

Drugą opcją jest podanie liczbowej wartość głośności jaką chcemy ustawić:

audioManager.setStreamVolume(streamType, volumeValue, flag); 

Strumienie mogą mieć różne zakresy wartości. Przykładowo dla głośności dzwonka jest to zakres 0 do 7.  Flagi definiują dodatkowe zachowanie podczas regulacji głośności. Dla przykładu dwie poniższe flagi powodują odpowiednio odtworzenie dźwięku przy zmianie głośności oraz umożliwiają przejście w tryb cichy/wibracji w przypadku zmniejszenia głośności poniżej zera.

AudioManager.FLAG_PLAY_SOUND

AudioManager.FLAG_ALLOW_RINGER_MODES 

Naturalnie powyższe przykłady nie wyczerpują wszystkich możliwości jakie daje nam AudioManager. Ostatnią istotną kwestią są uprawnienia aplikacji do modyfikacji dźwięku. Sama regulacja głośności oraz ustawienie telefonu w tryb cichy/wibracji nie wymaga niczego. Natomiast wyjście z tego trybu wymaga aby aplikacja posiadała dodatkowe uprawnienia do przerywania trybu „Nie przeszkadzać”. Dla systemów do API 23 należy umieścić wpis w pliku manifestu (nad ):

<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />

W przypadku Androida od wersji API 24, poza wpisem aplikacja musi poprosić użytkownika o dostęp do tych samych ustawień. Do sprawdzenia czy aplikacja posiada uprawnienia służy metoda isNotificationPolicyAccessGranted() klasy NotificationManager.

private void grantDoNotDisturbAccess() {
   NotificationManager notificationManager =
     (NotificationManager) getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
     && !notificationManager.isNotificationPolicyAccessGranted()){
       Intent intent = new
Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
       startActivity(intent);
   }
}