diff --git a/build.gradle b/build.gradle
index b0be9299ab..d003f75977 100644
--- a/build.gradle
+++ b/build.gradle
@@ -27,6 +27,7 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'witness'
apply plugin: 'io.fabric'
+apply plugin: 'kotlin-kapt'
repositories {
mavenLocal()
@@ -112,6 +113,7 @@ dependencies {
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
implementation 'com.github.bumptech.glide:glide:4.5.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
+ kapt 'com.github.bumptech.glide:compiler:4.5.0'
implementation 'com.makeramen:roundedimageview:2.1.0'
implementation 'com.pnikosis:materialish-progress:1.5'
implementation 'org.greenrobot:eventbus:3.0.0'
diff --git a/res/drawable/ic_circle_check.xml b/res/drawable/ic_circle_check.xml
new file mode 100644
index 0000000000..a258b5c30c
--- /dev/null
+++ b/res/drawable/ic_circle_check.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/res/drawable/ic_circle_dot_dot_dot.xml b/res/drawable/ic_circle_dot_dot_dot.xml
new file mode 100644
index 0000000000..28ecaa1fbe
--- /dev/null
+++ b/res/drawable/ic_circle_dot_dot_dot.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/res/drawable/ic_filled_circle_check.xml b/res/drawable/ic_filled_circle_check.xml
new file mode 100644
index 0000000000..c45e603d4c
--- /dev/null
+++ b/res/drawable/ic_filled_circle_check.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/res/layout/view_conversation.xml b/res/layout/view_conversation.xml
index 531313a97c..07d499dfb3 100644
--- a/res/layout/view_conversation.xml
+++ b/res/layout/view_conversation.xml
@@ -1,8 +1,8 @@
-
@@ -70,22 +70,35 @@
android:orientation="horizontal"
android:gravity="center_vertical">
-
+ android:layout_height="wrap_content">
+
+
+
+
+
+
- {
val result = ScanQRCodeWrapperFragment()
result.delegate = activity
- result.message = "Users can share their QR code by going into their account settings and tapping "Share QR Code""
+ result.message = "Users can share their QR code by going into their account settings and tapping \"Share QR Code\""
result
}
else -> throw IllegalStateException()
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
index da88fbef8b..ab740cfc2f 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.loki.redesign.activities
+import android.arch.lifecycle.Observer
import android.content.Intent
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
@@ -14,9 +15,12 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
+import org.thoughtcrime.securesms.mms.GlideApp
+import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.TextSecurePreferences
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener {
+ private lateinit var glide: GlideRequests
// region Lifecycle
constructor() : super()
@@ -27,14 +31,25 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
setContentView(R.layout.activity_home)
// Set title
supportActionBar!!.title = "Messages"
+ // Set up Glide
+ glide = GlideApp.with(this)
// Set up recycler view
val cursor = DatabaseFactory.getThreadDatabase(this).conversationList
- val conversationAdapter = HomeAdapter(this, cursor)
- conversationAdapter.conversationClickListener = this
- recyclerView.adapter = conversationAdapter
+ val homeAdapter = HomeAdapter(this, cursor)
+ homeAdapter.glide = glide
+ homeAdapter.conversationClickListener = this
+ recyclerView.adapter = homeAdapter
recyclerView.layoutManager = LinearLayoutManager(this)
// Set up new conversation button
newConversationButton.setOnClickListener { createPrivateChat() }
+ // Set up typing observer
+ ApplicationContext.getInstance(this).typingStatusRepository.typingThreads.observe(this, object : Observer> {
+
+ override fun onChanged(threadIDs: Set?) {
+ val adapter = recyclerView.adapter as HomeAdapter
+ adapter.typingThreadIDs = threadIDs ?: setOf()
+ }
+ })
// Set up public chats and RSS feeds if needed
if (TextSecurePreferences.getLocalNumber(this) != null) {
val application = ApplicationContext.getInstance(this)
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeAdapter.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeAdapter.kt
index 1ebf829b04..b5eb5885d7 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeAdapter.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeAdapter.kt
@@ -8,9 +8,13 @@ import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
+import org.thoughtcrime.securesms.mms.GlideRequests
class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter(context, cursor) {
private val threadDatabase = DatabaseFactory.getThreadDatabase(context)
+ lateinit var glide: GlideRequests
+ var typingThreadIDs = setOf()
+ set(value) { field = value; notifyDataSetChanged() }
var conversationClickListener: ConversationClickListener? = null
class ViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view)
@@ -26,7 +30,9 @@ class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter<
}
override fun onBindItemViewHolder(viewHolder: ViewHolder, cursor: Cursor) {
- viewHolder.view.bind(getThread(cursor)!!)
+ val thread = getThread(cursor)!!
+ val isTyping = typingThreadIDs.contains(thread.threadId)
+ viewHolder.view.bind(thread, isTyping, glide)
}
private fun getThread(cursor: Cursor): ThreadRecord? {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/ConversationView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/ConversationView.kt
index c40c313984..f145b4a9f5 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/views/ConversationView.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/views/ConversationView.kt
@@ -11,6 +11,7 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.LokiAPIUtilities.populateUserHexEncodedPublicKeyCacheIfNeeded
import org.thoughtcrime.securesms.loki.MentionUtilities.highlightMentions
+import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils
import org.whispersystems.signalservice.loki.api.LokiAPI
import java.util.*
@@ -43,7 +44,7 @@ class ConversationView : LinearLayout {
// endregion
// region Updating
- fun bind(thread: ThreadRecord) {
+ fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
this.thread = thread
populateUserHexEncodedPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a terrible place to do this
unreadMessagesIndicatorView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE
@@ -58,6 +59,7 @@ class ConversationView : LinearLayout {
profilePictureView.additionalHexEncodedPublicKey = null
profilePictureView.isRSSFeed = false
}
+ profilePictureView.glide = glide
profilePictureView.update()
val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else thread.recipient.name
displayNameTextView.text = senderDisplayName
@@ -66,6 +68,21 @@ class ConversationView : LinearLayout {
val snippet = highlightMentions(rawSnippet, thread.threadId, context)
snippetTextView.text = snippet
snippetTextView.typeface = if (thread.unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT
+ snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE
+ if (isTyping) {
+ typingIndicatorView.startAnimation()
+ } else {
+ typingIndicatorView.stopAnimation()
+ }
+ typingIndicatorView.visibility = if (isTyping) View.VISIBLE else View.GONE
+ statusIndicatorImageView.visibility = View.VISIBLE
+ when {
+ !thread.isOutgoing || thread.isVerificationStatusChange -> statusIndicatorImageView.visibility = View.GONE
+ thread.isFailed -> statusIndicatorImageView.setImageResource(R.drawable.ic_error)
+ thread.isPending -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot)
+ thread.isRemoteRead -> statusIndicatorImageView.setImageResource(R.drawable.ic_filled_circle_check)
+ else -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_check)
+ }
}
// endregion
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
index f2b0b237f1..351d314bdb 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
@@ -4,11 +4,17 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
+import android.widget.ImageView
import android.widget.RelativeLayout
+import com.bumptech.glide.load.engine.DiskCacheStrategy
import kotlinx.android.synthetic.main.view_profile_picture.view.*
import network.loki.messenger.R
+import org.thoughtcrime.securesms.database.Address
+import org.thoughtcrime.securesms.mms.GlideRequests
+import org.thoughtcrime.securesms.recipients.Recipient
class ProfilePictureView : RelativeLayout {
+ lateinit var glide: GlideRequests
var hexEncodedPublicKey: String? = null
var additionalHexEncodedPublicKey: String? = null
var isRSSFeed = false
@@ -44,6 +50,22 @@ class ProfilePictureView : RelativeLayout {
doubleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE
singleModeImageView.visibility = if (additionalHexEncodedPublicKey == null && !isRSSFeed) View.VISIBLE else View.INVISIBLE
rssTextView.visibility = if (isRSSFeed) View.VISIBLE else View.INVISIBLE
+ fun setProfilePictureIfNeeded(imageView: ImageView, hexEncodedPublicKey: String) {
+ glide.clear(imageView)
+ if (hexEncodedPublicKey.isNotEmpty()) {
+ val signalProfilePicture = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).contactPhoto
+ if (signalProfilePicture != null) {
+ glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
+ } else {
+ imageView.setImageDrawable(null)
+ }
+ } else {
+ imageView.setImageDrawable(null)
+ }
+ }
+ setProfilePictureIfNeeded(singleModeImageView, hexEncodedPublicKey)
+ setProfilePictureIfNeeded(doubleModeImageView1, hexEncodedPublicKey)
+ setProfilePictureIfNeeded(doubleModeImageView2, additionalHexEncodedPublicKey ?: "")
}
// endregion
}
\ No newline at end of file