Change ich in android chapter to wir

This commit is contained in:
Marcel Schwarz 2020-06-11 14:15:31 +02:00
parent 207f300e66
commit cdb87ea4c9

View File

@ -53,7 +53,7 @@
Wie zu erkennen ist lag der Fokus der Implementierung deutlich auf der Main Activtiy, da sie auch das Wichtigste der App beinhaltet. Prominent ist dabei der 'START'-Knopf an der Unterseite, mit dem die Aufzeichnung gestartet werden kann (genaueres im Kapitel \ref{subsec:main}). Wie zu erkennen ist lag der Fokus der Implementierung deutlich auf der Main Activtiy, da sie auch das Wichtigste der App beinhaltet. Prominent ist dabei der 'START'-Knopf an der Unterseite, mit dem die Aufzeichnung gestartet werden kann (genaueres im Kapitel \ref{subsec:main}).
\subsection{Authentifizierung} \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. \\ 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 wird das Token im privaten Speicher der App gespeichert. 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-Aufrufen Header-Daten mitgegeben werden. In unserem Fall ist das das \verb|Authorization|-Feld mit dem Token. 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-Aufrufen Header-Daten mitgegeben werden. In unserem Fall ist das das \verb|Authorization|-Feld mit dem Token.
\begin{lstlisting}[language=Kotlin] \begin{lstlisting}[language=Kotlin]
class AuthenticationInterceptor(pToken: String) : Interceptor { class AuthenticationInterceptor(pToken: String) : Interceptor {
@ -149,7 +149,7 @@ geofencingClient.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.ru
addOnFailureListener { ... } addOnFailureListener { ... }
} }
\end{lstlisting} \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. 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 haben wir \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}[language=Kotlin] \begin{lstlisting}[language=Kotlin]
private fun getGeofencingRequest(): GeofencingRequest { private fun getGeofencingRequest(): GeofencingRequest {
return GeofencingRequest.Builder().apply { return GeofencingRequest.Builder().apply {
@ -190,14 +190,14 @@ context!!.getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
Direkt unter der Top-Action-Bar wird der Benutzer mit dem Vornamen begrüßt (Abb.: \ref{Abb:main}). \\ Direkt unter der Top-Action-Bar wird der Benutzer mit dem Vornamen begrüßt (Abb.: \ref{Abb:main}). \\
In der Bedienfläche kann der Benutzer den Timetrack-Account auswählen und dessen Details ansehen, seine heutigen Arbeitszeiten ansehen und die Aufzeichnung starten oder stoppen. \\ In der Bedienfläche kann der Benutzer den Timetrack-Account auswählen und dessen Details ansehen, seine heutigen Arbeitszeiten ansehen und die Aufzeichnung starten oder stoppen. \\
Die Auswahl des Accounts erfolgt über ein Dropdown-Menü. Bei Auswahl wird sofort die zugehörige Beschreibung und die Vergütung angezeigt. Wenn die Aufzeichnung am laufen ist, wird das Dropdown-Menü ausgeblendet. Das verhindert, dass der Benutzer eine Aktivität für einen andern Account stoppen kann als er sie gestartet hat. Ist für den Benutzer noch kein Account vorhanden, wird "None" im Menü angezeigt und die beiden Felder für Beschreibung und Vergütung werden ausgeblendet. \\ Die Auswahl des Accounts erfolgt über ein Dropdown-Menü. Bei Auswahl wird sofort die zugehörige Beschreibung und die Vergütung angezeigt. Wenn die Aufzeichnung am laufen ist, wird das Dropdown-Menü ausgeblendet. Das verhindert, dass der Benutzer eine Aktivität für einen andern Account stoppen kann als er sie gestartet hat. Ist für den Benutzer noch kein Account vorhanden, wird "None" im Menü angezeigt und die beiden Felder für Beschreibung und Vergütung werden ausgeblendet. \\
Für die Anzeige der heutigen Arbeitszeiten habe ich eine RecyclerView verwendet. Das Layout dazu wird in einer extra XML-Datei definiert und mit Daten in einer Adapter-Klasse befüllt. Durch eine Backendabfrage bekomme ich die nötigen Daten dafür. Bei aktiver Aufzeichnung wir ein Element angezeigt mit der Startzeit und der Info, dass das Ende offen ist. Für die Anzeige der heutigen Arbeitszeiten haben wir eine RecyclerView verwendet. Das Layout dazu wird in einer extra XML-Datei definiert und mit Daten in einer Adapter-Klasse befüllt. Durch eine Backendabfrage bekommen wir die nötigen Daten dafür. Bei aktiver Aufzeichnung wir ein Element angezeigt mit der Startzeit und der Info, dass das Ende offen ist.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
\includegraphics[width=0.4\linewidth]{img/android/main_recording} \includegraphics[width=0.4\linewidth]{img/android/main_recording}
\caption{Laufende Aufzeichnung} \caption{Laufende Aufzeichnung}
\label{Abb:menu} \label{Abb:menu}
\end{figure} \end{figure}
Der Start-Stop-Button schält die Aufzeichnung um, in dem ein Backend-Endpunkt angesprochen wird. In der App habe ich eine boolean-Variable \verb|running| definiert, welche gespeichert hält ob die Aufzeichnung aktiv ist. Anhand ihr wird entschieden wie der Start-Stop-Button aussieht und ob beim Verlassen des Geofence noch gestoppt werden muss. Der Button ist nicht Auswählbar wenn sich der Nutzer außerhalb seines Arbeitsplatzes befindet und zeigt dies auch an (Abb.: \ref{Abb:outside}). Ist der Nutzer dann im Bereich, wird "Start" angezeigt und der Button ist freigeschaltet. Während der Aufzeichnung trägt der Button die Schrift "Stop". Hat der Nutzer noch keine Geo-Daten für seinen Arbeitsplatz definiert, wird auch das auf dem Button angezeigt. Der Start-Stop-Button schält die Aufzeichnung um, in dem ein Backend-Endpunkt angesprochen wird. In der App haben wir eine boolean-Variable \verb|running| definiert, welche gespeichert hält ob die Aufzeichnung aktiv ist. Anhand ihr wird entschieden wie der Start-Stop-Button aussieht und ob beim Verlassen des Geofence noch gestoppt werden muss. Der Button ist nicht Auswählbar wenn sich der Nutzer außerhalb seines Arbeitsplatzes befindet und zeigt dies auch an (Abb.: \ref{Abb:outside}). Ist der Nutzer dann im Bereich, wird "Start" angezeigt und der Button ist freigeschaltet. Während der Aufzeichnung trägt der Button die Schrift "Stop". Hat der Nutzer noch keine Geo-Daten für seinen Arbeitsplatz definiert, wird auch das auf dem Button angezeigt.
\begin{figure}[H] \begin{figure}[H]
\centering \centering
\includegraphics[width=0.4\linewidth]{img/android/btn_outside} \includegraphics[width=0.4\linewidth]{img/android/btn_outside}
@ -230,13 +230,13 @@ context!!.getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
\section{Probleme und Lösungen}\label{sec:Probleme} \section{Probleme und Lösungen}\label{sec:Probleme}
Damit die App auch die aktuellste Android Version unterstützt, mussten einige zusätzliche Punkte berücksichtigt werden. Neben der Berechtigung aus Kapitel \ref{subsec:geofence} musste in der \verb|build.gradle|-Datei Kompilierungsoptionen gesetzt werden. \\ Damit die App auch die aktuellste Android Version unterstützt, mussten einige zusätzliche Punkte berücksichtigt werden. Neben der Berechtigung aus Kapitel \ref{subsec:geofence} musste in der \verb|build.gradle|-Datei Kompilierungsoptionen gesetzt werden. \\
Zu beginn wollte ich alle Activities mit Fragments realisieren, sodass es nur eine Activity gibt und alles weitere Fragments sind. Allerdings war es schwieriger zwischen den Fragments zu wechseln, als in den Tutorials beschrieben. Deshalb wurde mir von einem Teamkollegen empfohlen auf nur Activities umzusteigen. Zwischen diesen ist das hin- und herschalten deutlich einfacher, hat jedoch kein Zugriff auf Elemente der anderen Activities. \\ Zu beginn wollten wir alle Activities mit Fragments realisieren, sodass es nur eine Activity gibt und alles weitere Fragments sind. Allerdings war es schwieriger zwischen den Fragments zu wechseln, als in den Tutorials beschrieben. Deshalb wurde mir von einem Teamkollegen empfohlen auf nur Activities umzusteigen. Zwischen diesen ist das hin- und herschalten deutlich einfacher, hat jedoch kein Zugriff auf Elemente der anderen Activities. \\
Das wurde bemerkbar, als ich aus der Klasse \verb|GeofenceBroadcastReceiver| eine Methode der \\\verb|MainActivity| zur Änderung der Oberfläche aufrufen wollte. Das hat den Grund, dass Android nicht sicher sagen kann das diese Activity gerade auch aktiv ist. Deshalb habe ich den Weg über die Shared Preferences gewählt mit einem Listener in der \verb|MainActivity|. \\ Das wurde bemerkbar, als wir aus der Klasse \verb|GeofenceBroadcastReceiver| eine Methode der \\\verb|MainActivity| zur Änderung der Oberfläche aufrufen wollte. Das hat den Grund, dass Android nicht sicher sagen kann das diese Activity gerade auch aktiv ist. Deshalb haben wir den Weg über die Shared Preferences gewählt mit einem Listener in der \verb|MainActivity|. \\
Initial wollte ich das Token in einer Datenklasse abspeichern, welche beim Einloggen befüllt wird. Dazu müsste allerdings das Objekt oder die Referenz zu jeder anderen Activity übergeben werden. Eine andere Möglichkeit stellen erneut die Shared Preferences dar. Wäre auch eine gute Lösung gewesen, welche ich aber dazu zu spät kennengelernt habe. Deshalb habe ich das Problem mit dem privaten Speicher gelöst. Er ist auch durch andere Apps und den Benutzer nicht einsehbar, bildet deshalb also keine Sicherheitslücke.\\ Initial wollten wir das Token in einer Datenklasse abspeichern, welche beim Einloggen befüllt wird. Dazu müsste allerdings das Objekt oder die Referenz zu jeder anderen Activity übergeben werden. Eine andere Möglichkeit stellen erneut die Shared Preferences dar. Wäre auch eine gute Lösung gewesen, welche wir aber dazu zu spät kennengelernt haben. Deshalb haben wir das Problem mit dem privaten Speicher gelöst. Er ist auch durch andere Apps und den Benutzer nicht einsehbar, bildet deshalb also keine Sicherheitslücke.\\
Unerwartet war, dass die Geofence-Funktion die normale Android Positionsbestimmung zusätzlich benötigt. Denn zuerst, hatte ich die Positionsbestimmung implementiert und dann die Geofence-Funktion, was funktioniert hat. Da in der Geofence-Funktion kein Code der normalen Positionsbestimmung referenziert wurde, dachte ich man könne diesen weglassen, was ein Trugschluss war. Auch der Versuch Teile der Positionsbestimmung wegzulassen war ohne Erfolg. Deshalb beinhaltet die App auch Code für die normale Positionsbestimmung. Unerwartet war, dass die Geofence-Funktion die normale Android Positionsbestimmung zusätzlich benötigt. Denn zuerst, hatten wir die Positionsbestimmung implementiert und dann die Geofence-Funktion, was funktioniert hat. Da in der Geofence-Funktion kein Code der normalen Positionsbestimmung referenziert wurde, dachten wir, man könne diesen weglassen, was ein Trugschluss war. Auch der Versuch Teile der Positionsbestimmung wegzulassen war ohne Erfolg. Deshalb beinhaltet die App auch Code für die normale Positionsbestimmung.
\section{Deployment} \section{Deployment}
Das Deployment spielte im Entwicklungsprozess der App keine große Rolle, da es Android-Studio benötigt um die App zu starten. Zum Abschluss habe ich allerdings den aktuellen Stand des Projekts in einer APK-Datei persistiert. Damit lässt sich die App auf andere Geräte installieren und in den App-Store laden. Zur Erstellung einer solchen APK muss ein Key zur Signatur angegeben werden. Das Deployment spielte im Entwicklungsprozess der App keine große Rolle, da es Android-Studio benötigt um die App zu starten. Zum Abschluss haben wir allerdings den aktuellen Stand des Projekts in einer APK-Datei persistiert. Damit lässt sich die App auf andere Geräte installieren und in den App-Store laden. Zur Erstellung einer solchen APK muss ein Key zur Signatur angegeben werden.