From 5b2d6a7cfe5d997f715de4d88b64de512937d113 Mon Sep 17 00:00:00 2001 From: wiecktobi Date: Mon, 8 Jun 2020 19:28:59 +0200 Subject: [PATCH] Finish chapter 6.3.3 --- documentation/parts/android.tex | 71 +++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/documentation/parts/android.tex b/documentation/parts/android.tex index 61b773a..e310d43 100644 --- a/documentation/parts/android.tex +++ b/documentation/parts/android.tex @@ -54,36 +54,81 @@ \subsection{Authentifizierung} Zur Authentifizierung benutzen wir JWT, welches bei jeder Anfrage ans Backend mit geschickt werden muss. Das Token erhält man beim Einloggen mit den richtigen Daten und muss persistiert werden, bis sich der Benutzer ausloggt. Dazu speichere ich das Token im privaten Speicher der App. In allen weiteren Activities kann dann auf den Speicher zugegriffen werden und das Token beim Erstellen des \verb|AuthenticationInterceptor|s mitgegeben werden. Beim Ausloggen wird einfach die Datei mit dem Token aus dem Speicher gelöscht. \\ Der \verb|AuthenticationInterceptor| ist Kind von der \verb|Interceptor|-Klasse aus der \verb|okhttp3|-Bibliothek, welche in Retrofit eingebunden ist. Mithilfe des Interceptors können REST-Abfragen Header-Daten mitgegeben werden. In unserem Fall ist das das \verb|Authorization|-Feld mit dem Token. - \begin{verbatim} + \begin{lstlisting} class AuthenticationInterceptor(pToken: String) : Interceptor { private val token = pToken - override fun intercept(chain: Interceptor.Chain): Response { val original = chain.request() val builder = original.newBuilder() - .header("Authorization", token) + .header("Authorization", token) val request = builder.build() return chain.proceed(request) } } - \end{verbatim} + \end{lstlisting} Der Interceptor wird dem HTTP-Client hinzugefügt, welcher später bei der Erzeugung des Retrofit-Builders notwendig ist. - \begin{verbatim} - val httpClient = OkHttpClient.Builder() - val interceptor = AuthenticationInterceptor(token) - httpClient.addInterceptor(interceptor) - \end{verbatim} + \begin{lstlisting} + val httpClient = OkHttpClient.Builder() + val interceptor = AuthenticationInterceptor(token) + httpClient.addInterceptor(interceptor) + \end{lstlisting} \subsection{Anzeige der Daten in der Main Activity}\label{subsec:AnzeigeDaten} Die Daten werden per REST-Aufruf mithilfe vom Retrofit-Framework vom Backend geholt. Um Anfragen zusenden benötigt man einen Retrofit-Builder. Diesem wird die anzufragende URL, ein JSON-Konverter und ein HTTP-Client mitgegeben. Aus diesem Builder und einer Service-Klasse, in der die Methoden definiert sind, wird ein Objekt erzeugt mit dem die Methoden aufrufbar sind. - + \begin{lstlisting} val builder = Retrofit.Builder() - .baseUrl("http://plesk.icaotix.de:5000") - .addConverterFactory(GsonConverterFactory.create()) - .client(httpClient.build()) + .baseUrl("http://plesk.icaotix.de:5000") + .addConverterFactory(GsonConverterFactory.create()) + .client(httpClient.build()) val retrofit = builder.build() service = retrofit.create(GeofenceService::class.java) + \end{lstlisting} + Die Klasse \verb|GeofenceService| dient, wie oben beschrieben, zur definition der Endpunkte in From von Methodenaufrufen. Dort wird definiert, ob es ein \verb|POST| oder \verb|GET| Entpunkt ist, wie der Pfad lautet und was für Parameter mitgegeben werden. + \begin{lstlisting} + @POST("/login") + fun login(@Body login_data: ValuesUserLogin): Call + @GET("whoami") + fun getUser(): Call + + @GET("accounts/search/findByUsername") + fun getAccounts(@Query("username") username : String): Call + \end{lstlisting} + Der Rückgabewert der Methoden ist immer vom Typ \verb|Call|. Wenn aus dem Body Werte gelesen werden sollen, muss eine Art Skelett-Klasse angelegt werden mit den relevanten Feldern. Die Klasse \verb|ValuesUser| stellt Werte der Antwort bereit, wie z. B. den Vornamen. + \begin{lstlisting} + class ValuesUser(firstname: String) { + @SerializedName("firstname") + var firstname = firstname + } + \end{lstlisting} + Der Aufruf der Methode erfolgt Asynchron. Deshalb darf sich nicht auf das Ergebnis des Aufrufs direkt danach verlassen werden, sonst bekommt man eine Null-Pointer-Excetion. Die Methode \verb|enqueue| besitzt ein Callback-Objekt als Parameter, welches \verb|onResponse| und \verb|onFailure| überschreibt. Dort wird entsprechend definiert was in den jeweiligen Fällen ausgeführt werden soll. + \begin{lstlisting} + val call = service.getUser() + call.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + val firstname = response.body()?.firstname + lbl_username.text = "Hello " + firstname + } else { + println("Response not successful: ${response.code()}") + } + } + override fun onFailure(call: Call, t: Throwable) { + println("Response 'whoami' failed. " + t.message) + } + }) + \end{lstlisting} + \bigskip + In dieser Art und Weise werden alle Anfragen ans Backend gehandhabt. Dazu zählen: + \begin{itemize} + \item Abfragen der Location-Daten zu dem Benutzer für den Geofence + \item Befüllen des Dropdown-Menüs mit den Timetrack-Accounts des Benutzers + \item Anzeigen der Beschreibung und der Vergütung + \item Befüllen des RecyclerViews mit den heutigen Einträgen + \item Auslösen des Start-/Stopp-Events + \item Einloggen + \end{itemize} + \subsection{Geofencing} \section{Funktionen der App} \subsection{Login Screen}