czwartek, 9 sierpnia 2018

Jak ustawić własne czcionki w aplikacji

  1. Pobierz pliki czcionek, których potrzebujesz
  2. Przekonwertuj czcionki do formatu .ttf (jeśli są w innym)
  3. Utwórz katalog czcionek w swoim projekcie (jeśli go nie ma)
    1. Wybierz menu konekstowe z katalogu res
    2. New -> Android Resource Directory
    3. Recource type: font
  4. Dodaj do katalogu font swoje czcionki
  5. Wprowadź swoje czcionki do projektu, np.
    1. Dodaj je do swojego stylu: <item name="android:fontFamily">@font/pt_sans</item> (API >=16)
    2. Dodaj je do swojego obiektu tekstowego w layout: android:fontFamily="@font/pt_sans"
    3. Dodaj je w kodzie swojej aplikacji do obiektu tekstowego

wtorek, 7 sierpnia 2018

Exception - org.json.JSON.typeMismatch

Dlaczego wystąpił?

Myślałam, że parsowany string to JSONObject a to był JSONArray.
Plik .json (a właściwie String) zrobiłam sama, więc wystarczyło usunąć [ ] z początku i końca.

niedziela, 5 sierpnia 2018

Room - kilka słów o przechowywaniu danych

  1. Dodaj odpowiednie zależności do build.gradle(Module: app)
    def room_version = "1.1.1"
    implementation "android.arch.persistence.room:runtime:$room_version"
    annotationProcessor "android.arch.persistence.room:compiler:$room_version"
    testImplementation "android.arch.persistence.room:testing:$room_version"
  2. Zsynchronizuj build.gradle
    1. kliknij prawym przyciskiem na build.gradle
    2. wybierz Synchronize build.gradle
  3. Dodaj klasę obiektu, który będzie reprezentował tabelę
    1. Daj jej annotację @Entity
    2. Dodaj kolumnę @PrimaryKey
    3. Dodaj pozostałe kolumny
    4. Dodaj gettery i settery elementów obiektu (na potrzeby Room)
      @Entity
      public class StoryObject {
          @PrimaryKey
          private int storyId;
          @ColumnInfo(name = "main_text")
          private String mainText;
  4. Stwórz interfejs z metodami dostępu do bazy - DAO
    1. Daj jej annotację @Dao
    2. Dodaj metody dostępu (napisane w SQL)
      @Dao
      public interface StoryDao {
          @Query("SELECT * FROM story")
          List<StoryObject> getAll();
          @Insert
          void insertAll(StoryObject... objects);
          @Delete
          void delete(StoryObject object);
      }
  5. Stwórz klasę (abstract) bazy danych
    1. Dodaj jej annotację @Database i przypisz klasy, które ją reprezentują (entities)
    2. Dodaj metodę (abstract), która zwróci odpowiedni obiekt Dao
      @Database(entities = {StoryObject.class}, version = 1)
      public abstract class StoryDatabase extends RoomDatabase {
          public abstract StoryDao getStoryDao();
      }
  6. W kodzie aplikacji wywołaj budowanie bazy danych
    StoryDatabase db = Room.databaseBuilder(getApplicationContext(),
            StoryDatabase.class, "story_database").build();

poniedziałek, 30 lipca 2018

Release – checklista

  • Change log - powiązanie z zadaniami (historyjkami) - scope of work vs lista commitów
  • Kiedy wypuszczono produkt
  • Jaki jest tag w repozytorium
  • Jakie były testy i gdzie są raporty
  • Gdzie znajduje się backup
  • Dokumentacja i konfiguracja - aktualne wersje

sobota, 28 lipca 2018

Android - testy wydania

Kilka dni temu wysłałam kolejną wersję aplikacji, jeszcze ciągle w wersji próbnej, testowej, w trakcie pracy. Ogólnie wydawałoby się - nic poważnego. Ale jednak wysłałam ją komuś - to jest poważne, powinnam być za to odpowiedzialna. Zrozumiałam, że nie zrobiłam jej wcześniej absolutnie podstawowych testów wydania. Nawet nie mam przygotowanych... Pora to zmienić.

Testy wydania aplikacji - android (ogólne)

  1. Aplikacja aktualizuje się z poprzedniej wersji do nowej (update)
  2. Aplikacja w aktualnej wersji może być zainstalowana na "czystym" urządzeniu (na takim, na którym nie jest zainstalowana) (instal)
  3. Aplikacja po wybraniu ikony włącza się i pokazuje ekran startowy (start)
  4. Aplikacja po wybraniu przycisku cofinij wyłącza się (end)
  5. Aplikacja wykonuje podstawową funkcję (work)
  6. Aplikacja aktualizuje się do poprzedniej wersji (downgrade)

Testy wydania aplikacji - android (dodatkowe)

  1. Aplikacja działa bez internetu (internet permission)
    1. brak wyjątków
    2. informacja o braku połączenia
    3. niezakłócone działanie
  2. Aplikacja działa przy obracaniu ekranu (orientation)
    1. dźwięk
    2. obraz
    3. funkcje
  3. ...

poniedziałek, 23 lipca 2018

Jak dodać odtwarzacz audio?

  1. Dodaj do pliku build.gradle -> dependencies
    compile 'com.google.android.exoplayer:exoplayer:r2.2.0'
  2. Dodaj do swojej aktywności
    implements View.OnClickListener, ExoPlayer.EventListener
  3. Dodaj do swojej aktywności obsługującej audio zmienne ExoPlayer
    private SimpleExoPlayer mExoPlayer;
    private SimpleExoPlayerView mPlayerView;
    private static MediaSessionCompat mMediaSession;
    private PlaybackStateCompat.Builder mStateBuilder;
    private NotificationManager mNotificationManager;
  4. Zainicjalizuj wstępnie zmienne
    mPlayerView = (SimpleExoPlayerView) findViewById(R.id.playerView);
    mPlayerView.setPlayer(mExoPlayer);
    initializeMediaSession();
    initializePlayer(Uri.parse(audioPath));

  5. Zaimplementuj inicjalizację MediaSession
     mMediaSession = new MediaSessionCompat(this, this.getClass().getSimpleName());
    mMediaSession.setFlags(
            MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    mMediaSession.setMediaButtonReceiver(null);
    mStateBuilder = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_PLAY |
                    PlaybackStateCompat.ACTION_PAUSE |
                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
                    PlaybackStateCompat.ACTION_PLAY_PAUSE);
    mMediaSession.setPlaybackState(mStateBuilder.build());
    mMediaSession.setCallback(new MySessionCallback());
    mMediaSession.setActive(true);
  6. Zaimplementuj podklasę MySessionCallback
            @Override
            public void onPlay() {
                mExoPlayer.setPlayWhenReady(true);
            }

            @Override
            public void onPause() {
                mExoPlayer.setPlayWhenReady(false);
            }

            @Override
            public void onSkipToPrevious() {
                mExoPlayer.seekTo(0);
            }
  7. Zaimplementuj inicjalizację playera
    if (mExoPlayer == null) {
        // Create an instance of the ExoPlayer.
        TrackSelector trackSelector = new DefaultTrackSelector();
        LoadControl loadControl = new DefaultLoadControl();
        mExoPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
        mPlayerView.setPlayer(mExoPlayer);

        // Set the ExoPlayer.EventListener to this activity.
        mExoPlayer.addListener(this);
       
        // Prepare the MediaSource.
        String userAgent = Util.getUserAgent(this, "ClassicalMusicQuiz");
        MediaSource mediaSource = new ExtractorMediaSource(mediaUri, new DefaultDataSourceFactory(
                this, userAgent), new DefaultExtractorsFactory(), null, null);
        mExoPlayer.prepare(mediaSource);
        mExoPlayer.setPlayWhenReady(true);
    }
  8. Zaimplementuj funkcję zamykania playera
    mNotificationManager.cancelAll();
    mExoPlayer.stop();
    mExoPlayer.release();
    mExoPlayer = null;
  9. Dodaj funkcję zamykania playera do odDestroy()
        super.onDestroy();
        releasePlayer();
        mMediaSession.setActive(false);
  10. Zaimplementuj funkcję onPlayerStateChanged()
    if((playbackState == ExoPlayer.STATE_READY) && playWhenReady){
        mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
                mExoPlayer.getCurrentPosition(), 1f);
    } else if((playbackState == ExoPlayer.STATE_READY)){
        mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
                mExoPlayer.getCurrentPosition(), 1f);
    }
    mMediaSession.setPlaybackState(mStateBuilder.build());
    showNotification(mStateBuilder.build());
  11. Zaimplementuj MediaReceiver
    public static class MediaReceiver extends BroadcastReceiver {
        public MediaReceiver() {
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            MediaButtonReceiver.handleIntent(mMediaSession, intent);
        }
    }
  12. Dodaj do manifestu aktywność obsługującą odtwarzacz
    <activity android:name=".QuizActivity"
        android:launchMode="singleTop"/>
    <receiver android:name=".QuizActivity$MediaReceiver">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>
  13. Przygotuj layout uwzględniający SimpleExoPlayerView
     <com.google.android.exoplayer2.ui.SimpleExoPlayerView
            android:id="@+id/playerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="0dp"
            android:layout_marginRight="0dp"
            android:layout_marginBottom="0dp"
            android:layout_marginLeft="0dp"/>

