Hook up home screen logic

pull/58/head
Niels Andriesse 5 years ago
parent 95dc4e6590
commit ded709a58b

@ -27,6 +27,7 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'witness' apply plugin: 'witness'
apply plugin: 'io.fabric' apply plugin: 'io.fabric'
apply plugin: 'kotlin-kapt'
repositories { repositories {
mavenLocal() mavenLocal()
@ -112,6 +113,7 @@ dependencies {
implementation 'com.github.chrisbanes:PhotoView:2.1.3' implementation 'com.github.chrisbanes:PhotoView:2.1.3'
implementation 'com.github.bumptech.glide:glide:4.5.0' implementation 'com.github.bumptech.glide:glide:4.5.0'
annotationProcessor 'com.github.bumptech.glide:compiler: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.makeramen:roundedimageview:2.1.0'
implementation 'com.pnikosis:materialish-progress:1.5' implementation 'com.pnikosis:materialish-progress:1.5'
implementation 'org.greenrobot:eventbus:3.0.0' implementation 'org.greenrobot:eventbus:3.0.0'

@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="15"
android:viewportWidth="15" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000"
android:pathData="M7.5,7.5m-6.5,0a6.5,6.5 0,1 1,13 0a6.5,6.5 0,1 1,-13 0"
android:strokeColor="#FFFFFF" android:strokeWidth="1"/>
<path android:fillColor="#FFFFFF" android:pathData="M4.77,7.61c-0.15,-0.15 -0.38,-0.15 -0.53,0c-0.15,0.15 -0.15,0.38 0,0.53l1.88,1.88c0.15,0.15 0.38,0.15 0.53,0l4.13,-4.12c0.15,-0.15 0.15,-0.38 0,-0.53c-0.15,-0.15 -0.38,-0.15 -0.53,0L6.38,9.22L4.77,7.61z"/>
</vector>

@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="15"
android:viewportWidth="15" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000"
android:pathData="M7.5,7.5m-6.5,0a6.5,6.5 0,1 1,13 0a6.5,6.5 0,1 1,-13 0"
android:strokeColor="#FFFFFF" android:strokeWidth="1"/>
<path android:fillColor="#FFFFFF" android:pathData="M4.76,7.47c0,-0.32 0.26,-0.57 0.57,-0.57c0.32,0 0.57,0.25 0.57,0.57c0,0.31 -0.25,0.57 -0.57,0.57C5.02,8.04 4.76,7.78 4.76,7.47zM7.04,7.47c0,-0.32 0.26,-0.57 0.57,-0.57c0.32,0 0.57,0.25 0.57,0.57c0,0.31 -0.25,0.57 -0.57,0.57C7.3,8.04 7.04,7.78 7.04,7.47zM9.32,7.47c0,-0.32 0.26,-0.57 0.57,-0.57c0.32,0 0.57,0.25 0.57,0.57c0,0.31 -0.25,0.57 -0.57,0.57C9.58,8.04 9.32,7.78 9.32,7.47z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:height="24dp" android:viewportHeight="13"
android:viewportWidth="13" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M6.5,6.5m-6.5,0a6.5,6.5 0,1 1,13 0a6.5,6.5 0,1 1,-13 0"/>
<path android:fillColor="#231F20" android:pathData="M3.77,6.61c-0.15,-0.15 -0.38,-0.15 -0.53,0c-0.15,0.15 -0.15,0.38 0,0.53l1.88,1.88c0.15,0.15 0.38,0.15 0.53,0L9.78,4.9c0.15,-0.15 0.15,-0.38 0,-0.53c-0.15,-0.15 -0.38,-0.15 -0.53,0L5.38,8.22L3.77,6.61z"/>
</vector>

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/conversation_view_background" android:background="@drawable/conversation_view_background"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"> android:gravity="center_vertical">
@ -70,22 +70,35 @@
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"> android:gravity="center_vertical">
<TextView <RelativeLayout
android:id="@+id/snippetTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:maxLines="1"
android:ellipsize="end" <TextView
android:textSize="@dimen/medium_font_size" android:id="@+id/snippetTextView"
android:textColor="@color/text" android:layout_width="wrap_content"
android:text="Sorry, gotta go fight crime again" /> android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:text="Sorry, gotta go fight crime again" />
<org.thoughtcrime.securesms.components.TypingIndicatorView
android:id="@+id/typingIndicatorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:typingIndicator_tint="@color/text" />
</RelativeLayout>
<View <View
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<View <ImageView
android:id="@+id/statusIndicatorImageView" android:id="@+id/statusIndicatorImageView"
android:layout_width="@dimen/conversation_view_status_indicator_size" android:layout_width="@dimen/conversation_view_status_indicator_size"
android:layout_height="@dimen/conversation_view_status_indicator_size" android:layout_height="@dimen/conversation_view_status_indicator_size"

@ -78,7 +78,7 @@ private class CreatePrivateChatActivityAdapter(val activity: CreatePrivateChatAc
1 -> { 1 -> {
val result = ScanQRCodeWrapperFragment() val result = ScanQRCodeWrapperFragment()
result.delegate = activity result.delegate = activity
result.message = "Users can share their QR code by going into their account settings and tapping &quot;Share QR Code&quot;" result.message = "Users can share their QR code by going into their account settings and tapping \"Share QR Code\""
result result
} }
else -> throw IllegalStateException() else -> throw IllegalStateException()

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.loki.redesign.activities package org.thoughtcrime.securesms.loki.redesign.activities
import android.arch.lifecycle.Observer
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager 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.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.redesign.utilities.push import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView 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 import org.thoughtcrime.securesms.util.TextSecurePreferences
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener { class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener {
private lateinit var glide: GlideRequests
// region Lifecycle // region Lifecycle
constructor() : super() constructor() : super()
@ -27,14 +31,25 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
setContentView(R.layout.activity_home) setContentView(R.layout.activity_home)
// Set title // Set title
supportActionBar!!.title = "Messages" supportActionBar!!.title = "Messages"
// Set up Glide
glide = GlideApp.with(this)
// Set up recycler view // Set up recycler view
val cursor = DatabaseFactory.getThreadDatabase(this).conversationList val cursor = DatabaseFactory.getThreadDatabase(this).conversationList
val conversationAdapter = HomeAdapter(this, cursor) val homeAdapter = HomeAdapter(this, cursor)
conversationAdapter.conversationClickListener = this homeAdapter.glide = glide
recyclerView.adapter = conversationAdapter homeAdapter.conversationClickListener = this
recyclerView.adapter = homeAdapter
recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.layoutManager = LinearLayoutManager(this)
// Set up new conversation button // Set up new conversation button
newConversationButton.setOnClickListener { createPrivateChat() } newConversationButton.setOnClickListener { createPrivateChat() }
// Set up typing observer
ApplicationContext.getInstance(this).typingStatusRepository.typingThreads.observe(this, object : Observer<Set<Long>> {
override fun onChanged(threadIDs: Set<Long>?) {
val adapter = recyclerView.adapter as HomeAdapter
adapter.typingThreadIDs = threadIDs ?: setOf()
}
})
// Set up public chats and RSS feeds if needed // Set up public chats and RSS feeds if needed
if (TextSecurePreferences.getLocalNumber(this) != null) { if (TextSecurePreferences.getLocalNumber(this) != null) {
val application = ApplicationContext.getInstance(this) val application = ApplicationContext.getInstance(this)

@ -8,9 +8,13 @@ import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
import org.thoughtcrime.securesms.mms.GlideRequests
class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter<HomeAdapter.ViewHolder>(context, cursor) { class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter<HomeAdapter.ViewHolder>(context, cursor) {
private val threadDatabase = DatabaseFactory.getThreadDatabase(context) private val threadDatabase = DatabaseFactory.getThreadDatabase(context)
lateinit var glide: GlideRequests
var typingThreadIDs = setOf<Long>()
set(value) { field = value; notifyDataSetChanged() }
var conversationClickListener: ConversationClickListener? = null var conversationClickListener: ConversationClickListener? = null
class ViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view) 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) { 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? { private fun getThread(cursor: Cursor): ThreadRecord? {

@ -11,6 +11,7 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.LokiAPIUtilities.populateUserHexEncodedPublicKeyCacheIfNeeded import org.thoughtcrime.securesms.loki.LokiAPIUtilities.populateUserHexEncodedPublicKeyCacheIfNeeded
import org.thoughtcrime.securesms.loki.MentionUtilities.highlightMentions import org.thoughtcrime.securesms.loki.MentionUtilities.highlightMentions
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.whispersystems.signalservice.loki.api.LokiAPI import org.whispersystems.signalservice.loki.api.LokiAPI
import java.util.* import java.util.*
@ -43,7 +44,7 @@ class ConversationView : LinearLayout {
// endregion // endregion
// region Updating // region Updating
fun bind(thread: ThreadRecord) { fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
this.thread = thread this.thread = thread
populateUserHexEncodedPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a terrible place to do this populateUserHexEncodedPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a terrible place to do this
unreadMessagesIndicatorView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE unreadMessagesIndicatorView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE
@ -58,6 +59,7 @@ class ConversationView : LinearLayout {
profilePictureView.additionalHexEncodedPublicKey = null profilePictureView.additionalHexEncodedPublicKey = null
profilePictureView.isRSSFeed = false profilePictureView.isRSSFeed = false
} }
profilePictureView.glide = glide
profilePictureView.update() profilePictureView.update()
val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else thread.recipient.name val senderDisplayName = if (thread.recipient.isLocalNumber) context.getString(R.string.note_to_self) else thread.recipient.name
displayNameTextView.text = senderDisplayName displayNameTextView.text = senderDisplayName
@ -66,6 +68,21 @@ class ConversationView : LinearLayout {
val snippet = highlightMentions(rawSnippet, thread.threadId, context) val snippet = highlightMentions(rawSnippet, thread.threadId, context)
snippetTextView.text = snippet snippetTextView.text = snippet
snippetTextView.typeface = if (thread.unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT 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 // endregion
} }

@ -4,11 +4,17 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import com.bumptech.glide.load.engine.DiskCacheStrategy
import kotlinx.android.synthetic.main.view_profile_picture.view.* import kotlinx.android.synthetic.main.view_profile_picture.view.*
import network.loki.messenger.R 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 { class ProfilePictureView : RelativeLayout {
lateinit var glide: GlideRequests
var hexEncodedPublicKey: String? = null var hexEncodedPublicKey: String? = null
var additionalHexEncodedPublicKey: String? = null var additionalHexEncodedPublicKey: String? = null
var isRSSFeed = false var isRSSFeed = false
@ -44,6 +50,22 @@ class ProfilePictureView : RelativeLayout {
doubleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE doubleModeImageViewContainer.visibility = if (additionalHexEncodedPublicKey != null && !isRSSFeed) View.VISIBLE else View.INVISIBLE
singleModeImageView.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 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 // endregion
} }
Loading…
Cancel
Save