AccountDetailsFragment.kt 10.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Copyright © 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
 */

package at.bitfire.davdroid.ui.setup

import android.accounts.Account
import android.accounts.AccountManager
13
import android.annotation.SuppressLint
14
import android.app.Activity
15 16 17
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent
18
import android.database.sqlite.SQLiteDatabase
19
import android.os.AsyncTask
20 21 22 23 24
import android.os.Bundle
import android.provider.CalendarContract
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Ricki Hirner's avatar
Ricki Hirner committed
25
import androidx.fragment.app.Fragment
26 27 28 29
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.DavService
import at.bitfire.davdroid.InvalidAccountException
import at.bitfire.davdroid.R
30 31 32
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.model.ServiceDB.*
import at.bitfire.davdroid.resource.LocalTaskList
33 34
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.Settings
35 36
import at.bitfire.ical4android.TaskProvider
import at.bitfire.vcard4android.GroupMethod
Ricki Hirner's avatar
Ricki Hirner committed
37
import com.google.android.material.snackbar.Snackbar
38
import kotlinx.android.synthetic.main.login_account_details.*
39
import kotlinx.android.synthetic.main.login_account_details.view.*
40
import java.lang.ref.WeakReference
41 42
import java.util.logging.Level

43
class AccountDetailsFragment: Fragment() {
44 45

    companion object {
46
        const val KEY_CONFIG = "config"
47 48 49 50

        fun newInstance(config: DavResourceFinder.Configuration): AccountDetailsFragment {
            val frag = AccountDetailsFragment()
            val args = Bundle(1)
51
            args.putParcelable(KEY_CONFIG, config)
52 53 54 55 56 57 58 59 60
            frag.arguments = args
            return frag
        }
    }


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        val v = inflater.inflate(R.layout.login_account_details, container, false)

61
        v.back.setOnClickListener {
62
            requireFragmentManager().popBackStack()
63
        }
64

65
        val args = requireNotNull(arguments)
66
        val config = args.getParcelable(KEY_CONFIG) as DavResourceFinder.Configuration
67

68 69 70
        v.account_name.setText(config.calDAV?.email ?:
                config.credentials.userName ?:
                config.credentials.certificateAlias)
71

72 73
        val settings = Settings.getInstance(requireActivity())

74 75
        // CardDAV-specific
        v.carddav.visibility = if (config.cardDAV != null) View.VISIBLE else View.GONE
76 77
        if (settings.has(AccountSettings.KEY_CONTACT_GROUP_METHOD))
            v.contact_group_method.isEnabled = false
78

79
        v.create_account.setOnClickListener {
80 81 82
            val name = v.account_name.text.toString()
            if (name.isEmpty())
                v.account_name.error = getString(R.string.login_account_name_required)
83 84 85
            else {
                val idx = view!!.contact_group_method.selectedItemPosition
                val groupMethodName = resources.getStringArray(R.array.settings_contact_group_method_values)[idx]
86

87 88
                v.create_account.visibility = View.GONE
                v.create_account_progress.visibility = View.VISIBLE
89

90
                CreateAccountTask(requireActivity(),
91 92 93 94 95
                        name,
                        args.getParcelable(KEY_CONFIG) as DavResourceFinder.Configuration,
                        GroupMethod.valueOf(groupMethodName)).execute()
            }
        }
96

97 98 99 100 101 102 103
        val forcedGroupMethod = settings.getString(AccountSettings.KEY_CONTACT_GROUP_METHOD)?.let { GroupMethod.valueOf(it) }
        if (forcedGroupMethod != null) {
            v.contact_group_method.isEnabled = false
            for ((i, method) in resources.getStringArray(R.array.settings_contact_group_method_values).withIndex()) {
                if (method == forcedGroupMethod.name) {
                    v.contact_group_method.setSelection(i)
                    break
104
                }
105 106 107
            }
        } else
            v.contact_group_method.isEnabled = true
108

109
        return v
110 111 112
    }


113
    class CreateAccountTask(
114
            activity: Activity,
115

116 117 118
            private val accountName: String,
            private val config: DavResourceFinder.Configuration,
            private val groupMethod: GroupMethod
119 120
    ): AsyncTask<Void, Void, Boolean>() {

121 122 123 124
        @SuppressLint("StaticFieldLeak")    // we'll only keep the application Context
        private val appContext = activity.applicationContext
        private val activityRef = WeakReference(activity)

125
        override fun doInBackground(vararg params: Void?): Boolean {
126
            val account = Account(accountName, appContext.getString(R.string.account_type))
127 128 129 130 131

            // create Android account
            val userData = AccountSettings.initialUserData(config.credentials)
            Logger.log.log(Level.INFO, "Creating Android account with initial config", arrayOf(account, userData))

132
            val accountManager = AccountManager.get(appContext)
133 134 135 136 137
            if (!accountManager.addAccountExplicitly(account, config.credentials.password, userData))
                return false

            // add entries for account to service DB
            Logger.log.log(Level.INFO, "Writing account configuration to database", config)
138
            OpenHelper(appContext).use { dbHelper ->
139 140
                val db = dbHelper.writableDatabase
                try {
141
                    val accountSettings = AccountSettings(appContext, account)
142

143
                    val refreshIntent = Intent(appContext, DavService::class.java)
144 145 146 147 148 149
                    refreshIntent.action = DavService.ACTION_REFRESH_COLLECTIONS

                    if (config.cardDAV != null) {
                        // insert CardDAV service
                        val id = insertService(db, accountName, Services.SERVICE_CARDDAV, config.cardDAV)

150 151 152
                        // initial CardDAV account settings
                        accountSettings.setGroupMethod(groupMethod)

153 154
                        // start CardDAV service detection (refresh collections)
                        refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id)
155
                        appContext.startService(refreshIntent)
156 157

                        // contact sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_address_books.xml
158
                        accountSettings.setSyncInterval(appContext.getString(R.string.address_books_authority), Constants.DEFAULT_SYNC_INTERVAL)
159
                    } else
160
                        ContentResolver.setIsSyncable(account, appContext.getString(R.string.address_books_authority), 0)
161 162 163 164 165 166 167

                    if (config.calDAV != null) {
                        // insert CalDAV service
                        val id = insertService(db, accountName, Services.SERVICE_CALDAV, config.calDAV)

                        // start CalDAV service detection (refresh collections)
                        refreshIntent.putExtra(DavService.EXTRA_DAV_SERVICE_ID, id)
168
                        appContext.startService(refreshIntent)
169 170 171 172 173 174

                        // calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_calendars.xml
                        accountSettings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL)

                        // enable task sync if OpenTasks is installed
                        // further changes will be handled by PackageChangedReceiver
175
                        if (LocalTaskList.tasksProviderAvailable(appContext)) {
176 177 178 179 180 181 182 183 184 185 186 187 188
                            ContentResolver.setIsSyncable(account, TaskProvider.ProviderName.OpenTasks.authority, 1)
                            accountSettings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Constants.DEFAULT_SYNC_INTERVAL)
                        }
                    } else
                        ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 0)

                } catch(e: InvalidAccountException) {
                    Logger.log.log(Level.SEVERE, "Couldn't access account settings", e)
                    return false
                }
            }
            return true
        }
189

190
        override fun onPostExecute(result: Boolean) {
191 192 193 194 195 196 197 198 199 200
            activityRef.get()?.let { activity ->
                if (result) {
                    activity.setResult(Activity.RESULT_OK)
                    activity.finish()
                } else {
                    Snackbar.make(activity.findViewById(android.R.id.content), R.string.login_account_not_created, Snackbar.LENGTH_LONG).show()

                    activity.create_account.visibility = View.VISIBLE
                    activity.create_account_progress.visibility = View.GONE
                }
201 202 203
            }
        }

204 205
        private fun insertService(db: SQLiteDatabase, accountName: String, service: String, info: DavResourceFinder.Configuration.ServiceInfo): Long {
            // insert service
206 207 208 209 210 211 212
            val serviceValues = ContentValues(3)
            serviceValues.put(Services.ACCOUNT_NAME, accountName)
            serviceValues.put(Services.SERVICE, service)
            info.principal?.let {
                serviceValues.put(Services.PRINCIPAL, it.toString())
            }
            val serviceID = db.insertWithOnConflict(Services._TABLE, null, serviceValues, SQLiteDatabase.CONFLICT_REPLACE)
213 214 215

            // insert home sets
            for (homeSet in info.homeSets) {
216 217 218 219
                val homeSetValues = ContentValues(2)
                homeSetValues.put(HomeSets.SERVICE_ID, serviceID)
                homeSetValues.put(HomeSets.URL, homeSet.toString())
                db.insertWithOnConflict(HomeSets._TABLE, null, homeSetValues, SQLiteDatabase.CONFLICT_REPLACE)
220
            }
221

222 223
            // insert collections
            for (collection in info.collections.values) {
224 225 226
                val collectionValues = collection.toDB()
                collectionValues.put(Collections.SERVICE_ID, serviceID)
                db.insertWithOnConflict(Collections._TABLE, null, collectionValues, SQLiteDatabase.CONFLICT_REPLACE)
227
            }
228

229
            return serviceID
230 231 232 233
        }

    }

234
}