niedziela, 22 lipca 2018

Jak przechowywać dane - json

Jedną z możliwości przechowywania danych jest plik w formacie .json (JavaScript Object Notation).

Cechy w porównaniu z XML i JAML
  • dostęp do danych w formacie JSON jest bardziej naturalny z poziomu języka JavaScript niż dostęp do tych samych danych w formacie XML
  • nie jest już jednak tak naturalny dla Javy i wymaga stosowania specjalnych bibliotek
  • w nowszych wersjach standardów dopuszczana jest wymiana danych za pomocą JSON-a (np. w REST)
  • w praktyce zajmuje znacząco mniej miejsca niż analogiczny obiekt przesyłany za pomocą formatu XLM.
  • JSON jest łatwiejszy do analizowania składni niż jego nadzbiór YAML
Przykład (kurs android - udacity)

[

{

"name": "Toccata and Fugue in D minor",

"id": 0,

"uri": "asset:///toccata_fugue.mp3",

"composer": "Johann Sebastian Bach",

"albumArtID": "bach"

},

{

"name": "Fur Elise",

"id": 1,

"uri": "asset:///fur_elise.mp3",

"composer": "Ludwig van Beethoven",

"albumArtID": "beethoven"

}
]














Jak zastosować?

Stworzyć obiekt .java, który będzie odczytywał dane, np.:

