Merge pull request #401 from gotify/fixes

fix: reconnecting with wrong url/credentials in the background after logout
This commit is contained in:
Jannis Mattheis
2025-02-15 12:33:33 +01:00
committed by GitHub
3 changed files with 164 additions and 135 deletions

View File

@@ -1,6 +1,7 @@
package com.github.gotify.service package com.github.gotify.service
import android.app.AlarmManager import android.app.AlarmManager
import android.app.AlarmManager.OnAlarmListener
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@@ -29,9 +30,10 @@ internal class WebSocketConnection(
private val ID = AtomicLong(0) private val ID = AtomicLong(0)
} }
private var alarmManagerCallback: OnAlarmListener? = null
private var handlerCallback: Runnable? = null
private val client: OkHttpClient private val client: OkHttpClient
private val reconnectHandler = Handler(Looper.getMainLooper()) private val reconnectHandler = Handler(Looper.getMainLooper())
private val reconnectCallback = Runnable { start() }
private var errorCount = 0 private var errorCount = 0
private var webSocket: WebSocket? = null private var webSocket: WebSocket? = null
@@ -106,6 +108,13 @@ internal class WebSocketConnection(
@Synchronized @Synchronized
fun close() { fun close() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
alarmManagerCallback?.run(alarmManager::cancel)
alarmManagerCallback = null
} else {
handlerCallback?.run(reconnectHandler::removeCallbacks)
handlerCallback = null
}
if (webSocket != null) { if (webSocket != null) {
webSocket?.close(1000, "") webSocket?.close(1000, "")
closed() closed()
@@ -119,8 +128,10 @@ internal class WebSocketConnection(
state = State.Disconnected state = State.Disconnected
} }
fun scheduleReconnectNow(seconds: Long) = scheduleReconnect(ID.get(), seconds)
@Synchronized @Synchronized
fun scheduleReconnect(seconds: Long) { fun scheduleReconnect(id: Long, seconds: Long) {
if (state == State.Connecting || state == State.Connected) { if (state == State.Connecting || state == State.Connected) {
return return
} }
@@ -130,17 +141,23 @@ internal class WebSocketConnection(
Logger.info("WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)") Logger.info("WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)")
val future = Calendar.getInstance() val future = Calendar.getInstance()
future.add(Calendar.SECOND, seconds.toInt()) future.add(Calendar.SECOND, seconds.toInt())
alarmManagerCallback?.run(alarmManager::cancel)
val cb = OnAlarmListener { syncExec(id) { start() } }
alarmManagerCallback = cb
alarmManager.setExact( alarmManager.setExact(
AlarmManager.RTC_WAKEUP, AlarmManager.RTC_WAKEUP,
future.timeInMillis, future.timeInMillis,
"reconnect-tag", "reconnect-tag",
{ start() }, cb,
null null
) )
} else { } else {
Logger.info("WebSocket: scheduling a restart in $seconds second(s)") Logger.info("WebSocket: scheduling a restart in $seconds second(s)")
reconnectHandler.removeCallbacks(reconnectCallback) handlerCallback?.run(reconnectHandler::removeCallbacks)
reconnectHandler.postDelayed(reconnectCallback, TimeUnit.SECONDS.toMillis(seconds)) val cb = Runnable { syncExec(id) { start() } }
handlerCallback = cb
reconnectHandler.postDelayed(cb, TimeUnit.SECONDS.toMillis(seconds))
} }
} }
@@ -190,7 +207,7 @@ internal class WebSocketConnection(
val minutes = (errorCount * 2 - 1).coerceAtMost(20) val minutes = (errorCount * 2 - 1).coerceAtMost(20)
onFailure.execute(response?.message ?: "unreachable", minutes) onFailure.execute(response?.message ?: "unreachable", minutes)
scheduleReconnect(TimeUnit.MINUTES.toSeconds(minutes.toLong())) scheduleReconnect(id, TimeUnit.MINUTES.toSeconds(minutes.toLong()))
} }
super.onFailure(webSocket, t, response) super.onFailure(webSocket, t, response)
} }

View File

@@ -172,10 +172,7 @@ internal class WebSocketService : Service() {
} }
private fun doReconnect() { private fun doReconnect() {
if (connection == null) { connection?.scheduleReconnectNow(15)
return
}
connection!!.scheduleReconnect(15)
} }
private fun onFailure(status: String, minutes: Int) { private fun onFailure(status: String, minutes: Int) {

View File

@@ -1,137 +1,152 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
tools:openDrawer="start">
<LinearLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent">
android:orientation="vertical">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/push_title_hint"
android:inputType="text"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/push_content_hint"
android:inputType="textCapSentences|textMultiLine"
android:maxLength="2000"
android:maxLines="10" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtTxtPriority"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123456789"
android:hint="@string/push_priority_hint"
android:imeOptions="actionDone"
android:inputType="numberSigned"
android:maxLength="3"
android:text="0" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/txtAppListDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/appListDescription" />
<Spinner
android:id="@+id/appSpinner"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:spinnerMode="dropdown"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/missingAppsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:visibility="gone">
<ImageView
android:id="@+id/missingAppsIcon"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_weight="0"
app:srcCompat="@drawable/ic_info" />
<Space
android:layout_width="10dp"
android:layout_height="match_parent"
android:layout_weight="0" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/missingAppsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:text="@string/push_missing_app_info"
android:textAlignment="center"
android:visibility="visible" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/push_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp"
android:text="@string/push_button"
android:textColor="@android:color/white" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<include <include
android:id="@+id/app_bar_drawer" android:id="@+id/app_bar_drawer"
layout="@layout/app_bar_drawer" layout="@layout/app_bar_drawer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.textfield.TextInputLayout </androidx.drawerlayout.widget.DrawerLayout>
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/push_title_hint"
android:inputType="text"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/push_content_hint"
android:inputType="textCapSentences|textMultiLine"
android:maxLength="2000"
android:maxLines="10" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edtTxtPriority"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123456789"
android:hint="@string/push_priority_hint"
android:imeOptions="actionDone"
android:inputType="numberSigned"
android:maxLength="3"
android:text="0" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/txtAppListDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/appListDescription" />
<Spinner
android:id="@+id/appSpinner"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:spinnerMode="dropdown"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/missingAppsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:visibility="gone">
<ImageView
android:id="@+id/missingAppsIcon"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_weight="0"
app:srcCompat="@drawable/ic_info" />
<Space
android:layout_width="10dp"
android:layout_height="match_parent"
android:layout_weight="0" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/missingAppsText"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:text="@string/push_missing_app_info"
android:textAlignment="center"
android:visibility="visible" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/push_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="20dp"
android:textColor="@android:color/white"
android:text="@string/push_button" />
</LinearLayout>
</ScrollView>