Android: Retrofit

Před několika lety jsem komunikoval se serverem přes balíček Volley. Protože komunikuji hlavně přes REST rozhraní, nahradil jsem Volley balíčkem Retrofit, který je pro tyto účely doporučovaný.

Retrofit je HTTP klient pro Android (Javu), který vám usnadňuje připojení k webové službě REST API. Usnadňuje konzumaci dat JSON a XML. Podporuje příkazy: GET, POST, PUT, PATCH a DELETE. Pojďme si ukázat jak na to.

Nainstalujeme závislosti:

// Retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'

// JSON Parsing
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

+ internet permission

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.chikeandroid.retrofittutorial">
 
    <uses-permission android:name="android.permission.INTERNET" />
 
    <application>
       ....
    </application>
 
</manifest>

Retrofit je knihovna, která používá anotované rozhraní pro volání REST API. Vytvoříme jednoduchý požadavek GET pro seznam knih, který se automaticky namapuje na objekty. API pro seznam knih vrací tento JSON:

{  
    "books":[  
        {  
            "author":"Božena Němcová",
            "title":"Babička"
        },
        {  
            "author":"Božena Němcová",
            "title":"V zámku a v podzámčí"
        },
        {  
            "author":"Karel Čapek",
            "title":"Krakatit"
        },
        {  
            "author":"Karel Čapek",
            "title":"Jak se co dělá"
        }
   ]
}

jsonschema2pojo

Z JSONu necháme vygenerovat objekty přes službu jsonschema2pojo. Vložíme JSON, vybereme Source type: JSON, Annotation style: Gson a odškrtneme Allow additional properties.

Rozhraní služby

Modelovou třídu máme vytvořenou, vytvoříme rozhraní služby, které bude spravovat API endpointy. Vytvoříme GET požadavek pro načtení všech knih:

package cz.vencax.retrofit;

import cz.vencax.retrofit.model.Book;
import cz.vencax.retrofit.model.Books;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;

public interface BookService {
    @GET("books")
    Call<Books> getAllBooks();
}

Všimněte si anotace @GET označující typ požadavku a nabývající hodnotu books, která označuje endpoint (tato hodnota se přidá k BASE_URL popsané níže).

RetrofitClient

požadavky na REST API budeme odesílat přes třídu RetrofitClient:

package cz.vencax.retrofit;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    static final String BASE_URL = "http://demo7646366.mockable.io/";
    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit==null) {

            Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }
        return retrofit;
    }
}

Kde BASE_URL nabývá hodnoty se základním URL API, ke kterému se přidávají hodnoty z rozhraní služby popsané výše.

Získání dat v activity

V activity poté data jednoduše získáme a můžeme s nimi dále pracovat. U pozorňuji, že data jsou získána asynchronně:

    private void getBooksFromApi() {

        Log.d("xxx", "getBooksFromApi");

        BookService bookService = RetrofitClient.getClient().create(BookService.class);
        Call<Books> books = bookService.getAllBooks();
        books.enqueue(new Callback<Books>() {

            @Override
            public void onResponse(Call<Books> call, Response<Books> response) {
                Log.d("xxx", "onResponse: " + response);
                if (response.isSuccessful()) {
                    Log.d("xxx", "response.isSuccessful()" + response.body());
                    Books books = response.body();

                    Log.d("xxx", "response.isSuccessful(), books count: " + books.getBooks().size());
                    for (Book book : books.getBooks()) {
                        Log.d("xxx", "Book: " + book.getTitle() + ", " + book.getAuthor());
                    }
                } else {
                    Log.d("xxx", "response NOT isSuccessful()" + response.errorBody().source());
                }
            }

            @Override
            public void onFailure(Call<Books> call, Throwable t) {
                Log.d("xxx", "onFailure: " + t);
            }
        });
    }

Touto jednoduchou ukázkou jsem vám představil jak na to. Jednoduché, že?

Odeslání dat na server – POST

Pokud však potřebujete posílat nějaká data, je nutné je ukládat přes metody PUT nebo POST. Pojďme se podívat jak na to s metodou POST (PUT by byla analogická).

V rozhraní služby vytvoříme metodu POST s endpointem a parametrem @body:

package cz.vencax.retrofit;

import cz.vencax.retrofit.model.Book;
import cz.vencax.retrofit.model.Books;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;

