I. Einführung
In diesem Projekt haben wir eine einfache mobile App entwickelt, mit der zwei Geräte über Bluetooth miteinander kommunizieren können. Mit der App können Nutzer in Echtzeit Textnachrichten austauschen, ohne dass sie auf eine Internetverbindung angewiesen sind. Ziel ist es, eine leicht verständliche und praktische Anwendung zu erstellen, die den Alltag in Situationen ohne stabile Internetverbindung oder bei direkter Geräteverbindung erleichtert.
Die Idee entstand aus dem Wunsch, eine unkomplizierte Lösung für Orte ohne Mobilfunknetz oder WLAN zu entwickeln. Unsere App richtet sich an Nutzer, die eine direkte Kommunikation zwischen zwei Geräten wünschen – sei es bei Outdooraktivitäten, auf Reisen oder in Notsituationen.
II. Software-Anforderungsanalyse
Die App ermöglicht es Benutzern, in Echtzeit Textnachrichten auszutauschen – ganz ohne Internetverbindung. Entwickelt für den Einsatz bei Ausfall von Mobilfunknetzen oder WLAN (z. B. bei Outdooraktivitäten, Reisen oder Notfällen), bietet die App eine intuitive Benutzeroberfläche und zuverlässigen Verbindungsaufbau über Bluetooth.
III. Funktionalität
- Nachrichten senden und empfangen: Beim Start der App wird der Nutzer aufgefordert, zuerst eine Verbindung zu einem anderen Gerät herzustellen. Anschließend können beide Seiten Nachrichten austauschen, wobei jeder Nachricht der Name des Absenders und ein Zeitstempel beigefügt werden.
- Bluetooth-Verbindung herstellen: Die App prüft, ob Bluetooth aktiviert ist. Ist dies nicht der Fall, wird der Nutzer aufgefordert, es einzuschalten, woraufhin eine Liste der bereits gekoppelten Geräte angezeigt wird.
- Verbindungsaufbau und Steuerung: Die App wartet auf eingehende Verbindungsanfragen und kann auch aktiv eine Verbindung aufbauen. Bei erfolgreicher Verbindung ist eine bidirektionale Kommunikation möglich.
- Einfache Bedienung: Eine übersichtliche Benutzeroberfläche sorgt dafür, dass alle wichtigen Funktionen wie Nachrichtenversand und Geräteauswahl einfach zugänglich sind.
IV. Szenarien
- Urlaubssituation: Kommunikation in abgelegenen Gebieten ohne stabile Internetverbindung.
- Notfallszenario: Verbindung zwischen Rettungskräften oder Personen in der Umgebung bei Ausfall von Mobilfunknetzen.
- Treffen und Veranstaltungen: Kurzfristige Verbindung bei überlasteten Netzwerken, um Gruppen schnell zu koordinieren.
- Outdoor-Aktivitäten: Austausch von Nachrichten und Standorten bei schwacher oder fehlender Internetverbindung.
V. Architektur
Die Architektur der App umfasst mehrere Komponenten:
- Benutzeroberfläche (UI) Schicht: MainActivity und ChatMessageItem bilden die Benutzeroberfläche, in der Nachrichten angezeigt, eingegeben und verarbeitet werden.
- Kommunikationsmodul: BluetoothAdapter, AcceptThread, ConnectThread und ConnectedThread sorgen für den Aufbau und die Verwaltung der Bluetooth-Verbindungen sowie für den Nachrichtenaustausch. Das communicationService-Objekt repräsentiert hierbei den aktiven Kommunikationskanal.
- Integration von Sensoren: Der Licht- und der Beschleunigungssensor werden zur Steuerung von Bildschirmhelligkeit und zur Erkennung von Schüttelbewegungen verwendet, um im Notfall benutzerdefinierte Hilfemeldungen auszulösen.
- Standort und Systemdienste: LocationManager, Vibrator und System Settings ermöglichen die Erfassung von Standortdaten, haptisches Feedback und die Steuerung der Bildschirmhelligkeit.
- Verarbeitung und JSON-Erstellung: Nachrichten werden formatiert und, insbesondere im Fall von Notfällen, in JSON-Datenpakete umgewandelt.
Diese klare Trennung der Komponenten ermöglicht eine einfache Entwicklung, Testung und spätere Anpassungen.
VI. Code-Muster
Ein vereinfachter Codeabschnitt aus ChatMessageItem:
Dieser Code demonstriert, wie Chat-Nachrichten formatiert und angezeigt werden.
class ChatMessageItem(
private val message: String,
private val name: String,
private val color: Int,
private val isSentByMe: Boolean,
private val timestamp: Long = System.currentTimeMillis()
) : Item<GroupieViewHolder>() {
override fun getLayout(): Int {
return R.layout.item_message
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
val tvFrom = viewHolder.itemView.findViewById<TextView>(R.id.tvFrom)
val tvMessage = viewHolder.itemView.findViewById<TextView>(R.id.tvMessage)
val tvTime = viewHolder.itemView.findViewById<TextView>(R.id.tvTime)
val cardRoot = viewHolder.itemView.findViewById<MaterialCardView>(R.id.cardRoot)
tvFrom.text = name
tvMessage.text = message
cardRoot.setCardBackgroundColor(color)
val sdf = SimpleDateFormat("HH:mm", Locale.getDefault())
tvTime.text = sdf.format(Date(timestamp))
val params = cardRoot.layoutParams as FrameLayout.LayoutParams
params.gravity = if (isSentByMe) Gravity.START else Gravity.END
cardRoot.layoutParams = params
}
}
Ein Auszug aus enableBluetooth() in der MainActivity:
Dieser Code zeigt, wie die Bluetooth-Funktionalität initialisiert und eine Verbindung aufgebaut wird.
private fun enableBluetooth() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (bluetoothAdapter == null) {
Snackbar.make(
binding.root,
"Ihr Gerät unterstützt kein Bluetooth.",
Snackbar.LENGTH_LONG
).show()
return
}
binding.tvDeviceName.text = bluetoothAdapter!!.name
binding.tvDeviceAddress.text = bluetoothAdapter!!.address
if (bluetoothAdapter?.isEnabled == false) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
setupBluetoothClientConnection()
AcceptThread().start()
}
Beispiel eines XML-Layouts:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvLogoText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bluetooth ChatApp"
android:textStyle="bold"
android:textColor="@color/blueChatText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Weitere UI-Elemente ... -->
</androidx.constraintlayout.widget.ConstraintLayout>
VII. Implementierung
Erste Version: Grundlegende Funktionen wie Nachrichtenversand und Bluetooth-Verbindung wurden implementiert.
Version 2: Neues Design (Re-Skinned Project) wurde eingeführt, Dark Theme-Probleme traten jedoch auf.
Version 3: Erweiterungen wie Zeitstempel und Anpassungen im Chat-Layout zur Verbesserung der Stabilität wurden integriert.
Version 4 und 4.1: Neue Features wie Vibrationsfunktion und Notfallbutton wurden implementiert.
Letzte Version: Die Benutzeroberfläche wurde komplett überarbeitet und an moderne Designstandards angepasst. Zusätzlich wurden neue Funktionen integriert, wie die automatische Anpassung der Bildschirmhelligkeit mittels Umgebungslichtsensor und eine innovative Funktion, bei der durch kräftiges Schütteln des Geräts eine benutzerdefinierte Hilfemeldung ausgelöst wird.
VIII. Testen
Es wurden funktionale und Usability-Tests auf drei verschiedenen Geräten mit unterschiedlichen Android-Versionen durchgeführt, um die Kompatibilität und Zuverlässigkeit der App zu gewährleisten.
IX. Herausforderungen
Während der Entwicklung traten Probleme auf, wie z.B. das automatische Schließen der App bei deaktiviertem Standort auf älteren Android-Versionen (Android 10) und die fehlerhafte Darstellung von Notfallnachrichten (als JSON-Datenpaket statt formatierten Text). Auf Geräten mit Android-Versionen über 12 trat dieser Fehler nicht auf (siehe Bild 1).
Bild 1
Auf dem Gerät des Empfängers erscheint die Nachricht jedoch nur als JSON-Datenpaket, was zu einer unklaren Darstellung der Information führt (siehe Bild 2).
Bild 2
Bild 3
Weitere Versuche, erweiterte Funktionen wie Nachrichtenstatus (gesendet, empfangen, gelesen) und Multimedia-Unterstützung (Bilder, Videos, Sprachnachrichten, Dateien) zu integrieren, waren nicht erfolgreich.
X. Fazit
Das Projekt hat gezeigt, dass auch mit einfachen Mitteln eine zuverlässige Verbindung zwischen mobilen Geräten hergestellt werden kann. Trotz einiger Herausforderungen, insbesondere bei der Kompatibilität älterer Android-Versionen, konnte eine praktische Lösung für die direkte Kommunikation geschaffen werden.
Wichtige Erkenntnisse: Einfache Kommunikation, stabile Verbindung und die Bedeutung von Teamarbeit bei der Problemlösung.
XI. Installation und Setup
| Version | Datum | Beschreibung | APK |
|---|---|---|---|
| 5.0 | 16.03.2025 | Komplett überarbeitete Benutzeroberfläche, automatische Bildschirmhelligkeitsanpassung mittels Lichtsensor und Notfall-Hilfemeldung per Schütteln. | APK herunterladen |
| 4.1 | Kleinere Bugfixes. | ||
| 4.0 | Implementierung der Vibrationsfunktion und des Notfallbuttons. | ||
| 3.0 | Erweiterungen wie Zeitstempel und Anpassungen im Chat-Layout zur Verbesserung der Stabilität. | ||
| 2.0 | Re-Skinned Project: Neues Design, Dark Theme-Probleme teilweise vorhanden. | ||
| 1.0 | Grundlegende Funktionen wie Nachrichtenversand und Aufbau der Bluetooth-Verbindung. |
XII. Sensoreinsatz
Im folgenden Codeabschnitt wird gezeigt, wie die Sensoren (Licht- und Beschleunigungssensor) in der App verwendet werden, um die Bildschirmhelligkeit automatisch anzupassen und bei kräftigem Schütteln eine benutzerdefinierte Notfallnachricht auszulösen:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.etReplyLayout.visibility = View.GONE
binding.btnSendToConnected.visibility = View.GONE
vibrator = if (Build.VERSION.SDK_INT >= 31) {
try {
val vibratorManagerClass = Class.forName("android.os.VibratorManager")
val managerService = getSystemService(Class.forName("android.content.Context")
.getField("VIBRATOR_MANAGER_SERVICE")
.get(null) as String)
val getDefaultMethod = vibratorManagerClass.getMethod("getDefaultVibrator")
getDefaultMethod.invoke(managerService) as Vibrator
} catch (e: Exception) {
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
} else {
getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
// Initialize location manager
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
binding.btnSaveEmergencyMessage.setOnClickListener {
val msg = binding.etEmergencyMessage.text.toString().trim()
if (msg.isNotEmpty()) {
savedEmergencyMessage = msg
// Düzenleme modunu gizle, Anzeige-Modus aktivieren
binding.layoutEmergencyEdit.visibility = View.GONE
binding.layoutEmergencyDisplay.visibility = View.VISIBLE
binding.tvEmergencyMessage.text = savedEmergencyMessage
Toast.makeText(this, "Notfallnachricht aufgezeichnet.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Die Nachricht kann nicht leer sein!", Toast.LENGTH_SHORT).show()
}
}
binding.btnEditEmergencyMessage.setOnClickListener {
binding.layoutEmergencyDisplay.visibility = View.GONE
binding.layoutEmergencyEdit.visibility = View.VISIBLE
binding.etEmergencyMessage.setText(savedEmergencyMessage)
}
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
// Check location permissions
checkLocationPermissions()
setup()
setupEmergencyButton()
enableBluetooth()
}
XIII. Nutzungsszenarien-Video
Klicken Sie hier, um das Video anzusehen oder nutzen Sie den alternativen Link.
XIV. Hilfe und Kontakt
Bei Fragen oder Problemen wenden Sie sich bitte an:
- Email: hi@ahmetharun.com
- Adresse: Köthen(Anhalt), Deutschland