private Sample(int sampleID, String composer, String title, String uri, String albumArtID) {
        mSampleID = sampleID;
        mComposer = composer;
        mTitle = title;
        mUri = uri;
        mAlbumArtID = albumArtID;
    }


środa, 11 lipca 2018

wtorek, 10 lipca 2018

Jak dodać menu do aplikacji?

  1. Nadaj nazwy swoim opcjom - np. wprowadź je jako wartości w strings.xml
  2. Utwórz katalog zasobów dla menu:
    1. Kliknij prawy przyciskiem na katalog res
    2. Wybierz New Resource Directory
    3. Zmień Recource Type z value na menu
  3.  Dodaj w katalogu plik menu.xml:
    1. Kliknij prawy przyciskiem na katalog menu
    2. Wybierz New -> Menu Resource File
  4. Wprowadź ustawienia opcji menu, np.:
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android" 
     xmlns:app="http://schemas.android.com/apk/res-auto"> 
          <item android:id="@+id/menu_day1" 
                android:orderInCategory="1"         
                app:showAsAction="never" 
                android:title="@string/menu_d1" />
    </menu> 
  5. Zaimplementuj w aktywności, która udostępnia menu funkcje onCreateOptionsMenu i onOptionsItemSelected, np.:
    @Overridepublic boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);    return true;}
    
    @Overridepublic boolean onOptionsItemSelected(MenuItem item) {
        return selectItem(item);}
    
    public boolean selectItem(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_day1:
                return true;        case R.id.menu_day2:
                return true;        case R.id.menu_day3:
                return true;        case R.id.menu_day4:
                return true;        case R.id.menu_day5:
                return true;        default:
                return super.onOptionsItemSelected(item);    }
    }

poniedziałek, 25 czerwca 2018

Co robi widget czyli kilka słów o onUpdate

