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

Add ability to configure core, fixes #17

parent 52692764
Branches
Tags
No related merge requests found
Pipeline #344 passed
Showing
with 1014 additions and 147 deletions
......@@ -239,6 +239,13 @@
android:parentActivityName=".ui.setup.accounts.selection.AccountSelectionActivity"
android:windowSoftInputMode="adjustResize" />
<!-- Core Setup Flow -->
<activity
android:name=".ui.setup.core.CoreSetupActivity"
android:exported="false"
android:label="@string/setup_core_title"
android:parentActivityName=".ui.chat.ChatActivity"
android:windowSoftInputMode="adjustResize" />
<!-- Core User Setup Flow -->
<activity
android:name=".ui.setup.user.UserSetupActivity"
......@@ -246,6 +253,7 @@
android:label="@string/setup_user_title"
android:parentActivityName=".ui.chat.ChatActivity"
android:windowSoftInputMode="adjustResize" />
<!-- Network Setup Flow -->
<activity
android:name=".ui.setup.network.NetworkSetupActivity"
android:exported="false"
......
......@@ -80,6 +80,8 @@ import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActiv
import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionFragmentProvider
import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupActivity
import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupFragmentProvider
import de.kuschku.quasseldroid.ui.setup.core.CoreSetupActivity
import de.kuschku.quasseldroid.ui.setup.core.CoreSetupFragmentProvider
import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupActivity
import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupFragmentProvider
import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
......@@ -207,6 +209,10 @@ abstract class ActivityModule {
@ContributesAndroidInjector(modules = [NetworkSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindNetworkSetupActivity(): NetworkSetupActivity
@ActivityScope
@ContributesAndroidInjector(modules = [CoreSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
abstract fun bindCoreSetupActivity(): CoreSetupActivity
@ActivityScope
@ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class])
abstract fun bindQuasselService(): QuasselService
......
......@@ -60,6 +60,7 @@ import de.kuschku.libquassel.protocol.Buffer_Type
import de.kuschku.libquassel.protocol.Message
import de.kuschku.libquassel.protocol.Message_Type
import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
import de.kuschku.libquassel.protocol.message.HandshakeMessage
import de.kuschku.libquassel.session.Error
import de.kuschku.libquassel.session.ISession
......@@ -84,6 +85,7 @@ import de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment
import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity
import de.kuschku.quasseldroid.ui.coresettings.CoreSettingsActivity
import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActivity
import de.kuschku.quasseldroid.ui.setup.core.CoreSetupActivity
import de.kuschku.quasseldroid.ui.setup.network.LinkNetwork
import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupActivity
import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
......@@ -337,11 +339,21 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
when (it) {
is Error.HandshakeError -> it.message.let {
when (it) {
is HandshakeMessage.ClientInitAck ->
if (it.coreConfigured == false)
CoreSetupActivity.launch(
this,
accountDatabase.accounts().findById(accountId),
CoreSetupData.of(it)
)
is HandshakeMessage.ClientInitReject ->
MaterialDialog.Builder(this)
.title(R.string.label_error_init)
.content(Html.fromHtml(it.errorString))
.neutralText(R.string.label_close)
.negativeText(R.string.label_disconnect)
.onNegative { _, _ ->
disconnect()
}
.titleColorAttr(R.attr.colorTextPrimary)
.backgroundColorAttr(R.attr.colorBackgroundCard)
.contentColorAttr(R.attr.colorTextPrimary)
......@@ -351,7 +363,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
MaterialDialog.Builder(this)
.title(R.string.label_error_setup)
.content(Html.fromHtml(it.errorString))
.neutralText(R.string.label_close)
.negativeText(R.string.label_disconnect)
.onNegative { _, _ ->
disconnect()
}
.titleColorAttr(R.attr.colorTextPrimary)
.backgroundColorAttr(R.attr.colorBackgroundCard)
.contentColorAttr(R.attr.colorTextPrimary)
......
......@@ -22,16 +22,13 @@ package de.kuschku.quasseldroid.ui.setup
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.util.SparseArray
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.ActionMenuView
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.core.graphics.drawable.DrawableCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
......@@ -123,13 +120,20 @@ abstract class ServiceBoundSetupActivity :
private lateinit var pageChangeListener: SetupActivityViewPagerPageChangeListener
private fun pageChanged() {
currentPage.value = adapter.getItem(viewPager.currentItem)
val drawable = if (viewPager.currentItem == adapter.totalCount - 1)
R.drawable.ic_check
else
R.drawable.ic_arrow_right
button.setImageResource(drawable)
currentPage.value?.requestFocus()
val page = adapter.getItem(viewPager.currentItem)
currentPage.value = page
val finish = viewPager.currentItem == adapter.totalCount - 1
button.contentDescription =
if (finish) descriptionFinish
else descriptionNext
button.setTooltip()
button.setImageDrawable(
if (finish) drawableFinish
else drawableNext
)
page.requestFocus()
}
fun updateRecentsHeader() {
......@@ -143,6 +147,12 @@ abstract class ServiceBoundSetupActivity :
updateRecentsHeader()
}
private var drawableFinish: Drawable? = null
private var drawableNext: Drawable? = null
private var descriptionFinish: String? = null
private var descriptionNext: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SetupTheme)
super.onCreate(savedInstanceState)
......@@ -152,6 +162,16 @@ abstract class ServiceBoundSetupActivity :
setContentView(R.layout.activity_setup)
ButterKnife.bind(this)
drawableFinish = getVectorDrawableCompat(R.drawable.ic_check)?.mutate()?.also {
DrawableCompat.setTint(it, -1)
}
drawableNext = getVectorDrawableCompat(R.drawable.ic_arrow_right)?.mutate()?.also {
DrawableCompat.setTint(it, -1)
}
descriptionFinish = getString(R.string.label_finish)
descriptionNext = getString(R.string.label_next)
menuView.popupTheme = R.style.Widget_PopupOverlay_Light
menuInflater.inflate(R.menu.activity_setup, menuView.menu)
menuView.setOnMenuItemClickListener {
......@@ -239,70 +259,6 @@ abstract class ServiceBoundSetupActivity :
pageChanged()
}
private class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
FragmentStatePagerAdapter(fragmentManager) {
private val retainedFragments = SparseArray<SlideFragment>()
val result = Bundle()
get() {
(0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
it.getData(field)
}
return field
}
var lastValidItem = -1
set(value) {
field = value
notifyDataSetChanged()
}
private val list = mutableListOf<SlideFragment>()
override fun getItem(position: Int): SlideFragment {
return retainedFragments.get(position) ?: list[position]
}
override fun getCount() = Math.min(list.size, lastValidItem + 2)
val totalCount get() = list.size
fun addFragment(fragment: SlideFragment) {
list.add(fragment)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position)
storeNewFragment(position, fragment as SlideFragment)
return fragment
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
retainedFragments.get(position)?.getData(result)
retainedFragments.remove(position)
super.destroyItem(container, position, `object`)
}
override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
super.restoreState(state, loader)
if (state != null) {
val bundle = state as Bundle
val keys = bundle.keySet()
for (key in keys) {
if (key.startsWith("f")) {
val index = Integer.parseInt(key.substring(1))
val f = fragmentManager.getFragment(bundle, key)
if (f != null && f is SlideFragment) {
storeNewFragment(index, f)
}
}
}
}
}
private fun storeNewFragment(index: Int, fragment: SlideFragment) {
fragment.initData = result
retainedFragments.put(index, fragment)
}
}
override fun onStart() {
if (Settings.appearance(this) != appearanceSettings) {
recreate()
......
......@@ -24,15 +24,10 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.util.SparseArray
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.ActionMenuView
import androidx.core.graphics.drawable.DrawableCompat
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
......@@ -225,70 +220,6 @@ abstract class SetupActivity : DaggerAppCompatActivity() {
pageChanged()
}
private class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
FragmentStatePagerAdapter(fragmentManager) {
private val retainedFragments = SparseArray<SlideFragment>()
val result = Bundle()
get() {
(0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
it.getData(field)
}
return field
}
var lastValidItem = -1
set(value) {
field = value
notifyDataSetChanged()
}
private val list = mutableListOf<SlideFragment>()
override fun getItem(position: Int): SlideFragment {
return retainedFragments.get(position) ?: list[position]
}
override fun getCount() = Math.min(list.size, lastValidItem + 2)
val totalCount get() = list.size
fun addFragment(fragment: SlideFragment) {
list.add(fragment)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position)
storeNewFragment(position, fragment as SlideFragment)
return fragment
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
retainedFragments.get(position)?.getData(result)
retainedFragments.remove(position)
super.destroyItem(container, position, `object`)
}
override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
super.restoreState(state, loader)
if (state != null) {
val bundle = state as Bundle
val keys = bundle.keySet()
for (key in keys) {
if (key.startsWith("f")) {
val index = Integer.parseInt(key.substring(1))
val f = fragmentManager.getFragment(bundle, key)
if (f != null && f is SlideFragment) {
storeNewFragment(index, f)
}
}
}
}
}
private fun storeNewFragment(index: Int, fragment: SlideFragment) {
fragment.initData = result
retainedFragments.put(index, fragment)
}
}
override fun onBackPressed() {
if (viewPager.currentItem == 0)
super.onBackPressed()
......
......@@ -92,6 +92,15 @@ abstract class SlideFragment : DaggerFragment() {
this.view?.requestFocus()
}
private var hasChangedListener: ((SlideFragment) -> Unit)? = null
fun setHasChangedListener(listener: ((SlideFragment) -> Unit)?) {
hasChangedListener = listener
}
protected fun hasChanged() {
hasChangedListener?.invoke(this)
}
protected abstract fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): 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.setup
import android.os.Bundle
import android.os.Parcelable
import android.util.SparseArray
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
FragmentStatePagerAdapter(fragmentManager) {
private val retainedFragments = SparseArray<SlideFragment>()
val result = Bundle()
get() {
(0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
it.getData(field)
}
return field
}
var lastValidItem = -1
set(value) {
field = value
notifyDataSetChanged()
}
private val list = mutableListOf<SlideFragment>()
override fun getItem(position: Int): SlideFragment {
return retainedFragments.get(position) ?: list[position]
}
override fun getCount() = Math.min(list.size, lastValidItem + 2)
val totalCount get() = list.size
fun addFragment(fragment: SlideFragment) {
list.add(fragment)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position)
storeNewFragment(position, fragment as SlideFragment)
return fragment
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
retainedFragments.get(position)?.getData(result)
retainedFragments.remove(position)
super.destroyItem(container, position, `object`)
}
override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
super.restoreState(state, loader)
if (state != null) {
val bundle = state as Bundle
val keys = bundle.keySet()
for (key in keys) {
if (key.startsWith("f")) {
val index = Integer.parseInt(key.substring(1))
val f = fragmentManager.getFragment(bundle, key)
if (f != null && f is SlideFragment) {
storeNewFragment(index, f)
}
}
}
}
}
private fun storeNewFragment(index: Int, fragment: SlideFragment) {
fragment.initData = result
fragment.setHasChangedListener { hasChanged(index, fragment) }
retainedFragments.put(index, fragment)
}
fun hasChanged(index: Int, fragment: SlideFragment) {
fragment.getData(result)
if (index > -1 && index < totalCount) {
getItem(index + 1).setData(result)
}
}
}
/*
* 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.setup.core
import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
import de.kuschku.quasseldroid.R
class CoreAuthenticatorBackendChooseSlide : CoreBackendChooseSlide() {
override val title = R.string.slide_core_authenticator_select_title
override val description = R.string.slide_core_authenticator_select_description
override val inputKey = CoreSetupData::authenticatorInfo
override val outputKey = "authenticator"
}
/*
* 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.setup.core
import de.kuschku.quasseldroid.R
class CoreAuthenticatorBackendSetupSlide : CoreBackendSetupSlide() {
override val title = R.string.slide_core_backend_setup_title
override val description = R.string.slide_core_backend_setup_description
override val inputKey = "authenticator"
override val outputKey = "authenticatorSetup"
}
/*
* 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.setup.core
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.widget.AppCompatRadioButton
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
import de.kuschku.quasseldroid.R
class CoreBackendAdapter : RecyclerView.Adapter<CoreBackendAdapter.BackendViewHolder>() {
private val selectionListeners = mutableSetOf<(CoreSetupBackend) -> Unit>()
private var selectedItem: Pair<CoreSetupBackend?, CoreSetupBackend?> = Pair(null, null)
fun selection() = selectedItem.second
private val clickListener = { item: CoreSetupBackend ->
selectionListener.invoke(item)
}
fun updateSelection(item: CoreSetupBackend) {
selectedItem = Pair(selectedItem.second, item)
submitList()
}
private val selectionListener = { item: CoreSetupBackend ->
updateSelection(item)
for (selectionListener in selectionListeners) {
selectionListener.invoke(item)
}
}
private var list: List<Pair<Boolean, CoreSetupBackend>> = emptyList()
fun submitList(
list: List<CoreSetupBackend> = this.list.map(Pair<Boolean, CoreSetupBackend>::second)) {
val oldList = this.list
val oldSelected = selectedItem.first
val newSelected = selectedItem.second
val newList = list.map {
Pair(it == newSelected, it)
}
this.list = newList
DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition].second
val newItem = newList[newItemPosition].second
return oldItem == newItem
}
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition].second
val newItem = newList[newItemPosition].second
return oldItem == newItem &&
oldItem != newSelected &&
newItem != newSelected &&
oldItem != oldSelected &&
newItem != oldSelected
}
}).dispatchUpdatesTo(this)
}
fun addSelectionListener(f: (CoreSetupBackend) -> Unit) {
selectionListeners.add(f)
}
override fun onBindViewHolder(holder: BackendViewHolder, position: Int) {
val item = list[position]
holder.bind(item.second, item.first)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackendViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.widget_core_backend, parent, false)
return BackendViewHolder(view, clickListener)
}
override fun getItemCount() = list.size
class BackendViewHolder(itemView: View, clickListener: (CoreSetupBackend) -> Unit) :
RecyclerView.ViewHolder(itemView) {
@BindView(R.id.backend_name)
lateinit var backendName: TextView
@BindView(R.id.backend_description)
lateinit var backendDescription: TextView
@BindView(R.id.backend_select)
lateinit var backendSelect: AppCompatRadioButton
private var item: CoreSetupBackend? = null
init {
ButterKnife.bind(this, itemView)
itemView.setOnClickListener {
item?.let(clickListener)
}
}
fun bind(backend: CoreSetupBackend, selected: Boolean) {
item = backend
backendName.text = backend.displayName
backendDescription.text = backend.description
backendSelect.isChecked = selected
}
fun clear() {
item = null
backendName.text = ""
backendDescription.text = ""
backendSelect.isChecked = false
}
}
}
/*
* 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.setup.core
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.setup.SlideFragment
abstract class CoreBackendChooseSlide : SlideFragment() {
@BindView(R.id.account_list)
lateinit var backendList: RecyclerView
override fun isValid() = adapter.selection() != null
protected val adapter = CoreBackendAdapter()
abstract val outputKey: String
abstract val inputKey: ((CoreSetupData) -> List<CoreSetupBackend>)
override fun getData(data: Bundle) {
data.putSerializable(
outputKey,
adapter.selection()
)
}
override fun setData(data: Bundle) {
val message = data.getSerializable("data") as? CoreSetupData
adapter.submitList(message?.let(inputKey).orEmpty())
(data.getSerializable(outputKey) as? CoreSetupBackend)?.let(adapter::updateSelection)
}
override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.setup_select_account, container, false)
ButterKnife.bind(this, view)
backendList.layoutManager = LinearLayoutManager(context)
backendList.itemAnimator = DefaultItemAnimator()
backendList.adapter = adapter
adapter.addSelectionListener {
updateValidity()
hasChanged()
}
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.setup.core
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import butterknife.BindView
import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.QVariant_
import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.setup.SlideFragment
abstract class CoreBackendSetupSlide : SlideFragment() {
@BindView(R.id.frame)
lateinit var frame: LinearLayout
@BindView(R.id.no_options_info)
lateinit var noOptionsInfo: View
override fun isValid() = true
abstract val inputKey: String
abstract val outputKey: String
private var widgets = mutableListOf<QuasselSetupEntry>()
override fun setData(data: Bundle) {
widgets.clear()
frame.removeAllViews()
noOptionsInfo.visibility = View.VISIBLE
val backend = data.getSerializable(inputKey) as? CoreSetupBackend
for (configEntry in backend?.setupData.orEmpty()) {
val entry = QuasselSetupEntry(frame.context,
null,
0,
configEntry)
widgets.add(entry)
frame.addView(entry)
noOptionsInfo.visibility = View.GONE
}
}
override fun getData(data: Bundle) {
val setupData = HashMap<String, QVariant_>()
for (configEntry in widgets) {
setupData[configEntry.key()] = configEntry.value()
}
data.putSerializable(outputKey, setupData)
}
override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.setup_core_backend_configure, container, false)
ButterKnife.bind(this, view)
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.setup.core
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import de.kuschku.libquassel.protocol.QVariant_
import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
import de.kuschku.libquassel.protocol.message.HandshakeMessage
import de.kuschku.libquassel.quassel.ExtendedFeature
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.quasseldroid.persistence.AccountDatabase
import de.kuschku.quasseldroid.ui.setup.ServiceBoundSetupActivity
class CoreSetupActivity : ServiceBoundSetupActivity() {
override val initData = Bundle()
override fun onCreate(savedInstanceState: Bundle?) {
initData.clear()
initData.putAll(intent.extras)
super.onCreate(savedInstanceState)
}
override fun onDone(data: Bundle) {
val user = initData.getString("user")
val pass = initData.getString("pass")
val storageBackend = data.getSerializable("storage") as? CoreSetupBackend
val storageBackendSetup = data.getSerializable("storageSetup") as? HashMap<String, QVariant_>
val authenticatorBackend = data.getSerializable("authenticator") as? CoreSetupBackend
val authenticatorBackendSetup = data.getSerializable("authenticatorSetup") as? HashMap<String, QVariant_>
val setupData = 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()
}
override val fragments
get() = if ((initData.getSerializable("data") as? CoreSetupData)
?.features
?.hasFeature(ExtendedFeature.Authenticators) == true) {
listOf(
CoreStorageBackendChooseSlide(),
CoreStorageBackendSetupSlide(),
CoreAuthenticatorBackendChooseSlide(),
CoreAuthenticatorBackendSetupSlide()
)
} else {
listOf(
CoreStorageBackendChooseSlide(),
CoreStorageBackendSetupSlide()
)
}
companion object {
fun launch(
context: Context,
account: AccountDatabase.Account? = null,
data: CoreSetupData? = null
) = context.startActivity(intent(context, account, data))
fun intent(
context: Context,
account: AccountDatabase.Account? = null,
data: CoreSetupData? = null
) = Intent(context, CoreSetupActivity::class.java).apply {
if (account != null) {
putExtra("user", account.user)
putExtra("pass", account.pass)
}
if (data != null) {
putExtra("data", data)
}
}
}
}
/*
* 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.setup.core
import androidx.fragment.app.FragmentActivity
import dagger.Binds
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class CoreSetupFragmentProvider {
@Binds
abstract fun bindFragmentActivity(activity: CoreSetupActivity): FragmentActivity
@ContributesAndroidInjector
abstract fun bindCoreStorageBackendChooseSlide(): CoreStorageBackendChooseSlide
@ContributesAndroidInjector
abstract fun bindCoreStorageBackendSetupSlide(): CoreStorageBackendSetupSlide
@ContributesAndroidInjector
abstract fun bindCoreAuthenticatorBackendChooseSlide(): CoreAuthenticatorBackendChooseSlide
@ContributesAndroidInjector
abstract fun bindCoreAuthenticatorBackendSetupSlide(): CoreAuthenticatorBackendSetupSlide
}
/*
* 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.setup.core
import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
import de.kuschku.quasseldroid.R
class CoreStorageBackendChooseSlide : CoreBackendChooseSlide() {
override val title = R.string.slide_core_backend_select_title
override val description = R.string.slide_core_backend_select_description
override val inputKey = CoreSetupData::backendInfo
override val outputKey = "storage"
}
/*
* 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.setup.core
import de.kuschku.quasseldroid.R
class CoreStorageBackendSetupSlide : CoreBackendSetupSlide() {
override val title = R.string.slide_core_backend_setup_title
override val description = R.string.slide_core_backend_setup_description
override val inputKey = "storage"
override val outputKey = "storageSetup"
}
/*
* 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.setup.core
import android.content.Context
import android.text.InputType
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import butterknife.BindView
import butterknife.ButterKnife
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import de.kuschku.libquassel.protocol.QVariant_
import de.kuschku.libquassel.protocol.Type
import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackendConfigElement
import de.kuschku.libquassel.protocol.value
import de.kuschku.quasseldroid.R
class QuasselSetupEntry : FrameLayout {
@BindView(R.id.wrapper)
lateinit var wrapper: TextInputLayout
@BindView(R.id.field)
lateinit var field: TextInputEditText
private var data: CoreSetupBackendConfigElement? = null
constructor(context: Context) :
this(context, null)
constructor(context: Context, attrs: AttributeSet?) :
this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
this(context, attrs, defStyleAttr, null)
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0,
data: CoreSetupBackendConfigElement? = null
) : super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.widget_quassel_setup_entry, this, true)
ButterKnife.bind(this)
if (data != null) {
this.data = data
wrapper.hint = data.displayName
when {
data.defaultValue.type == Type.QString &&
data.key.contains("password", ignoreCase = true) -> {
wrapper.isPasswordVisibilityToggleEnabled = true
field.inputType =
InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_PASSWORD
field.setText(data.defaultValue.value(""))
}
data.defaultValue.type == Type.QString &&
data.key.contains("hostname", ignoreCase = true) -> {
field.inputType =
InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_URI
field.setText(data.defaultValue.value(""))
}
data.defaultValue.type == Type.QString -> {
field.inputType =
InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
field.setText(data.defaultValue.value(""))
}
data.defaultValue.type == Type.Int -> {
field.inputType =
InputType.TYPE_CLASS_NUMBER
field.setText(data.defaultValue.value<Int>()?.toString())
}
}
}
}
fun key() = data?.key ?: ""
fun value(): QVariant_ {
val rawValue = field.text.toString()
val data = this.data
return when (data?.defaultValue?.type) {
Type.QString -> {
QVariant_.of(rawValue, data.defaultValue.type)
}
Type.Int -> {
QVariant_.of(rawValue.toInt(), data.defaultValue.type)
}
else -> {
QVariant_.of("", Type.QString)
}
}
}
}
<!--
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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp">
<TextView
android:id="@+id/no_options_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_core_backend_no_options" />
<LinearLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
<!--
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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:baselineAligned="false"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
android:minHeight="72dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<LinearLayout
android:layout_width="48dp"
android:layout_height="72dp">
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/backend_select"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:background="@null"
android:clickable="false" />
</LinearLayout>
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:minHeight="72dp"
android:orientation="vertical">
<TextView
android:id="@+id/backend_name"
style="@style/Widget.RtlConformTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textSize="14sp"
tools:text="Remote" />
<TextView
android:id="@+id/backend_description"
style="@style/Widget.RtlConformTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:gravity="center_vertical|start"
android:textSize="14sp"
tools:text="testUser on localhost" />
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?><!--
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/>.
-->
<com.google.android.material.textfield.TextInputLayout 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:id="@+id/wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="LabelFor">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true" />
</com.google.android.material.textfield.TextInputLayout>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment