17
CHANGELOG.md
|
@ -1,6 +1,23 @@
|
|||
Changelog
|
||||
==========
|
||||
|
||||
Version 3.1.2 *(2018-01-23)*
|
||||
----------------------------
|
||||
|
||||
* Properly handle vcf files exported from Thunderbird
|
||||
* Misc smaller improvements
|
||||
|
||||
Version 3.1.1 *(2018-01-22)*
|
||||
----------------------------
|
||||
|
||||
* An f-droid build test version
|
||||
|
||||
Version 3.1.0 *(2018-01-16)*
|
||||
----------------------------
|
||||
|
||||
* Added contact import/export functions
|
||||
* Allow sharing contacts
|
||||
|
||||
Version 3.0.3 *(2018-01-03)*
|
||||
----------------------------
|
||||
|
||||
|
|
15
README.md
|
@ -8,16 +8,19 @@ You can use it for managing user emails and events too. It has the ability to so
|
|||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
|
||||
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.contacts'><img src='http://simplemobiletools.github.io/assets/public/google-play.png' alt='Get it on Google Play' height='45' /></a>
|
||||
<a href='https://f-droid.org/app/com.simplemobiletools.contacts'><img src='http://simplemobiletools.github.io/assets/public/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
|
||||
|
||||
<img alt="App image" src="screenshots/app.png" width="250" />
|
||||
<img alt="App image" src="screenshots/app_2.png" width="250" />
|
||||
<img alt="App image" src="screenshots/app_3.png" width="250" />
|
||||
<a href='https://play.google.com/store/apps/details?id=com.simplemobiletools.contacts'><img src='http://simplemobiletools.github.io/assets/public/google-play.png' alt='Get it on Google Play' height='45' /></a>
|
||||
<a href='https://f-droid.org/packages/com.simplemobiletools.contacts'><img src='http://simplemobiletools.github.io/assets/public/f-droid.png' alt='Get it on F-Droid' height='45' /></a>
|
||||
|
||||
<div style="display:flex;">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app.png" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.png" width="30%">
|
||||
<img alt="App image" src="fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.png" width="30%">
|
||||
</div>
|
||||
|
||||
License
|
||||
-------
|
||||
Copyright 2017 SimpleMobileTools
|
||||
Copyright 2017-present SimpleMobileTools
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -4,13 +4,14 @@ apply plugin: 'kotlin-android-extensions'
|
|||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.simplemobiletools.contacts"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionCode 4
|
||||
versionName "3.0.3"
|
||||
versionCode 7
|
||||
versionName "3.1.2"
|
||||
setProperty("archivesBaseName", "contacts")
|
||||
}
|
||||
|
||||
|
@ -41,11 +42,11 @@ ext {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.simplemobiletools:commons:3.5.7'
|
||||
implementation 'com.simplemobiletools:commons:3.8.2'
|
||||
implementation 'joda-time:joda-time:2.9.9'
|
||||
|
||||
//debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
|
||||
//releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
|
||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
|
||||
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
|
||||
}
|
||||
|
||||
Properties props = new Properties()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<uses-permission android:name="android.permission.CALL_PHONE"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.USE_FINGERPRINT"
|
||||
|
@ -40,6 +41,16 @@
|
|||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<data android:mimeType="text/directory"/>
|
||||
<data android:mimeType="text/vcard"/>
|
||||
<data android:mimeType="text/x-vcard"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
|
|
|
@ -2,16 +2,17 @@ package com.simplemobiletools.contacts
|
|||
|
||||
import android.app.Application
|
||||
import com.simplemobiletools.commons.extensions.checkUseEnglish
|
||||
import com.squareup.leakcanary.LeakCanary
|
||||
|
||||
class App : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/*if (BuildConfig.DEBUG) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
if (LeakCanary.isInAnalyzerProcess(this)) {
|
||||
return
|
||||
}
|
||||
LeakCanary.install(this)
|
||||
}*/
|
||||
}
|
||||
|
||||
checkUseEnglish()
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ import android.graphics.drawable.Drawable
|
|||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
import android.provider.MediaStore
|
||||
import android.support.v4.content.FileProvider
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
|
@ -31,7 +31,6 @@ import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
|
|||
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS
|
||||
import com.simplemobiletools.commons.helpers.getDateFormats
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.contacts.BuildConfig
|
||||
import com.simplemobiletools.contacts.R
|
||||
import com.simplemobiletools.contacts.extensions.*
|
||||
import com.simplemobiletools.contacts.helpers.*
|
||||
|
@ -45,16 +44,11 @@ import kotlinx.android.synthetic.main.item_event.view.*
|
|||
import kotlinx.android.synthetic.main.item_phone_number.view.*
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class ContactActivity : SimpleActivity() {
|
||||
private val DEFAULT_EMAIL_TYPE = ContactsContract.CommonDataKinds.Email.TYPE_HOME
|
||||
private val DEFAULT_PHONE_NUMBER_TYPE = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
|
||||
private val DEFAULT_EVENT_TYPE = ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY
|
||||
|
||||
private val INTENT_TAKE_PHOTO = 1
|
||||
private val INTENT_CHOOSE_PHOTO = 2
|
||||
private val INTENT_CROP_PHOTO = 3
|
||||
|
@ -93,6 +87,35 @@ class ContactActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_contact, menu)
|
||||
if (wasActivityInitialized) {
|
||||
menu.findItem(R.id.delete).isVisible = contact?.id != 0
|
||||
menu.findItem(R.id.share).isVisible = contact?.id != 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.save -> saveContact()
|
||||
R.id.delete -> deleteContact()
|
||||
R.id.share -> shareContact()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData)
|
||||
if (resultCode == RESULT_OK) {
|
||||
when (requestCode) {
|
||||
INTENT_TAKE_PHOTO, INTENT_CHOOSE_PHOTO -> startCropPhotoIntent(lastPhotoIntentUri!!)
|
||||
INTENT_CROP_PHOTO -> updateContactPhoto(lastPhotoIntentUri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initContact() {
|
||||
var contactId = intent.getIntExtra(CONTACT_ID, 0)
|
||||
val action = intent.action
|
||||
|
@ -179,33 +202,6 @@ class ContactActivity : SimpleActivity() {
|
|||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_contact, menu)
|
||||
if (wasActivityInitialized) {
|
||||
menu.findItem(R.id.delete).isVisible = contact?.id != 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.save -> saveContact()
|
||||
R.id.delete -> deleteContact()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData)
|
||||
if (resultCode == RESULT_OK) {
|
||||
when (requestCode) {
|
||||
INTENT_TAKE_PHOTO, INTENT_CHOOSE_PHOTO -> startCropPhotoIntent(lastPhotoIntentUri!!)
|
||||
INTENT_CROP_PHOTO -> updateContactPhoto(lastPhotoIntentUri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startCropPhotoIntent(uri: Uri) {
|
||||
lastPhotoIntentUri = getCachePhotoUri()
|
||||
Intent("com.android.camera.action.CROP").apply {
|
||||
|
@ -306,9 +302,17 @@ class ContactActivity : SimpleActivity() {
|
|||
private fun setupNewContact() {
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
|
||||
supportActionBar?.title = resources.getString(R.string.new_contact)
|
||||
contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), "", 0, 0)
|
||||
contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), "", 0, 0, "")
|
||||
contact_source.text = config.lastUsedContactSource
|
||||
contact_source.setOnClickListener { showAccountSourcePicker() }
|
||||
contact_source.setOnClickListener {
|
||||
showContactSourcePicker(contact_source.value) {
|
||||
contact_source.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareContact() {
|
||||
shareContacts(arrayListOf(contact!!))
|
||||
}
|
||||
|
||||
private fun showPhotoPlaceholder() {
|
||||
|
@ -453,14 +457,14 @@ class ContactActivity : SimpleActivity() {
|
|||
|
||||
private fun showNumberTypePicker(numberTypeField: TextView) {
|
||||
val items = arrayListOf(
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, getString(R.string.mobile)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_HOME, getString(R.string.home)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_WORK, getString(R.string.work)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_MAIN, getString(R.string.main_number)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK, getString(R.string.work_fax)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME, getString(R.string.home_fax)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_PAGER, getString(R.string.pager)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Phone.TYPE_OTHER, getString(R.string.other))
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_MOBILE, getString(R.string.mobile)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_HOME, getString(R.string.home)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_WORK, getString(R.string.work)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_MAIN, getString(R.string.main_number)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_FAX_WORK, getString(R.string.work_fax)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_FAX_HOME, getString(R.string.home_fax)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_PAGER, getString(R.string.pager)),
|
||||
RadioItem(CommonDataKinds.Phone.TYPE_OTHER, getString(R.string.other))
|
||||
)
|
||||
|
||||
val currentNumberTypeId = getPhoneNumberTypeId(numberTypeField.value)
|
||||
|
@ -471,10 +475,10 @@ class ContactActivity : SimpleActivity() {
|
|||
|
||||
private fun showEmailTypePicker(emailTypeField: TextView) {
|
||||
val items = arrayListOf(
|
||||
RadioItem(ContactsContract.CommonDataKinds.Email.TYPE_HOME, getString(R.string.home)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Email.TYPE_WORK, getString(R.string.work)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE, getString(R.string.mobile)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other))
|
||||
RadioItem(CommonDataKinds.Email.TYPE_HOME, getString(R.string.home)),
|
||||
RadioItem(CommonDataKinds.Email.TYPE_WORK, getString(R.string.work)),
|
||||
RadioItem(CommonDataKinds.Email.TYPE_MOBILE, getString(R.string.mobile)),
|
||||
RadioItem(CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other))
|
||||
)
|
||||
|
||||
val currentEmailTypeId = getEmailTypeId(emailTypeField.value)
|
||||
|
@ -485,9 +489,9 @@ class ContactActivity : SimpleActivity() {
|
|||
|
||||
private fun showEventTypePicker(eventTypeField: TextView) {
|
||||
val items = arrayListOf(
|
||||
RadioItem(ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, getString(R.string.birthday)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY, getString(R.string.anniversary)),
|
||||
RadioItem(ContactsContract.CommonDataKinds.Event.TYPE_OTHER, getString(R.string.other))
|
||||
RadioItem(CommonDataKinds.Event.TYPE_BIRTHDAY, getString(R.string.birthday)),
|
||||
RadioItem(CommonDataKinds.Event.TYPE_ANNIVERSARY, getString(R.string.anniversary)),
|
||||
RadioItem(CommonDataKinds.Event.TYPE_OTHER, getString(R.string.other))
|
||||
)
|
||||
|
||||
val currentEventTypeId = getEventTypeId(eventTypeField.value)
|
||||
|
@ -579,6 +583,7 @@ class ContactActivity : SimpleActivity() {
|
|||
|
||||
private fun insertNewContact() {
|
||||
isSaving = true
|
||||
toast(R.string.inserting)
|
||||
if (ContactsHelper(this@ContactActivity).insertContact(contact!!)) {
|
||||
finish()
|
||||
} else {
|
||||
|
@ -643,27 +648,6 @@ class ContactActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun showAccountSourcePicker() {
|
||||
ContactsHelper(this).getContactSources {
|
||||
val items = ArrayList<RadioItem>()
|
||||
val sources = it
|
||||
val currentSource = contact_source.value
|
||||
var currentSourceIndex = -1
|
||||
sources.forEachIndexed { index, account ->
|
||||
items.add(RadioItem(index, account))
|
||||
if (account == currentSource) {
|
||||
currentSourceIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
RadioGroupDialog(this, items, currentSourceIndex) {
|
||||
contact_source.text = sources[it as Int]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavorite() {
|
||||
val isStarred = isContactStarred()
|
||||
contact_toggle_favorite.apply {
|
||||
|
@ -757,62 +741,51 @@ class ContactActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getCachePhotoUri(): Uri {
|
||||
val imagesFolder = File(cacheDir, "my_cache")
|
||||
if (!imagesFolder.exists()) {
|
||||
imagesFolder.mkdirs()
|
||||
}
|
||||
|
||||
val file = File(imagesFolder, "Photo_${System.currentTimeMillis()}.jpg")
|
||||
file.createNewFile()
|
||||
return FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.provider", file)
|
||||
}
|
||||
|
||||
private fun getPhoneNumberTextId(type: Int) = when (type) {
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE -> R.string.mobile
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> R.string.home
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> R.string.work
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
|
||||
CommonDataKinds.Phone.TYPE_MOBILE -> R.string.mobile
|
||||
CommonDataKinds.Phone.TYPE_HOME -> R.string.home
|
||||
CommonDataKinds.Phone.TYPE_WORK -> R.string.work
|
||||
CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
|
||||
CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
|
||||
CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
|
||||
CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
|
||||
else -> R.string.other
|
||||
}
|
||||
|
||||
private fun getPhoneNumberTypeId(value: String) = when (value) {
|
||||
getString(R.string.mobile) -> ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
|
||||
getString(R.string.home) -> ContactsContract.CommonDataKinds.Phone.TYPE_HOME
|
||||
getString(R.string.work) -> ContactsContract.CommonDataKinds.Phone.TYPE_WORK
|
||||
getString(R.string.main_number) -> ContactsContract.CommonDataKinds.Phone.TYPE_MAIN
|
||||
getString(R.string.work_fax) -> ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK
|
||||
getString(R.string.home_fax) -> ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME
|
||||
getString(R.string.pager) -> ContactsContract.CommonDataKinds.Phone.TYPE_PAGER
|
||||
else -> ContactsContract.CommonDataKinds.Phone.TYPE_OTHER
|
||||
getString(R.string.mobile) -> CommonDataKinds.Phone.TYPE_MOBILE
|
||||
getString(R.string.home) -> CommonDataKinds.Phone.TYPE_HOME
|
||||
getString(R.string.work) -> CommonDataKinds.Phone.TYPE_WORK
|
||||
getString(R.string.main_number) -> CommonDataKinds.Phone.TYPE_MAIN
|
||||
getString(R.string.work_fax) -> CommonDataKinds.Phone.TYPE_FAX_WORK
|
||||
getString(R.string.home_fax) -> CommonDataKinds.Phone.TYPE_FAX_HOME
|
||||
getString(R.string.pager) -> CommonDataKinds.Phone.TYPE_PAGER
|
||||
else -> CommonDataKinds.Phone.TYPE_OTHER
|
||||
}
|
||||
|
||||
private fun getEmailTextId(type: Int) = when (type) {
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_HOME -> R.string.home
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_WORK -> R.string.work
|
||||
ContactsContract.CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
|
||||
CommonDataKinds.Email.TYPE_HOME -> R.string.home
|
||||
CommonDataKinds.Email.TYPE_WORK -> R.string.work
|
||||
CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
|
||||
else -> R.string.other
|
||||
}
|
||||
|
||||
private fun getEmailTypeId(value: String) = when (value) {
|
||||
getString(R.string.home) -> ContactsContract.CommonDataKinds.Email.TYPE_HOME
|
||||
getString(R.string.work) -> ContactsContract.CommonDataKinds.Email.TYPE_WORK
|
||||
getString(R.string.mobile) -> ContactsContract.CommonDataKinds.Email.TYPE_MOBILE
|
||||
else -> ContactsContract.CommonDataKinds.Email.TYPE_OTHER
|
||||
getString(R.string.home) -> CommonDataKinds.Email.TYPE_HOME
|
||||
getString(R.string.work) -> CommonDataKinds.Email.TYPE_WORK
|
||||
getString(R.string.mobile) -> CommonDataKinds.Email.TYPE_MOBILE
|
||||
else -> CommonDataKinds.Email.TYPE_OTHER
|
||||
}
|
||||
|
||||
private fun getEventTextId(type: Int) = when (type) {
|
||||
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> R.string.birthday
|
||||
ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> R.string.anniversary
|
||||
CommonDataKinds.Event.TYPE_BIRTHDAY -> R.string.birthday
|
||||
CommonDataKinds.Event.TYPE_ANNIVERSARY -> R.string.anniversary
|
||||
else -> R.string.other
|
||||
}
|
||||
|
||||
private fun getEventTypeId(value: String) = when (value) {
|
||||
getString(R.string.birthday) -> ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY
|
||||
getString(R.string.anniversary) -> ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY
|
||||
else -> ContactsContract.CommonDataKinds.Event.TYPE_OTHER
|
||||
getString(R.string.birthday) -> CommonDataKinds.Event.TYPE_BIRTHDAY
|
||||
getString(R.string.anniversary) -> CommonDataKinds.Event.TYPE_ANNIVERSARY
|
||||
else -> CommonDataKinds.Event.TYPE_OTHER
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,25 +4,34 @@ import android.app.SearchManager
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v4.view.MenuItemCompat
|
||||
import android.support.v4.view.ViewPager
|
||||
import android.support.v7.widget.SearchView
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.contacts.BuildConfig
|
||||
import com.simplemobiletools.contacts.R
|
||||
import com.simplemobiletools.contacts.adapters.ViewPagerAdapter
|
||||
import com.simplemobiletools.contacts.dialogs.ChangeSortingDialog
|
||||
import com.simplemobiletools.contacts.dialogs.ExportContactsDialog
|
||||
import com.simplemobiletools.contacts.dialogs.FilterContactSourcesDialog
|
||||
import com.simplemobiletools.contacts.dialogs.ImportContactsDialog
|
||||
import com.simplemobiletools.contacts.extensions.config
|
||||
import com.simplemobiletools.contacts.extensions.getTempFile
|
||||
import com.simplemobiletools.contacts.extensions.onTabSelectionChanged
|
||||
import com.simplemobiletools.contacts.helpers.ContactsHelper
|
||||
import com.simplemobiletools.contacts.helpers.VcfExporter
|
||||
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.fragment_contacts.*
|
||||
import kotlinx.android.synthetic.main.fragment_favorites.*
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class MainActivity : SimpleActivity(), RefreshContactsListener {
|
||||
private var isFirstResume = true
|
||||
|
@ -102,10 +111,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
|
|||
if (viewpager.adapter == null) {
|
||||
initFragments()
|
||||
}
|
||||
contacts_fragment.initContacts()
|
||||
contacts_fragment.onActivityResume()
|
||||
favorites_fragment.initContacts()
|
||||
favorites_fragment.onActivityResume()
|
||||
contacts_fragment?.initContacts()
|
||||
contacts_fragment?.onActivityResume()
|
||||
favorites_fragment?.initContacts()
|
||||
favorites_fragment?.onActivityResume()
|
||||
}
|
||||
isFirstResume = false
|
||||
}
|
||||
|
@ -130,6 +139,8 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
|
|||
when (item.itemId) {
|
||||
R.id.sort -> showSortingDialog()
|
||||
R.id.filter -> showFilterDialog()
|
||||
R.id.import_contacts -> tryImportContacts()
|
||||
R.id.export_contacts -> tryExportContacts()
|
||||
R.id.settings -> startActivity(Intent(applicationContext, SettingsActivity::class.java))
|
||||
R.id.about -> launchAbout()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
|
@ -156,9 +167,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
|
|||
isSubmitButtonEnabled = false
|
||||
queryHint = getString(if (viewpager.currentItem == 0) R.string.search_contacts else R.string.search_favorites)
|
||||
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun onQueryTextSubmit(query: String) = false
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
if (isSearchOpen) {
|
||||
|
@ -232,6 +241,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
|
|||
it.icon?.applyColorFilter(getAdjustedPrimaryColor())
|
||||
}
|
||||
)
|
||||
|
||||
if (intent?.action == Intent.ACTION_VIEW && intent.data != null) {
|
||||
tryImportContactsFromFile(intent.data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSortingDialog() {
|
||||
|
@ -243,10 +256,86 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
|
|||
|
||||
fun showFilterDialog() {
|
||||
FilterContactSourcesDialog(this) {
|
||||
contacts_fragment.forceListRedraw = true
|
||||
contacts_fragment.initContacts()
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImportContacts() {
|
||||
handlePermission(PERMISSION_READ_STORAGE) {
|
||||
if (it) {
|
||||
importContacts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importContacts() {
|
||||
FilePickerDialog(this) {
|
||||
showImportContactsDialog(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showImportContactsDialog(path: String) {
|
||||
ImportContactsDialog(this, path) {
|
||||
if (it) {
|
||||
runOnUiThread {
|
||||
refreshContacts()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImportContactsFromFile(uri: Uri) {
|
||||
when {
|
||||
uri.scheme == "file" -> showImportContactsDialog(uri.path)
|
||||
uri.scheme == "content" -> {
|
||||
val tempFile = getTempFile()
|
||||
if (tempFile == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
val out = FileOutputStream(tempFile)
|
||||
inputStream.copyTo(out)
|
||||
showImportContactsDialog(tempFile.absolutePath)
|
||||
}
|
||||
else -> toast(R.string.invalid_file_format)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryExportContacts() {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
exportContacts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportContacts() {
|
||||
FilePickerDialog(this, pickFile = false, showFAB = true) {
|
||||
ExportContactsDialog(this, it) { file, contactSources ->
|
||||
Thread {
|
||||
ContactsHelper(this).getContacts {
|
||||
val contacts = it.filter { contactSources.contains(it.source) }
|
||||
if (contacts.isEmpty()) {
|
||||
toast(R.string.no_entries_for_exporting)
|
||||
} else {
|
||||
toast(R.string.exporting)
|
||||
VcfExporter().exportContacts(this, file, contacts as ArrayList<Contact>) {
|
||||
toast(when (it) {
|
||||
VcfExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
|
||||
VcfExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
|
||||
else -> R.string.exporting_failed
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchAbout() {
|
||||
startAboutActivity(R.string.app_name, LICENSE_KOTLIN or LICENSE_MULTISELECT or LICENSE_JODA or LICENSE_GLIDE, BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
|||
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
||||
import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
|
||||
import com.simplemobiletools.commons.extensions.isActivityDestroyed
|
||||
import com.simplemobiletools.commons.views.FastScroller
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.contacts.R
|
||||
import com.simplemobiletools.contacts.activities.SimpleActivity
|
||||
import com.simplemobiletools.contacts.extensions.config
|
||||
import com.simplemobiletools.contacts.extensions.openContact
|
||||
import com.simplemobiletools.contacts.extensions.shareContacts
|
||||
import com.simplemobiletools.contacts.helpers.ContactsHelper
|
||||
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
|
@ -25,8 +27,8 @@ import kotlinx.android.synthetic.main.item_contact_with_number.view.*
|
|||
import java.util.*
|
||||
|
||||
class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList<Contact>, val listener: RefreshContactsListener?,
|
||||
val isFavoritesFragment: Boolean, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit) :
|
||||
MyRecyclerViewAdapter(activity, recyclerView, itemClick) {
|
||||
val isFavoritesFragment: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller, itemClick: (Any) -> Unit) :
|
||||
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
|
||||
|
||||
lateinit private var contactDrawable: Drawable
|
||||
var config = activity.config
|
||||
|
@ -61,9 +63,10 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList<Co
|
|||
when (id) {
|
||||
R.id.cab_edit -> editContact()
|
||||
R.id.cab_select_all -> selectAll()
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
R.id.cab_add_to_favorites -> addToFavorites()
|
||||
R.id.cab_share -> shareContacts()
|
||||
R.id.cab_remove -> removeFavorites()
|
||||
R.id.cab_delete -> askConfirmDelete()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,10 +163,23 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList<Co
|
|||
finishActMode()
|
||||
}
|
||||
|
||||
private fun shareContacts() {
|
||||
if (selectedPositions.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val contacts = ArrayList<Contact>()
|
||||
selectedPositions.forEach {
|
||||
contacts.add(contactItems[it])
|
||||
}
|
||||
|
||||
activity.shareContacts(contacts)
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: ViewHolder?) {
|
||||
super.onViewRecycled(holder)
|
||||
if (!activity.isActivityDestroyed()) {
|
||||
Glide.with(activity).clear(holder?.itemView?.contact_tmb)
|
||||
Glide.with(activity).clear(holder?.itemView?.contact_tmb!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,6 @@ class SelectContactsAdapter(val activity: SimpleActivity, val contacts: List<Con
|
|||
|
||||
override fun onViewRecycled(holder: ViewHolder?) {
|
||||
super.onViewRecycled(holder)
|
||||
Glide.with(activity).clear(holder?.itemView?.contact_tmb)
|
||||
Glide.with(activity).clear(holder?.itemView?.contact_tmb!!)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package com.simplemobiletools.contacts.dialogs
|
||||
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.view.ViewGroup
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.contacts.R
|
||||
import com.simplemobiletools.contacts.activities.SimpleActivity
|
||||
import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter
|
||||
import com.simplemobiletools.contacts.extensions.config
|
||||
import com.simplemobiletools.contacts.helpers.ContactsHelper
|
||||
import kotlinx.android.synthetic.main.dialog_export_contacts.view.*
|
||||
import java.io.File
|
||||
|
||||
class ExportContactsDialog(val activity: SimpleActivity, val path: String, val callback: (file: File, contactSources: HashSet<String>) -> Unit) {
|
||||
|
||||
init {
|
||||
val view = (activity.layoutInflater.inflate(R.layout.dialog_export_contacts, null) as ViewGroup).apply {
|
||||
export_contacts_folder.text = activity.humanizePath(path)
|
||||
export_contacts_filename.setText("contacts_${System.currentTimeMillis() / 1000}")
|
||||
|
||||
ContactsHelper(activity).getContactSources {
|
||||
activity.runOnUiThread {
|
||||
export_contacts_list.adapter = FilterContactSourcesAdapter(activity, it, activity.config.displayContactSources)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.export_contacts) {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val filename = view.export_contacts_filename.value
|
||||
when {
|
||||
filename.isEmpty() -> activity.toast(R.string.empty_name)
|
||||
filename.isAValidFilename() -> {
|
||||
val file = File(path, "$filename.vcf")
|
||||
if (file.exists()) {
|
||||
activity.toast(R.string.name_taken)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val contactSources = (view.export_contacts_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
|
||||
callback(file, contactSources)
|
||||
dismiss()
|
||||
}
|
||||
else -> activity.toast(R.string.invalid_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.simplemobiletools.contacts.dialogs
|
||||
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.view.ViewGroup
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.extensions.value
|
||||
import com.simplemobiletools.contacts.R
|
||||
import com.simplemobiletools.contacts.activities.SimpleActivity
|
||||
import com.simplemobiletools.contacts.extensions.config
|
||||
import com.simplemobiletools.contacts.extensions.showContactSourcePicker
|
||||
import com.simplemobiletools.contacts.helpers.VcfImporter
|
||||
import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.IMPORT_FAIL
|
||||
import kotlinx.android.synthetic.main.dialog_import_contacts.view.*
|
||||
|
||||
class ImportContactsDialog(val activity: SimpleActivity, val path: String, val callback: (refreshView: Boolean) -> Unit) {
|
||||
init {
|
||||
val view = (activity.layoutInflater.inflate(R.layout.dialog_import_contacts, null) as ViewGroup).apply {
|
||||
import_contacts_title.text = activity.config.lastUsedContactSource
|
||||
import_contacts_title.setOnClickListener {
|
||||
activity.showContactSourcePicker(import_contacts_title.value) {
|
||||
import_contacts_title.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.import_contacts) {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
activity.toast(R.string.importing)
|
||||
Thread {
|
||||
val result = VcfImporter(activity).importContacts(path, view.import_contacts_title.value)
|
||||
handleParseResult(result)
|
||||
dismiss()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleParseResult(result: VcfImporter.ImportResult) {
|
||||
activity.toast(when (result) {
|
||||
VcfImporter.ImportResult.IMPORT_OK -> R.string.importing_successful
|
||||
VcfImporter.ImportResult.IMPORT_PARTIAL -> R.string.importing_some_entries_failed
|
||||
else -> R.string.importing_failed
|
||||
})
|
||||
callback(result != IMPORT_FAIL)
|
||||
}
|
||||
}
|
|
@ -3,12 +3,19 @@ package com.simplemobiletools.contacts.extensions
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.simplemobiletools.commons.R
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.getFilePublicUri
|
||||
import com.simplemobiletools.commons.extensions.shareUri
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.contacts.BuildConfig
|
||||
import com.simplemobiletools.contacts.activities.SimpleActivity
|
||||
import com.simplemobiletools.contacts.helpers.ContactsHelper
|
||||
import com.simplemobiletools.contacts.helpers.VcfExporter
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
import java.io.File
|
||||
|
||||
fun SimpleActivity.startCallIntent(recipient: String) {
|
||||
handlePermission(PERMISSION_CALL_PHONE) {
|
||||
|
@ -40,3 +47,50 @@ fun SimpleActivity.tryStartCall(contact: Contact) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (newSource: String) -> Unit) {
|
||||
ContactsHelper(this).getContactSources {
|
||||
val items = ArrayList<RadioItem>()
|
||||
val sources = it
|
||||
var currentSourceIndex = -1
|
||||
sources.forEachIndexed { index, account ->
|
||||
items.add(RadioItem(index, account))
|
||||
if (account == currentSource) {
|
||||
currentSourceIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
RadioGroupDialog(this, items, currentSourceIndex) {
|
||||
callback(sources[it as Int])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) {
|
||||
val file = getTempFile()
|
||||
if (file == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
VcfExporter().exportContacts(this, file, contacts) {
|
||||
if (it == VcfExporter.ExportResult.EXPORT_OK) {
|
||||
val uri = getFilePublicUri(file, BuildConfig.APPLICATION_ID)
|
||||
shareUri(uri, BuildConfig.APPLICATION_ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseSimpleActivity.getTempFile(): File? {
|
||||
val folder = File(cacheDir, "contacts")
|
||||
if (!folder.exists()) {
|
||||
if (!folder.mkdir()) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return File(folder, "contacts.vcf")
|
||||
}
|
||||
|
|
|
@ -7,14 +7,17 @@ import android.database.Cursor
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.ContactsContract
|
||||
import android.support.v4.content.FileProvider
|
||||
import com.simplemobiletools.commons.R
|
||||
import com.simplemobiletools.commons.extensions.getIntValue
|
||||
import com.simplemobiletools.commons.extensions.isLollipopPlus
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.contacts.BuildConfig
|
||||
import com.simplemobiletools.contacts.activities.ContactActivity
|
||||
import com.simplemobiletools.contacts.helpers.CONTACT_ID
|
||||
import com.simplemobiletools.contacts.helpers.Config
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
import java.io.File
|
||||
|
||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
||||
|
||||
|
@ -94,3 +97,16 @@ fun lookupContactUri(lookup: String, context: Context): Uri {
|
|||
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup)
|
||||
return ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
|
||||
}
|
||||
|
||||
fun Context.getCachePhoto(): File {
|
||||
val imagesFolder = File(cacheDir, "my_cache")
|
||||
if (!imagesFolder.exists()) {
|
||||
imagesFolder.mkdirs()
|
||||
}
|
||||
|
||||
val file = File(imagesFolder, "Photo_${System.currentTimeMillis()}.jpg")
|
||||
file.createNewFile()
|
||||
return file
|
||||
}
|
||||
|
||||
fun Context.getCachePhotoUri(file: File = getCachePhoto()) = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.provider", file)
|
||||
|
|
|
@ -26,6 +26,8 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
|
|||
private var contactsIgnoringSearch = ArrayList<Contact>()
|
||||
lateinit private var config: Config
|
||||
|
||||
var forceListRedraw = false
|
||||
|
||||
fun setupFragment(activity: MainActivity) {
|
||||
config = activity.config
|
||||
if (this.activity == null) {
|
||||
|
@ -114,8 +116,9 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
|
|||
fragment_list.beVisibleIf(contacts.isNotEmpty())
|
||||
|
||||
val currAdapter = fragment_list.adapter
|
||||
if (currAdapter == null) {
|
||||
ContactsAdapter(activity as SimpleActivity, contacts, activity, this is FavoritesFragment, fragment_list) {
|
||||
if (currAdapter == null || forceListRedraw) {
|
||||
forceListRedraw = false
|
||||
ContactsAdapter(activity as SimpleActivity, contacts, activity, this is FavoritesFragment, fragment_list, fragment_fastscroller) {
|
||||
if (config.callContact) {
|
||||
val contact = it as Contact
|
||||
if (contact.phoneNumbers.isNotEmpty()) {
|
||||
|
@ -132,6 +135,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
|
|||
fragment_list.adapter = this
|
||||
}
|
||||
|
||||
fragment_fastscroller.setScrollTo(0)
|
||||
fragment_fastscroller.setViews(fragment_list) {
|
||||
val item = (fragment_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
|
||||
fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
|
||||
|
@ -165,16 +169,24 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
|
|||
filtered.sort()
|
||||
filtered.sortBy { !it.getFullName(startNameWithSurname).startsWith(text, true) }
|
||||
|
||||
if (filtered.isEmpty() && this@MyViewPagerFragment is FavoritesFragment) {
|
||||
fragment_placeholder.text = activity.getString(R.string.no_items_found)
|
||||
}
|
||||
|
||||
fragment_placeholder.beVisibleIf(filtered.isEmpty())
|
||||
updateItems(filtered)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSearchOpened() {
|
||||
contactsIgnoringSearch = (fragment_list.adapter as ContactsAdapter).contactItems as ArrayList
|
||||
contactsIgnoringSearch = (fragment_list.adapter as? ContactsAdapter)?.contactItems as ArrayList
|
||||
}
|
||||
|
||||
fun onSearchClosed() {
|
||||
(fragment_list.adapter as ContactsAdapter).updateItems(contactsIgnoringSearch)
|
||||
if (this is FavoritesFragment) {
|
||||
fragment_placeholder.text = activity?.getString(R.string.no_favorites)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateViewStuff() {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.simplemobiletools.contacts.helpers
|
||||
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
|
||||
// shared prefs
|
||||
val CALL_CONTACT_ON_CLICK = "call_contact_on_click"
|
||||
val SHOW_PHONE_NUMBERS = "show_phone_numbers"
|
||||
|
@ -15,3 +17,35 @@ val PHOTO_ADDED = 1
|
|||
val PHOTO_REMOVED = 2
|
||||
val PHOTO_CHANGED = 3
|
||||
val PHOTO_UNCHANGED = 4
|
||||
|
||||
// default contact values
|
||||
val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME
|
||||
val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE
|
||||
val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY
|
||||
|
||||
// export/import
|
||||
val BEGIN_VCARD = "BEGIN:VCARD"
|
||||
val END_VCARD = "END:VCARD"
|
||||
val N = "N:"
|
||||
val TEL = "TEL"
|
||||
val BDAY = "BDAY:"
|
||||
val ANNIVERSARY = "ANNIVERSARY:"
|
||||
val PHOTO = "PHOTO"
|
||||
val EMAIL = "EMAIL"
|
||||
val ENCODING = "ENCODING"
|
||||
val BASE64 = "BASE64"
|
||||
val JPEG = "JPEG"
|
||||
val VERSION_2_1 = "VERSION:2.1"
|
||||
|
||||
// phone number/email types
|
||||
val CELL = "CELL"
|
||||
val WORK = "WORK"
|
||||
val HOME = "HOME"
|
||||
val PREF = "PREF"
|
||||
val MAIN = "MAIN"
|
||||
val FAX = "FAX"
|
||||
val WORK_FAX = "WORK;FAX"
|
||||
val HOME_FAX = "HOME;FAX"
|
||||
val PAGER = "PAGER"
|
||||
val MOBILE = "MOBILE"
|
||||
val VOICE = "VOICE"
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.database.Cursor
|
|||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
import android.provider.MediaStore
|
||||
import android.util.SparseArray
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
|
@ -35,7 +36,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val uri = ContactsContract.Data.CONTENT_URI
|
||||
val projection = getContactProjection()
|
||||
val selection = "${ContactsContract.Data.MIMETYPE} = ?"
|
||||
val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
val sortOrder = getSortString()
|
||||
|
||||
var cursor: Cursor? = null
|
||||
|
@ -44,17 +45,18 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
|
||||
val firstName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
|
||||
val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
|
||||
val surname = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
|
||||
val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.PHOTO_URI) ?: ""
|
||||
val firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
|
||||
val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
|
||||
val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
|
||||
val photoUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_URI) ?: ""
|
||||
val number = ArrayList<PhoneNumber>() // proper value is obtained below
|
||||
val emails = ArrayList<Email>()
|
||||
val events = ArrayList<Event>()
|
||||
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME)
|
||||
val starred = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredName.STARRED)
|
||||
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
|
||||
val starred = cursor.getIntValue(CommonDataKinds.StructuredName.STARRED)
|
||||
val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
|
||||
val contact = Contact(id, firstName, middleName, surname, photoUri, number, emails, events, accountName, starred, contactId)
|
||||
val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
||||
val contact = Contact(id, firstName, middleName, surname, photoUri, number, emails, events, accountName, starred, contactId, thumbnailUri)
|
||||
contacts.put(id, contact)
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
|
@ -88,11 +90,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
|
||||
private fun getPhoneNumbers(contactId: Int? = null): SparseArray<ArrayList<PhoneNumber>> {
|
||||
val phoneNumbers = SparseArray<ArrayList<PhoneNumber>>()
|
||||
val uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
|
||||
val uri = CommonDataKinds.Phone.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
ContactsContract.Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE
|
||||
CommonDataKinds.Phone.NUMBER,
|
||||
CommonDataKinds.Phone.TYPE
|
||||
)
|
||||
|
||||
val selection = if (contactId == null) null else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
|
||||
|
@ -104,16 +106,19 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
|
||||
val number = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.NUMBER)
|
||||
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Phone.TYPE)
|
||||
val number = cursor.getStringValue(CommonDataKinds.Phone.NUMBER) ?: continue
|
||||
val type = cursor.getIntValue(CommonDataKinds.Phone.TYPE)
|
||||
|
||||
if (phoneNumbers[id] == null) {
|
||||
phoneNumbers.put(id, ArrayList())
|
||||
}
|
||||
|
||||
phoneNumbers[id].add(PhoneNumber(number, type))
|
||||
val phoneNumber = PhoneNumber(number, type)
|
||||
phoneNumbers[id].add(phoneNumber)
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
|
@ -122,11 +127,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
|
||||
private fun getEmails(contactId: Int? = null): SparseArray<ArrayList<Email>> {
|
||||
val emails = SparseArray<ArrayList<Email>>()
|
||||
val uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI
|
||||
val uri = CommonDataKinds.Email.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
ContactsContract.Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Email.DATA,
|
||||
ContactsContract.CommonDataKinds.Email.TYPE
|
||||
CommonDataKinds.Email.DATA,
|
||||
CommonDataKinds.Email.TYPE
|
||||
)
|
||||
|
||||
val selection = if (contactId == null) null else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
|
||||
|
@ -138,8 +143,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
|
||||
val email = cursor.getStringValue(ContactsContract.CommonDataKinds.Email.DATA)
|
||||
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Email.TYPE)
|
||||
val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: continue
|
||||
val type = cursor.getIntValue(CommonDataKinds.Email.TYPE)
|
||||
|
||||
if (emails[id] == null) {
|
||||
emails.put(id, ArrayList())
|
||||
|
@ -148,6 +153,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
emails[id]!!.add(Email(email, type))
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
|
@ -159,18 +167,18 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val events = SparseArray<ArrayList<Event>>()
|
||||
val uri = ContactsContract.Data.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
ContactsContract.CommonDataKinds.Event.START_DATE,
|
||||
ContactsContract.CommonDataKinds.Event.TYPE
|
||||
CommonDataKinds.Event.START_DATE,
|
||||
CommonDataKinds.Event.TYPE
|
||||
)
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?"
|
||||
val selectionArgs = arrayOf(contactId.toString(), ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contactId.toString(), CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
do {
|
||||
val startDate = cursor.getStringValue(ContactsContract.CommonDataKinds.Event.START_DATE)
|
||||
val type = cursor.getIntValue(ContactsContract.CommonDataKinds.Event.TYPE)
|
||||
val startDate = cursor.getStringValue(CommonDataKinds.Event.START_DATE)
|
||||
val type = cursor.getIntValue(CommonDataKinds.Event.TYPE)
|
||||
|
||||
if (events[contactId] == null) {
|
||||
events.put(contactId, ArrayList())
|
||||
|
@ -196,22 +204,23 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val uri = ContactsContract.Data.CONTENT_URI
|
||||
val projection = getContactProjection()
|
||||
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
|
||||
val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id.toString())
|
||||
val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id.toString())
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
val firstName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
|
||||
val middleName = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
|
||||
val surname = cursor.getStringValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
|
||||
val photoUri = cursor.getStringValue(ContactsContract.CommonDataKinds.Phone.PHOTO_URI) ?: ""
|
||||
val firstName = cursor.getStringValue(CommonDataKinds.StructuredName.GIVEN_NAME) ?: ""
|
||||
val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
|
||||
val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
|
||||
val photoUri = cursor.getStringValue(CommonDataKinds.Phone.PHOTO_URI) ?: ""
|
||||
val number = getPhoneNumbers(id)[id] ?: ArrayList()
|
||||
val emails = getEmails(id)[id] ?: ArrayList()
|
||||
val events = getEvents(id)[id] ?: ArrayList()
|
||||
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME)
|
||||
val starred = cursor.getIntValue(ContactsContract.CommonDataKinds.StructuredName.STARRED)
|
||||
val accountName = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
|
||||
val starred = cursor.getIntValue(CommonDataKinds.StructuredName.STARRED)
|
||||
val contactId = cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
|
||||
return Contact(id, firstName, middleName, surname, photoUri, number, emails, events, accountName, starred, contactId)
|
||||
val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
|
||||
return Contact(id, firstName, middleName, surname, photoUri, number, emails, events, accountName, starred, contactId, thumbnailUri)
|
||||
}
|
||||
} finally {
|
||||
cursor?.close()
|
||||
|
@ -264,21 +273,22 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
private fun getContactProjection() = arrayOf(
|
||||
ContactsContract.Data.CONTACT_ID,
|
||||
ContactsContract.Data.RAW_CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.PHOTO_URI,
|
||||
ContactsContract.CommonDataKinds.StructuredName.STARRED,
|
||||
CommonDataKinds.StructuredName.GIVEN_NAME,
|
||||
CommonDataKinds.StructuredName.MIDDLE_NAME,
|
||||
CommonDataKinds.StructuredName.FAMILY_NAME,
|
||||
CommonDataKinds.StructuredName.PHOTO_URI,
|
||||
CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI,
|
||||
CommonDataKinds.StructuredName.STARRED,
|
||||
ContactsContract.RawContacts.ACCOUNT_NAME
|
||||
)
|
||||
|
||||
private fun getSortString(): String {
|
||||
val sorting = activity.config.sorting
|
||||
var sort = when {
|
||||
sorting and SORT_BY_FIRST_NAME != 0 -> "${ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME} COLLATE NOCASE"
|
||||
sorting and SORT_BY_MIDDLE_NAME != 0 -> "${ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME} COLLATE NOCASE"
|
||||
sorting and SORT_BY_SURNAME != 0 -> "${ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME} COLLATE NOCASE"
|
||||
else -> ContactsContract.CommonDataKinds.Phone.NUMBER
|
||||
sorting and SORT_BY_FIRST_NAME != 0 -> "${CommonDataKinds.StructuredName.GIVEN_NAME} COLLATE NOCASE"
|
||||
sorting and SORT_BY_MIDDLE_NAME != 0 -> "${CommonDataKinds.StructuredName.MIDDLE_NAME} COLLATE NOCASE"
|
||||
sorting and SORT_BY_SURNAME != 0 -> "${CommonDataKinds.StructuredName.FAMILY_NAME} COLLATE NOCASE"
|
||||
else -> CommonDataKinds.Phone.NUMBER
|
||||
}
|
||||
|
||||
if (sorting and SORT_DESCENDING != 0) {
|
||||
|
@ -294,18 +304,18 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val operations = ArrayList<ContentProviderOperation>()
|
||||
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply {
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?"
|
||||
val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
withSelection(selection, selectionArgs)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
|
||||
withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
|
||||
withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
|
||||
withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
|
||||
operations.add(build())
|
||||
}
|
||||
|
||||
// delete phone numbers
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
|
||||
val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
withSelection(selection, selectionArgs)
|
||||
operations.add(build())
|
||||
}
|
||||
|
@ -314,9 +324,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.phoneNumbers.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Phone.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Phone.NUMBER, it.value)
|
||||
withValue(CommonDataKinds.Phone.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +334,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
// delete emails
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
|
||||
val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
withSelection(selection, selectionArgs)
|
||||
operations.add(build())
|
||||
}
|
||||
|
@ -333,9 +343,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.emails.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Email.DATA, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Email.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Email.DATA, it.value)
|
||||
withValue(CommonDataKinds.Email.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +353,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
// delete events
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
|
||||
val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
withSelection(selection, selectionArgs)
|
||||
operations.add(build())
|
||||
}
|
||||
|
@ -352,9 +362,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.events.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Event.START_DATE, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Event.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Event.START_DATE, it.value)
|
||||
withValue(CommonDataKinds.Event.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -398,8 +408,8 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
|
||||
operations.add(build())
|
||||
}
|
||||
|
||||
|
@ -411,7 +421,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
private fun removePhoto(contact: Contact, operations: ArrayList<ContentProviderOperation>): ArrayList<ContentProviderOperation> {
|
||||
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
|
||||
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?"
|
||||
val selectionArgs = arrayOf(contact.id.toString(), ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
withSelection(selection, selectionArgs)
|
||||
operations.add(build())
|
||||
}
|
||||
|
@ -421,7 +431,6 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
|
||||
fun insertContact(contact: Contact): Boolean {
|
||||
return try {
|
||||
activity.toast(R.string.inserting)
|
||||
val operations = ArrayList<ContentProviderOperation>()
|
||||
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply {
|
||||
withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source)
|
||||
|
@ -432,10 +441,10 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
// names
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
|
||||
withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
|
||||
withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
|
||||
withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
|
||||
operations.add(build())
|
||||
}
|
||||
|
||||
|
@ -443,9 +452,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.phoneNumbers.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Phone.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Phone.NUMBER, it.value)
|
||||
withValue(CommonDataKinds.Phone.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -454,9 +463,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.emails.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Email.DATA, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Email.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Email.DATA, it.value)
|
||||
withValue(CommonDataKinds.Email.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -465,9 +474,9 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
contact.events.forEach {
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Event.START_DATE, it.value)
|
||||
withValue(ContactsContract.CommonDataKinds.Event.TYPE, it.type)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Event.START_DATE, it.value)
|
||||
withValue(CommonDataKinds.Event.TYPE, it.type)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -482,15 +491,15 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val thumbnailSize = getThumbnailSize()
|
||||
val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false)
|
||||
scaledSizePhotoData = bitmapToByteArray(scaledPhoto)
|
||||
scaledPhoto.recycle()
|
||||
|
||||
fullSizePhotoData = bitmapToByteArray(bitmap)
|
||||
scaledPhoto.recycle()
|
||||
bitmap.recycle()
|
||||
|
||||
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
|
||||
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
|
||||
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
|
||||
operations.add(build())
|
||||
}
|
||||
}
|
||||
|
@ -540,7 +549,7 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
|
|||
val uri = ContactsContract.Data.CONTENT_URI
|
||||
val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY)
|
||||
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
|
||||
val selectionArgs = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, contactId)
|
||||
val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, contactId)
|
||||
var cursor: Cursor? = null
|
||||
try {
|
||||
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package com.simplemobiletools.contacts.helpers
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
import android.provider.MediaStore
|
||||
import android.util.Base64
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.getFileOutputStream
|
||||
import com.simplemobiletools.commons.extensions.writeLn
|
||||
import com.simplemobiletools.contacts.helpers.VcfExporter.ExportResult.*
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
|
||||
class VcfExporter {
|
||||
private val ENCODED_PHOTO_LINE_LENGTH = 74
|
||||
|
||||
enum class ExportResult {
|
||||
EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL
|
||||
}
|
||||
|
||||
private var contactsExported = 0
|
||||
private var contactsFailed = 0
|
||||
|
||||
fun exportContacts(activity: BaseSimpleActivity, file: File, contacts: ArrayList<Contact>, callback: (result: ExportResult) -> Unit) {
|
||||
activity.getFileOutputStream(file) {
|
||||
if (it == null) {
|
||||
callback(EXPORT_FAIL)
|
||||
return@getFileOutputStream
|
||||
}
|
||||
|
||||
it.bufferedWriter().use { out ->
|
||||
for (contact in contacts) {
|
||||
out.writeLn(BEGIN_VCARD)
|
||||
out.writeLn(VERSION_2_1)
|
||||
out.writeLn("$N${contact.surname};${contact.firstName};${contact.middleName};;")
|
||||
|
||||
contact.phoneNumbers.forEach {
|
||||
out.writeLn("$TEL;${getPhoneNumberLabel(it.type)}:${it.value}")
|
||||
}
|
||||
|
||||
contact.emails.forEach {
|
||||
val type = getEmailTypeLabel(it.type)
|
||||
val delimiterType = if (type.isEmpty()) "" else ";$type"
|
||||
out.writeLn("$EMAIL$delimiterType:${it.value}")
|
||||
}
|
||||
|
||||
contact.events.forEach {
|
||||
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
|
||||
out.writeLn("$BDAY${it.value}")
|
||||
}
|
||||
}
|
||||
|
||||
if (contact.thumbnailUri.isNotEmpty()) {
|
||||
val firstLine = "$PHOTO;$ENCODING=$BASE64;$JPEG:"
|
||||
val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, Uri.parse(contact.thumbnailUri))
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, byteArrayOutputStream)
|
||||
bitmap.recycle()
|
||||
val byteArray = byteArrayOutputStream.toByteArray()
|
||||
val encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP)
|
||||
|
||||
val encodedFirstLineSection = encoded.substring(0, ENCODED_PHOTO_LINE_LENGTH - firstLine.length)
|
||||
out.writeLn(firstLine + encodedFirstLineSection)
|
||||
var curStartIndex = encodedFirstLineSection.length
|
||||
do {
|
||||
val part = encoded.substring(curStartIndex, Math.min(curStartIndex + ENCODED_PHOTO_LINE_LENGTH - 1, encoded.length))
|
||||
out.writeLn(" $part")
|
||||
curStartIndex += ENCODED_PHOTO_LINE_LENGTH - 1
|
||||
} while (curStartIndex < encoded.length)
|
||||
|
||||
out.writeLn("")
|
||||
}
|
||||
|
||||
out.writeLn(END_VCARD)
|
||||
contactsExported++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(when {
|
||||
contactsExported == 0 -> EXPORT_FAIL
|
||||
contactsFailed > 0 -> EXPORT_PARTIAL
|
||||
else -> EXPORT_OK
|
||||
})
|
||||
}
|
||||
|
||||
private fun getPhoneNumberLabel(type: Int) = when (type) {
|
||||
CommonDataKinds.Phone.TYPE_MOBILE -> CELL
|
||||
CommonDataKinds.Phone.TYPE_HOME -> HOME
|
||||
CommonDataKinds.Phone.TYPE_WORK -> WORK
|
||||
CommonDataKinds.Phone.TYPE_MAIN -> PREF
|
||||
CommonDataKinds.Phone.TYPE_FAX_WORK -> WORK_FAX
|
||||
CommonDataKinds.Phone.TYPE_FAX_HOME -> HOME_FAX
|
||||
CommonDataKinds.Phone.TYPE_PAGER -> PAGER
|
||||
else -> VOICE
|
||||
}
|
||||
|
||||
private fun getEmailTypeLabel(type: Int) = when (type) {
|
||||
CommonDataKinds.Email.TYPE_HOME -> HOME
|
||||
CommonDataKinds.Email.TYPE_WORK -> WORK
|
||||
CommonDataKinds.Email.TYPE_MOBILE -> MOBILE
|
||||
else -> ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package com.simplemobiletools.contacts.helpers
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.provider.ContactsContract.CommonDataKinds
|
||||
import android.util.Base64
|
||||
import android.widget.Toast
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import com.simplemobiletools.contacts.activities.SimpleActivity
|
||||
import com.simplemobiletools.contacts.extensions.getCachePhoto
|
||||
import com.simplemobiletools.contacts.extensions.getCachePhotoUri
|
||||
import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.*
|
||||
import com.simplemobiletools.contacts.models.Contact
|
||||
import com.simplemobiletools.contacts.models.Email
|
||||
import com.simplemobiletools.contacts.models.Event
|
||||
import com.simplemobiletools.contacts.models.PhoneNumber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class VcfImporter(val activity: SimpleActivity) {
|
||||
enum class ImportResult {
|
||||
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL
|
||||
}
|
||||
|
||||
private var curFirstName = ""
|
||||
private var curMiddleName = ""
|
||||
private var curSurname = ""
|
||||
private var curPhotoUri = ""
|
||||
private var curPhoneNumbers = ArrayList<PhoneNumber>()
|
||||
private var curEmails = ArrayList<Email>()
|
||||
private var curEvents = ArrayList<Event>()
|
||||
|
||||
private var isGettingPhoto = false
|
||||
private var currentPhotoString = StringBuilder()
|
||||
private var currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
|
||||
|
||||
private var contactsImported = 0
|
||||
private var contactsFailed = 0
|
||||
|
||||
fun importContacts(path: String, targetContactSource: String): ImportResult {
|
||||
try {
|
||||
val inputStream = if (path.contains("/")) {
|
||||
File(path).inputStream()
|
||||
} else {
|
||||
activity.assets.open(path)
|
||||
}
|
||||
|
||||
inputStream.bufferedReader().use {
|
||||
while (true) {
|
||||
val line = it.readLine() ?: break
|
||||
if (line.trim().isEmpty()) {
|
||||
if (isGettingPhoto) {
|
||||
savePhoto()
|
||||
isGettingPhoto = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
when {
|
||||
line.toUpperCase() == BEGIN_VCARD -> resetValues()
|
||||
line.toUpperCase().startsWith(N) -> parseNames(line.substring(N.length))
|
||||
line.toUpperCase().startsWith(TEL) -> addPhoneNumber(line.substring(TEL.length))
|
||||
line.toUpperCase().startsWith(EMAIL) -> addEmail(line.substring(EMAIL.length))
|
||||
line.toUpperCase().startsWith(BDAY) -> addBirthday(line.substring(BDAY.length))
|
||||
line.toUpperCase().startsWith(ANNIVERSARY) -> addAnniversary(line.substring(ANNIVERSARY.length))
|
||||
line.toUpperCase().startsWith(PHOTO) -> addPhoto(line.substring(PHOTO.length))
|
||||
line.toUpperCase() == END_VCARD -> saveContact(targetContactSource)
|
||||
isGettingPhoto -> currentPhotoString.append(line.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e, Toast.LENGTH_LONG)
|
||||
contactsFailed++
|
||||
}
|
||||
|
||||
return when {
|
||||
contactsImported == 0 -> IMPORT_FAIL
|
||||
contactsFailed > 0 -> IMPORT_PARTIAL
|
||||
else -> IMPORT_OK
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseNames(names: String) {
|
||||
val nameParts = names.split(";")
|
||||
curSurname = nameParts[0]
|
||||
curFirstName = nameParts[1]
|
||||
if (nameParts.size > 2) {
|
||||
curMiddleName = nameParts[2]
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPhoneNumber(phoneNumber: String) {
|
||||
val phoneParts = phoneNumber.trimStart(';').split(":")
|
||||
var rawType = phoneParts[0]
|
||||
var subType = ""
|
||||
if (rawType.contains('=')) {
|
||||
val types = rawType.split('=')
|
||||
if (types.any { it.contains(';') }) {
|
||||
subType = types[1].split(';')[0]
|
||||
}
|
||||
rawType = types.last()
|
||||
}
|
||||
|
||||
val type = getPhoneNumberTypeId(rawType.toUpperCase(), subType)
|
||||
val value = phoneParts[1]
|
||||
curPhoneNumbers.add(PhoneNumber(value, type))
|
||||
}
|
||||
|
||||
private fun getPhoneNumberTypeId(type: String, subType: String) = when (type) {
|
||||
CELL -> CommonDataKinds.Phone.TYPE_MOBILE
|
||||
HOME -> CommonDataKinds.Phone.TYPE_HOME
|
||||
WORK -> CommonDataKinds.Phone.TYPE_WORK
|
||||
PREF, MAIN -> CommonDataKinds.Phone.TYPE_MAIN
|
||||
WORK_FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
|
||||
HOME_FAX -> CommonDataKinds.Phone.TYPE_FAX_HOME
|
||||
FAX -> if (subType == WORK) CommonDataKinds.Phone.TYPE_FAX_WORK else CommonDataKinds.Phone.TYPE_FAX_HOME
|
||||
PAGER -> CommonDataKinds.Phone.TYPE_PAGER
|
||||
else -> CommonDataKinds.Phone.TYPE_OTHER
|
||||
}
|
||||
|
||||
private fun addEmail(email: String) {
|
||||
val emailParts = email.trimStart(';').split(":")
|
||||
var rawType = emailParts[0]
|
||||
if (rawType.contains('=')) {
|
||||
rawType = rawType.split('=').last()
|
||||
}
|
||||
val type = getEmailTypeId(rawType.toUpperCase())
|
||||
val value = emailParts[1]
|
||||
curEmails.add(Email(value, type))
|
||||
}
|
||||
|
||||
private fun getEmailTypeId(type: String) = when (type) {
|
||||
HOME -> CommonDataKinds.Email.TYPE_HOME
|
||||
WORK -> CommonDataKinds.Email.TYPE_WORK
|
||||
MOBILE -> CommonDataKinds.Email.TYPE_MOBILE
|
||||
else -> CommonDataKinds.Email.TYPE_OTHER
|
||||
}
|
||||
|
||||
private fun addBirthday(birthday: String) {
|
||||
curEvents.add(Event(birthday, CommonDataKinds.Event.TYPE_BIRTHDAY))
|
||||
}
|
||||
|
||||
private fun addAnniversary(anniversary: String) {
|
||||
curEvents.add(Event(anniversary, CommonDataKinds.Event.TYPE_ANNIVERSARY))
|
||||
}
|
||||
|
||||
private fun addPhoto(photo: String) {
|
||||
val photoParts = photo.trimStart(';').split(';')
|
||||
if (photoParts.size == 2) {
|
||||
val typeParts = photoParts[1].split(':')
|
||||
currentPhotoCompressionFormat = getPhotoCompressionFormat(typeParts[0])
|
||||
val encoding = photoParts[0].split('=').last()
|
||||
if (encoding == BASE64) {
|
||||
isGettingPhoto = true
|
||||
currentPhotoString.append(typeParts[1].trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPhotoCompressionFormat(type: String) = when (type.toLowerCase()) {
|
||||
"png" -> Bitmap.CompressFormat.PNG
|
||||
"webp" -> Bitmap.CompressFormat.WEBP
|
||||
else -> Bitmap.CompressFormat.JPEG
|
||||
}
|
||||
|
||||
private fun savePhoto() {
|
||||
val file = activity.getCachePhoto()
|
||||
val imageAsBytes = Base64.decode(currentPhotoString.toString().toByteArray(), Base64.DEFAULT)
|
||||
val bitmap = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.size)
|
||||
var fileOutputStream: FileOutputStream? = null
|
||||
try {
|
||||
fileOutputStream = FileOutputStream(file)
|
||||
bitmap.compress(currentPhotoCompressionFormat, 100, fileOutputStream)
|
||||
} finally {
|
||||
fileOutputStream?.close()
|
||||
}
|
||||
|
||||
curPhotoUri = activity.getCachePhotoUri(file).toString()
|
||||
}
|
||||
|
||||
private fun saveContact(source: String) {
|
||||
val contact = Contact(0, curFirstName, curMiddleName, curSurname, curPhotoUri, curPhoneNumbers, curEmails, curEvents, source, 0, 0, "")
|
||||
if (ContactsHelper(activity).insertContact(contact)) {
|
||||
contactsImported++
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetValues() {
|
||||
curFirstName = ""
|
||||
curMiddleName = ""
|
||||
curSurname = ""
|
||||
curPhotoUri = ""
|
||||
curPhoneNumbers = ArrayList()
|
||||
curEmails = ArrayList()
|
||||
curEvents = ArrayList()
|
||||
|
||||
isGettingPhoto = false
|
||||
currentPhotoString = StringBuilder()
|
||||
currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import com.simplemobiletools.commons.helpers.SORT_DESCENDING
|
|||
|
||||
data class Contact(val id: Int, var firstName: String, var middleName: String, var surname: String, var photoUri: String,
|
||||
var phoneNumbers: ArrayList<PhoneNumber>, var emails: ArrayList<Email>, var events: ArrayList<Event>, var source: String,
|
||||
var starred: Int, val contactId: Int) : Comparable<Contact> {
|
||||
var starred: Int, val contactId: Int, val thumbnailUri: String) : Comparable<Contact> {
|
||||
companion object {
|
||||
var sorting: Int = 0
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/export_contacts_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/export_contacts_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_margin"
|
||||
android:paddingRight="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_contacts_folder_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/folder"
|
||||
android:textSize="@dimen/smaller_text_size"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_contacts_folder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:layout_marginLeft="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
android:paddingRight="@dimen/small_margin"
|
||||
android:paddingTop="@dimen/small_margin"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_contacts_filename_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filename_without_vcf"
|
||||
android:textSize="@dimen/smaller_text_size"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/export_contacts_filename"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:layout_marginLeft="@dimen/activity_margin"
|
||||
android:paddingRight="@dimen/small_margin"
|
||||
android:paddingTop="@dimen/normal_margin"
|
||||
android:textSize="@dimen/normal_text_size"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/export_contacts_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:layout_marginBottom="@dimen/medium_margin"
|
||||
android:layout_marginTop="@dimen/medium_margin"
|
||||
android:background="@color/divider_grey"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_contacts_pick_sources_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/include_contact_sources"
|
||||
android:textSize="@dimen/smaller_text_size"/>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/export_contacts_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/import_contacts_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_margin"
|
||||
android:paddingRight="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/import_contacts_source_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/target_contact_source"
|
||||
android:textSize="@dimen/smaller_text_size"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/import_contacts_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/activity_margin"
|
||||
tools:text="Phone"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -16,6 +16,11 @@
|
|||
android:icon="@drawable/ic_star_on"
|
||||
android:title="@string/add_to_favorites"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/cab_share"
|
||||
android:icon="@drawable/ic_share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/cab_delete"
|
||||
android:icon="@drawable/ic_delete"
|
||||
|
|
|
@ -17,6 +17,14 @@
|
|||
android:icon="@drawable/ic_filter"
|
||||
android:title="@string/filter"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/import_contacts"
|
||||
android:title="@string/import_contacts_from_vcf"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/export_contacts"
|
||||
android:title="@string/export_contacts_to_vcf"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
android:title="@string/settings"
|
||||
|
|
|
@ -11,4 +11,9 @@
|
|||
android:icon="@drawable/ic_delete"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/share"
|
||||
android:icon="@drawable/ic_share"
|
||||
android:title="@string/share"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
|
|
|
@ -21,8 +21,9 @@
|
|||
|
||||
<!-- Settings -->
|
||||
<string name="call_contact_on_click">Kontakt bei Klick anrufen</string>
|
||||
<string name="start_name_with_surname">Namen mit Vornamen beginnen</string>
|
||||
<string name="start_name_with_surname">Namen mit Nachnamen beginnen</string>
|
||||
<string name="show_phone_numbers">Zeige Telefonnummern im Hauptmenü</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">Email</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">Kontakte durchsuchen</string>
|
||||
<string name="search_favorites">Favoriten durchsuchen</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Kontakte importieren</string>
|
||||
<string name="export_contacts">Kontakte exportieren</string>
|
||||
<string name="import_contacts_from_vcf">Kontakte aus .vcf Datei importieren</string>
|
||||
<string name="export_contacts_to_vcf">Kontakte in .vcf Datei exportieren</string>
|
||||
<string name="target_contact_source">Zielkontaktquelle</string>
|
||||
<string name="include_contact_sources">Kontaktquellen einschließen</string>
|
||||
<string name="filename_without_vcf">Dateiname (ohne .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">Eine App zum Verwalten von Kontakten, ganz ohne Werbung.</string>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<string name="app_launcher_name">연락처</string>
|
||||
<string name="address">주소</string>
|
||||
<string name="inserting">등록중…</string>
|
||||
<string name="updating">갱신중…</string>
|
||||
<string name="updating">수정중…</string>
|
||||
<string name="missing_contact_source">연락처가 포함된 계정을 선택하세요.</string>
|
||||
|
||||
<string name="new_contact">새로운 연락처</string>
|
||||
|
@ -23,6 +23,7 @@
|
|||
<string name="call_contact_on_click">클릭으로 전화걸기</string>
|
||||
<string name="start_name_with_surname">성을 먼저 표시</string>
|
||||
<string name="show_phone_numbers">메인 스크린에 전화번호 표시</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">이메일</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">연락처 검색</string>
|
||||
<string name="search_favorites">자주쓰는 연락처 검색</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">연락처 가져오기</string>
|
||||
<string name="export_contacts">연락처 내보내기</string>
|
||||
<string name="import_contacts_from_vcf">.vcf file에서 가져오기</string>
|
||||
<string name="export_contacts_to_vcf">.vcf file로 내보내기</string>
|
||||
<string name="target_contact_source">내보내기 대상</string>
|
||||
<string name="include_contact_sources">가져오기 대상</string>
|
||||
<string name="filename_without_vcf">파일이름 (.vcf 확장자 생략)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">광고가 없는 연락처 관리 애플리케이션입니다.</string>
|
||||
|
@ -60,7 +70,7 @@
|
|||
연락처를 생성하고 관리하는 간단한 앱입니다. 연락처는 기본적으로 기기에만 저장되며 Google 또는 다른 계정을 통해 동기화 할 수도 있습니다. 즐겨 찾는 연락처는 별도의 목록에 표시 할 수 있습니다.
|
||||
|
||||
다양한 조건으로 정렬 및 필터링이 가능하며 성과 이름을 표시하는 옵션도 제공합니다. 또한 애플리케이션을 이용해 사용자 이메일 및 이벤트 관리를 할 수도 있습니다.
|
||||
|
||||
|
||||
광고가 포함되어 있거나, 불필요한 권한을 요청하지 않습니다. 이 앱의 모든 소스는 오픈소스이며, 사용자가 직접 애플리케이션의 컬러를 설정 할 수 있습니다.
|
||||
|
||||
이 앱은 다양한 시리즈의 모바일앱 중 하나입니다. 나머지는 http://www.simplemobiletools.com 에서 찾아 보실 수 있습니다.
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<string name="call_contact_on_click">Call contact on click</string>
|
||||
<string name="start_name_with_surname">Start name with surname</string>
|
||||
<string name="show_phone_numbers">Show phone numbers on the main screen</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">E-mail</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">Search contacts</string>
|
||||
<string name="search_favorites">Search favorites</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Import contacts</string>
|
||||
<string name="export_contacts">Export contacts</string>
|
||||
<string name="import_contacts_from_vcf">Import contacts from a .vcf file</string>
|
||||
<string name="export_contacts_to_vcf">Export contacts to a .vcf file</string>
|
||||
<string name="target_contact_source">Target contact source</string>
|
||||
<string name="include_contact_sources">Include contact sources</string>
|
||||
<string name="filename_without_vcf">Filename (without .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">A contacts app for managing your contacts without ads.</string>
|
||||
|
|
|
@ -23,9 +23,10 @@
|
|||
<string name="call_contact_on_click">Вызывать контакт при нажатии</string>
|
||||
<string name="start_name_with_surname">Отображать сначала фамилии</string>
|
||||
<string name="show_phone_numbers">Отображать номера телефонов на главном экране</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">Эл.почта</string>
|
||||
<string name="email">Эл. почта</string>
|
||||
<string name="home">Домашний</string>
|
||||
<string name="work">Рабочий</string>
|
||||
<string name="other">Другой</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">Поиск контактов</string>
|
||||
<string name="search_favorites">Поиск избранных</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Импортировать контакты</string>
|
||||
<string name="export_contacts">Экспортировать контакты</string>
|
||||
<string name="import_contacts_from_vcf">Импортировать контакты из .vcf файла</string>
|
||||
<string name="export_contacts_to_vcf">Экспортировать контакты в .vcf файл</string>
|
||||
<string name="target_contact_source">Назначенный к обновлению источник контактов</string>
|
||||
<string name="include_contact_sources">Включить источники контактов</string>
|
||||
<string name="filename_without_vcf">Имя файла (без .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">Приложение для управления контактами без рекламы.</string>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<string name="call_contact_on_click">Zavolať kontakt po kliknutí</string>
|
||||
<string name="start_name_with_surname">Začať meno priezviskom</string>
|
||||
<string name="show_phone_numbers">Zobraziť telefónne čísla na hlavnej obrazovke</string>
|
||||
<string name="show_contact_avatars">Zobraziť obrázky kontaktov</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">Email</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">Hľadať v kontaktoch</string>
|
||||
<string name="search_favorites">Hľadať medzi obľúbenými</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Importovať kontakty</string>
|
||||
<string name="export_contacts">Exportovať kontakty</string>
|
||||
<string name="import_contacts_from_vcf">Importovať kontakty z .vcf súboru</string>
|
||||
<string name="export_contacts_to_vcf">Exportovať kontakty do .vcf súboru</string>
|
||||
<string name="target_contact_source">Cieľový zdroj kontaktov</string>
|
||||
<string name="include_contact_sources">Zahrnúť zdroje kontaktov</string>
|
||||
<string name="filename_without_vcf">Názov súboru (bez .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">Aplikácia pre správu vašich kontaktov bez reklám.</string>
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<resources>
|
||||
<string name="app_name">Simple Contacts</string>
|
||||
<string name="app_launcher_name">Kontakter</string>
|
||||
<string name="address">Adress</string>
|
||||
<string name="inserting">Lägger till…</string>
|
||||
<string name="updating">Uppdaterar…</string>
|
||||
<string name="missing_contact_account">Du måste välja vilket konto kontakten ska tillhöra</string>
|
||||
|
||||
<string name="new_contact">Ny kontakt</string>
|
||||
<string name="edit_contact">Redigera kontakt</string>
|
||||
<string name="select_contact">Välj kontakt</string>
|
||||
<string name="select_contacts">Välj kontakter</string>
|
||||
<string name="first_name">Förnamn</string>
|
||||
<string name="middle_name">Mellannamn</string>
|
||||
<string name="surname">Efternamn</string>
|
||||
|
||||
<!-- Photo -->
|
||||
<string name="take_photo">Ta foto</string>
|
||||
<string name="choose_photo">Välj foto</string>
|
||||
<string name="remove_photo">Ta bort foto</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="call_contact_on_click">Ring kontakter när jag trycker på dem</string>
|
||||
<string name="start_name_with_surname">Visa efternamn först</string>
|
||||
<string name="show_phone_numbers">Visa telefonnummer i huvudvyn</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">E-post</string>
|
||||
<string name="home">Hem</string>
|
||||
<string name="work">Arbete</string>
|
||||
<string name="other">Annat</string>
|
||||
|
||||
<!-- Phone numbers -->
|
||||
<string name="number">Nummer</string>
|
||||
<string name="mobile">Mobil</string>
|
||||
<string name="main_number">Primärt nummer</string>
|
||||
<string name="work_fax">Arbetsfax</string>
|
||||
<string name="home_fax">Hemfax</string>
|
||||
<string name="pager">Personsökare</string>
|
||||
<string name="no_phone_number_found">Inget telefonnummer hittades</string>
|
||||
|
||||
<!-- Events -->
|
||||
<string name="birthday">Födelsedag</string>
|
||||
<string name="anniversary">Årsdag</string>
|
||||
|
||||
<!-- Favorites -->
|
||||
<string name="no_favorites">Det verkar som att du inte har lagt till några favoritkontakter ännu.</string>
|
||||
<string name="add_favorites">Lägg till favoriter</string>
|
||||
<string name="add_to_favorites">Lägg till i favoriter</string>
|
||||
<string name="remove_from_favorites">Ta bort från favoriter</string>
|
||||
|
||||
<!-- Search -->
|
||||
<string name="search_contacts">Sök efter kontakter</string>
|
||||
<string name="search_favorites">Sök efter favoriter</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Importera kontakter</string>
|
||||
<string name="export_contacts">Exportera kontakter</string>
|
||||
<string name="import_contacts_from_vcf">Importera kontakter från en .vcf-fil</string>
|
||||
<string name="export_contacts_to_vcf">Exportera kontakter till en .vcf-fil</string>
|
||||
<string name="target_contact_source">Målkontaktkälla</string>
|
||||
<string name="include_contact_sources">Inkludera kontaktkällor</string>
|
||||
<string name="filename_without_vcf">Filnamn (utan .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">En app för att hantera dina kontakter utan reklam.</string>
|
||||
<string name="app_long_description">
|
||||
En enkel app för att skapa och hantera kontakter från olika källor. Kontakterna kan lagras bara på din enhet, men kan också synkroniseras med ditt Google-konto eller andra konton. Du kan visa dina favoritkontakter i en separat lista.
|
||||
|
||||
Du kan också använda den för att hantera dina kontakters e-postadresser och händelser. Den kan sortera och filtrera efter flera parametrar. Den kan också visa efternamn först.
|
||||
|
||||
Innehåller ingen reklam eller onödiga behörigheter. Den har helt öppen källkod och anpassningsbara färger.
|
||||
|
||||
Denna app är bara en del av en större serie appar. Du hittar resten av dem på http://www.simplemobiletools.com
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Default colors -->
|
||||
<color name="default_text_color">@color/theme_dark_text_color</color>
|
||||
<color name="default_background_color">@color/theme_dark_background_color</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<string name="call_contact_on_click">Call contact on click</string>
|
||||
<string name="start_name_with_surname">Start name with surname</string>
|
||||
<string name="show_phone_numbers">Show phone numbers on the main screen</string>
|
||||
<string name="show_contact_avatars">Show contact avatars</string>
|
||||
|
||||
<!-- Emails -->
|
||||
<string name="email">Email</string>
|
||||
|
@ -53,6 +54,15 @@
|
|||
<string name="search_contacts">Search contacts</string>
|
||||
<string name="search_favorites">Search favorites</string>
|
||||
|
||||
<!-- Export / Import -->
|
||||
<string name="import_contacts">Import contacts</string>
|
||||
<string name="export_contacts">Export contacts</string>
|
||||
<string name="import_contacts_from_vcf">Import contacts from a .vcf file</string>
|
||||
<string name="export_contacts_to_vcf">Export contacts to a .vcf file</string>
|
||||
<string name="target_contact_source">Target contact source</string>
|
||||
<string name="include_contact_sources">Include contact sources</string>
|
||||
<string name="filename_without_vcf">Filename (without .vcf)</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">A contacts app for managing your contacts without ads.</string>
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
<external-path name="external_files" path="."/>
|
||||
<root-path name="external_files" path="/storage/" />
|
||||
<cache-path name="cache_files" path="my_cache/" />
|
||||
<cache-path name="shared_contacts" path="contacts/"/>
|
||||
</paths>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.10'
|
||||
ext.kotlin_version = '1.2.20'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
|
||||
|
||||
You can use it for managing user emails and events too. It has the ability to sort/filter by multiple parameters, optionally display surname as the first name.
|
||||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
|
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 86 KiB |
|
@ -0,0 +1 @@
|
|||
A contacts app for managing your contacts without ads.
|
|
@ -0,0 +1 @@
|
|||
Simple Contacts
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
|
||||
|
|
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 87 KiB |