mirror of https://github.com/etesync/android
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
330 lines
13 KiB
330 lines
13 KiB
package com.etesync.syncadapter.ui.importlocal
|
|
|
|
import android.accounts.Account
|
|
import android.app.ProgressDialog
|
|
import android.content.ContentValues.TAG
|
|
import android.content.Context
|
|
import android.database.Cursor
|
|
import android.graphics.Canvas
|
|
import android.graphics.Rect
|
|
import android.graphics.drawable.Drawable
|
|
import android.os.AsyncTask
|
|
import android.os.Bundle
|
|
import android.provider.ContactsContract
|
|
import android.util.Log
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.widget.ImageView
|
|
import android.widget.TextView
|
|
import androidx.fragment.app.Fragment
|
|
import androidx.fragment.app.commit
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import at.bitfire.vcard4android.ContactsStorageException
|
|
import com.etesync.syncadapter.R
|
|
import com.etesync.syncadapter.log.Logger
|
|
import com.etesync.syncadapter.model.CollectionInfo
|
|
import com.etesync.syncadapter.resource.LocalAddressBook
|
|
import com.etesync.syncadapter.resource.LocalContact
|
|
import com.etesync.syncadapter.resource.LocalGroup
|
|
import java.util.*
|
|
|
|
|
|
class LocalContactImportFragment : Fragment() {
|
|
private lateinit var account: Account
|
|
private lateinit var uid: String
|
|
|
|
private var recyclerView: RecyclerView? = null
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
retainInstance = true
|
|
}
|
|
|
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
val view = inflater.inflate(R.layout.fragment_local_contact_import, container, false)
|
|
|
|
recyclerView = view.findViewById<View>(R.id.recyclerView) as RecyclerView
|
|
|
|
recyclerView!!.layoutManager = LinearLayoutManager(activity)
|
|
recyclerView!!.addItemDecoration(DividerItemDecoration(activity!!))
|
|
return view
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
importAccount()
|
|
}
|
|
|
|
protected fun importAccount() {
|
|
val provider = context!!.contentResolver.acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI)
|
|
val cursor: Cursor?
|
|
try {
|
|
cursor = provider!!.query(ContactsContract.RawContacts.CONTENT_URI,
|
|
arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE), null, null,
|
|
ContactsContract.RawContacts.ACCOUNT_NAME + " ASC, " + ContactsContract.RawContacts.ACCOUNT_TYPE)
|
|
} catch (except: Exception) {
|
|
Log.w(TAG, "Addressbook provider is missing columns, continuing anyway")
|
|
|
|
except.printStackTrace()
|
|
return
|
|
}
|
|
|
|
val localAddressBooks = ArrayList<LocalAddressBook>()
|
|
var account: Account? = null
|
|
val accountNameIndex = cursor!!.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME)
|
|
val accountTypeIndex = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE)
|
|
while (cursor.moveToNext()) {
|
|
val accountName = cursor.getString(accountNameIndex)
|
|
val accountType = cursor.getString(accountTypeIndex)
|
|
if (account == null || !(account.name == accountName && account.type == accountType)) {
|
|
if (accountName != null && accountType != null) {
|
|
account = Account(accountName, accountType)
|
|
localAddressBooks.add(LocalAddressBook(context!!, account, provider))
|
|
}
|
|
}
|
|
}
|
|
|
|
cursor.close()
|
|
provider.release()
|
|
|
|
recyclerView!!.adapter = ImportContactAdapter(context!!, localAddressBooks, object : OnAccountSelected {
|
|
override fun accountSelected(index: Int) {
|
|
ImportContacts().execute(localAddressBooks[index])
|
|
}
|
|
})
|
|
}
|
|
|
|
protected inner class ImportContacts : AsyncTask<LocalAddressBook, Int, ResultFragment.ImportResult>() {
|
|
private lateinit var progressDialog: ProgressDialog
|
|
|
|
override fun onPreExecute() {
|
|
progressDialog = ProgressDialog(activity)
|
|
progressDialog.setTitle(R.string.import_dialog_title)
|
|
progressDialog.setMessage(getString(R.string.import_dialog_adding_entries))
|
|
progressDialog.setCanceledOnTouchOutside(false)
|
|
progressDialog.setCancelable(false)
|
|
progressDialog.isIndeterminate = false
|
|
progressDialog.setIcon(R.drawable.ic_import_export_black)
|
|
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
|
|
progressDialog.show()
|
|
}
|
|
|
|
override fun doInBackground(vararg addressBooks: LocalAddressBook): ResultFragment.ImportResult {
|
|
return importContacts(addressBooks[0])
|
|
}
|
|
|
|
override fun onProgressUpdate(vararg values: Int?) {
|
|
progressDialog.progress = values[0]!!
|
|
}
|
|
|
|
override fun onPostExecute(result: ResultFragment.ImportResult) {
|
|
val activity = activity
|
|
if (activity == null) {
|
|
Logger.log.warning("Activity is null on import.")
|
|
return
|
|
}
|
|
|
|
if (progressDialog.isShowing && !activity.isDestroyed && !activity.isFinishing) {
|
|
progressDialog.dismiss()
|
|
}
|
|
onImportResult(result)
|
|
}
|
|
|
|
private fun importContacts(localAddressBook: LocalAddressBook): ResultFragment.ImportResult {
|
|
val result = ResultFragment.ImportResult()
|
|
try {
|
|
val addressBook = LocalAddressBook.findByUid(requireContext(),
|
|
requireContext().contentResolver.acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI)!!,
|
|
account, uid)
|
|
?: throw Exception("Could not find address book")
|
|
val localContacts = localAddressBook.findAllContacts()
|
|
val localGroups = localAddressBook.findAllGroups()
|
|
val oldIdToNewId = HashMap<Long, Long>()
|
|
val total = localContacts.size + localGroups.size
|
|
progressDialog.max = total
|
|
result.total = total.toLong()
|
|
var progress = 0
|
|
for (currentLocalContact in localContacts) {
|
|
val contact = currentLocalContact.contact
|
|
|
|
try {
|
|
contact!!
|
|
var localContact: LocalContact? = if (contact.uid == null)
|
|
null
|
|
else
|
|
addressBook.findByUid(contact.uid!!) as LocalContact?
|
|
|
|
if (localContact != null) {
|
|
localContact.updateAsDirty(contact)
|
|
result.updated++
|
|
} else {
|
|
localContact = LocalContact(addressBook, contact, contact.uid, null)
|
|
localContact.createAsDirty()
|
|
result.added++
|
|
}
|
|
|
|
oldIdToNewId[currentLocalContact.id!!] = localContact.id!!
|
|
} catch (e: ContactsStorageException) {
|
|
e.printStackTrace()
|
|
result.e = e
|
|
}
|
|
|
|
publishProgress(++progress)
|
|
}
|
|
for (currentLocalGroup in localGroups) {
|
|
val group = currentLocalGroup.contact
|
|
|
|
try {
|
|
val members = currentLocalGroup.getMembers().map {
|
|
oldIdToNewId[it]!!
|
|
}
|
|
group!!
|
|
|
|
var localGroup: LocalGroup? = if (group.uid == null)
|
|
null
|
|
else
|
|
addressBook.findByUid(group.uid!!) as LocalGroup?
|
|
|
|
if (localGroup != null) {
|
|
localGroup.updateAsDirty(group, members)
|
|
result.updated++
|
|
} else {
|
|
localGroup = LocalGroup(addressBook, group, group.uid, null)
|
|
localGroup.createAsDirty(members)
|
|
result.added++
|
|
}
|
|
} catch (e: ContactsStorageException) {
|
|
e.printStackTrace()
|
|
result.e = e
|
|
}
|
|
|
|
publishProgress(++progress)
|
|
}
|
|
} catch (e: Exception) {
|
|
result.e = e
|
|
}
|
|
|
|
return result
|
|
}
|
|
}
|
|
|
|
fun onImportResult(importResult: ResultFragment.ImportResult) {
|
|
val fragment = ResultFragment.newInstance(importResult)
|
|
parentFragmentManager.commit(true) {
|
|
add(fragment, "importResult")
|
|
}
|
|
}
|
|
|
|
class ImportContactAdapter
|
|
/**
|
|
* Initialize the dataset of the Adapter.
|
|
*
|
|
* @param addressBooks containing the data to populate views to be used by RecyclerView.
|
|
*/
|
|
(context: Context, private val mAddressBooks: List<LocalAddressBook>, private val mOnAccountSelected: OnAccountSelected) : RecyclerView.Adapter<ImportContactAdapter.ViewHolder>() {
|
|
private val accountResolver: AccountResolver
|
|
|
|
/**
|
|
* Provide a reference to the enumType of views that you are using (custom ViewHolder)
|
|
*/
|
|
class ViewHolder(v: View, onAccountSelected: OnAccountSelected) : RecyclerView.ViewHolder(v) {
|
|
internal val titleTextView: TextView
|
|
internal val descTextView: TextView
|
|
internal val iconImageView: ImageView
|
|
|
|
init {
|
|
// Define click listener for the ViewHolder's View.
|
|
v.setOnClickListener { onAccountSelected.accountSelected(adapterPosition) }
|
|
titleTextView = v.findViewById<View>(R.id.title) as TextView
|
|
descTextView = v.findViewById<View>(R.id.description) as TextView
|
|
iconImageView = v.findViewById<View>(R.id.icon) as ImageView
|
|
}
|
|
}
|
|
|
|
init {
|
|
accountResolver = AccountResolver(context)
|
|
}
|
|
|
|
// Create new views (invoked by the layout manager)
|
|
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
|
|
// Create a new view.
|
|
val v = LayoutInflater.from(viewGroup.context)
|
|
.inflate(R.layout.import_content_list_account, viewGroup, false)
|
|
|
|
return ViewHolder(v, mOnAccountSelected)
|
|
}
|
|
|
|
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
|
viewHolder.titleTextView.text = mAddressBooks[position].account.name
|
|
val accountInfo = accountResolver.resolve(mAddressBooks[position].account.type)
|
|
viewHolder.descTextView.text = accountInfo.name
|
|
viewHolder.iconImageView.setImageDrawable(accountInfo.icon)
|
|
}
|
|
|
|
override fun getItemCount(): Int {
|
|
return mAddressBooks.size
|
|
}
|
|
|
|
companion object {
|
|
private val TAG = "ImportContactAdapter"
|
|
}
|
|
}
|
|
|
|
interface OnAccountSelected {
|
|
fun accountSelected(index: Int)
|
|
}
|
|
|
|
class DividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
|
|
|
|
private val mDivider: Drawable?
|
|
|
|
init {
|
|
val a = context.obtainStyledAttributes(ATTRS)
|
|
mDivider = a.getDrawable(0)
|
|
a.recycle()
|
|
}
|
|
|
|
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
|
drawVertical(c, parent)
|
|
}
|
|
|
|
fun drawVertical(c: Canvas, parent: RecyclerView) {
|
|
val left = parent.paddingLeft
|
|
val right = parent.width - parent.paddingRight
|
|
|
|
val childCount = parent.childCount
|
|
for (i in 0 until childCount) {
|
|
val child = parent.getChildAt(i)
|
|
val params = child
|
|
.layoutParams as RecyclerView.LayoutParams
|
|
val top = child.bottom + params.bottomMargin
|
|
val bottom = top + mDivider!!.intrinsicHeight
|
|
mDivider.setBounds(left, top, right, bottom)
|
|
mDivider.draw(c)
|
|
}
|
|
}
|
|
|
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
|
outRect.set(0, 0, 0, mDivider!!.intrinsicHeight)
|
|
}
|
|
|
|
companion object {
|
|
|
|
private val ATTRS = intArrayOf(android.R.attr.listDivider)
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
|
|
fun newInstance(account: Account, uid: String): LocalContactImportFragment {
|
|
val ret = LocalContactImportFragment()
|
|
ret.account = account
|
|
ret.uid = uid
|
|
return ret
|
|
}
|
|
}
|
|
}
|