Skip to content
Snippets Groups Projects
Verified Commit a1cf7a82 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Added ability to change passwords

parent b8264940
No related branches found
No related tags found
No related merge requests found
Showing
with 407 additions and 8 deletions
......@@ -99,6 +99,11 @@
android:exported="false"
android:label="@string/label_topic"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.chat.passwordchange.PasswordChangeActivity"
android:exported="false"
android:label="@string/label_password_change"
android:windowSoftInputMode="adjustResize" />
<!-- Core Settings -->
<activity
......
......@@ -32,6 +32,8 @@ import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoActivity
import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoFragmentProvider
import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoActivity
import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoFragmentProvider
import de.kuschku.quasseldroid.ui.chat.passwordchange.PasswordChangeActivity
import de.kuschku.quasseldroid.ui.chat.passwordchange.PasswordChangeFragmentProvider
import de.kuschku.quasseldroid.ui.chat.topic.TopicActivity
import de.kuschku.quasseldroid.ui.chat.topic.TopicFragmentProvider
import de.kuschku.quasseldroid.ui.clientsettings.about.AboutActivity
......@@ -109,6 +111,10 @@ abstract class ActivityModule {
@ContributesAndroidInjector(modules = [TopicFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindTopicActivity(): TopicActivity
@ActivityScope
@ContributesAndroidInjector(modules = [PasswordChangeFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindPasswordChangeActivity(): PasswordChangeActivity
@ActivityScope
@ContributesAndroidInjector(modules = [ClientSettingsFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindClientSettingsActivity(): ClientSettingsActivity
......
......@@ -319,7 +319,6 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
}
}
}
// If we connect to a new network without statusbuffer, the bufferid may be -networkId.
// In that case, once we’re connected (and a status buffer exists), we want to switch to it.
combineLatest(viewModel.allBuffers, viewModel.buffer).map { (buffers, current) ->
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.chat.passwordchange
import android.content.Context
import android.content.Intent
import de.kuschku.quasseldroid.util.ui.settings.ServiceBoundSettingsActivity
class PasswordChangeActivity : ServiceBoundSettingsActivity(PasswordChangeFragment()) {
companion object {
fun launch(context: Context) = context.startActivity(intent(context))
fun intent(context: Context) = Intent(context, PasswordChangeActivity::class.java)
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.chat.passwordchange
import android.os.Bundle
import android.text.Editable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.Observer
import butterknife.BindView
import butterknife.ButterKnife
import com.google.android.material.textfield.TextInputLayout
import de.kuschku.libquassel.quassel.syncables.RpcHandler
import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.helpers.mapMapNullable
import de.kuschku.libquassel.util.helpers.mapSwitchMap
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.persistence.AccountDatabase
import de.kuschku.quasseldroid.util.TextValidator
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
import me.zhanghai.android.materialprogressbar.MaterialProgressBar
import javax.inject.Inject
class PasswordChangeFragment : ServiceBoundFragment() {
@BindView(R.id.user)
lateinit var user: EditText
@BindView(R.id.password_old_wrapper)
lateinit var oldPasswordWrapper: TextInputLayout
@BindView(R.id.password_old)
lateinit var oldPassword: EditText
@BindView(R.id.password_new)
lateinit var newPassword: EditText
@BindView(R.id.password_repeat_wrapper)
lateinit var repeatPasswordWrapper: TextInputLayout
@BindView(R.id.password_repeat)
lateinit var repeatPassword: EditText
@BindView(R.id.error)
lateinit var error: TextView
@BindView(R.id.save)
lateinit var save: Button
@BindView(R.id.progress)
lateinit var progress: MaterialProgressBar
@Inject
lateinit var accountDatabase: AccountDatabase
private var waiting: AccountDatabase.Account? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_passwordchange, container, false)
ButterKnife.bind(this, view)
val account = accountDatabase.accounts().findById(accountId)
user.setText(account?.user)
viewModel.session
.mapMapNullable(ISession::rpcHandler)
.mapSwitchMap(RpcHandler::passwordChanged)
.filter(Optional<Boolean>::isPresent)
.map(Optional<Boolean>::get)
.toLiveData().observe(this, Observer {
val waiting = this.waiting
if (waiting != null) {
if (it) {
save.setText(R.string.label_save)
save.isEnabled = true
save.isClickable = true
progress.visibility = View.GONE
error.visibility = View.GONE
runInBackground {
accountDatabase.accounts().save(waiting)
}
this.waiting = null
activity?.finish()
} else {
error.visibility = View.VISIBLE
}
}
})
oldPassword.addTextChangedListener(object : TextValidator(
activity,
oldPasswordWrapper::setError,
getString(R.string.label_password_error_wrong)
) {
override fun validate(text: Editable) = text.toString() == account?.pass
})
repeatPassword.addTextChangedListener(object : TextValidator(
activity,
repeatPasswordWrapper::setError,
getString(R.string.label_password_error_nomatch)
) {
override fun validate(text: Editable) = text.toString() == newPassword.text.toString()
})
save.setOnClickListener {
save.setText(R.string.label_saving)
save.isEnabled = false
save.isClickable = false
progress.visibility = View.VISIBLE
error.visibility = View.GONE
val pass = newPassword.text.toString()
waiting = account?.copy(pass = pass)
viewModel.session.value?.orNull()?.rpcHandler?.changePassword(0L,
user.text.toString(),
oldPassword.text.toString(),
pass)
}
return view
}
}
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.ui.chat.passwordchange
import androidx.fragment.app.FragmentActivity
import dagger.Binds
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class PasswordChangeFragmentProvider {
@Binds
abstract fun bindFragmentActivity(activity: PasswordChangeActivity): FragmentActivity
@ContributesAndroidInjector
abstract fun bindPasswordChangeFragment(): PasswordChangeFragment
}
......@@ -36,6 +36,7 @@ import de.kuschku.libquassel.quassel.syncables.Identity
import de.kuschku.libquassel.quassel.syncables.Network
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoActivity
import de.kuschku.quasseldroid.ui.chat.passwordchange.PasswordChangeActivity
import de.kuschku.quasseldroid.ui.coresettings.aliaslist.AliasListActivity
import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistCreateActivity
import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistEditActivity
......@@ -63,6 +64,9 @@ class CoreSettingsFragment : ServiceBoundFragment() {
@BindView(R.id.coreinfo)
lateinit var coreinfo: View
@BindView(R.id.passwordchange)
lateinit var passwordchange: View
@BindView(R.id.networks)
lateinit var networks: RecyclerView
......@@ -116,6 +120,10 @@ class CoreSettingsFragment : ServiceBoundFragment() {
CoreInfoActivity.launch(requireContext())
}
passwordchange.setOnClickListener {
PasswordChangeActivity.launch(requireContext())
}
networks.adapter = networkAdapter
networks.layoutManager = LinearLayoutManager(context)
networks.addItemDecoration(itemDecoration)
......
......@@ -51,17 +51,14 @@ class CoreSetupActivity : ServiceBoundSetupActivity() {
val authenticatorBackend = data.getSerializable("authenticator") as? CoreSetupBackend
val authenticatorBackendSetup = data.getSerializable("authenticatorSetup") as? HashMap<String, QVariant_>
val setupData = HandshakeMessage.CoreSetupData(
viewModel.sessionManager.value?.orNull()?.setupCore(HandshakeMessage.CoreSetupData(
adminUser = user,
adminPassword = pass,
backend = storageBackend?.backendId,
setupData = storageBackendSetup.orEmpty(),
authenticator = authenticatorBackend?.backendId,
authSetupData = authenticatorBackendSetup.orEmpty()
)
viewModel.sessionManager.value?.orNull()?.setupCore(setupData)
println(setupData)
))
setResult(Activity.RESULT_OK)
finish()
......
......@@ -23,12 +23,12 @@ import android.app.Activity
import android.text.Editable
import android.text.TextWatcher
abstract class TextValidator(private val activity: Activity,
abstract class TextValidator(private val activity: Activity?,
private val errorListener: (String?) -> Unit,
private val error: String) : TextWatcher {
override fun afterTextChanged(p0: Editable) {
isValid = validate(p0)
activity.runOnUiThread {
activity?.runOnUiThread {
errorListener(if (isValid) null else error)
}
onChanged()
......
<!--
Quasseldroid - Quassel client for Android
Copyright (c) 2019 Janne Koschinski
Copyright (c) 2019 The Quassel Project
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License version 3 as published
by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<androidx.core.widget.NestedScrollView 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:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/userWrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="@string/label_account_user"
tools:ignore="LabelFor">
<EditText
android:id="@+id/user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:inputType="textVisiblePassword|textNoSuggestions"
app:errorEnabled="true"
tools:text="kuschku" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/password_old_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="@string/label_password_old"
app:passwordToggleEnabled="true"
tools:ignore="LabelFor">
<EditText
android:id="@+id/password_old"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
app:errorEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/label_password_new"
app:passwordToggleEnabled="true"
tools:ignore="LabelFor">
<EditText
android:id="@+id/password_new"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
app:errorEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/password_repeat_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:hint="@string/label_password_repeat"
app:passwordToggleEnabled="true"
tools:ignore="LabelFor">
<EditText
android:id="@+id/password_repeat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
app:errorEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="16dp"
android:textColor="?colorForegroundError"
android:visibility="gone"
tools:text="Error occured: Passwords do not match"
tools:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/save"
style="@style/Widget.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_save" />
<Space
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progress"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
......@@ -211,5 +211,19 @@
style="@style/Widget.CoreSettings.PrimaryItemSwitch"
android:text="@string/label_info_core" />
</LinearLayout>
<LinearLayout
android:id="@+id/passwordchange"
style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"
android:focusable="true">
<androidx.appcompat.widget.AppCompatImageView
style="@style/Widget.CoreSettings.PrimaryItemIcon"
app:srcCompat="@drawable/ic_key_variant" />
<TextView
style="@style/Widget.CoreSettings.PrimaryItemSwitch"
android:text="@string/label_password_change" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
......@@ -88,6 +88,12 @@
<string name="label_open">Open</string>
<string name="label_part">Leave</string>
<string name="label_part_long">Leave Channel</string>
<string name="label_password_change">Change Password</string>
<string name="label_password_error_wrong">Wrong Password</string>
<string name="label_password_error_nomatch">Passwords do not match</string>
<string name="label_password_new">New Password</string>
<string name="label_password_old">Old Password</string>
<string name="label_password_repeat">Repeat Password</string>
<string name="label_placeholder_message">Write a message…</string>
<string name="label_placeholder_topic">Describe the channel topic…</string>
<string name="label_privacy_policy">Privacy Policy</string>
......@@ -95,6 +101,7 @@
<string name="label_query_long">Open private chat with user</string>
<string name="label_rename">Rename</string>
<string name="label_save">Save</string>
<string name="label_saving">Saving…</string>
<string name="label_search_buffer">Search…</string>
<string name="label_select">Select</string>
<string name="label_send">Send</string>
......
......@@ -28,6 +28,7 @@ import de.kuschku.libquassel.session.BacklogStorage
import de.kuschku.libquassel.session.NotificationManager
import de.kuschku.libquassel.session.Session
import de.kuschku.libquassel.util.helpers.deserializeString
import de.kuschku.libquassel.util.rxjava.ReusableUnicastSubject
import java.nio.ByteBuffer
class RpcHandler(
......@@ -48,7 +49,11 @@ class RpcHandler(
override fun networkCreated(networkId: NetworkId) = session.addNetwork(networkId)
override fun networkRemoved(networkId: NetworkId) = session.removeNetwork(networkId)
private val passwordChangedSubject = ReusableUnicastSubject.create<Boolean>()
val passwordChanged = passwordChangedSubject.publish().refCount()
override fun passwordChanged(ignored: Long, success: Boolean) {
passwordChangedSubject.onNext(success)
}
override fun disconnectFromCore() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment