Bimba.git

commit 448f03b6eb4aa60b58b517a0333fcc8fba4a4e2a

Author: Adam <git@apiote.xyz>

begin new login

%!v(PANIC=String method: strings: negative Repeat count)


diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 91096abf593d7294f7fb9a4c254b0b069b7e539e..49d36b02d45d1048704889f39a42ec6340d001a9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -24,6 +24,7 @@ 		versionName = "3.6.1"
 
 		testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
 		resourceConfigurations += listOf("en", "pl", "it", "de", "fr", "en-rUS")
+		manifestPlaceholders["appAuthRedirectScheme"] = "bimba"
 	}
 
 	buildTypes {
@@ -76,6 +77,7 @@ 	implementation("com.google.guava:guava:31.0.1-android")
 	implementation(project(":fruchtfleisch"))
 	implementation("ch.acra:acra-http:5.11.3")
 	implementation("ch.acra:acra-notification:5.11.3")
+	implementation("net.openid:appauth:0.11.1")
 
 	coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
 




diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 66a021e365d8b0f0506f6a62a283ce3d9d891df6..56791c555a19198a39fff5b75aef95583d9fe6b0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -32,13 +32,6 @@ 		 			android:name=".search.LineGraphActivity"
 			android:exported="false"
 			android:theme="@style/Theme.Bimba.Style.NoActionBar" />
-		<activity
-			android:name=".settings.ServerChooserActivity"
-			android:exported="false">
-			<meta-data
-				android:name="android.app.lib_name"
-				android:value="" />
-		</activity>
 		<activity android:name=".onboarding.OnboardingActivity" />
 		<activity
 			android:name=".settings.feeds.FeedChooserActivity"




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt
index ac05a4944b6d577160c3793997583c9c7126ccf1..463fd988ac44fadfa794b06ca42128cce9585455 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/dashboard/MainActivity.kt
@@ -40,9 +40,9 @@ import xyz.apiote.bimba.czwek.dashboard.ui.map.MapFragment
 import xyz.apiote.bimba.czwek.dashboard.ui.voyage.VoyageFragment
 import xyz.apiote.bimba.czwek.databinding.ActivityMainBinding
 import xyz.apiote.bimba.czwek.onboarding.FirstRunActivity
+import xyz.apiote.bimba.czwek.onboarding.OnboardingActivity
 import xyz.apiote.bimba.czwek.search.ResultsActivity
 import xyz.apiote.bimba.czwek.settings.DownloadCitiesWorker
-import xyz.apiote.bimba.czwek.settings.ServerChooserActivity
 import xyz.apiote.bimba.czwek.settings.SettingsActivity
 import xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity
 
@@ -104,8 +104,9 @@ 		})
 
 		binding.navigationDrawer.setNavigationItemSelectedListener {
 			when (it.itemId) {
-				R.id.drawer_servers -> {
-					startActivity(Intent(this, ServerChooserActivity::class.java))
+				R.id.drawer_account -> {
+					// TODO if logged-in -> AccountActivity, else -> OnboardingActivity
+					startActivity(Intent(this, OnboardingActivity::class.java))
 				}
 
 				R.id.drawer_cities -> {




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/onboarding/OnboardingActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/onboarding/OnboardingActivity.kt
index f5df578827b6c4cc1cdb8844307a9ca4fa94b4dc..f934bd2eb9c7e7f084b89b67dd6e0cf0d18e97c1 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/onboarding/OnboardingActivity.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/onboarding/OnboardingActivity.kt
@@ -4,26 +4,37 @@ // SPDX-License-Identifier: GPL-3.0-or-later
 
 package xyz.apiote.bimba.czwek.onboarding
 
-import android.graphics.Typeface
+import android.content.Intent
+import android.content.SharedPreferences
+import android.net.Uri
 import android.os.Bundle
-import android.text.Spannable
-import android.text.SpannableStringBuilder
-import android.text.style.RelativeSizeSpan
-import android.text.style.StyleSpan
-import android.widget.Button
+import android.util.Log
 import androidx.activity.enableEdgeToEdge
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.edit
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
-import xyz.apiote.bimba.czwek.R
+import androidx.core.widget.addTextChangedListener
+import net.openid.appauth.AuthState
+import net.openid.appauth.AuthorizationException
+import net.openid.appauth.AuthorizationRequest
+import net.openid.appauth.AuthorizationResponse
+import net.openid.appauth.AuthorizationService
+import net.openid.appauth.AuthorizationServiceConfiguration
+import net.openid.appauth.AuthorizationServiceConfiguration.RetrieveConfigurationCallback
+import net.openid.appauth.ResponseTypeValues
+import xyz.apiote.bimba.czwek.api.Server
 import xyz.apiote.bimba.czwek.databinding.ActivityOnboardingBinding
-import xyz.apiote.bimba.czwek.settings.ServerChooserActivity
+import xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity
+
 
 class OnboardingActivity : AppCompatActivity() {
 	private var _binding: ActivityOnboardingBinding? = null
 	private val binding get() = _binding!!
+	private lateinit var preferences: SharedPreferences
+	private lateinit var authState: AuthState
 
 	private val activityLauncher =
 		registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
@@ -43,41 +54,108 @@ 			val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
 			v.updatePadding(right = insets.right, left = insets.left, bottom = insets.bottom)
 			windowInsets
 		}
+		preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE)
 
-		prepareButton(
-			binding.buttonSimple,
-			getString(R.string.onboarding_simple),
-			getString(R.string.onboarding_simple_action),
-			true
-		)
-		prepareButton(
-			binding.buttonAdvanced,
-			getString(R.string.onboarding_advanced),
-			getString(R.string.onboarding_advanced_action),
-			false
-		)
+		preferences.edit(true) {
+			putBoolean(IN_FEEDS_TRANSACTION, true)
+		}
+
+		binding.server.editText!!.addTextChangedListener { editable ->
+			binding.accountButton.isEnabled = !editable.isNullOrBlank()
+			binding.anonymousButton.isEnabled = !editable.isNullOrBlank()
+		}
+
+		if (!FirstRunActivity.getFirstRun(this)) {
+			Server.get(this).let { server ->
+				binding.server.editText!!.setText(server.host)
+			}
+		}
+
+		binding.accountButton.setOnClickListener {
+			// TODO OIDC flow https://github.com/openid/AppAuth-Android
+			oidcFlow()
+		}
+
+		binding.anonymousButton.setOnClickListener {
+			moveOn()
+		}
 	}
 
-	private fun prepareButton(button: Button, title: String, description: String, simple: Boolean) {
-		button.text = SpannableStringBuilder().apply {
-			append(
-				title,
-				StyleSpan(Typeface.BOLD),
-				Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
-			)
-			append("\n")
-			append(
-				description,
-				RelativeSizeSpan(.75f),
-				Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
-			)
+	private fun setServer(hostname: String) {
+		preferences.edit(true) {
+			putString(Server.HOST_KEY, hostname)
 		}
-		button.setOnClickListener {
-			moveOn(simple)
+	}
+
+	private fun moveOn() {
+		// TODO test 401/403
+		// TODO show rate-limit info, terms and privacy
+		binding.server.editText?.text?.toString()?.let { serverAddress -> setServer(serverAddress) }
+		activityLauncher.launch(Intent(this, FeedChooserActivity::class.java))
+	}
+
+	private fun oidcFlow() {
+		AuthorizationServiceConfiguration.fetchFromIssuer(
+			Uri.parse("https://oauth-bimba.apiote.xyz"),
+			RetrieveConfigurationCallback { serviceConfiguration, ex ->
+				if (ex != null) {
+					Log.e("OIDC", "failed to fetch configuration")
+					return@RetrieveConfigurationCallback
+				}
+				authState = AuthState(serviceConfiguration!!)
+
+				/*
+				{"client_id":"46e1be27-a747-4507-a1d4-c383f137745f","client_name":"Bimba","client_secret_expires_at":0,"client_uri":"https://bimba.app","contacts":["questions@bimba.app"],"created_at":"2024-09-09T12:41:30Z","grant_types":["authorization_code","refresh_token"],"jwks":{},"logo_uri":"https://bimba.app/bimba.svg","metadata":{},"owner":"","policy_uri":"","redirect_uris":["bimba://callback"],"registration_access_token":"ory_at_xQWn3kMRuev5ZmCM_BgwnuOo57GRKWz-7LAA8EH2OTA.GXZCCf7XCg8fIwjpGZjZGdgJPMSsMSM9E-6B7bE8nB0","registration_client_uri":"https://oauth-bimba.apiote.xyz/oauth2/register/","request_object_signing_alg":"RS256","response_types":["code","id_token"],"scope":"openid offline","skip_consent":false,"skip_logout_consent":false,"subject_type":"public","token_endpoint_auth_method":"none","tos_uri":"","updated_at":"2024-09-09T14:41:29.982386+02:00","userinfo_signed_response_alg":"none"}
+				 */
+				val authRequest = AuthorizationRequest.Builder(
+					serviceConfiguration,
+					"46e1be27-a747-4507-a1d4-c383f137745f",
+					ResponseTypeValues.CODE,
+					Uri.parse("bimba://callback")
+				)
+					.setScope("openid offline")
+					.build();
+
+				val authIntent = AuthorizationService(this).getAuthorizationRequestIntent(authRequest)
+				startActivityForResult(authIntent, RC_AUTH)
+			})
+	}
+
+	override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+		super.onActivityResult(requestCode, resultCode, data)
+		if (requestCode == RC_AUTH) {
+			val resp = AuthorizationResponse.fromIntent(data!!)
+			val ex = AuthorizationException.fromIntent(data)
+			if (resp != null) {
+				authState.update(resp, ex)
+				AuthorizationService(this).performTokenRequest(
+					resp.createTokenExchangeRequest()
+				) { tokenResponse, tokenException ->
+					if (tokenResponse != null) {
+						authState.update(resp, ex)
+						getSharedPreferences("auth", MODE_PRIVATE).edit {
+							putString("state", authState.jsonSerializeString())
+						}
+						if (tokenResponse.idToken == null) {
+							// TODO openid is needed; show some dialogue
+						}
+						Log.i("OIDC", "got token: id: ${tokenResponse.idToken}\n access: ${tokenResponse.accessToken}\n params: ${tokenResponse.additionalParameters}")
+						// TODO moveOn()
+					} else {
+						// FIXME token exchange failed: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).
+						Log.e("OIDC", "token exchange failed: ${tokenException!!.message}")
+						tokenException.printStackTrace()
+					}
+				}
+			} else {
+				Log.e("OIDC", "auth failed: ${ex!!.message}")
+			}
 		}
 	}
 
-	private fun moveOn(simple: Boolean) {
-		activityLauncher.launch(ServerChooserActivity.getIntent(this, simple))
+	companion object {
+		const val PREFERENCES_NAME = "shp"
+		const val IN_FEEDS_TRANSACTION = "inFeedsTransaction"
+		const val RC_AUTH = 42
 	}
 }
\ No newline at end of file




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/settings/ServerChooserActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/settings/ServerChooserActivity.kt
deleted file mode 100644
index 8eb69c2ac80b7122daaae47c52eb377f6755e1d4..0000000000000000000000000000000000000000
--- a/app/src/main/java/xyz/apiote/bimba/czwek/settings/ServerChooserActivity.kt
+++ /dev/null
@@ -1,235 +0,0 @@
-// SPDX-FileCopyrightText: Adam Evyčędo
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-package xyz.apiote.bimba.czwek.settings
-
-import android.content.Context
-import android.content.Intent
-import android.content.SharedPreferences
-import android.graphics.Color
-import android.os.Bundle
-import android.util.Log
-import androidx.activity.enableEdgeToEdge
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.core.content.edit
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.updatePadding
-import androidx.core.widget.addTextChangedListener
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.MainScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.yaml.snakeyaml.error.YAMLException
-import xyz.apiote.bimba.czwek.R
-import xyz.apiote.bimba.czwek.api.Bimba
-import xyz.apiote.bimba.czwek.api.Server
-import xyz.apiote.bimba.czwek.api.TrafficFormatException
-import xyz.apiote.bimba.czwek.api.getBimba
-import xyz.apiote.bimba.czwek.databinding.ActivityServerChooserBinding
-import xyz.apiote.bimba.czwek.onboarding.FirstRunActivity
-import xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity
-
-class ServerChooserActivity : AppCompatActivity() {
-	companion object {
-		const val PARAM_SIMPLE = "simple"
-		const val IN_FEEDS_TRANSACTION = "inFeedsTransaction"
-		const val PREFERENCES_NAME = "shp"
-		fun getIntent(context: Context, simple: Boolean) = Intent(context, ServerChooserActivity::class.java).apply {
-				putExtra(PARAM_SIMPLE, simple)
-		}
-	}
-
-	private var _binding: ActivityServerChooserBinding? = null
-	private val binding get() = _binding!!
-
-	private val activityLauncher =
-		registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-			if (!preferences.getBoolean(IN_FEEDS_TRANSACTION, true)) {
-				finish()
-			}
-		}
-
-	private lateinit var preferences: SharedPreferences
-
-	override fun onCreate(savedInstanceState: Bundle?) {
-		enableEdgeToEdge()
-		super.onCreate(savedInstanceState)
-
-		preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE)
-
-		if (intent.getBooleanExtra(PARAM_SIMPLE, false)) {
-			setServer(Server.DEFAULT, "")
-			checkServer(true)
-		} else {
-			_binding = ActivityServerChooserBinding.inflate(layoutInflater)
-			setContentView(binding.root)
-
-			ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
-				val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
-				v.updatePadding(right = insets.right, left = insets.left, top = insets.top)
-				windowInsets
-			}
-
-			preferences.edit(true) {
-				putBoolean(IN_FEEDS_TRANSACTION, true)
-			}
-
-			if (preferences.getBoolean("shibboleet", false)) {
-				binding.button.setBackgroundColor(Color.rgb(35, 93, 121))
-				binding.button.setTextColor(Color.WHITE)
-			}
-
-			binding.button.isEnabled = false
-			binding.serverField.editText!!.addTextChangedListener { editable ->
-				binding.button.isEnabled = !editable.isNullOrBlank()
-			}
-
-			if (!FirstRunActivity.getFirstRun(this)) {
-				Server.get(this).let { server ->
-					binding.serverField.editText!!.setText(server.host)
-					binding.tokenField.editText!!.setText(server.token)
-				}
-			}
-
-			binding.button.setOnClickListener {
-				when (binding.serverField.editText!!.text.toString()) {
-					":shibboleet" -> {
-						binding.button.setBackgroundColor(Color.rgb(35, 93, 121))
-						binding.button.setTextColor(Color.WHITE)
-						preferences.edit(true) {
-							putBoolean("shibboleet", true)
-						}
-						if (!FirstRunActivity.getFirstRun(this)) {
-							Server.get(this).let { server ->
-								binding.serverField.editText!!.setText(server.host)
-								binding.tokenField.editText!!.setText(server.token)
-							}
-						}
-					}
-
-					";shibboleet" -> {
-						_binding = ActivityServerChooserBinding.inflate(layoutInflater)
-						setContentView(binding.root)
-						preferences.edit(true) {
-							putBoolean("shibboleet", false)
-						}
-						if (!FirstRunActivity.getFirstRun(this)) {
-							Server.get(this).let { server ->
-								binding.serverField.editText!!.setText(server.host)
-								binding.tokenField.editText!!.setText(server.token)
-							}
-						}
-					}
-
-					else -> {
-						setServer(
-							binding.serverField.editText!!.text.toString(),
-							binding.tokenField.editText!!.text.toString()
-						)
-						checkServer(false)
-					}
-				}
-			}
-		}
-	}
-
-	private fun showDialog(
-		title: Int, description: Int, icon: Int, onPositive: (() -> Unit)?
-	) {
-		MaterialAlertDialogBuilder(this).setIcon(AppCompatResources.getDrawable(this, icon))
-			.setTitle(getString(title)).setMessage(getString(description))
-			.setNegativeButton(R.string.cancel) { _, _ -> }.apply {
-				if (onPositive != null) {
-					setPositiveButton(R.string.continue_) { _, _ ->
-						onPositive()
-					}
-				}
-			}.show()
-	}
-
-	private fun checkServer(isSimple: Boolean) {
-		MainScope().launch {
-			val result = getBimba(this@ServerChooserActivity, Server.get(this@ServerChooserActivity))
-			if (result.error != null) {
-				showDialog(R.string.error, result.error.stringResource, result.error.imageResource, null)
-				Log.w(
-					"ServerChooser", "${result.error.statusCode}, ${getString(result.error.stringResource)}"
-				)
-				return@launch
-			}
-			val bimba = try {
-				withContext(Dispatchers.IO) {
-					Bimba.unmarshal(result.stream!!)
-				}
-			} catch (e: YAMLException) {
-				Log.w("ServerChooser", e.message ?: "YAML error")
-				showDialog(R.string.error, R.string.error_traffic_spec, R.drawable.error_server, null)
-				return@launch
-			} catch (e: TrafficFormatException) {
-				Log.w("ServerChooser", e.message)
-				showDialog(R.string.error, R.string.error_traffic_spec, R.drawable.error_server, null)
-				return@launch
-			}
-
-			if (preferences.getBoolean("shibboleet", false)) {
-				val validServers = bimba.servers.filter { !it.getOrDefault("url", null).isNullOrBlank() }
-				val servers = validServers.toTypedArray()
-				MaterialAlertDialogBuilder(this@ServerChooserActivity)
-					.setTitle(R.string.choose_server)
-					.setItems(servers.map { it["description"] }.toTypedArray()) { _, i ->
-						updateServer(servers[i]["url"].toString())
-						moveOn(bimba, false)
-					}
-					.show()
-				return@launch
-			}
-
-			updateServer(bimba.servers[0]["url"]!!)
-			moveOn(bimba, isSimple)
-			return@launch
-		}
-	}
-
-	private fun moveOn(bimba: Bimba, isSimple: Boolean) {
-		val token = preferences.getString(Server.TOKEN_KEY, "")
-
-		if (bimba.isPrivate() && token == "") {
-			showDialog(R.string.error, R.string.server_private_question, R.drawable.error_sec, null)
-			return
-		}
-		if (bimba.isRateLimited() && token == "" && !isSimple) {
-			showDialog(
-				R.string.rate_limit, R.string.server_rate_limited_question, R.drawable.error_limit
-			) {
-				runFeedsActivity()
-			}
-			return
-		}
-		runFeedsActivity()
-	}
-
-	private fun setServer(hostname: String, token: String) {
-		preferences.edit(true) {
-			putString(Server.HOST_KEY, hostname)
-			putString(Server.TOKEN_KEY, token)
-		}
-	}
-
-	private fun updateServer(apiPath: String) {
-		preferences.edit(true) {
-			putString(Server.API_PATH_KEY, apiPath)
-		}
-	}
-
-	private fun runFeedsActivity() {
-		activityLauncher.launch(Intent(this, FeedChooserActivity::class.java))
-		if (intent.getBooleanExtra(PARAM_SIMPLE, false)) {
-			finish()
-		}
-	}
-}
\ No newline at end of file




diff --git a/app/src/main/java/xyz/apiote/bimba/czwek/settings/feeds/FeedChooserActivity.kt b/app/src/main/java/xyz/apiote/bimba/czwek/settings/feeds/FeedChooserActivity.kt
index 3d49614ac2d04edf2269267b404c0f4c5ecc7b7e..b712d05ae2e23872fe8dea94d7303af43f68b91d 100644
--- a/app/src/main/java/xyz/apiote/bimba/czwek/settings/feeds/FeedChooserActivity.kt
+++ b/app/src/main/java/xyz/apiote/bimba/czwek/settings/feeds/FeedChooserActivity.kt
@@ -14,7 +14,6 @@ import androidx.core.content.edit
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
-import androidx.core.widget.doAfterTextChanged
 import androidx.lifecycle.ViewModelProvider
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.transition.TransitionManager
@@ -24,8 +23,8 @@ import xyz.apiote.bimba.czwek.api.Server
 import xyz.apiote.bimba.czwek.dashboard.MainActivity
 import xyz.apiote.bimba.czwek.databinding.ActivityFeedChooserBinding
 import xyz.apiote.bimba.czwek.onboarding.FirstRunActivity
+import xyz.apiote.bimba.czwek.onboarding.OnboardingActivity
 import xyz.apiote.bimba.czwek.repo.FeedInfo
-import xyz.apiote.bimba.czwek.settings.ServerChooserActivity
 
 // TODO on internet connection -> getServer
 // TODO swipe to refresh?
@@ -68,14 +67,6 @@ 		setUpRecycler()
 
 		getServer()
 
-		binding.searchBar.editText?.doAfterTextChanged { editable ->
-			if (editable != null) {
-				updateItems((viewModel.feeds.value ?: emptyMap()).values.filter {
-					it.name.contains(editable, true)
-				}, null)
-			}
-		}
-
 		binding.button.setOnClickListener {
 			moveOn()
 		}
@@ -131,7 +122,7 @@ 	private fun moveOn() {
 		viewModel.settings.value?.save(this, Server.get(this))
 		val preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE)
 		preferences.edit(true) {
-			putBoolean(ServerChooserActivity.IN_FEEDS_TRANSACTION, false)
+			putBoolean(OnboardingActivity.IN_FEEDS_TRANSACTION, false)
 		}
 		if (FirstRunActivity.getFirstRun(this)) {
 			val intent = Intent(this, MainActivity::class.java)
@@ -161,11 +152,6 @@ 		notify: Boolean = true
 	) {
 		binding.feedsOverlay.visibility = View.GONE
 		binding.resultsRecycler.visibility = View.VISIBLE
-		binding.searchBar.visibility = if ((viewModel.feeds.value?.size ?: 0) > 12) {
-			View.VISIBLE
-		} else {
-			View.GONE
-		}
 		binding.button.visibility = View.VISIBLE
 		adapter.update(feeds, feedsSettings, notify)
 		if (feeds?.isEmpty() == true) {




diff --git a/app/src/main/res/drawable/account.xml b/app/src/main/res/drawable/account.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c4f8ce8167c6e1a550c0fbe5f26b506554e196f2
--- /dev/null
+++ b/app/src/main/res/drawable/account.xml
@@ -0,0 +1,17 @@
+<!--
+SPDX-FileCopyrightText: Google
+
+SPDX-License-Identifier: Apache-2.0
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+	android:width="24dp"
+	android:height="24dp"
+	android:tint="?attr/colorOnSurface"
+	android:viewportWidth="24"
+	android:viewportHeight="24">
+
+	<path
+		android:fillColor="@android:color/white"
+		android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,6c1.93,0 3.5,1.57 3.5,3.5S13.93,13 12,13s-3.5,-1.57 -3.5,-3.5S10.07,6 12,6zM12,20c-2.03,0 -4.43,-0.82 -6.14,-2.88C7.55,15.8 9.68,15 12,15s4.45,0.8 6.14,2.12C16.43,19.18 14.03,20 12,20z" />
+
+</vector>




diff --git a/app/src/main/res/drawable/checkmark.xml b/app/src/main/res/drawable/checkmark.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3103b5a7baeee9327e2d3e84140d5b3e7ecc02b7
--- /dev/null
+++ b/app/src/main/res/drawable/checkmark.xml
@@ -0,0 +1,18 @@
+<!--
+SPDX-FileCopyrightText: Google
+
+SPDX-License-Identifier: Apache-2.0
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+	android:width="24dp"
+	android:height="24dp"
+	android:tint="?attr/colorOnSurface"
+	android:viewportWidth="24"
+	android:viewportHeight="24">
+
+	<path
+		android:fillColor="@android:color/white"
+		android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
+
+</vector>




diff --git a/app/src/main/res/drawable/edit.xml b/app/src/main/res/drawable/edit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..70b1ec3fa132c43251eef2f4f4490d7d02706c3b
--- /dev/null
+++ b/app/src/main/res/drawable/edit.xml
@@ -0,0 +1,18 @@
+<!--
+SPDX-FileCopyrightText: Google
+
+SPDX-License-Identifier: Apache-2.0
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+	android:width="24dp"
+	android:height="24dp"
+	android:tint="?attr/colorOnSurface"
+	android:viewportWidth="24"
+	android:viewportHeight="24">
+
+	<path
+		android:fillColor="@android:color/white"
+		android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
+
+</vector>




diff --git a/app/src/main/res/drawable/logout.xml b/app/src/main/res/drawable/logout.xml
new file mode 100644
index 0000000000000000000000000000000000000000..65140ae90f047fff227726f0a4a27eb70caf4916
--- /dev/null
+++ b/app/src/main/res/drawable/logout.xml
@@ -0,0 +1,19 @@
+<!--
+SPDX-FileCopyrightText: Google
+
+SPDX-License-Identifier: Apache-2.0
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+	android:width="24dp"
+	android:height="24dp"
+	android:autoMirrored="true"
+	android:tint="?attr/colorOnSurface"
+	android:viewportWidth="24"
+	android:viewportHeight="24">
+
+	<path
+		android:fillColor="@android:color/white"
+		android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z" />
+
+</vector>




diff --git a/app/src/main/res/layout/account.xml b/app/src/main/res/layout/account.xml
index 3de5cf75542f454fbc97f2b5bcc7a56b4d3f657e..f0edee1eea3f31121dbf710167a97dcb94d810e0 100644
--- a/app/src/main/res/layout/account.xml
+++ b/app/src/main/res/layout/account.xml
@@ -1,47 +1,166 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.coordinatorlayout.widget.CoordinatorLayout 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">
 
-	<com.google.android.material.textview.MaterialTextView
-		android:id="@+id/headline"
-		android:layout_width="wrap_content"
+	<com.google.android.material.appbar.AppBarLayout
+		android:layout_width="match_parent"
 		android:layout_height="wrap_content"
-		android:layout_marginStart="32dp"
-		android:layout_marginTop="64dp"
-		android:text="Way to go, Adam!"
-		android:textAppearance="@style/TextAppearance.Material3.HeadlineLarge"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toTopOf="parent" />
+		android:fitsSystemWindows="true">
+
+		<com.google.android.material.appbar.CollapsingToolbarLayout
+			style="?attr/collapsingToolbarLayoutLargeStyle"
+			android:layout_width="match_parent"
+			android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
+			app:contentScrim="?attr/colorPrimary"
+			app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+			tools:title="Way to go, Adam!">
+
+			<com.google.android.material.appbar.MaterialToolbar
+				android:layout_width="match_parent"
+				android:layout_height="?attr/actionBarSize"
+				android:elevation="0dp"
+				app:layout_collapseMode="pin"
+				app:menu="@menu/account" />
 
-	<com.google.android.material.textview.MaterialTextView
-		android:id="@+id/login"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="32dp"
-		android:layout_marginTop="16dp"
-		android:text="Logged in as adam@bimba.app\non my-bimba.apiote.xyz"
-		android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@+id/headline" />
+		</com.google.android.material.appbar.CollapsingToolbarLayout>
 
-	<Button
-		android:id="@+id/account_button"
-		style="?attr/materialIconButtonStyle"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="16dp"
-		app:icon="@drawable/open_outside"
-		app:layout_constraintBottom_toBottomOf="@+id/login"
-		app:layout_constraintStart_toEndOf="@+id/login"
-		app:layout_constraintTop_toTopOf="@+id/login" />
+	</com.google.android.material.appbar.AppBarLayout>
 
-	<View
-		android:id="@+id/divider"
+	<androidx.constraintlayout.widget.ConstraintLayout
 		android:layout_width="match_parent"
-		android:layout_height="1dp"
-		android:layout_marginTop="16dp"
-		android:background="?android:attr/listDivider"
-		app:layout_constraintTop_toBottomOf="@+id/login" />
-</androidx.constraintlayout.widget.ConstraintLayout>
+		android:layout_height="match_parent"
+		app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/identity_label"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:text="@string/logged_in_as"
+			android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toTopOf="parent" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/identity"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginEnd="4dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+			app:layout_constraintEnd_toStartOf="@+id/identity_button"
+			app:layout_constraintTop_toTopOf="@+id/identity_label"
+			tools:text="\@me:apiote.xyz" />
+
+		<Button
+			android:id="@+id/identity_button"
+			style="?attr/materialIconButtonStyle"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:contentDescription="@string/open_browser_to_edit_identity"
+			android:paddingStart="0dp"
+			android:paddingEnd="16dp"
+			app:icon="@drawable/open_outside"
+			app:layout_constraintBottom_toBottomOf="@+id/identity"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toTopOf="@+id/identity" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/server_label"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:text="@string/using"
+			android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/identity" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/server"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginEnd="16dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toTopOf="@+id/server_label"
+			tools:text="bimba.apiote.xyz" />
+
+		<View
+			android:id="@+id/divider"
+			android:layout_width="match_parent"
+			android:layout_height="1dp"
+			android:layout_marginTop="16dp"
+			android:background="?android:attr/listDivider"
+			app:layout_constraintTop_toBottomOf="@+id/server" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/matrix_label"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:text="@string/matrix_handle"
+			android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/divider" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/matrix"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginEnd="16dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toTopOf="@+id/matrix_label"
+			tools:text="\@me:apiote.xyz" />
+
+		<!-- TODO colorError for unverified -->
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/matrix_verification"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="4dp"
+			android:layout_marginEnd="16dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodySmall"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toBottomOf="@id/matrix"
+			tools:text="verified" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/seat_label"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:text="@string/subscription"
+			android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toBottomOf="@+id/matrix_verification" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/subscription_end"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="4dp"
+			android:layout_marginEnd="16dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodySmall"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toBottomOf="@id/seat"
+			tools:text="ends on Thu, May 28" />
+
+		<com.google.android.material.textview.MaterialTextView
+			android:id="@+id/seat"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginEnd="16dp"
+			android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
+			app:layout_constraintEnd_toEndOf="parent"
+			app:layout_constraintTop_toTopOf="@+id/seat_label"
+			tools:text="back seat" />
+
+	</androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>




diff --git a/app/src/main/res/layout/account_edit.xml b/app/src/main/res/layout/account_edit.xml
index 0d4867f726bc0c3f708f0d068ffb09750af1ee93..a912c25b2175296d2b4ce9b1baf3d2de9879facb 100644
--- a/app/src/main/res/layout/account_edit.xml
+++ b/app/src/main/res/layout/account_edit.xml
@@ -1,6 +1,79 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.coordinatorlayout.widget.CoordinatorLayout 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">
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+	<com.google.android.material.appbar.AppBarLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:fitsSystemWindows="true">
+
+		<com.google.android.material.appbar.CollapsingToolbarLayout
+			style="?attr/collapsingToolbarLayoutLargeStyle"
+			android:layout_width="match_parent"
+			android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
+			app:contentScrim="?attr/colorPrimary"
+			app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
+			tools:title="Way to go, Adam!">
+
+			<com.google.android.material.appbar.MaterialToolbar
+				android:layout_width="match_parent"
+				android:layout_height="?attr/actionBarSize"
+				android:elevation="0dp"
+				app:layout_collapseMode="pin"
+				app:menu="@menu/account_edit" />
+
+		</com.google.android.material.appbar.CollapsingToolbarLayout>
+
+	</com.google.android.material.appbar.AppBarLayout>
+
+	<androidx.constraintlayout.widget.ConstraintLayout
+		android:layout_width="match_parent"
+		android:layout_height="match_parent"
+		app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+		<com.google.android.material.textfield.TextInputLayout
+			android:id="@+id/name"
+			android:layout_width="300dp"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:hint="@string/name"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toTopOf="parent">
+
+			<com.google.android.material.textfield.TextInputEditText
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content" />
+
+		</com.google.android.material.textfield.TextInputLayout>
+
+		<com.google.android.material.textfield.TextInputLayout
+			android:id="@+id/matrix"
+			android:layout_width="300dp"
+			android:layout_height="wrap_content"
+			android:layout_marginStart="16dp"
+			android:layout_marginTop="16dp"
+			android:hint="@string/matrix_handle"
+			app:layout_constraintStart_toStartOf="parent"
+			app:layout_constraintTop_toBottomOf="@id/name">
+
+			<com.google.android.material.textfield.TextInputEditText
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content" />
+
+		</com.google.android.material.textfield.TextInputLayout>
+
+		<Button
+			android:id="@+id/outlinedButton"
+			style="@style/Widget.Material3.Button.TextButton"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_marginTop="4dp"
+			android:text="@string/resend_verification_code"
+			app:layout_constraintEnd_toEndOf="@+id/matrix"
+			app:layout_constraintTop_toBottomOf="@+id/matrix" />
+	</androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>




diff --git a/app/src/main/res/layout/activity_feed_chooser.xml b/app/src/main/res/layout/activity_feed_chooser.xml
index 6c1203c951164d69fce52f6ab6ba8030869cf919..185ec670caea8124a3461672b379a839a7e80889 100644
--- a/app/src/main/res/layout/activity_feed_chooser.xml
+++ b/app/src/main/res/layout/activity_feed_chooser.xml
@@ -6,97 +6,107 @@
 SPDX-License-Identifier: GPL-3.0-or-later
 -->
 
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
 	xmlns:app="http://schemas.android.com/apk/res-auto"
-	xmlns:tool="http://schemas.android.com/tools"
+	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
 	android:layout_height="match_parent"
-	tool:context="xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity">
+	tools:context="xyz.apiote.bimba.czwek.settings.feeds.FeedChooserActivity">
+
+	<com.google.android.material.appbar.AppBarLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:fitsSystemWindows="true">
+
+		<com.google.android.material.appbar.MaterialToolbar
+			android:id="@+id/topAppBar"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:minHeight="?attr/actionBarSize"
+			app:menu="@menu/feeds_menu"
+			app:title="@string/title_cities" >
+			<com.google.android.material.textfield.TextInputEditText
+				android:layout_width="match_parent"
+				android:layout_marginStart="16dp"
+				android:layout_marginEnd="16dp"
+				android:layout_height="wrap_content"
+				android:visibility="gone"
+				/>
+		</com.google.android.material.appbar.MaterialToolbar>
+
+	</com.google.android.material.appbar.AppBarLayout>
 
 	<androidx.constraintlayout.widget.ConstraintLayout
-		android:id="@+id/feeds_overlay"
 		android:layout_width="match_parent"
 		android:layout_height="match_parent">
 
-		<com.google.android.material.progressindicator.CircularProgressIndicator
-			android:id="@+id/progress"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:indeterminate="true"
-			app:layout_constraintBottom_toBottomOf="parent"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toTopOf="parent" />
+		<androidx.constraintlayout.widget.ConstraintLayout
+			android:id="@+id/feeds_overlay"
+			android:layout_width="match_parent"
+			android:layout_height="match_parent">
 
-		<ImageView
-			android:id="@+id/error_image"
-			android:layout_width="92dp"
-			android:layout_height="92dp"
+			<com.google.android.material.progressindicator.CircularProgressIndicator
+				android:id="@+id/progress"
+				android:layout_width="wrap_content"
+				android:layout_height="wrap_content"
+				android:indeterminate="true"
+				app:layout_constraintBottom_toBottomOf="parent"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toTopOf="parent" />
+
+			<ImageView
+				android:id="@+id/error_image"
+				android:layout_width="92dp"
+				android:layout_height="92dp"
+				android:visibility="gone"
+				app:layout_constraintBottom_toBottomOf="parent"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toTopOf="parent"
+				tools:ignore="ContentDescription"
+				tools:src="@drawable/error_net" />
+
+			<com.google.android.material.textview.MaterialTextView
+				android:id="@+id/error_text"
+				android:layout_width="0dp"
+				android:layout_height="wrap_content"
+				android:layout_marginStart="16dp"
+				android:layout_marginTop="8dp"
+				android:layout_marginEnd="16dp"
+				android:textAlignment="center"
+				android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
+				android:visibility="gone"
+				app:layout_constraintEnd_toEndOf="parent"
+				app:layout_constraintStart_toStartOf="parent"
+				app:layout_constraintTop_toBottomOf="@+id/error_image"
+				tools:text="No connection" />
+		</androidx.constraintlayout.widget.ConstraintLayout>
+
+		<androidx.recyclerview.widget.RecyclerView
+			android:id="@+id/results_recycler"
+			android:layout_width="match_parent"
+			android:layout_height="0dp"
+			android:layout_marginStart="8dp"
+			android:layout_marginTop="8dp"
+			android:layout_marginEnd="8dp"
+			android:layout_marginBottom="8dp"
 			android:visibility="gone"
-			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_behavior="@string/appbar_scrolling_view_behavior"
+			app:layout_constraintBottom_toTopOf="@+id/button"
 			app:layout_constraintEnd_toEndOf="parent"
 			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toTopOf="parent"
-			tool:ignore="ContentDescription"
-			tool:src="@drawable/error_net" />
+			app:layout_constraintTop_toTopOf="parent" />
 
-		<com.google.android.material.textview.MaterialTextView
-			android:id="@+id/error_text"
-			android:layout_width="0dp"
+		<Button
+			android:id="@+id/button"
+			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
-			android:layout_marginStart="16dp"
-			android:layout_marginTop="8dp"
 			android:layout_marginEnd="16dp"
-			android:textAlignment="center"
-			android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
+			android:layout_marginBottom="16dp"
+			android:text="@string/save"
 			android:visibility="gone"
-			app:layout_constraintEnd_toEndOf="parent"
-			app:layout_constraintStart_toStartOf="parent"
-			app:layout_constraintTop_toBottomOf="@+id/error_image"
-			tool:text="No connection" />
+			app:layout_constraintBottom_toBottomOf="parent"
+			app:layout_constraintEnd_toEndOf="parent" />
 	</androidx.constraintlayout.widget.ConstraintLayout>
-
-	<com.google.android.material.textfield.TextInputLayout
-		android:id="@+id/search_bar"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="8dp"
-		android:layout_marginTop="8dp"
-		android:layout_marginEnd="8dp"
-		android:hint="@string/filter_localities"
-		android:visibility="gone"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toTopOf="parent">
-
-		<com.google.android.material.textfield.TextInputEditText
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content" />
-	</com.google.android.material.textfield.TextInputLayout>
-
-	<androidx.recyclerview.widget.RecyclerView
-		android:id="@+id/results_recycler"
-		android:layout_width="match_parent"
-		android:layout_height="0dp"
-		android:layout_marginStart="8dp"
-		android:layout_marginTop="8dp"
-		android:layout_marginEnd="8dp"
-		android:layout_marginBottom="8dp"
-		android:visibility="gone"
-		app:layout_behavior="@string/appbar_scrolling_view_behavior"
-		app:layout_constraintBottom_toTopOf="@+id/button"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@id/search_bar" />
-
-	<Button
-		android:id="@+id/button"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginEnd="16dp"
-		android:layout_marginBottom="16dp"
-		android:text="@string/save"
-		android:visibility="gone"
-		app:layout_constraintBottom_toBottomOf="parent"
-		app:layout_constraintEnd_toEndOf="parent" />
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file




diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml
index 35dc2687c8efce3fa3babc4e1486026d8c6e05c5..dc3a1d72dbf4dd6190d42ec6984161a265996cbe 100644
--- a/app/src/main/res/layout/activity_onboarding.xml
+++ b/app/src/main/res/layout/activity_onboarding.xml
@@ -1,65 +1,32 @@
 <?xml version="1.0" encoding="utf-8"?>
-
-<!--
-SPDX-FileCopyrightText: Adam Evyčędo
-
-SPDX-License-Identifier: GPL-3.0-or-later
--->
-
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 	xmlns:app="http://schemas.android.com/apk/res-auto"
-	xmlns:tool="http://schemas.android.com/tools"
+	xmlns:tools="http://schemas.android.com/tools"
 	android:layout_width="match_parent"
-	android:layout_height="match_parent"
-	tool:context="xyz.apiote.bimba.czwek.onboarding.OnboardingActivity">
-
-	<androidx.constraintlayout.widget.Guideline
-		android:id="@+id/guideline2"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:orientation="horizontal"
-		app:layout_constraintGuide_percent=".125" />
+	android:layout_height="match_parent">
 
-	<com.google.android.material.textview.MaterialTextView
-		android:layout_width="0dp"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="8dp"
-		android:layout_marginEnd="8dp"
-		android:text="@string/onboarding_question"
-		android:textAlignment="center"
-		android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium"
+	<com.google.android.material.imageview.ShapeableImageView
+		android:id="@+id/logo"
+		android:layout_width="100dp"
+		android:layout_height="100dp"
+		android:layout_marginTop="32dp"
+		android:background="@color/bimba_grey"
 		app:layout_constraintEnd_toEndOf="parent"
 		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toTopOf="@+id/guideline2" />
+		app:layout_constraintTop_toTopOf="parent"
+		app:shapeAppearanceOverlay="@style/roundedImageView"
+		app:srcCompat="@drawable/ic_launcher_foreground" />
 
-	<Button
-		android:id="@+id/button_simple"
-		style="?attr/materialButtonOutlinedStyle"
-		android:layout_width="200dp"
-		android:layout_height="100dp"
-		android:layout_marginBottom="16dp"
-		app:cornerRadius="10dp"
-		app:layout_constraintBottom_toTopOf="@+id/guideline"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent" />
-
-	<androidx.constraintlayout.widget.Guideline
-		android:id="@+id/guideline"
+	<com.google.android.material.textview.MaterialTextView
+		android:id="@+id/app_name"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
-		android:orientation="horizontal"
-		app:layout_constraintGuide_percent=".5" />
-
-	<Button
-		android:id="@+id/button_advanced"
-		style="?attr/materialButtonOutlinedStyle"
-		android:layout_width="200dp"
-		android:layout_height="100dp"
 		android:layout_marginTop="16dp"
-		app:cornerRadius="10dp"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@+id/guideline" />
+		android:text="@string/app_name"
+		android:textAppearance="@style/TextAppearance.AppCompat.Display1"
+		app:layout_constraintEnd_toEndOf="@+id/logo"
+		app:layout_constraintStart_toStartOf="@+id/logo"
+		app:layout_constraintTop_toBottomOf="@+id/logo" />
 
 	<com.google.android.material.textview.MaterialTextView
 		android:layout_width="0dp"
@@ -74,4 +41,45 @@ 		android:textStyle="italic"
 		app:layout_constraintBottom_toBottomOf="parent"
 		app:layout_constraintEnd_toEndOf="parent"
 		app:layout_constraintStart_toStartOf="parent" />
+
+	<com.google.android.material.textfield.TextInputLayout
+		android:id="@+id/server"
+		android:layout_width="400dp"
+		android:layout_height="wrap_content"
+		android:layout_marginStart="16dp"
+		android:layout_marginTop="64dp"
+		android:layout_marginEnd="16dp"
+		android:hint="@string/bimba_server_address_hint"
+		app:layout_constraintEnd_toEndOf="parent"
+		app:layout_constraintStart_toStartOf="parent"
+		app:layout_constraintTop_toBottomOf="@+id/app_name">
+
+		<com.google.android.material.textfield.TextInputEditText
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:text="bimba.apiote.xyz"
+			tools:ignore="HardcodedText" />
+
+	</com.google.android.material.textfield.TextInputLayout>
+
+	<Button
+		android:id="@+id/account_button"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginTop="64dp"
+		android:text="@string/log_in_or_sign_up"
+		app:layout_constraintEnd_toEndOf="@+id/server"
+		app:layout_constraintStart_toStartOf="@+id/server"
+		app:layout_constraintTop_toBottomOf="@+id/server" />
+
+	<Button
+		android:id="@+id/anonymous_button"
+		android:layout_width="wrap_content"
+		android:layout_height="wrap_content"
+		android:layout_marginTop="16dp"
+		android:text="@string/continue_without_account"
+		app:layout_constraintEnd_toEndOf="@+id/account_button"
+		app:layout_constraintStart_toStartOf="@+id/account_button"
+		app:layout_constraintTop_toBottomOf="@+id/account_button" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file




diff --git a/app/src/main/res/layout/login.xml b/app/src/main/res/layout/login.xml
deleted file mode 100644
index dc3a1d72dbf4dd6190d42ec6984161a265996cbe..0000000000000000000000000000000000000000
--- a/app/src/main/res/layout/login.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<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">
-
-	<com.google.android.material.imageview.ShapeableImageView
-		android:id="@+id/logo"
-		android:layout_width="100dp"
-		android:layout_height="100dp"
-		android:layout_marginTop="32dp"
-		android:background="@color/bimba_grey"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toTopOf="parent"
-		app:shapeAppearanceOverlay="@style/roundedImageView"
-		app:srcCompat="@drawable/ic_launcher_foreground" />
-
-	<com.google.android.material.textview.MaterialTextView
-		android:id="@+id/app_name"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="16dp"
-		android:text="@string/app_name"
-		android:textAppearance="@style/TextAppearance.AppCompat.Display1"
-		app:layout_constraintEnd_toEndOf="@+id/logo"
-		app:layout_constraintStart_toStartOf="@+id/logo"
-		app:layout_constraintTop_toBottomOf="@+id/logo" />
-
-	<com.google.android.material.textview.MaterialTextView
-		android:layout_width="0dp"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="8dp"
-		android:layout_marginEnd="8dp"
-		android:layout_marginBottom="16dp"
-		android:text="@string/seatbelts_everyone"
-		android:textAlignment="center"
-		android:textAppearance="@style/TextAppearance.Material3.BodySmall"
-		android:textStyle="italic"
-		app:layout_constraintBottom_toBottomOf="parent"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent" />
-
-	<com.google.android.material.textfield.TextInputLayout
-		android:id="@+id/server"
-		android:layout_width="400dp"
-		android:layout_height="wrap_content"
-		android:layout_marginStart="16dp"
-		android:layout_marginTop="64dp"
-		android:layout_marginEnd="16dp"
-		android:hint="@string/bimba_server_address_hint"
-		app:layout_constraintEnd_toEndOf="parent"
-		app:layout_constraintStart_toStartOf="parent"
-		app:layout_constraintTop_toBottomOf="@+id/app_name">
-
-		<com.google.android.material.textfield.TextInputEditText
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:text="bimba.apiote.xyz"
-			tools:ignore="HardcodedText" />
-
-	</com.google.android.material.textfield.TextInputLayout>
-
-	<Button
-		android:id="@+id/account_button"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="64dp"
-		android:text="@string/log_in_or_sign_up"
-		app:layout_constraintEnd_toEndOf="@+id/server"
-		app:layout_constraintStart_toStartOf="@+id/server"
-		app:layout_constraintTop_toBottomOf="@+id/server" />
-
-	<Button
-		android:id="@+id/anonymous_button"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="16dp"
-		android:text="@string/continue_without_account"
-		app:layout_constraintEnd_toEndOf="@+id/account_button"
-		app:layout_constraintStart_toStartOf="@+id/account_button"
-		app:layout_constraintTop_toBottomOf="@+id/account_button" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file




diff --git a/app/src/main/res/menu/account.xml b/app/src/main/res/menu/account.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc3b526989d14d93d2619016ddafe280fbb33d60
--- /dev/null
+++ b/app/src/main/res/menu/account.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+	<item
+		android:id="@+id/edit"
+		android:icon="@drawable/edit"
+		android:title="@string/edit"
+		app:showAsAction="ifRoom" />
+	<item
+		android:id="@+id/logout"
+		android:icon="@drawable/logout"
+		android:title="@string/log_out"
+		app:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file




diff --git a/app/src/main/res/menu/account_edit.xml b/app/src/main/res/menu/account_edit.xml
new file mode 100644
index 0000000000000000000000000000000000000000..960bf340e27c216bd3f962889429766ecce83464
--- /dev/null
+++ b/app/src/main/res/menu/account_edit.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+	<item
+		android:id="@+id/save"
+		android:icon="@drawable/checkmark"
+		android:title="@string/save"
+		app:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file




diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml
index 5a5194c75d78419d3249ea66b2dc7a1e1354d202..26110bf2cbf93264d7add29c4011b2d9d7143670 100644
--- a/app/src/main/res/menu/drawer.xml
+++ b/app/src/main/res/menu/drawer.xml
@@ -12,10 +12,10 @@ 		android:id="@+id/drawer_feeds"
 		android:title="@string/title_feeds">
 		<menu>
 			<item
-				android:id="@+id/drawer_servers"
+				android:id="@+id/drawer_account"
 				android:checkable="true"
-				android:icon="@drawable/feeds_servers"
-				android:title="@string/title_servers" />
+				android:icon="@drawable/account"
+				android:title="@string/title_account" />
 			<item
 				android:id="@+id/drawer_cities"
 				android:checkable="true"




diff --git a/app/src/main/res/menu/feeds_menu.xml b/app/src/main/res/menu/feeds_menu.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bc87c01e295ede32e965dc043da89413cc2cefc3
--- /dev/null
+++ b/app/src/main/res/menu/feeds_menu.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:app="http://schemas.android.com/apk/res-auto">
+	<item
+		android:id="@+id/server"
+		android:icon="@drawable/feeds_servers"
+		android:title="@string/title_servers"
+		app:showAsAction="ifRoom" />
+	<item
+		android:id="@+id/filter"
+		android:icon="@drawable/filter"
+		android:title="@string/title_filter"
+		app:showAsAction="ifRoom" />
+</menu>
\ No newline at end of file




diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 78ddc5284daf508032464dfe9be1bb7066e450c2..d3cb71f422f0093ed7e5c8c2bc6768a53b46f8a4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -287,4 +287,14 @@ 	link to Matrix channel
 	<string name="email_button_description">link to email</string>
 	<string name="log_in_or_sign_up">Log in or Sign up</string>
 	<string name="continue_without_account">Continue without account</string>
+	<string name="open_browser_to_edit_identity">open browser to edit identity</string>
+	<string name="edit">Edit</string>
+	<string name="log_out">Log out</string>
+	<string name="matrix_handle">Matrix handle</string>
+	<string name="subscription">Subscription</string>
+	<string name="logged_in_as">Logged in as</string>
+	<string name="using">Using</string>
+	<string name="name">Name</string>
+	<string name="resend_verification_code">Resend verification code</string>
+	<string name="title_account">Account</string>
 </resources>