public interface BookService {
    @POST("send")
    Call<Book> postBook(@Body Book book);
}

Na endpoint BASE_URL/send odešleme objekt Book – tento objekt se automaticky převede na JSON.

Odeslání POST požadavku z activity:

    private void postBookToApi() {

        Log.d("xxx", "postBookToApi");

        BookService bookService = RetrofitClient.getClient().create(BookService.class);

        Book bookPost = new Book();
        bookPost.setAuthor("vEnCa-X");
        bookPost.setTitle("Jak na Android - Retrofit");

        Call<Book> book = bookService.postBook(bookPost);
        book.enqueue(new Callback<Book>() {

            @Override
            public void onResponse(Call<Book> call, Response<Book> response) {
                Log.d("xxx", "onResponse: " + response);
                if (response.isSuccessful()) {
                    Log.d("xxx", "response.isSuccessful(): " + response.body());
                    Book book = response.body();
                    Log.d("xxx", "Book: " + book.getTitle() + ", " + book.getAuthor());
                } else {
                    Log.d("xxx", "response NOT isSuccessful()" + response.errorBody().source());
                }
            }

            @Override
            public void onFailure(Call<Book> call, Throwable t) {
                Log.d("xxx", "onFailure: " + t);
            }
        });
    }

Další způsob bez call

Response<Book> book = bookService.postBook(bookPost).execute();
if (book.isSuccessful()) {
   Log.d("xxx", "book reponse: " + book);
}

Problémy

  • máš aktuální verze balíčků?

Jaký je rozdíl mezi lampami MFL / WFL / NSP?

MFL, WFL a NSP jsou zkratky pro velikost světelného paprsku.

Description Code Typcal MR16 Beam Angle Typical PAR Beam Angle
Very Narrow Spot VNSP <7 degrees <15 degrees
Narrow Spot NSP 5-15 degrees 15-30 degrees
Spot SP 16-22 degrees 30-60 degrees
Narrow Flood NFL 23-32 degrees 60-90 degrees
Flood FL 32-45 degrees 90-120 degrees
Wide Flood WFL 45-60 degrees 120-160 degrees
Very Wide Flood VWFL >60 degrees >160 degrees

 

 

Android: životní cyklus activity a fragmentu při otočení displeje

Activity

Pojďme si ukázat životní cyklus activity. Červeně je zvýrazněn životní cyklus při otočení displeje. Pokud chcete přenést nějaký stav do nově otočené activity, máte možnost si tento stav uložit v metodě onSaveInstanceState() a obnovit ho v onRestoreInstanceState() nebo v onCreate()

Fragment

U fragmentu máme podobné možnosti. Stavy opět ukládáme v metodě  onSaveInstanceState() a obnovíme v onRestoreInstanceState() nebo v onCreate() nebo v onCreateView()

Nebo můžeme v metodě onCreate() zavolat setRetainInstance(true); čímž se životní cyklus při rotaci změní takto:

Tento způsob je vhodný například pro použití services, které při rotaci v onDestroy nezabijeme ale chce s nimi dále pracovat.

X na rybách: metoda feeder

Od doby kdy jsem byl naposledy chytat ryby se toho hodně změnilo. Dneska nejčastější a nejvíce doporučovanou metodou pro chytání ryb je takzvané chytání na feeder. Jedná se o speciální metodu chytání na položenou. Máte speciální krmítko (většinou z jedné strany ploché), pod krmítkem obratlík a na obratlíku 1 návazec (do 15cm délky). Návazců můžete mít více a vybrat vodný podle toho jak velké ryby chcete chytat a podle aktuálních podmínek. Tato metoda je velice efektivní. Krmítko dopadne na dno vždy plochou stranou dolů. Voňavé krmení z krmítka přiláká rybu, která vidí 1 větší nástrahu na háčku, které neodolá. Při této metodě je velká šance, že ryba nástrahu najde a zkusí ji ochutnat, díky krátkému návazci se háček chytí v rybí tlamce a krmítko podpoří efekt samozáseku.

Krmítko a obratlík

Na kmenový vlasec nasadíme krmítko, pod které navážeme obratlík. Chytá se na „placaté“ krmítko. Krmítko slouží zároveň jako zátěž. Existují různé váhy krmítka. Váhu krmítka volte podle odhozové váhy zátěže prutu.

