Fix scrolling issue in subscribe dialog; fix base URL background color; fix dark mode action bar

This commit is contained in:
Philipp Heckel 2022-02-06 15:51:30 -05:00
parent edb80cd45c
commit 678be49bff
7 changed files with 104 additions and 77 deletions

View file

@ -13,7 +13,7 @@ android {
targetSdkVersion 31 targetSdkVersion 31
versionCode 21 versionCode 21
versionName "1.9.0" versionName "1.8.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -118,7 +118,7 @@ class ApiService {
return call return call
} }
fun authTopicRead(baseUrl: String, topic: String, user: User?): Boolean { fun checkAuth(baseUrl: String, topic: String, user: User?): Boolean {
if (user == null) { if (user == null) {
Log.d(TAG, "Checking anonymous read against ${topicUrl(baseUrl, topic)}") Log.d(TAG, "Checking anonymous read against ${topicUrl(baseUrl, topic)}")
} else { } else {
@ -127,11 +127,14 @@ class ApiService {
val url = topicUrlAuth(baseUrl, topic) val url = topicUrlAuth(baseUrl, topic)
val request = requestBuilder(url, user).build() val request = requestBuilder(url, user).build()
client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
return if (user == null) { if (response.isSuccessful) {
response.isSuccessful || response.code == 404 // Treat 404 as success (old server; to be removed in future versions) return true
} else { } else if (user == null && response.code == 404) {
response.isSuccessful return true // Special case: Anonymous login to old servers return 404 since /<topic>/auth doesn't exist
} else if (response.code == 401 || response.code == 403) { // See server/server.go
return false
} }
throw Exception("Unexpected server response ${response.code}")
} }
} }

View file

@ -7,6 +7,7 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.TypedValue
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
@ -88,7 +89,9 @@ class AddFragment : DialogFragment() {
// Fields for "subscribe page" // Fields for "subscribe page"
subscribeTopicText = view.findViewById(R.id.add_dialog_subscribe_topic_text) subscribeTopicText = view.findViewById(R.id.add_dialog_subscribe_topic_text)
subscribeBaseUrlLayout = view.findViewById(R.id.add_dialog_subscribe_base_url_layout) subscribeBaseUrlLayout = view.findViewById(R.id.add_dialog_subscribe_base_url_layout)
subscribeBaseUrlLayout.background = view.background
subscribeBaseUrlText = view.findViewById(R.id.add_dialog_subscribe_base_url_text) subscribeBaseUrlText = view.findViewById(R.id.add_dialog_subscribe_base_url_text)
subscribeBaseUrlText.background = view.background
subscribeInstantDeliveryBox = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_box) subscribeInstantDeliveryBox = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_box)
subscribeInstantDeliveryCheckbox = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_checkbox) subscribeInstantDeliveryCheckbox = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_checkbox)
subscribeInstantDeliveryDescription = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_description) subscribeInstantDeliveryDescription = view.findViewById(R.id.add_dialog_subscribe_instant_delivery_description)
@ -100,6 +103,14 @@ class AddFragment : DialogFragment() {
subscribeErrorTextImage = view.findViewById(R.id.add_dialog_subscribe_error_text_image) subscribeErrorTextImage = view.findViewById(R.id.add_dialog_subscribe_error_text_image)
subscribeErrorTextImage.visibility = View.GONE subscribeErrorTextImage.visibility = View.GONE
// Hack: Make end icon smaller, see https://stackoverflow.com/a/57098715/1440785
val dimension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30f, resources.displayMetrics)
val endIconImageView = subscribeBaseUrlLayout.findViewById<ImageView>(R.id.text_input_end_icon)
endIconImageView.minimumHeight = dimension.toInt()
endIconImageView.minimumWidth = dimension.toInt()
subscribeBaseUrlLayout.requestLayout()
// Fields for "login page" // Fields for "login page"
loginUsernameText = view.findViewById(R.id.add_dialog_login_username) loginUsernameText = view.findViewById(R.id.add_dialog_login_username)
loginPasswordText = view.findViewById(R.id.add_dialog_login_password) loginPasswordText = view.findViewById(R.id.add_dialog_login_password)
@ -280,14 +291,14 @@ class AddFragment : DialogFragment() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
try { try {
val user = repository.getUser(baseUrl) // May be null val user = repository.getUser(baseUrl) // May be null
val authorized = api.authTopicRead(baseUrl, topic, user) val authorized = api.checkAuth(baseUrl, topic, user)
if (authorized) { if (authorized) {
Log.d(TAG, "Access granted to topic ${topicUrl(baseUrl, topic)}") Log.d(TAG, "Access granted to topic ${topicUrl(baseUrl, topic)}")
dismissDialog() dismissDialog()
} else { } else {
if (user != null) { if (user != null) {
Log.w(TAG, "Access not allowed to topic ${topicUrl(baseUrl, topic)}, but user already exists") Log.w(TAG, "Access not allowed to topic ${topicUrl(baseUrl, topic)}, but user already exists")
showErrorAndReenableSubscribeView(getString(R.string.add_dialog_login_error_not_authorized)) showErrorAndReenableSubscribeView(getString(R.string.add_dialog_login_error_not_authorized, user.username))
} else { } else {
Log.w(TAG, "Access not allowed to topic ${topicUrl(baseUrl, topic)}, showing login dialog") Log.w(TAG, "Access not allowed to topic ${topicUrl(baseUrl, topic)}, showing login dialog")
val activity = activity ?: return@launch // We may have pressed "Cancel" val activity = activity ?: return@launch // We may have pressed "Cancel"
@ -327,14 +338,14 @@ class AddFragment : DialogFragment() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
Log.d(TAG, "Checking read access for user ${user.username} to topic ${topicUrl(baseUrl, topic)}") Log.d(TAG, "Checking read access for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
try { try {
val authorized = api.authTopicRead(baseUrl, topic, user) val authorized = api.checkAuth(baseUrl, topic, user)
if (authorized) { if (authorized) {
Log.d(TAG, "Access granted for user ${user.username} to topic ${topicUrl(baseUrl, topic)}, adding to database") Log.d(TAG, "Access granted for user ${user.username} to topic ${topicUrl(baseUrl, topic)}, adding to database")
repository.addUser(user) repository.addUser(user)
dismissDialog() dismissDialog()
} else { } else {
Log.w(TAG, "Access not allowed for user ${user.username} to topic ${topicUrl(baseUrl, topic)}") Log.w(TAG, "Access not allowed for user ${user.username} to topic ${topicUrl(baseUrl, topic)}")
showErrorAndReenableLoginView(getString(R.string.add_dialog_login_error_not_authorized)) showErrorAndReenableLoginView(getString(R.string.add_dialog_login_error_not_authorized, user.username))
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Connection to topic failed during login: ${e.message}", e) Log.w(TAG, "Connection to topic failed during login: ${e.message}", e)

View file

@ -6,13 +6,14 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="16dp"> android:paddingRight="16dp">
<ScrollView
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:id="@+id/add_dialog_subscribe_view">
android:id="@+id/add_dialog_subscribe_view" <androidx.constraintlayout.widget.ConstraintLayout
android:visibility="visible"> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/add_dialog_subscribe_title_text" android:id="@+id/add_dialog_subscribe_title_text"
android:layout_width="0dp" android:layout_width="0dp"
@ -57,46 +58,46 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_topic_text" app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_topic_text"
android:layout_marginTop="-3dp"/> android:layout_marginTop="-3dp"/>
<TextView <TextView
android:text="@string/add_dialog_use_another_server_description" android:text="@string/add_dialog_use_another_server_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/add_dialog_subscribe_use_another_server_description" android:layout_height="wrap_content" android:id="@+id/add_dialog_subscribe_use_another_server_description"
android:paddingStart="4dp" android:paddingTop="0dp" android:paddingStart="4dp" android:paddingTop="0dp"
android:visibility="gone" app:layout_constraintStart_toStartOf="parent" android:visibility="gone" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_checkbox" app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_checkbox"
android:layout_marginTop="-5dp"/> android:layout_marginTop="-5dp"/>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense.ExposedDropdownMenu"
android:id="@+id/add_dialog_subscribe_base_url_layout" android:id="@+id/add_dialog_subscribe_base_url_layout"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_margin="0dp"
android:background="@android:color/transparent"
android:padding="0dp"
android:visibility="gone"
app:endIconMode="custom"
app:hintEnabled="false"
app:boxBackgroundColor="@android:color/transparent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_description">
<AutoCompleteTextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/add_dialog_subscribe_base_url_text" android:layout_margin="0dp"
android:hint="@string/app_base_url" android:padding="0dp"
android:maxLines="1" android:visibility="gone"
android:layout_marginTop="0dp" app:endIconMode="custom"
android:layout_marginBottom="0dp" app:hintEnabled="false"
android:inputType="textNoSuggestions" app:boxBackgroundColor="@null" app:layout_constraintStart_toStartOf="parent"
android:textAppearance="?android:attr/textAppearanceMedium" app:layout_constraintEnd_toEndOf="parent"
android:paddingStart="0dp" app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_use_another_server_description">
android:paddingEnd="0dp" <AutoCompleteTextView
android:paddingTop="0dp" android:layout_width="match_parent"
android:paddingBottom="0dp" android:layout_height="wrap_content"
android:layout_marginStart="4dp" android:id="@+id/add_dialog_subscribe_base_url_text"
android:layout_marginEnd="4dp"/> android:hint="@string/app_base_url"
</com.google.android.material.textfield.TextInputLayout> android:maxLines="1"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
@ -118,14 +119,14 @@
app:layout_constraintEnd_toStartOf="@+id/main_item_date" android:paddingTop="3dp" app:layout_constraintEnd_toStartOf="@+id/main_item_date" android:paddingTop="3dp"
android:layout_marginTop="3dp"/> android:layout_marginTop="3dp"/>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:text="@string/add_dialog_instant_delivery_description" android:text="@string/add_dialog_instant_delivery_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/add_dialog_subscribe_instant_delivery_description" android:layout_height="wrap_content" android:id="@+id/add_dialog_subscribe_instant_delivery_description"
android:paddingStart="4dp" android:paddingTop="0dp" android:paddingStart="4dp" android:paddingTop="0dp"
android:visibility="gone" app:layout_constraintStart_toStartOf="parent" android:visibility="gone" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_box"/> app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_box"/>
<ImageView <ImageView
android:layout_width="20dp" android:layout_width="20dp"
android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp" android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"
@ -140,13 +141,15 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_description" android:paddingEnd="4dp" android:textColor="@color/primaryDangerButtonColor" app:layout_constraintStart_toEndOf="@id/add_dialog_subscribe_error_text_image" android:layout_marginTop="5dp" tools:visibility="gone"/> app:layout_constraintTop_toBottomOf="@id/add_dialog_subscribe_instant_delivery_description" android:paddingEnd="4dp" android:textColor="@color/primaryDangerButtonColor" app:layout_constraintStart_toEndOf="@id/add_dialog_subscribe_error_text_image" android:layout_marginTop="5dp" tools:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout </ScrollView>
<ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:id="@+id/add_dialog_login_view">
android:id="@+id/add_dialog_login_view" <androidx.constraintlayout.widget.ConstraintLayout
android:visibility="gone" android:layout_width="match_parent"
> android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/add_dialog_login_title" android:id="@+id/add_dialog_login_title"
android:layout_width="0dp" android:layout_width="0dp"
@ -204,4 +207,5 @@
app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description" app:layout_constraintBottom_toTopOf="@+id/add_dialog_login_description"
android:indeterminate="true" android:layout_marginBottom="5dp"/> android:indeterminate="true" android:layout_marginBottom="5dp"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</LinearLayout> </LinearLayout>

View file

@ -77,7 +77,7 @@
<string name="add_dialog_title">Subscribe to topic</string> <string name="add_dialog_title">Subscribe to topic</string>
<string name="add_dialog_description_below"> <string name="add_dialog_description_below">
Topics may not be password-protected, so choose a name that\'s not easy to Topics may not be password-protected, so choose a name that\'s not easy to
guess. Once subscribed, you can PUT/POST to receive notifications. guess. Once subscribed, you can PUT/POST notifications.
</string> </string>
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string> <string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
<string name="add_dialog_use_another_server">Use another server</string> <string name="add_dialog_use_another_server">Use another server</string>
@ -85,12 +85,11 @@
You can subscribe to topics from your own server. This option requires a foreground service. You can subscribe to topics from your own server. This option requires a foreground service.
</string> </string>
<string name="add_dialog_use_another_server_description_noinstant"> <string name="add_dialog_use_another_server_description_noinstant">
You can subscribe to topics from your own server. Simply type in the base You can subscribe to topics from your own server. Type the server URL below.
URL of your server.
</string> </string>
<string name="add_dialog_instant_delivery">Instant delivery in doze mode</string> <string name="add_dialog_instant_delivery">Instant delivery in doze mode</string>
<string name="add_dialog_instant_delivery_description"> <string name="add_dialog_instant_delivery_description">
Ensures that messages are immediately delivered, even if the device is inactive or in doze mode. Ensures that messages are immediately delivered, even if the device is inactive.
This requires a foreground service. This requires a foreground service.
</string> </string>
<string name="add_dialog_button_cancel">Cancel</string> <string name="add_dialog_button_cancel">Cancel</string>
@ -102,7 +101,7 @@
<string name="add_dialog_login_description">This topic requires you to login. Please type in a username and password.</string> <string name="add_dialog_login_description">This topic requires you to login. Please type in a username and password.</string>
<string name="add_dialog_login_username_hint">Username</string> <string name="add_dialog_login_username_hint">Username</string>
<string name="add_dialog_login_password_hint">Password</string> <string name="add_dialog_login_password_hint">Password</string>
<string name="add_dialog_login_error_not_authorized">Login failed. User not authorized.</string> <string name="add_dialog_login_error_not_authorized">Login failed. User %1$s not authorized.</string>
<string name="add_dialog_login_new_user">New user</string> <string name="add_dialog_login_new_user">New user</string>
<!-- Detail activity --> <!-- Detail activity -->

View file

@ -3,16 +3,10 @@
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<item name="colorPrimary">@color/primaryColor</item> <item name="colorPrimary">@color/primaryColor</item>
<item name="colorAccent">@color/primaryLightColor</item> <item name="colorAccent">@color/primaryLightColor</item>
<item name="actionBarStyle">@style/Custom.ActionBar</item>
<item name="android:statusBarColor">@color/primaryColor</item> <item name="android:statusBarColor">@color/primaryColor</item>
<item name="actionModeBackground">@color/primaryDarkColor</item> <item name="actionModeBackground">@color/primaryDarkColor</item>
</style> </style>
<!-- Action bar color identical in dark mode, see https://stackoverflow.com/a/58368668/1440785 -->
<style name="Custom.ActionBar" parent="Widget.MaterialComponents.Light.ActionBar.Solid">
<item name="background">@color/primaryColor</item>
</style>
<!-- Rounded corners in images, see https://stackoverflow.com/a/61960983/1440785 --> <!-- Rounded corners in images, see https://stackoverflow.com/a/61960983/1440785 -->
<style name="roundedCornersImageView" parent=""> <style name="roundedCornersImageView" parent="">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>

View file

@ -1,2 +1,18 @@
Features: Features:
* Support auth / access control (#19, thanks to @cmeis, @gedw99, @karmanyaahm,
@Mek101, @gc-ss, @julianfoad, @nmoseman, Jakob, PeterCxy, Techlosopher)
* Export/upload log now allows censored/uncensored logs (no ticket)
* Removed wake lock (except for notification dispatching, no ticket)
* Swipe to remove notifications (#117) * Swipe to remove notifications (#117)
Bug fixes:
* Fix download issues on SDK 29 "Movement not allowed" (#116, thanks Jakob)
* Fix for Android 12 crashes (#124, thanks @eskilop)
* Fix WebSocket retry logic bug with multiple servers (no ticket)
* Fix race in refresh logic leading to duplicate connections (no ticket)
* Fix scrolling issue in subscribe to topic dialog (#131, thanks @arminus)
* Fix base URL text field color in dark mode, and size with large fonts (no ticket)
* Fix action bar color in dark mode (make black, no ticket)
Notes:
* Foundational work for per-subscription settings