diff --git a/TODO.md b/TODO.md index 25a9ca9..ea0833e 100644 --- a/TODO.md +++ b/TODO.md @@ -5,8 +5,6 @@ - Select best friend to remind of - Keep track of reminded? - Stats -- Quick increment - -# 0.5 -- Auto increment from call/sms log? \ No newline at end of file +- Quick increment +- Store interactions, not only call/sms log diff --git a/app/src/main/java/fr/plnech/dunbar/Extensions.kt b/app/src/main/java/fr/plnech/dunbar/Extensions.kt index 627e2d3..0d3f6b5 100644 --- a/app/src/main/java/fr/plnech/dunbar/Extensions.kt +++ b/app/src/main/java/fr/plnech/dunbar/Extensions.kt @@ -1,8 +1,12 @@ package fr.plnech.dunbar -import android.content.Intent -import android.net.Uri +import android.content.ContentUris +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.provider.ContactsContract import fr.plnech.dunbar.model.Friend +import java.io.IOException fun String.plural(count: Int = 1): String { return when { @@ -11,17 +15,57 @@ fun String.plural(count: Int = 1): String { } } -fun Friend.smsIntent(): Intent? { - return phone?.let { - val uri = Uri.parse("smsto:${it}") - Intent(Intent.ACTION_SENDTO, uri).apply { - putExtra("sms_body", "Hey ${this@smsIntent.firstName}, how are you?") +fun Context.fetchFriends(): List { + val friends = mutableListOf() + + //TODO: More efficient query using non-null parameters + contentResolver.query( + ContactsContract.CommonDataKinds.Contactables.CONTENT_URI, + null, + null, + null, + null + )?.let { + while (it.moveToNext()) { + mutableMapOf().also { map -> + val indexId = it.getColumnIndex(ContactsContract.Contacts._ID) + val id = it.getLong(indexId) + val photo: Bitmap? = getFriendPhoto(id) + + it.columnNames.forEach { name -> + val index = it.getColumnIndex(name) + map[name] = it.getString(index) + } + friends.add(Friend(map, photo)) + } } + it.close() } + + return friends.sortedByDescending { it.timesContacted } } -fun Friend.callIntent(): Intent? { - return phone?.let { - Intent(Intent.ACTION_DIAL, Uri.parse("tel:${it}")) + +private fun Context.getFriendPhoto(id: Long): Bitmap? { + var photo: Bitmap? = null + + try { + val inputStream = ContactsContract.Contacts.openContactPhotoInputStream( + contentResolver, + ContentUris.withAppendedId( + ContactsContract.Contacts.CONTENT_URI, + java.lang.Long.valueOf(id) + ) + ) + + if (inputStream != null) { + photo = BitmapFactory.decodeStream(inputStream) + } + + inputStream?.close() + } catch (e: IOException) { + e.printStackTrace() } + println("Photo for $id: $photo") + return photo } \ No newline at end of file diff --git a/app/src/main/java/fr/plnech/dunbar/data/Friends.kt b/app/src/main/java/fr/plnech/dunbar/data/Friends.kt new file mode 100644 index 0000000..7e90ff0 --- /dev/null +++ b/app/src/main/java/fr/plnech/dunbar/data/Friends.kt @@ -0,0 +1,10 @@ +package fr.plnech.dunbar.data + +import android.content.ContentUris +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.provider.ContactsContract +import fr.plnech.dunbar.model.Friend +import java.io.IOException + diff --git a/app/src/main/java/fr/plnech/dunbar/model/Friend.kt b/app/src/main/java/fr/plnech/dunbar/model/Friend.kt index f2d4af5..4adabf4 100644 --- a/app/src/main/java/fr/plnech/dunbar/model/Friend.kt +++ b/app/src/main/java/fr/plnech/dunbar/model/Friend.kt @@ -1,6 +1,8 @@ package fr.plnech.dunbar.model +import android.content.Intent import android.graphics.Bitmap +import android.net.Uri import android.provider.ContactsContract.Contacts import java.util.* @@ -19,7 +21,7 @@ data class Friend(val map: MutableMap, val photo: Bitmap?) { get() = map[Contacts._ID]!!.toInt() val phone: String? - get() = if(map[Contacts.HAS_PHONE_NUMBER] == "1") map["data1"] else null + get() = if (map[Contacts.HAS_PHONE_NUMBER] == "1") map["data1"] else null val lastTimeStamp = map[Contacts.LAST_TIME_CONTACTED]!!.toLong() @@ -34,4 +36,22 @@ data class Friend(val map: MutableMap, val photo: Bitmap?) { val visibleOutsideSearch: String? get() = map[Contacts.IN_DEFAULT_DIRECTORY] -} \ No newline at end of file + + fun smsIntent(): Intent? { + return phone?.let { + val uri = Uri.parse("smsto:${it}") + Intent(Intent.ACTION_SENDTO, uri).apply { + putExtra("sms_body", "Hey ${firstName}, how are you?") + } + } + } + + fun callIntent(): Intent? { + return phone?.let { + Intent(Intent.ACTION_DIAL, Uri.parse("tel:${it}")) + } + } + + +} + diff --git a/app/src/main/java/fr/plnech/dunbar/notif/FriendReminder.kt b/app/src/main/java/fr/plnech/dunbar/notif/FriendReminder.kt index 6a6a33f..7432922 100644 --- a/app/src/main/java/fr/plnech/dunbar/notif/FriendReminder.kt +++ b/app/src/main/java/fr/plnech/dunbar/notif/FriendReminder.kt @@ -5,7 +5,6 @@ import android.content.Context import android.os.Build import androidx.core.app.NotificationCompat import fr.plnech.dunbar.model.Friend -import fr.plnech.dunbar.smsIntent val CHANNEL_ID = "dunbar" diff --git a/app/src/main/java/fr/plnech/dunbar/ui/FriendsActivity.kt b/app/src/main/java/fr/plnech/dunbar/ui/FriendsActivity.kt index 2d6097b..a4b1f70 100644 --- a/app/src/main/java/fr/plnech/dunbar/ui/FriendsActivity.kt +++ b/app/src/main/java/fr/plnech/dunbar/ui/FriendsActivity.kt @@ -1,10 +1,6 @@ package fr.plnech.dunbar.ui -import android.content.ContentUris -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.os.Bundle -import android.provider.ContactsContract import android.view.Menu import android.view.MenuItem import android.widget.Toast @@ -13,18 +9,19 @@ import androidx.core.app.NotificationManagerCompat import androidx.recyclerview.widget.LinearLayoutManager import fr.plnech.dunbar.R import fr.plnech.dunbar.data.Messages +import fr.plnech.dunbar.fetchFriends import fr.plnech.dunbar.model.Friend import fr.plnech.dunbar.notif.FriendReminder import fr.plnech.dunbar.plural import kotlinx.android.synthetic.main.activity_friends.* import kotlinx.android.synthetic.main.content_friends.* -import java.io.IOException +import java.util.* class FriendsActivity : AppCompatActivity() { private lateinit var messages: Messages - private val friends = mutableListOf() + private var friends = listOf() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -35,6 +32,7 @@ class FriendsActivity : AppCompatActivity() { fab.setOnClickListener { view -> reloadFriends() + notifyFriend() } reloadFriends() @@ -43,11 +41,23 @@ class FriendsActivity : AppCompatActivity() { private fun notifyFriend() { val idNotif = 0 - with(NotificationManagerCompat.from(this)) { - val notification = - FriendReminder(applicationContext).createNotification(friends[0], this@FriendsActivity) - notification?.let { - notify(idNotif, notification) + val notRecentlyTalked = friends.filter { + it.lastDate != null && (Date().time - it.lastDate!!.time) > 1 * 60 * 60 * 1000 + } + println("${notRecentlyTalked.size} good old friends I could talk to.") + val friend = notRecentlyTalked.minBy { it.timesContacted } + + + friend?.let { + with(NotificationManagerCompat.from(this)) { + val notification = + FriendReminder(applicationContext).createNotification( + friend, + this@FriendsActivity + ) + notification?.let { + notify(idNotif, notification) + } } } } @@ -69,7 +79,7 @@ class FriendsActivity : AppCompatActivity() { } private fun reloadFriends() { - fetchFriends() + friends = fetchFriends() displayFriends() // fetchMessages() } @@ -82,76 +92,15 @@ class FriendsActivity : AppCompatActivity() { ).show() } - private fun fetchFriends() { - val newFriends = mutableListOf() - - contentResolver.query( - ContactsContract.CommonDataKinds.Contactables.CONTENT_URI, - null, - null, - null, - null - )?.let { - while (it.moveToNext()) { - mutableMapOf().also { map -> - val indexId = it.getColumnIndex(ContactsContract.Contacts._ID) - val id = it.getLong(indexId) - val photo: Bitmap? = getPhoto(id) - - it.columnNames.forEach { name -> - val index = it.getColumnIndex(name) - map[name] = it.getString(index) - } - newFriends.add(Friend(map, photo)) - } - } - it.close() - - friends.clear() - friends.addAll(newFriends) - } - - friends.sortBy { it.timesContacted } - } private fun displayFriends() { val adapter = FriendsAdapter(friends) + val nbFriends = adapter.itemCount friendsList.layoutManager = LinearLayoutManager(this) friendsList.setHasFixedSize(true) friendsList.adapter = adapter -// friendsList.setOnItemClickListener { parent, view, position, id -> -// val contact: String = adapter.getItem(position)?.toString() ?: "NONE" -// -// Snackbar.make(friendsList, contact, Snackbar.LENGTH_INDEFINITE).show() -// Toast.makeText(this, contact, Toast.LENGTH_LONG).show() -// } - - val nbFriends = adapter.itemCount welcomeTitle.text = "$nbFriends ${"friend".plural(nbFriends)} on Dunbar" } - private fun getPhoto(id: Long): Bitmap? { - var photo: Bitmap? = null - - try { - val inputStream = ContactsContract.Contacts.openContactPhotoInputStream( - contentResolver, - ContentUris.withAppendedId( - ContactsContract.Contacts.CONTENT_URI, - java.lang.Long.valueOf(id) - ) - ) - - if (inputStream != null) { - photo = BitmapFactory.decodeStream(inputStream) - } - - inputStream?.close() - } catch (e: IOException) { - e.printStackTrace() - } - println("Photo for $id: $photo") - return photo - } } diff --git a/app/src/main/java/fr/plnech/dunbar/ui/FriendsAdapter.kt b/app/src/main/java/fr/plnech/dunbar/ui/FriendsAdapter.kt index 4af11de..456bd9e 100644 --- a/app/src/main/java/fr/plnech/dunbar/ui/FriendsAdapter.kt +++ b/app/src/main/java/fr/plnech/dunbar/ui/FriendsAdapter.kt @@ -7,9 +7,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.recyclerview.widget.RecyclerView import fr.plnech.dunbar.R -import fr.plnech.dunbar.callIntent import fr.plnech.dunbar.model.Friend -import fr.plnech.dunbar.smsIntent import kotlinx.android.synthetic.main.contact.view.*