Výroba návazce

Návazec uděláme z pletené šňůry. Na jeden konec dáme háček a na druhý konec očko. Na návazec navlčeme s boile needle gumičku, která chrání před vyvlečením návazce z obratlíku.

Uzel pro vytvoření smyčky na návazci

  1. zdvojte vlasec přeložením
  2. na zdvojeném vlasci udělejte smyčku
  3. zvonu prostrčte konec zdvojeného vlasce smyčkou
  4. utáhněte uzel a zastřihněte konec

 

Pokud je pletená šňůra tlustší, vynechte krok 3 aby bylo možné přes uzel nasadit gumičku. Skvělá věc na smyčky je easy loop. Tahle jednoduše děláte smyčky:

Jak navázat háček s očkem

navázání háčku na návazec:

Na kmenový vlasec – uzel na háčku

Na kmenový vlasec – uzel na vlasci

Jan navázat háček s lopatkou

 

Android: dialog a jeho zachování při rotaci displeje

Pokud v Androidu používáte dialogy pro zobrazení nějaké informace uživateli, nesmíte zapomenout na znovuvytvoření activity (při rotaci displeje, zasunutím zařízení do doku, …) při které je activity ukončena a znovu vytvořena. Tím dojde ke zrušení dialogu. Vytvoří se vám v aplikaci při tomto úkonu správně znovu?

Pokud používáte klasický AlertDialog (po nějakém úkonu – ne zobrazení v onCreate) nebude dialog při otočení displeje zobrazen

DialogFragment

Řešením tohoto problému je DialogFragment, který zůstane zobrazen i po restartování activity. Uvádím jednoduchou kontru kódu jak na to:

MyAlertDialogFragment.java

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;

public class MyAlertDialogFragment extends DialogFragment {

    public static MyAlertDialogFragment newInstance(String title) {
        MyAlertDialogFragment frag = new MyAlertDialogFragment();
        Bundle args = new Bundle();
        args.putString("title", title);
        frag.setArguments(args);
        return frag;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        String title = getArguments().getString("title");

        return new AlertDialog.Builder(getActivity())
                .setTitle(title)
                .setPositiveButton("YES",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                //...
                            }
                        }
                )
                .setNegativeButton("CANCEL",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                //...
                            }
                        }
                ).create();
    }
}

Activity:

Button button= (Button) findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
      DialogFragment newFragment = MyAlertDialogFragment.newInstance("time : " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime()) );
      newFragment.show(getSupportFragmentManager(), "dialog");
   }
});

 

 

Android: zpřístupnění nově vytvořeného souboru přes USB v počítači

Řeším zajímavý úkol: vytvoř v Android zařízení soubor, který bude po připojení telefonu k PC vidět v PC a bude možné jej překopírovat z telefonu do PC.

Dejte si pozor, protože od Android 5 musíte jednotlivá oprávnění povolovat na vyžádání, my budeme potřebovat oprávnění pro ukládání na úložiště. Pro jednoduchost vynechám kód nutný pro získání oprávnění zápisu do úložiště.

Do AndroidManifext.xml přidáme oprávnění:

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

A jdeme na zdrojový kód (nezapomeňte kontrolovat oprávnění zápisu na úložiště – od SDK Android 5!):

//vytvorime slozku do ktere budeme pridavat soubory
//slozka bude v "rootu" uloziste a jmenuje se xml
File rootFolder = new File(Environment.getExternalStorageDirectory(), "xml");
if (!rootFolder.exists()) {
   rootFolder.mkdir();
}

try {
   //vytvorime soubor ktery bude pristupny v pocitaci
   File file = new File(rootFolder , "xml" + getCurrentTimeStampForFile() + ".xml");

   FileWriter writer = new FileWriter(file);
   try {
      writer.write("obsah souboru");
   } finally {
      writer.close();
   }

   //toto je dulezite, timto prikazem se soubor zviditelni a bude po pripojeni telefonu pres USB videt v pocitaci
   MediaScannerConnection.scanFile(mContext, new String[] {file.toString()}, null, null);

} catch (Exception e) {
   //problem
}

Nejdůležitější příkaz je:

MediaScannerConnection.scanFile(mContext, new String[] {file.toString()}, null, null);

Tímto příkazem se soubor zviditelní mezi ostatními soubory a bude možné ho překopírovat do počítače

