From 712346c7ae69a9c3a2c5be819141fba9b7901a37 Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Fri, 28 Aug 2020 15:19:50 +0300 Subject: [PATCH] Import: implement import in etebase. --- .../ui/etebase/ImportCollectionFragment.kt | 79 +++++++++++++++++++ .../ui/etebase/ViewCollectionFragment.kt | 16 +++- .../ui/importlocal/ImportActivity.kt | 9 +-- .../ui/importlocal/ImportFragment.kt | 60 ++++++++------ .../LocalCalendarImportFragment.kt | 30 +++---- .../importlocal/LocalContactImportFragment.kt | 28 +++---- .../ui/importlocal/ResultFragment.kt | 4 - 7 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 app/src/main/java/com/etesync/syncadapter/ui/etebase/ImportCollectionFragment.kt diff --git a/app/src/main/java/com/etesync/syncadapter/ui/etebase/ImportCollectionFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/etebase/ImportCollectionFragment.kt new file mode 100644 index 00000000..14017fe9 --- /dev/null +++ b/app/src/main/java/com/etesync/syncadapter/ui/etebase/ImportCollectionFragment.kt @@ -0,0 +1,79 @@ +package com.etesync.syncadapter.ui.etebase + +import android.os.Bundle +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.activityViewModels +import androidx.fragment.app.commit +import com.etesync.syncadapter.CachedCollection +import com.etesync.syncadapter.Constants +import com.etesync.syncadapter.R +import com.etesync.syncadapter.ui.BaseActivity +import com.etesync.syncadapter.ui.importlocal.ImportFragment +import com.etesync.syncadapter.ui.importlocal.LocalCalendarImportFragment +import com.etesync.syncadapter.ui.importlocal.LocalContactImportFragment + +class ImportCollectionFragment : Fragment() { + private val model: AccountViewModel by activityViewModels() + private val collectionModel: CollectionViewModel by activityViewModels() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val ret = inflater.inflate(R.layout.import_actions_list, container, false) + setHasOptionsMenu(true) + + if (savedInstanceState == null) { + collectionModel.observe(this) { + (activity as? BaseActivity?)?.supportActionBar?.setTitle(R.string.import_dialog_title) + if (container != null) { + initUi(inflater, ret, it) + } + } + } + + return ret + } + + private fun initUi(inflater: LayoutInflater, v: View, cachedCollection: CachedCollection) { + val accountHolder = model.value!! + + var card = v.findViewById(R.id.import_file) + var img = card.findViewById(R.id.action_icon) as ImageView + var text = card.findViewById(R.id.action_text) as TextView + img.setImageResource(R.drawable.ic_file_white) + text.setText(R.string.import_button_file) + card.setOnClickListener { + parentFragmentManager.commit { + add(ImportFragment.newInstance(accountHolder.account, cachedCollection), null) + } + } + + card = v.findViewById(R.id.import_account) + img = card.findViewById(R.id.action_icon) as ImageView + text = card.findViewById(R.id.action_text) as TextView + img.setImageResource(R.drawable.ic_account_circle_white) + text.setText(R.string.import_button_local) + card.setOnClickListener { + if (cachedCollection.meta.collectionType == Constants.ETEBASE_TYPE_CALENDAR) { + parentFragmentManager.commit { + replace(R.id.fragment_container, LocalCalendarImportFragment(accountHolder.account, cachedCollection.col.uid)) + addToBackStack(null) + } + } else if (cachedCollection.meta.collectionType == Constants.ETEBASE_TYPE_ADDRESS_BOOK) { + parentFragmentManager.commit { + replace(R.id.fragment_container, LocalContactImportFragment(accountHolder.account, cachedCollection.col.uid)) + addToBackStack(null) + } + } + // FIXME: should be in the fragments once we kill legacy + (activity as? BaseActivity?)?.supportActionBar?.setTitle(R.string.import_select_account) + } + + if (collectionModel.value!!.meta.collectionType == Constants.ETEBASE_TYPE_TASKS) { + card.visibility = View.GONE + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/etesync/syncadapter/ui/etebase/ViewCollectionFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/etebase/ViewCollectionFragment.kt index 48ffcca6..0d188f04 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/etebase/ViewCollectionFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/etebase/ViewCollectionFragment.kt @@ -5,7 +5,6 @@ import android.graphics.Color.parseColor import android.os.Bundle import android.view.* import android.widget.TextView -import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -15,7 +14,6 @@ import com.etesync.syncadapter.Constants import com.etesync.syncadapter.R import com.etesync.syncadapter.resource.LocalCalendar import com.etesync.syncadapter.ui.BaseActivity -import com.etesync.syncadapter.ui.EditCollectionActivity import com.etesync.syncadapter.ui.WebViewActivity import com.etesync.syncadapter.utils.HintManager import com.etesync.syncadapter.utils.ShowcaseBuilder @@ -143,7 +141,19 @@ class ViewCollectionFragment : Fragment() { dialog.show() } } R.id.on_import -> { - Toast.makeText(context, "Import", Toast.LENGTH_LONG).show() + if (cachedCollection.col.accessLevel != "ro") { + parentFragmentManager.commit { + replace(R.id.fragment_container, ImportCollectionFragment()) + addToBackStack(null) + } + } else { + val dialog = AlertDialog.Builder(requireContext()) + .setIcon(R.drawable.ic_info_dark) + .setTitle(R.string.not_allowed_title) + .setMessage(R.string.edit_owner_only_anon) + .setPositiveButton(android.R.string.yes) { _, _ -> }.create() + dialog.show() + } } } return super.onOptionsItemSelected(item) diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt index f7b22533..60a1398f 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportActivity.kt @@ -14,7 +14,7 @@ import com.etesync.syncadapter.R import com.etesync.syncadapter.model.CollectionInfo import com.etesync.syncadapter.ui.BaseActivity -class ImportActivity : BaseActivity(), SelectImportMethod, ResultFragment.OnImportCallback, DialogInterface { +class ImportActivity : BaseActivity(), SelectImportMethod, DialogInterface { private lateinit var account: Account protected lateinit var info: CollectionInfo @@ -83,13 +83,6 @@ class ImportActivity : BaseActivity(), SelectImportMethod, ResultFragment.OnImpo return super.onKeyDown(keyCode, event) } - override fun onImportResult(importResult: ResultFragment.ImportResult) { - val fragment = ResultFragment.newInstance(importResult) - supportFragmentManager.beginTransaction() - .add(fragment, "importResult") - .commitAllowingStateLoss() - } - override fun cancel() { finish() } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportFragment.kt index 5decaa76..6a3b5da8 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ImportFragment.kt @@ -14,12 +14,13 @@ import android.os.Bundle import android.provider.CalendarContract import android.provider.ContactsContract import androidx.fragment.app.DialogFragment +import androidx.fragment.app.commit import at.bitfire.ical4android.* import at.bitfire.vcard4android.BatchOperation import at.bitfire.vcard4android.Contact import at.bitfire.vcard4android.ContactsStorageException -import com.etesync.syncadapter.Constants.KEY_ACCOUNT -import com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO +import com.etesync.syncadapter.CachedCollection +import com.etesync.syncadapter.Constants.* import com.etesync.syncadapter.R import com.etesync.syncadapter.log.Logger import com.etesync.syncadapter.model.CollectionInfo @@ -34,19 +35,14 @@ import java.io.InputStream import java.io.InputStreamReader -class ImportFragment : DialogFragment() { +class ImportFragment(private val account: Account, private val uid: String, private val enumType: CollectionInfo.Type) : DialogFragment() { - private lateinit var account: Account - private lateinit var info: CollectionInfo private var inputStream: InputStream? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) isCancelable = false retainInstance = true - - account = arguments!!.getParcelable(KEY_ACCOUNT)!! - info = arguments!!.getSerializable(KEY_COLLECTION_INFO) as CollectionInfo } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -56,7 +52,7 @@ class ImportFragment : DialogFragment() { } else { val data = ImportResult() data.e = Exception(getString(R.string.import_permission_required)) - (activity as ResultFragment.OnImportCallback).onImportResult(data) + onImportResult(data) dismissAllowingStateLoss() } @@ -117,7 +113,7 @@ class ImportFragment : DialogFragment() { intent.addCategory(Intent.CATEGORY_OPENABLE) intent.action = Intent.ACTION_GET_CONTENT - when (info.enumType) { + when (enumType) { CollectionInfo.Type.CALENDAR -> intent.type = "text/calendar" CollectionInfo.Type.TASKS -> intent.type = "text/calendar" CollectionInfo.Type.ADDRESS_BOOK -> intent.type = "text/x-vcard" @@ -131,7 +127,7 @@ class ImportFragment : DialogFragment() { val data = ImportResult() data.e = Exception("Failed to open file chooser.\nPlease install one.") - (activity as ResultFragment.OnImportCallback).onImportResult(data) + onImportResult(data) dismissAllowingStateLoss() } @@ -145,7 +141,7 @@ class ImportFragment : DialogFragment() { if (data != null) { // Get the URI of the selected file val uri = data.data!! - Logger.log.info("Starting import into ${info.uid} from file ${uri}") + Logger.log.info("Starting import into ${uid} from file ${uri}") try { inputStream = activity!!.contentResolver.openInputStream(uri) @@ -156,7 +152,7 @@ class ImportFragment : DialogFragment() { val importResult = ImportResult() importResult.e = e - (activity as ResultFragment.OnImportCallback).onImportResult(importResult) + onImportResult(importResult) dismissAllowingStateLoss() } @@ -171,7 +167,7 @@ class ImportFragment : DialogFragment() { } fun loadFinished(data: ImportResult) { - (activity as ResultFragment.OnImportCallback).onImportResult(data) + onImportResult(data) Logger.log.info("Finished import") @@ -216,7 +212,7 @@ class ImportFragment : DialogFragment() { val context = context!! val importReader = InputStreamReader(inputStream) - if (info.enumType == CollectionInfo.Type.CALENDAR) { + if (enumType == CollectionInfo.Type.CALENDAR) { val events = Event.eventsFromReader(importReader, null) importReader.close() @@ -238,7 +234,7 @@ class ImportFragment : DialogFragment() { val localCalendar: LocalCalendar? try { - localCalendar = LocalCalendar.findByName(account, provider, LocalCalendar.Factory, info.uid!!) + localCalendar = LocalCalendar.findByName(account, provider, LocalCalendar.Factory, uid!!) if (localCalendar == null) { throw FileNotFoundException("Failed to load local resource.") } @@ -269,7 +265,7 @@ class ImportFragment : DialogFragment() { entryProcessed() } - } else if (info.enumType == CollectionInfo.Type.TASKS) { + } else if (enumType == CollectionInfo.Type.TASKS) { val tasks = Task.tasksFromReader(importReader) importReader.close() @@ -296,7 +292,7 @@ class ImportFragment : DialogFragment() { provider?.let { val localTaskList: LocalTaskList? try { - localTaskList = LocalTaskList.findByName(account, it, LocalTaskList.Factory, info.uid!!) + localTaskList = LocalTaskList.findByName(account, it, LocalTaskList.Factory, uid!!) if (localTaskList == null) { throw FileNotFoundException("Failed to load local resource.") } @@ -324,7 +320,7 @@ class ImportFragment : DialogFragment() { entryProcessed() } } - } else if (info.enumType == CollectionInfo.Type.ADDRESS_BOOK) { + } else if (enumType == CollectionInfo.Type.ADDRESS_BOOK) { val uidToLocalId = HashMap() val downloader = ContactsSyncManager.ResourceDownloader(context) val contacts = Contact.fromReader(importReader, downloader) @@ -345,7 +341,7 @@ class ImportFragment : DialogFragment() { return result } - val localAddressBook = LocalAddressBook.findByUid(context, provider, account, info.uid!!) + val localAddressBook = LocalAddressBook.findByUid(context, provider, account, uid!!) if (localAddressBook == null) { throw FileNotFoundException("Failed to load local address book.") } @@ -423,18 +419,30 @@ class ImportFragment : DialogFragment() { } } + fun onImportResult(importResult: ImportResult) { + val fragment = ResultFragment.newInstance(importResult) + parentFragmentManager.commit(true) { + add(fragment, "importResult") + } + } + companion object { private val REQUEST_CODE = 6384 // onActivityResult request private val TAG_PROGRESS_MAX = "progressMax" fun newInstance(account: Account, info: CollectionInfo): ImportFragment { - val frag = ImportFragment() - val args = Bundle(1) - args.putParcelable(KEY_ACCOUNT, account) - args.putSerializable(KEY_COLLECTION_INFO, info) - frag.arguments = args - return frag + return ImportFragment(account, info.uid!!, info.enumType!!) + } + + fun newInstance(account: Account, cachedCollection: CachedCollection): ImportFragment { + val enumType = when (cachedCollection.meta.collectionType) { + ETEBASE_TYPE_CALENDAR -> CollectionInfo.Type.CALENDAR + ETEBASE_TYPE_TASKS -> CollectionInfo.Type.TASKS + ETEBASE_TYPE_ADDRESS_BOOK -> CollectionInfo.Type.ADDRESS_BOOK + else -> throw Exception("Got unsupported collection type") + } + return ImportFragment(account, cachedCollection.col.uid, enumType) } } } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalCalendarImportFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalCalendarImportFragment.kt index 6218dca8..92158852 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalCalendarImportFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalCalendarImportFragment.kt @@ -14,9 +14,8 @@ import android.widget.ExpandableListView import android.widget.ImageView import android.widget.TextView import androidx.fragment.app.ListFragment +import androidx.fragment.app.commit import at.bitfire.ical4android.CalendarStorageException -import com.etesync.syncadapter.Constants.KEY_ACCOUNT -import com.etesync.syncadapter.Constants.KEY_COLLECTION_INFO import com.etesync.syncadapter.R import com.etesync.syncadapter.log.Logger import com.etesync.syncadapter.model.CollectionInfo @@ -24,17 +23,10 @@ import com.etesync.syncadapter.resource.LocalCalendar import com.etesync.syncadapter.resource.LocalEvent -class LocalCalendarImportFragment : ListFragment() { - - private lateinit var account: Account - private lateinit var info: CollectionInfo - +class LocalCalendarImportFragment(private val account: Account, private val uid: String) : ListFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true - - account = arguments!!.getParcelable(KEY_ACCOUNT)!! - info = arguments!!.getSerializable(KEY_COLLECTION_INFO) as CollectionInfo } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -200,7 +192,7 @@ class LocalCalendarImportFragment : ListFragment() { if (progressDialog.isShowing && !activity.isDestroyed) { progressDialog.dismiss() } - (activity as ResultFragment.OnImportCallback).onImportResult(result) + onImportResult(result) } private fun importEvents(fromCalendar: LocalCalendar): ResultFragment.ImportResult { @@ -208,7 +200,7 @@ class LocalCalendarImportFragment : ListFragment() { try { val localCalendar = LocalCalendar.findByName(account, context!!.contentResolver.acquireContentProviderClient(CalendarContract.CONTENT_URI)!!, - LocalCalendar.Factory, info!!.uid!!) + LocalCalendar.Factory, uid) val localEvents = fromCalendar.findAll() val total = localEvents.size progressDialog.max = total @@ -248,15 +240,17 @@ class LocalCalendarImportFragment : ListFragment() { } } + fun onImportResult(importResult: ResultFragment.ImportResult) { + val fragment = ResultFragment.newInstance(importResult) + parentFragmentManager.commit(true) { + add(fragment, "importResult") + } + } + companion object { fun newInstance(account: Account, info: CollectionInfo): LocalCalendarImportFragment { - val frag = LocalCalendarImportFragment() - val args = Bundle(1) - args.putParcelable(KEY_ACCOUNT, account) - args.putSerializable(KEY_COLLECTION_INFO, info) - frag.arguments = args - return frag + return LocalCalendarImportFragment(account, info.uid!!) } } } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt index 079e0318..63916afb 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/LocalContactImportFragment.kt @@ -18,6 +18,7 @@ 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 @@ -32,18 +33,12 @@ import com.etesync.syncadapter.resource.LocalGroup import java.util.* -class LocalContactImportFragment : Fragment() { - - private lateinit var account: Account - private lateinit var info: CollectionInfo +class LocalContactImportFragment(private val account: Account, private val uid: String) : Fragment() { private var recyclerView: RecyclerView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true - - account = arguments!!.getParcelable(KEY_ACCOUNT) - info = arguments!!.getSerializable(KEY_COLLECTION_INFO) as CollectionInfo } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -134,7 +129,7 @@ class LocalContactImportFragment : Fragment() { if (progressDialog.isShowing && !activity.isDestroyed) { progressDialog.dismiss() } - (activity as ResultFragment.OnImportCallback).onImportResult(result) + onImportResult(result) } private fun importContacts(localAddressBook: LocalAddressBook): ResultFragment.ImportResult { @@ -142,7 +137,7 @@ class LocalContactImportFragment : Fragment() { try { val addressBook = LocalAddressBook.findByUid(context!!, context!!.contentResolver.acquireContentProviderClient(ContactsContract.RawContacts.CONTENT_URI)!!, - account, info.uid!!)!! + account, uid)!! val localContacts = localAddressBook.findAllContacts() val localGroups = localAddressBook.findAllGroups() val oldIdToNewId = HashMap() @@ -214,6 +209,13 @@ class LocalContactImportFragment : Fragment() { } } + fun onImportResult(importResult: ResultFragment.ImportResult) { + val fragment = ResultFragment.newInstance(importResult) + parentFragmentManager.commit(true) { + add(fragment, "importResult") + } + } + class ImportContactAdapter /** * Initialize the dataset of the Adapter. @@ -316,13 +318,7 @@ class LocalContactImportFragment : Fragment() { companion object { fun newInstance(account: Account, info: CollectionInfo): LocalContactImportFragment { - val frag = LocalContactImportFragment() - val args = Bundle(1) - args.putParcelable(KEY_ACCOUNT, account) - args.putSerializable(KEY_COLLECTION_INFO, info) - frag.arguments = args - - return frag + return LocalContactImportFragment(account, info.uid!!) } } } diff --git a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ResultFragment.kt b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ResultFragment.kt index 1971b018..3f8b3694 100644 --- a/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ResultFragment.kt +++ b/app/src/main/java/com/etesync/syncadapter/ui/importlocal/ResultFragment.kt @@ -72,10 +72,6 @@ class ResultFragment : DialogFragment() { } } - interface OnImportCallback { - fun onImportResult(importResult: ImportResult) - } - companion object { private val KEY_RESULT = "result"