Funkcja onUpdate:
public void onUpdate (Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
  • Context: obiekt typu Context, z którym aktualnie działa widget
  • AppWidgetManager: obiekt typu AppWidgetManager, który umożliwi wywołanie funkcji aktualizacji - AppWidgetManager.updateAppWidget(ComponentName, RemoteViews)
  • int: numery ID widgetów (jego instancji), które będą aktualizowane
Jest to najczęściej wywoływana funkcja widgetu (czasami jedyna wymagająca implementacji).

Tu definiujemy:
  • jak ma być skonfigurowany widget (jeśli nie ma specjalnej aktywności do konfiguracji)
  • co ma zrobić widget gdy wystąpi jakieś działanie użytkownika
  • jaką aktywność ma otworzyć widget (jeśli ma)
  • jak mają działać elementy wigdetu, np. przyciski (jeśli je ma)
Warto pamiętać:
  • updatePeriodMillis - update działa zgodnie z tym co zdefiniowano w pierwszej instancji widgetu (nawet jeśli druga instancja została skonfigurowana inaczej, to te ustawienia zostaną zignorowane
  • AppWidgetProvider jest rozszerzeniem BroadcastReceiver co nie daje gwarancji, że gdy metoda widgetu zakończy wykonywanie, to proces będzie nadal działał
    • w przypadku gdy wykonanie wszystkiego może potrwać dłużej zaleca się w metodzie onUpdate włączyć serwis

piątek, 22 czerwca 2018

Jak działa AppWidgetProvider czyli jakie widget otrzymuje sygnały i co się wtedy dzieje

AppWidgetProvider (zadeklarowany w Manifeście jako <receiver>) umożliwia odbieranie sygnałów przeznaczonych tylko dla widgetów z następujących metod:
  • onUpdate()
    • wywoływana automatycznie zgodnie z parametrem updatePeriodMillis
    • wywoływana automatycznie przy dodawaniu widgetu, żeby np. domyślnie skofigurować widget, jeśli trzeba uruchomić serwis
    • wywoływana z aktywności przy dodawaniu widgetu tylko wtedy, jeśli zostało tak zaprogramowane - programista musi zapewnić, że widget będzie w zupełności skonfigurowany, jeśli decyduje się sosać aktywność umożliwiającą konfigurację
  • onAppWidgetOptionsChanged()
    • wywoływana przy dodawaniu widgetu
    • wywoływana przy zmianie rozmiaru widgetu
    • umożliwia pokazanie lub schowanie elementów widgetu w zależności od jego rozmiaru
    • wprowadzona w Android 4.1 (API 16)
  • onDeleted(Context, int[])
    •  wywoływana gdy App Widget host zamyka widget - konkretną instancję, tylko jedną
  • onEnabled(Context)
    • wywoływana gdy dodawany jest pierwszy obiekt danego widgetu
    • jeśli widget może mieć więcej niż jedną instancję, przy dodawaniu drugiej metoda nie jest wywoływana
    • metoda odpowiednia do implementacji wszystkich funkcji, które mają się wykonać tylko raz, np. stworzenie bazy danych, szczególna konfiguracja
  • onDisabled(Context)
    • wywoływana gdy z App Widget host usuwana jest ostatnia instancja widgetu
    • metoda odpowiednia do implementacji funkcji, które "posprzątają" po widgecie, np. usuną czasowe bazy danych
  • onReceive(Context, Intent)
    • wywoływana przy każdym odebraniu sygnału
    • wywoływana przed każdą ze wspomnianych powyżej metod
    • nie musi być implementowana - AppWidgetProvider ma domyślną implementację, która w odpowiedni sposób radzi sobie z odbieranymi sygnałami i wywołuje odpowiednie funkcje

Wygląd widgetu - zmiana rozmiaru

Widget nie musi mieć stałego, wybranego przez nas rozmiaru - może być konfigurowany przez użytkownika.
Nie musi też mieć stałych elementów - można je dopasować do wybranego rozmiaru.
Podczas zmiany rozmiaru widgetu wywoływana jest metoda onAppWidgetOptionsChanged(). To w jej treści możemy określić które elementy będą widoczne a które nie. Żeby zorientować się jaki jest aktualny (ustawiony przez użytkownika) rozmiar trzeba wywołać metodę getAppWidgetOptions(), która zwróci w odpowiedzi obiekt typu Bundle, który zawiera:

  • OPTION_APPWIDGET_MIN_WIDTH - dolna granica szerokości w jednostkach dp
  • OPTION_APPWIDGET_MAX_WIDTH - górna granica szerokości w jednostkach dp
  • OPTION_APPWIDGET_MIN_HEIGHT - dolna granica długości w jednostkach dp
  • OPTION_APPWIDGET_MAX_HEIGHT - górna granica długości w jednostkach dp

Wygląd widgetu - podstawowy projekt

Układ widgetu bazuje na klasie RemoteViews

Tworząc układ widgetu trzeba pamiętać o:
  • sprawdzeniu, czy wybrany układ (layout) i elementy są wspierane przez RemoteViews
  • marginesach całego widgetu - widget nie powinien stykać się z krawędziami ekranu i z innymi aplikacjami (od wersji Android 4.0 (targetSdkVersion >= 14) te marginesy są ustawiane domyślnie, nie trzeba ich dodatkowo wprowadzać)
  • marginesach elementów widgetu - powinny być od siebie trochę odsunięte, żeby dało się ich używać
Ciekawym elementem układu jest stub - niewidoczny obiakt, który można w trakcie wykonywania programu zmienić na inny, powiązany.

czwartek, 21 czerwca 2018

Konfiguracja widgetu czyli metadane

Metadane określają podstawowe parametry widgetu:
  • minWidth, minHeight - minimalna, domyślna przestrzeń zajmowana przez widget
    •  widgety zajmują na ekranie pewne zdefiniowane komórki (tak samo ustawiając ikonki aplikacji na ekranie też nie możemy ich mieć gdziekolwiek), jeśli podana wartość nie będzie odpowiadała wielkości pełnych komórek, zostanie zaokrąglona w górę, tak aby zająć pełną liczbę zdefiniowanych komórek
    • minimalna wartość nie może być większa niż 4x4 komórki (żeby zachować przenoszalność widgetu na różne urządzenia)
  •  minResizeWidth, minResizeHeight - wielkość, poniżej której widget nie będzie nadawał się do użycia
    • ustawienie tych parametrów oznacza, że użytkownik będzie mógł zmienić wielkość widgetu na mniejszy niż domyślny (ale nie mniejszy niż podany w tym parametrze)
  •  updatePeriodMillis - częstotliwość z jaką widget będzie wysyłał żądanie aktualizacji (onUpdate(), AppWidgetProvider )
    • podana wartość jest tylko szacunkowa, system wykona aktualizację mniej więcej w tym czasie, jeśli będzie mógł
    • trzeba ostrożnie dobrać ten parametr, żeby nie zużywać za dużo baterii 
    • sysem wybudzi urządzenie z uśpienia gdy nadejdzie czas aktualizacji ustawiony w tym parametrze
    • żeby uniknąć budzenia urządzenia, można ustawić aktualizację opartą na alarmie "niebudzącym" urządzenia
      • korzystając z AlarmManager ustaw alarm przez Intent otrzymywany przez AppWidgetProvider
      • ustaw alarmowi typ ELAPSED_REALTIME lub RTC
      • ustaw updatePeriodMillis = 0
  • initialLayout - ścieżka do pliku określającego wygląd (layout) widgetu
  • configure - aktywność, która zostanie uruchomiona po dodaniu widgetu w celu umożliwienia użytkownikowi skonfigurowania go
  • previewImage - podgląd przykładowo skonfigurowanego widgetu dla użytkownika, dostępny dla niego gdy wybiera widgety
    • jeśli nie ma ustwionego tego parametru, jako podgląd wyświetlana będzie ikona aplikacji
  • autoAdvanceViewId - id widoku "rozszerzonego"
    • dla widgetów, które mogą być rozbudowywane (advanceable)
    • informuje host co powinno być rozbudowywane
    • host będzie wykonywał akcję advance() gdy będzie to sensowne, czyli np. tylko gdy widget będzie widoczny
  • resizeMode - określa zasady zmiany rozmiaru widgetu - horizontal, vertical, none
  • widgetCategory - określa gdzie może być wyświetlany widget - home_screen, keyguard (tylko do Android 5.0), both

środa, 20 czerwca 2018

AndroidManifest - dodajemy widget

W manifeście informujemy aplikację o składowych częściach widgetu.

(deklarujemy obiekt, który będzie mógł odbierać informacje od aplikacji i/lub systemu)
<receiver android:name="nazwa AppWidgetProvider" > 
(tu powyżej -"name"- mówimy aplikacji jak nazywa się klasa, która ma informacje jak będzie zachowywał się ten obiekt - widget)
    <intent-filter> 
(tu deklarujemy, na co widget będzie reagował, co będzie odbierał)
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> 
(jeśli widget ma być aktualizowany należy to koniecznie zadeklarować)
    </intent-filter> (tu mówimy gdzie są dane konfiguracyjne widgetu)
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>
więcej o atrybucie <receiver>

wtorek, 19 czerwca 2018

Z czego składa się widget?

  • AppWidgetProviderInfo - plik XML zawierający metadane naszego widgetu
    • metadane -  dane o danych – ustrukturalizowane informacje stosowane do opisu zasobów informacji lub obiektów informacji, dostarczające szczegółowych danych, dotyczących atrybutów zasobów lub obiektów informacji, w celu ułatwienia ich znalezienia, identyfikacji, a także zarządzania tymi zasobami
  • AppWidgetProvider (implementacja) - zdefiniownie tego jak widget będzie się zachowywał pod wpływem różnych działań
  • View layout - plik XML opisujący wygląd widgetu
  • App Widget configuration Activity (opcjonalnie) - sktywność, która otworzy się w momencie dodawania widgetu i umożliwi ustawienie konfiguracji
  • zmiana w AndroidManifest.xml - deklaracja atrybutu <receiver> o takiej nazwie jaką ma dodany wcześniej AppWidgetProviderInfo

poniedziałek, 18 czerwca 2018

Co to jest widget?

To taka miniaturka aplikacji - np. większość telefonów ma na Home Screen ustawiony zegar.
Widget:
  • może być osadzony w innych aplikacjach (taka aplikacja to App Widget Host i jest nią np. home screen),
  • cyklicznie się aktualizuje,
  • jest wyświetlany dzięki App Widget Provider.

piątek, 18 maja 2018

Dlaczego moja aplikacja nie ma tej ładnej ikonki?

Znowu się na to złapałam.
Wygrywam apkę na wirtualkę a tam androidek zamiast mojej ikonki.
Sprawdzam manifest - OK.
Sprawdzam grafikę - OK.
Co się dzieje?
Jest nie w tym katalogu - znowy bezmyślnie dodałam ją do katalogu anydpi. A z tego katalogu poprawnie obsługiwane są grafiki wektorowe a nie rastowe...
Po przełożeniu mojego .png do innego katalogu wszystko zadziałało.

czwartek, 17 maja 2018

Dlaczego "R" jest czerwone?

Coś jest nie tak z moimi zasobami.
Po raz kolejny mam nadzieję, że to kompilator ma jakiś problem - przecież dopiero go włączyłam, pewnie coś się źle załadowało. W dodatku znowu chce zrobić jakiś update. Pewnie to to.

NIE!

Zrobiłam "niewielką" modyfikację w zasobach, dodałam tylko jedną linijkę w stylach, kompilator jej nie podkreślił, uznałam, że wszystko jest OK. A jednak nie było.

Od dziś:
Jeśli zasoby nie budują się poprawnie, upewnij się, że wprowadzone zmiany są poprawne.

piątek, 11 maja 2018

Czytać komentarze?

Komentarze dotyczące ocen aplikacji mogą być bardzo pomocne podczas tworzenia aplikacji.
Dzięki nim poznajemy różne oczekiwania klientów, możemy zobaczyć perspektywy, z jakich ludzie będą oceniać naszą aplikację.
Wiadomo, że nie uda nam się zadwolić każdego i że nie warto tego robić. Dzięki wiedzy na temat tego co może się podobać lub nie podobać użytkownikom:
  • zaprojektujemy UX dla wybranej grupy odbiorców, dobierzemy odpowiednie narzędzia przekazu
  • dopasujemy UI do tego co aktualnie najbardziej podoba się użytkownikom
  • opiszemy projekt tak, żeby instalowały go tylko zainteresowane osoby (unikniemy odinstalowania i zniechęcenia naszymi produktami)


Przejrzałam kilka aplikacji i ich komentarze - niestety bywało różnie. Część osób daje rzeczywiście wartościowy komentarz, ale często niestety spotykałam się z komentarzami bez sensu (w stylu "bla bla bla" lub "muj kot lubi ta aplikacje").

Bardzo wartościowe są opinie niezadowolonych użytkowników, zwłaszcza jeśli nie przeczytali czegoś dotyczącego działania aplikacji - bo dobra aplikacja nie potrzebuje instrukcji.

Chyba powinnam też zacząć komentować zainstalowane aplikacje.

Drobiazgi SQL

Ten post będzie edytowany

Jak połączyć kilka warunków w wyszukiwaniu z bazy danych?

SELECT column1, column2, column3 FROM tableName WHERE condition1 AND condition2;




Ciąg dalszy nastąpi...

czwartek, 10 maja 2018

Moje charakterystyczne kolory

Kolory moich turkusowych aplikacji.
Wybrałam sobie kilka - nazwijmy je turkusowy i pudrowy róż (oba w wersji normalnej i ciemnej) oraz prawie biały i prawie czarny.

<color name="colorPrimary">#39CCCC</color>
<color name="colorPrimaryDark">#198787</color>
<color name="colorAccent">#da878f</color>
<color name="colorAccentDark">#962631</color>
<color name="colorDark">#121616</color> 
<color name="colorLight">#f7dadd</color> 

Będę je jeszcze testować ;)

wtorek, 8 maja 2018

BabyPhone - zadania


Zadania:
  1. założyć projekt - github, katalog - 8.05.2018, 1,5h (o masakra - następnym razem przy zakładaniu nowego projektu muszę baaardzo dokładnie się przyjrzeć wszystkim czynnościom, które wykonuję, bo coś jest bardzo nie tak...)
  2. wstępny manifest - aplikacja otwiera aktywność ustawień  - 17.05.2018, 0,5h (wow, nie zaglądałam do projektu od 9 dni? zadanie nie zajęło 0,5, ale jak zwykle pojawiły się jakieś komplikacje...)
  3. aplikacja umożliwia włączanie widgetu widocznego przy zalogowanym ekranie
  4. aplikacja umożliwia wyłączanie widgetu
  5. widget uruchamia aktywność pokazującą obrazek
  6. obrazek z widgetu zamyka się po X sekundach (początokowo X=3)
  7. widget uruchamia serię obrazków - Y obrazków po sobie (początoko Y = 5), seria zamyka się
  8. do obrazka dodany jest dźwięk
  9. aplikacja zamiast obrazka wyświetla tekst
  10. aplikacja ma przykładową bazę danych - tabelę: obraz/tekst, dźwięk, zestaw (jeśli obiekt jest w jakimś zestawie to może być prezentowany, jest zestaw old i new do tych, które nie są prezentowane), data pierwszej prezetacji, liczba dni gdy był prezentowany

Pamiętać o:
  1. wieczornych commitach 
  2. komentarzach
  3. bierzącym blogowaniu
  4. testach automatycznych

Plan:
  • aktualizacja tego posta o informacje kiedy coś zostało zrobione i ile trwało - trudno mi planować bez jakichkolwiek danych.
  • weryfikacja - 2 tygodnie: 8 -  21.05.2018

poniedziałek, 30 kwietnia 2018

BabyPhone - zaczynamy

Powód:
Mój ukochany 2,5 letni syn uwielbia bawić się moim telefonem. Zawsze to lubił. Jego młodszy, pół roczny brat chętnie idzie w jego ślady.
Chcę przygotować aplikację, która zadba, żeby nie zrobili niczego problematycznego na moim telefonie.

Cel:
  • aplikacja dla dzieci
  • ma zaciekawić dziecko a najlepiej czegoś je nauczyć
  • ma zadbać o bezpieczeństwo telefonu

Wstępny pomysł:

Aplikacja będzie się otwierała przy zablokowanym telefonie, dzięki temu:
  • dziecko nie będzie się starało go odblokować
  • dziecko nie będzie miało dostąpu do innych aplikacji
  • dziecko nie będzie próbowało dzwonić na numery alarmowe
Oczywiście to tylko założenie... dziecko może jednak myśleć inaczej


Aplikacja musi być:
  • kolorowa
  • z interakcją
  • dźwiękowa
  • może jakiś ruch?
  • dobrze by było jakiś element nauki
  • możliwość dopasowywania przez rodzica
  • zmienna - żeby się nie nudziła

Element naukowy:
Jakiś czas temu słyszałam o metodzie uczenia dzieci czytania, matematyki i języków obcych od wieku niemowlęcego. Temat był kontrowersyjny, ostatecznie nie zdecydowałam się - wymaga od rodzica sporo pracy, czasu, poza tym ja nie chciałam moich dzieci niczego uczyć inaczej niż przez zabawę, doświaczanie, rozmowy i obserwację (mam jakieś takie przekonanie, że chcę im pokazać świat i nauczyć je cieszenia się tym światem, ciekawości świata itp.). Mniejsza o to - w każdym razie metoda jest i pomyślałam, że jest w sam raz na apkę.

Plan:
  • zacznę od ogólnej apki - BabyPhone 
  • dodam lekcję matematyki - BabyPhone Mat
  • dodam lekcję czytania - BabyPhone Read
  • dodam lekcję języka - BabyPhone Lang