Chamsys MagicQ finty

Bible pro osvětlovače používající pilt Chamsys – klávesové zkratky

F > S            fast to slowly

CW – ClockWise – po směru hodinových ručiček

CCW – Counter ClockWise – proti směru hodinových ručitek

CTC a CTO –
Korekční filtr pro změnu chromatické teploty barvy – světlo s výbojkou s tímto filtrem svítí jako halogenové světlo

Vyvolání palety

3 vyberu barvu – za 3 sekundy na vsech vsetlech stejne bude barva
3 * vyberu barvu bava – za 3 postuplne od leva najede barva
3 * / vyberu barvu – za 3 postuplne od prava najede barva
3 * – vyberu barvu – za 3 postupne od středu najede barva
3 * + vyberu barvu – za 3 postupne od krajů najede barva
3 * . vyberu barvu – za 3 nahodne najede barva

Patchování

5@2-1           – napatchuj 5 světel do universe 1 od adresy 2

Exec bitmapy

rozbalit a nakopírovat do: C:\Users\venca\Documents\MagicQ\show\bitmaps

Doporučený grid size 16/10

Nezapomenout vlevo zvolit: Appearance -> Images. Mapuju DN obrázek

Jak jsem nasadil Let’s Encrypt 2

Možná si ještě vzpomínáte na můj první článek o tom, Jak jsem nasadil Let’s Encrypt. Jo, nebyl příliš jednoduchý ale zase jste věděli co se vám na serveru děje. Dneska ukáži jednodušší způsob: Cerbot.

Instalace

apt-get install python-certbot-apache

Automatická instalace certifikátů a nastavení Apache:

certbot --apache

Pokud jste konzervativní a nechcete aby vám Cerbot zasahoval do konfigurace Apache, přidejte k příkazu certonly:

certbot --apache certonly

Po spuštění se zobrazí průvodce, který požaduje váš e-mail a nalezne všechny virtualhosty na serveru a nabídne pro ně vygenerování certifikátů. Vyberete ty, které chcete použít.

Certifikáty jsou poté vygenerovány do: /etc/letsencrypt/live/www.jmeno-domeny.cz/fullchain.pem

Obnovení platnosti certifikátu

Let’s Encrypt vydává certifikáty s platností 90 dní. Obnova certifikátů se provádí příkazem:

certbot renew --dry-run

If that appears to be working correctly, you can arrange for automatic renewal by adding a cronor systemd job which runs the following:

certbot renew

Vygenerování nového certifikátu

certbot --apache -d example.com -d www.example.com

Edit 5.5.2019
Certifikáty je nutné generovat jiným příkazem. Lets’s encrypt přestal používat šifrování HTTP-01 a přešel naTLS-SNI-01. Certifikát vygenerujeme:

certbot --authenticator standalone --installer apache -d venca-x.cz -d www.venca-x.cz --pre-hook "systemctl stop apache2" --post-hook "systemctl start apache2"



Debian 9: jak nastavit IPv6

Pokud váš poskytovatel připojení internetu nabízí IPv6, zde je ukázka nastavení. Tato konfigurace se provede i po restartu serveru.

vim /etc/network/interfaces

    iface eth0 inet6 static
        address 2001:db8::c0ca:1eaf
        netmask 64
        gateway 2001:db8::1ead:ed:beef
        dns-nameservers 2001:4860:4860::8844 2001:4860:4860::8888

Aby se změna projevila, je nutné restartovat interface

/etc/init.d/networking restart

Pokud jsme přihlášeni přes Putty, bude spojení přerušeno a je nutné se znovu připojit.

Správnou konfiguraci zjistíme příkazem:

ip address show

Správné nastavení ověříme pingem:

ping6 ipv6.google.com

 

Debian 9 (stretch) – instalace php 7.1

V poslední verzi Debianu Stretch je defaultně php 7.0, pokud potřebujete novější 7.1, je možné jej bez kompilace zdrojových kódů nainstalovat přes balíčky:

apt-get install apt-transport-https lsb-release ca-certificates
wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
apt-get update
apt-get install php7.1

Stejným způsobem nainstalujeme PHP7.2:

apt install php7.2 php7.2-cli php7.2-common php7.2-json php7.2-opcache php7.2-mysql php7.2-zip php7.2-fpm php7.2-mbstring