Add Kotlin source code formatting

This commit is contained in:
wiecktobi 2020-06-10 16:01:05 +02:00
parent dade003d84
commit 089df551a7
2 changed files with 31 additions and 14 deletions

View File

@ -26,6 +26,7 @@
% Code
\usepackage{listings}
\usepackage{color}
\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
@ -80,6 +81,22 @@
morestring=[b]',
morestring=[b]",
}
\lstdefinelanguage{Kotlin}{
comment=[l]{//},
commentstyle={\color{gray}\ttfamily},
emph={delegate, filter, first, firstOrNull, forEach, lazy, map, mapNotNull, println, return@},
emphstyle={\color{mauve}},
identifierstyle=\color{black},
keywords={abstract, actual, as, as?, break, by, class, companion, continue, data, do, dynamic, else, enum, expect, false, final, for, fun, get, if, import, in, interface, internal, is, null, object, override, package, private, public, return, set, super, suspend, this, throw, true, try, typealias, val, var, vararg, when, where, while},
keywordstyle={\color{blue}\bfseries},
morecomment=[s]{/*}{*/},
morestring=[b]",
morestring=[s]{"""*}{*"""},
ndkeywords={@Deprecated, @JvmField, @JvmName, @JvmOverloads, @JvmStatic, @JvmSynthetic, Array, Byte, Double, Float, Int, Integer, Iterable, Long, Runnable, Short, String},
ndkeywordstyle={\color{orange}\bfseries},
sensitive=true,
stringstyle={\color{dkgreen}\ttfamily},
}
\usepackage{setspace}
\setstretch{1.2} %Zeilenabstand

View File

@ -54,7 +54,7 @@
\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{lstlisting}
\begin{lstlisting}[language=Kotlin]
class AuthenticationInterceptor(pToken: String) : Interceptor {
private val token = pToken
override fun intercept(chain: Interceptor.Chain): Response {
@ -67,7 +67,7 @@
}
\end{lstlisting}
Der Interceptor wird dem HTTP-Client hinzugefügt, welcher später bei der Erzeugung des Retrofit-Builders notwendig ist.
\begin{lstlisting}
\begin{lstlisting}[language=Kotlin]
val httpClient = OkHttpClient.Builder()
val interceptor = AuthenticationInterceptor(token)
httpClient.addInterceptor(interceptor)
@ -75,7 +75,7 @@
\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}
\begin{lstlisting}[language=Kotlin]
val builder = Retrofit.Builder()
.baseUrl("http://plesk.icaotix.de:5000")
.addConverterFactory(GsonConverterFactory.create())
@ -84,7 +84,7 @@
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}
\begin{lstlisting}[language=Kotlin]
@POST("/login")
fun login(@Body login_data: ValuesUserLogin): Call<Void>
@ -95,14 +95,14 @@
fun getAccounts(@Query("username") username : String): Call<EmbeddedAccounts>
\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}
\begin{lstlisting}[language=Kotlin]
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}
\begin{lstlisting}[language=Kotlin]
val call = service.getUser()
call.enqueue(object : Callback<ValuesUser> {
override fun onResponse(call: Call<ValuesUser>, response: Response<ValuesUser>) {
@ -133,7 +133,7 @@
Die Geofencing-Funktion ist die zentrale Funktion für die App und auch für das gesamte Projekt. Deshalb war es wichtig, dass sie frühzeitig funktioniert. \\
Um die Positon eines Gerätes zu bestimmen bedarf es einer Berechtigung, die vom Benutzer bestätigt werden muss. Für Geräte mit API-Level 28 und niedriger muss dafür die \verb|ACCESS_FINE_LOCATION|-Berechtigung gesetzt werden und für API-Level 29 und höher \verb|ACCESS_BACKGROUND_LOCATION|. \\
Der Geofence wird initialisiert, wenn für den Benutzer Geo-Daten gespeichert sind. Ist dies der Fall, so wird ein \verb|GeofencingClient| angelegt, dem dann der Geofence hinzugefügt wird. Der Geofence wird erzeugt mit den Parametern: Breitengrad, Längengrad, Radius, der Lebenszeit des Fence und den Übergangstypen. Die Typen sind in unserem Fall \verb|GEOFENCE_TRANSITION_ENTER| und \verb|GEOFENCE_TRANSITION_EXIT|, da wir immer reagieren wollen, wenn der Nutzer den Bereich verlässt oder betritt.
\begin{lstlisting}
\begin{lstlisting}[language=Kotlin]
geofencingClient = LocationServices.getGeofencingClient(this)
geofence = Geofence.Builder().setRequestId("Geofence")
.setCircularRegion(lat, long, rad)
@ -142,14 +142,14 @@
.build()
\end{lstlisting}
Um den Geofence-Client zu starten wird auf das Objekt die \verb|addGeofences|-Methode ausgeführt mit einem \verb|GeofencingRequest|-Objekt und einem \verb|PendingIntent|-Objekt als Parameter.
\begin{lstlisting}
\begin{lstlisting}[language=Kotlin]
geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
addOnSuccessListener { ... }
addOnFailureListener { ... }
}
\end{lstlisting}
In der \verb|getGeofencingRequest|-Methode wird festgelegt auf welches initiale Event reagiert werden soll und der oben erstellte Geofence wird hinzugefügt. Als initiales Event habe ich \verb|INITIAL_TRIGGER_ENTER| gewählt, da es ausgelöst wird wenn man sich bereits im Bereich befindet und die App startet. Denn erst mit dem Eintrittsevent wird der Button zum Starten der Aufzeichnung freigeschaltet. Das \verb|geofencePendingIntent| definiert die BroadcastReceiver-Klasse, welche bei jedem Event aufgerufen wird.
\begin{lstlisting}
\begin{lstlisting}[language=Kotlin]
private fun getGeofencingRequest(): GeofencingRequest {
return GeofencingRequest.Builder().apply {
setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
@ -163,11 +163,11 @@
\end{lstlisting}
Die \verb|GeofenceBroadcastReceiver|-Klasse definiert was bei den jeweiligen Events ausgeführt werden soll. In unserem Fall ist dies das Verändern einer boolean Shared-Prefrences-Variable, je nachdem ob der Bereich betreten oder verlassen wurde. Warum diese Art und Weise gewählt wurde lesen Sie in Kapitel \ref{sec:Probleme}.\\
Das Code-Beispiel zeigt die Aktion beim Betreten des Bereichs.
\begin{lstlisting}
context!!.getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
?.edit()
?.putBoolean("ENABLED", true)
?.apply()
\begin{lstlisting}[language=Kotlin][language=Kotlin]
context!!.getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
?.edit()
?.putBoolean("ENABLED", true)
?.apply()
\end{lstlisting}
In der \verb|MainActivity| wird ein Listener für diese Shared-Prefrences-Variable definiert. Je nachdem zu welchem Wert sich die Variable ändert wird der Start/Stopp-Button freigeschalten oder gesperrt und wenn der Benutzer den Bereich verlässt, aber noch aufzeichnet, wird diese gestoppt und gespeichert. \\ \\