NewAccountWizard: add a new account setup wizard.

pull/134/head
Tom Hacohen 4 years ago
parent 82ce1783bc
commit db843d8798

@ -237,6 +237,10 @@
android:name=".ui.etebase.CollectionActivity"
android:exported="false"
/>
<activity
android:name=".ui.etebase.NewAccountWizardActivity"
android:exported="false"
/>
<activity
android:name=".ui.etebase.InvitationsActivity"
android:exported="false"

@ -0,0 +1,212 @@
package com.etesync.syncadapter.ui.etebase
import android.accounts.Account
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ProgressBar
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import com.etebase.client.Collection
import com.etebase.client.CollectionMetadata
import com.etebase.client.FetchOptions
import com.etebase.client.exceptions.EtebaseException
import com.etesync.syncadapter.R
import com.etesync.syncadapter.syncadapter.requestSync
import com.etesync.syncadapter.ui.BaseActivity
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
class NewAccountWizardActivity : BaseActivity() {
private lateinit var account: Account
private val model: AccountViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
account = intent.extras!!.getParcelable(EXTRA_ACCOUNT)!!
setContentView(R.layout.etebase_collection_activity)
if (savedInstanceState == null) {
setTitle(R.string.account_wizard_collections_title)
model.loadAccount(this, account)
supportFragmentManager.commit {
replace(R.id.fragment_container, WizardCheckFragment())
}
}
}
companion object {
private val EXTRA_ACCOUNT = "account"
fun newIntent(context: Context, account: Account): Intent {
val intent = Intent(context, NewAccountWizardActivity::class.java)
intent.putExtra(EXTRA_ACCOUNT, account)
return intent
}
}
}
fun reportErrorHelper(context: Context, e: Throwable) {
AlertDialog.Builder(context)
.setIcon(R.drawable.ic_info_dark)
.setTitle(R.string.exception)
.setMessage(e.localizedMessage)
.setPositiveButton(android.R.string.yes) { _, _ -> }.show()
}
class WizardCheckFragment : Fragment() {
private val model: AccountViewModel by activityViewModels()
private val loadingModel: LoadingViewModel by viewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val ret = inflater.inflate(R.layout.account_wizard_check, container, false)
if (savedInstanceState == null) {
if (container != null) {
initUi(inflater, ret)
model.observe(this, {
checkAccountInit()
})
}
}
return ret
}
private fun initUi(inflater: LayoutInflater, v: View) {
val button = v.findViewById<Button>(R.id.button_retry)
val progress = v.findViewById<ProgressBar>(R.id.loading)
button.setOnClickListener {
checkAccountInit()
}
loadingModel.observe(this, {
if (it) {
progress.visibility = View.VISIBLE
button.visibility = View.GONE
} else {
progress.visibility = View.GONE
button.visibility = View.VISIBLE
}
})
}
private fun checkAccountInit() {
val colMgr = model.value?.colMgr ?: return
loadingModel.setLoading(true)
doAsync {
try {
val collections = colMgr.list(FetchOptions().limit(1))
uiThread {
if (collections.data.size > 0) {
activity?.finish()
} else {
parentFragmentManager.commit {
replace(R.id.fragment_container, WizardFragment())
}
}
}
} catch (e: Exception) {
uiThread {
reportErrorHelper(requireContext(), e)
}
} finally {
uiThread {
loadingModel.setLoading(false)
}
}
}
}
}
class WizardFragment : Fragment() {
private val model: AccountViewModel by activityViewModels()
private val loadingModel: LoadingViewModel by viewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val ret = inflater.inflate(R.layout.account_wizard_collections, container, false)
if (savedInstanceState == null) {
if (container != null) {
initUi(inflater, ret)
}
}
return ret
}
private fun initUi(inflater: LayoutInflater, v: View) {
v.findViewById<Button>(R.id.button_create).setOnClickListener {
createCollections()
}
v.findViewById<Button>(R.id.button_skip).setOnClickListener {
activity?.finish()
}
val buttons = v.findViewById<View>(R.id.buttons_holder)
val progress = v.findViewById<ProgressBar>(R.id.loading)
loadingModel.observe(this, {
if (it) {
progress.visibility = View.VISIBLE
buttons.visibility = View.GONE
} else {
progress.visibility = View.GONE
buttons.visibility = View.VISIBLE
}
})
}
private fun createCollections() {
val accountHolder = model.value ?: return
val colMgr = accountHolder.colMgr
loadingModel.setLoading(true)
doAsync {
try {
val baseMeta = listOf(
Pair("etebase.vcard", "My Contacts"),
Pair("etebase.vevent", "My Calendar"),
Pair("etebase.vtodo", "My Tasks"),
)
baseMeta.forEach {
val meta = CollectionMetadata(it.first, it.second)
meta.mtime = System.currentTimeMillis()
val col = colMgr.create(meta, "")
uploadCollection(accountHolder, col)
}
requestSync(requireContext(), accountHolder.account)
activity?.finish()
} catch (e: EtebaseException) {
uiThread {
reportErrorHelper(requireContext(), e)
}
} finally {
uiThread {
loadingModel.setLoading(false)
}
}
}
}
private fun uploadCollection(accountHolder: AccountHolder, col: Collection) {
val etebaseLocalCache = accountHolder.etebaseLocalCache
val colMgr = accountHolder.colMgr
colMgr.upload(col)
synchronized(etebaseLocalCache) {
etebaseLocalCache.collectionSet(colMgr, col)
}
}
}

@ -19,6 +19,7 @@ import androidx.fragment.app.DialogFragment
import at.bitfire.ical4android.TaskProvider.Companion.TASK_PROVIDERS
import com.etesync.syncadapter.*
import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.ui.etebase.NewAccountWizardActivity
import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration
import com.etesync.syncadapter.utils.AndroidCompat
import com.etesync.syncadapter.utils.TaskProviderHandling
@ -42,14 +43,16 @@ class CreateAccountFragment : DialogFragment() {
val config = requireArguments().getSerializable(KEY_CONFIG) as Configuration
val activity = requireActivity()
if (createAccount(config.userName, config)) {
val account = createAccount(config.userName, config)
if (account != null) {
activity.setResult(Activity.RESULT_OK)
startActivity(NewAccountWizardActivity.newIntent(requireContext(), account))
activity.finish()
}
}
@Throws(InvalidAccountException::class)
protected fun createAccount(accountName: String, config: Configuration): Boolean {
protected fun createAccount(accountName: String, config: Configuration): Account? {
val account = Account(accountName, App.accountType)
// create Android account
@ -57,7 +60,7 @@ class CreateAccountFragment : DialogFragment() {
val accountManager = AccountManager.get(context)
if (!accountManager.addAccountExplicitly(account, config.password, null))
return false
return null
AccountSettings.setUserData(accountManager, account, config.url, config.userName)
@ -86,7 +89,7 @@ class CreateAccountFragment : DialogFragment() {
throw e
}
return true
return account
}
companion object {

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/account_wizard_collections_title"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<ProgressBar
android:id="@+id/loading"
style="@android:style/Widget.Material.ProgressBar.Large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="true"
android:keepScreenOn="true" />
<Button
android:id="@+id/button_retry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retry"
android:visibility="gone" />
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2013 2016 Ricki Hirner (bitfire web engineering).
~ All rights reserved. This program and the accompanying materials
~ are made available under the terms of the GNU Public License v3.0
~ which accompanies this distribution, and is available at
~ http://www.gnu.org/licenses/gpl.html
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/account_wizard_collections_title"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center"
android:text="@string/account_wizard_collections_body" />
<ProgressBar
android:id="@+id/loading"
style="@android:style/Widget.Material.ProgressBar.Large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="true"
android:keepScreenOn="true"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:id="@+id/buttons_holder"
style="@style/stepper_nav_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_skip"
style="@style/stepper_nav_button"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/skip" />
<Space
style="@style/stepper_nav_button"
android:layout_width="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/button_create"
style="@style/stepper_nav_button"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/create" />
</LinearLayout>
</LinearLayout>

@ -18,8 +18,11 @@
<string name="address_books_authority_title">Address books</string>
<string name="help">Help</string>
<string name="manage_accounts">Manage accounts</string>
<string name="please_wait">Please wait </string>
<string name="please_wait">Please wait…</string>
<string name="send">Send</string>
<string name="create">Create</string>
<string name="skip">Skip</string>
<string name="retry">Retry</string>
<string name="notification_channel_crash_reports">Crash Reports</string>
<string name="notification_channel_debugging">Debugging</string>
@ -83,6 +86,10 @@
<string name="accounts_showcase_add">You need to add an account in order to use EteSync. Click here to add one...</string>
<string name="accounts_missing_permissions">Missing permissions: %s</string>
<!-- Account Wizard -->
<string name="account_wizard_collections_title">Welcome to EteSync!</string>
<string name="account_wizard_collections_body">In order to start using EteSync you need to create collections to store your data. Click "Create" to create a default calendar, address book and a task list for you.</string>
<!-- AccountUpdateService -->
<!-- AppSettingsActivity -->

Loading…
Cancel
Save