Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abgabe v2 Moritz Wehlitz #15

Open
wants to merge 17 commits into
base: e11_save_chat
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Dokumentation

## Einleitung
Ich habe mehrere Erweiterungen am Code vorgenommen. Diese Änderungen beinhalten die Behebung eines Fehlers, das Hinzufügen von Einstellungen, die Einführung von Farben, die Anzeige von Benutzername und ChatGPT-Name (Assistantname), die Anpassung für leere Spracheingaben, die Umstellung von CharSequence auf String, die Integration von Übersetzungen, der Funktionalität Nachrichten als Text eingeben zu können, sowie die Änderung die TextView scrollbar zu machen. Diese Erweiterungen sollen die App funktionaler, benutzerfreundlicher und für den internationalen Markt zugänglicher machen.

## Anforderungen
Fehlerbehebung: Verhindern eines Absturzes der App
Einstellungen hinzufügen: Erweiterung der Einstellungsseite für Vergabe des Benutzernamens und ChatGPT-Namens
Farben hinzufügen: Visuelle Differenzierung der Nachrichten durch die Einführung von Farben
Absendernamen anzeigen: Anzeigen von Benutzername und ChatGPT-Name im Chat
Internationalisierung: Hinzufügen von Übersetzungen
Anfrage durch Texteingabe: Hinzufügen der Möglichkeit ChatGPT-Anfragen per Texteingabe auszuführen
Benutzerfreundlichkeit: TextView scrollbar machen

## Umsetzung
Ich habe mein Projekt damit begonnen einen sauberen Fork aufzusetzen, um die Umsetzung reibungslos gestalten zu können. Nachdem ich mich ein wenig in der Basisversion umgeguckt hatte, fiel mir auf, dass die Anwendung abstürzt, wenn man das Spracheingabefenster leer wegdrückt. Also baute ich an der entsprechenden Stelle einen „null check“ ein. Als nächstes suchte ich mir einen Einstiegspunkt, mit dem ich mein Projekt beginnen konnte. Einer der Anforderungen meines Projekts war es, deutlich erkennbar zu machen, von wem die Chatnachrichten stammen. Um dies zu bewerkstelligen wollte ich vor den Nachrichten den Namen des Absenders platzieren, sowie Farben hinzufügen. Da ich es für langweilig und unpersönlich hielt vor jede Nachricht des Nutzers einfach nur „User“ zu schreiben, fügte ich zunächst Einstellungen hinzu, über die der Nutzer sich und ChatGPT einen Namen geben kann. Anschließend definierte ich in der „PrefsFacade“-Klasse die entsprechenden „get“-Methoden, über welche später die Namen abgerufen werden können. Um farbigen Text hinzufügen zu können fügte ich dem MainFragment die Methode appendColoredText hinzu. Die Methode bekommt die TextView, den hinzuzufügenden Text, und eine Farbe übergeben. Zunächst wird die Länge des Inhalts der TextView abgefragt und in „start“ gespeichert. Anschleißend wird der neue Text hinzugefügt, die Länge erneut abgefragt und in „end“ gespeichert. Zwischen diesem Start- und Endpunkt, wo sich der neue Text befindet, wird die Textfarbe auf den übergebenen Wert gesetzt. Anschließend, so dachte ich, musste ich nur die Aufrufe der Standard append-Methode durch einen Aufruf von appendColoredText ersetzen und den Namen durch einen Aufruf der „get“-Methode, mithilfe des „+“-Operators, mit der Chat-Nachricht verbinden. Beim Aufruf meiner „appendColoredText“-Methode wurde mir eine Fehlermeldung angezeigt. Die Rückgabe der „toString“-Methode sei kein String. Als ich spontan „.toString“ an den Aufruf hing, verschwand die Fehlermeldung. Den Befehl „toString(userMessage).toString“ konnte ich so natürlich nicht stehen lassen. Ich stellte fest, dass die toString-Methode eine CharSequence zurückgab. Zunächst änderte ich den Eingabetyp der „appendColoredText“-Methode zu CharSequence. Dies behob den Fehler. Allerdings hielt ich String für den Standarddatentypen für Text, war es gewöhnt mit String zu arbeiten und mir viel auch kein anderer Grund ein keinen String zu benutzen. Also änderte ich alle Nutzungen von CharSequence zu String. Nachdem dieser Teil erfolgreich abgeschlossen war, entschied ich mich dazu den zunächst hardgecodeten Text der Einstellungen in Strings auszulagern und Übersetzungen hinzuzufügen. Während ich mich schonmal in den „XML“-Dateien befand, fügte ich direkt das Eingabefeld und den Button hinzu, welche ebenfalls Anforderung meines Projekts waren. Nachdem das Layout horizontal getestet war, wollte ich nachsehen, ob das Layout auch vertikal funktioniert. Das Layout sah zwar großartig aus, allerdings war die Farbe des Textes so wie der Name des Absenders verschwunden. Mir viel siedend heiß ein, dass wir gelernt hatten, dass die Activity beim Drehen des Bildschirms zerstört und neu aufgebaut wird, und dass wir zu diesem Zweck Parcelable in das Projekt eingebaut hatten. Ich erweiterte die „Message“-Klasse um die Strings „name“ und „color“. Im MainFragment gab ich beim Erstellen einer Message die entsprechenden Werte mit und passte die „updateTextView“-Methode so an, dass sie auch die „appendColoredText“-Methode nutzte. Im gleichen Atemzug änderte ich auch die toString erneut ab, um direkt dort den Namen des Absenders hinzuzufügen, anstatt diesen jedes Mal manuell anzuhängen. Der Text wurde nun auch nach dem Drehen des Displays korrekt angezeigt. Nachdem das Problem behoben war, machte ich mich daran die Übersetzungen fertigzustellen. Anschließend fügte ich zwei neue „get“-Methoden hinzu, um einen einfachen Zugriff auf das Eingabefeld, sowie den neuen Button zu gewährleisten. Um keinen doppelten Code zu erzeugen, verschob ich den Inhalt von LaunchSpeechRecognition in eine eigene Methode Namens „askChatGPT“. Diese rief ich nun bei einem Klick auf den Spracheingabeknopf oder Textknopf auf und übergab den entsprechenden Inhalt. Im Fall des Textknopfes wird vorher das Eingabefeld geleert. Abschließend fügte ich der TextView noch eine Scrollingmethod hinzu und entfernte überflüssige Kommentare.
Während des Schreibens der Dokumentation fiel mir auf, dass es sinnvoll wäre den Aufruf „Color.parseColor“ in die Methode appendColoredText zu verlagern, anstatt ihn jedes Mal vor dem Aufruf auszuführen. Also tat ich dies umgehend.

## Probleme

### Crash bei leerem Spracheingabe-Text
Wenn die Spracheingabe nach dem Öffnen ohne Eingabe wieder geschlossen wurde, stürzte die Anwendung ab. Ich behob den Fehler, indem ich einen „null check“ hinzufügte, bevor die Eingabe genutzt wird.

### Verlust der Farbe bei drehen des Displays
Wenn das Display gedreht wurde, verschwanden die Farbe des Textes, sowie User- und ChatGPT-Name. Dies lag daran, dass ich vergessen hatte beides über die Parcelable-Klassen zu speichern und in updateTextView wiederherzustellen.

## Fazit
Insgesamt verlief das Projekt nach Plan, und alle Anforderungen konnten erfolgreich umgesetzt werden. Trotzdem blieb ich nicht von Fehlern verschont, insbesondere von einem einzigen Fehler: dem Versäumnis, die neuen Änderungen in Parcelable zu implementieren. Dies führte zu zusätzlichem Aufwand und erforderte nachträgliche Anpassungen. Dennoch blieb der zeitliche Aufwand im Rahmen der ursprünglichen Erwartung, und die Herausforderungen, die sich durch das anfängliche Fehlen der „Parcelable“-Integration ergaben, konnten erfolgreich bewältigt werden.
83 changes: 62 additions & 21 deletions app/src/main/java/de/fhdw/app_entwicklung/chatgpt/MainFragment.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package de.fhdw.app_entwicklung.chatgpt;

import android.graphics.Color;
import android.os.Bundle;
import android.text.Spannable;
import android.text.method.ScrollingMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.activity.result.ActivityResultLauncher;
Expand Down Expand Up @@ -34,24 +39,9 @@ public class MainFragment extends Fragment {
private final ActivityResultLauncher<LaunchSpeechRecognition.SpeechRecognitionArgs> getTextFromSpeech = registerForActivityResult(
new LaunchSpeechRecognition(),
query -> {
Message userMessage = new Message(Author.User, query);
chat.addMessage(userMessage);
if (chat.getMessages().size() > 1) {
getTextView().append(CHAT_SEPARATOR);
if (query != null) {
askChatGPT(query);
}
getTextView().append(toString(userMessage));

MainActivity.backgroundExecutorService.execute(() -> {
String apiToken = prefs.getApiToken();
ChatGpt chatGpt = new ChatGpt(apiToken);
String answer = chatGpt.getChatCompletion(chat);

Message answerMessage = new Message(Author.Assistant, answer);
chat.addMessage(answerMessage);
getTextView().append(CHAT_SEPARATOR);
getTextView().append(toString(answerMessage));
textToSpeech.speak(answer);
});
});

public MainFragment() {
Expand All @@ -76,7 +66,38 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

getAskButton().setOnClickListener(v ->
getTextFromSpeech.launch(new LaunchSpeechRecognition.SpeechRecognitionArgs(Locale.GERMAN)));
getAskTextButton().setOnClickListener(v -> askTextButton());
updateTextView();
getTextView().setMovementMethod(new ScrollingMovementMethod());
}

private void askTextButton() {
String query = getRequest().getText().toString();
if (!query.isEmpty()) {
getRequest().setText("");
askChatGPT(query);
}
}

private void askChatGPT(String query) {
Message userMessage = new Message(Author.User, query, prefs.getUsername(), "#8a50b0");
chat.addMessage(userMessage);
if (chat.getMessages().size() > 1) {
getTextView().append(CHAT_SEPARATOR);
}
appendColoredText(getTextView(), toString(userMessage), userMessage.color);

MainActivity.backgroundExecutorService.execute(() -> {
String apiToken = prefs.getApiToken();
ChatGpt chatGpt = new ChatGpt(apiToken);
String answer = chatGpt.getChatCompletion(chat);

Message answerMessage = new Message(Author.Assistant, answer, prefs.getGptName(), "#69a1fa");
chat.addMessage(answerMessage);
getTextView().append(CHAT_SEPARATOR);
appendColoredText(getTextView(), toString(answerMessage), answerMessage.color);
textToSpeech.speak(answer);
});
}

@Override
Expand Down Expand Up @@ -104,16 +125,16 @@ private void updateTextView() {
getTextView().setText("");
List<Message> messages = chat.getMessages();
if (!messages.isEmpty()) {
getTextView().append(toString(messages.get(0)));
appendColoredText(getTextView(), toString(messages.get(0)), messages.get(0).color);
for (int i = 1; i < messages.size(); i++) {
getTextView().append(CHAT_SEPARATOR);
getTextView().append(toString(messages.get(i)));
appendColoredText(getTextView(), toString(messages.get(i)), messages.get(i).color);
}
}
}

private CharSequence toString(Message message) {
return message.message;
private String toString(Message message) {
return message.name + ": " + message.message;
}

private TextView getTextView() {
Expand All @@ -126,4 +147,24 @@ private Button getAskButton() {
return getView().findViewById(R.id.button_ask);
}

private Button getAskTextButton() {
//noinspection ConstantConditions
return getView().findViewById(R.id.button_ask_text);
}

private EditText getRequest() {
//noinspection ConstantConditions
return getView().findViewById(R.id.request);
}

public static void appendColoredText(TextView tv, String text, String color) {
int colorInt = Color.parseColor(color);
int start = tv.getText().length();
tv.append(text);
int end = tv.getText().length();

Spannable spannableText = (Spannable) tv.getText();
spannableText.setSpan(new ForegroundColorSpan(colorInt), start, end, 0);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ protected void onCreate(Bundle savedInstanceState) {
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
assert actionBar != null;
actionBar.setTitle(R.string.settings);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,12 @@ public String getApiToken() {
return PreferenceManager.getDefaultSharedPreferences(context).getString("api_token", "");
}

public String getUsername(){
return PreferenceManager.getDefaultSharedPreferences(context).getString("username", "");
}

public String getGptName(){
return PreferenceManager.getDefaultSharedPreferences(context).getString("gpt_name", "");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,32 @@ public class Message implements Parcelable {
public final Date date;
public final Author author;
public final String message;
public final String name;
public final String color;

public Message(Author author, String message) {
this(new Date(), author, message);
public Message(Author author, String message, String name, String color) {
this(new Date(), author, message, name, color);
}

public Message(Date date, Author author, String message) {
public Message(Date date, Author author, String message, String name, String color) {
this.date = date;
this.author = author;
this.message = message;
this.name = name;
this.color = color;
}

protected Message(Parcel in) {
this(new Date(in.readLong()), Author.valueOf(in.readString()), in.readString());
this(new Date(in.readLong()), Author.valueOf(in.readString()), in.readString(), in.readString(), in.readString());
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(date.getTime());
dest.writeString(author.name());
dest.writeString(message);
dest.writeString(name);
dest.writeString(color);
}

@Override
Expand Down
36 changes: 30 additions & 6 deletions app/src/main/res/layout/fragment_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,54 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
tools:context=".MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="24dp"
android:fillViewport="true"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@+id/button_ask"
app:layout_constraintBottom_toTopOf="@+id/request"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button_ask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ask"
android:text="@string/voice_input"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button_ask_text" />

<EditText
android:id="@+id/request"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:ems="10"
android:hint="@string/empty"
android:importantForAutofill="no"
android:inputType="text"
android:minHeight="48dp"
app:layout_constraintBottom_toTopOf="@+id/button_ask_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
tools:ignore="SpeakableTextPresentCheck" />

<Button
android:id="@+id/button_ask_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ask"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button_ask"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
6 changes: 6 additions & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
<resources>
<string name="ask">Fragen</string>
<string name="settings">Einstellungen</string>
<string name="voice_input">Spracheingabe</string>
<string name="personalization">Personalisierung</string>
<string name="username">Nutzername</string>
<string name="gpt_name">ChatGPT-Name</string>
<string name="api">API</string>
<string name="api_token">API-Token</string>
</resources>
7 changes: 7 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<resources>
<string name="app_name" translatable="false">ChatGpt</string>
<string name="ask">Ask</string>
<string name="voice_input">Voice Input</string>
<string name="settings">Settings</string>
<string name="empty" translatable="false" />
<string name="personalization" >Personalization</string>
<string name="username">Username</string>
<string name="gpt_name">ChatGPT Name</string>
<string name="api">API</string>
<string name="api_token">API Token</string>
</resources>
26 changes: 22 additions & 4 deletions app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<PreferenceCategory app:title="API">
<PreferenceCategory app:title="@string/api">

<EditTextPreference
app:key="api_token"
app:title="API Token"
app:defaultValue="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"
app:key="api_token"
app:title="@string/api_token"
app:useSimpleSummaryProvider="true" />

</PreferenceCategory>
<PreferenceCategory android:title="@string/personalization">

<EditTextPreference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:defaultValue="User"
app:key="username"
app:title="@string/username"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:defaultValue="ChatGPT"
app:key="gpt_name"
app:title="@string/gpt_name"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>

</PreferenceScreen>
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.1.0' apply false
id 'com.android.library' version '8.1.0' apply false
id 'com.android.application' version '8.1.4' apply false
id 'com.android.library' version '8.1.4' apply false
}