Skip to content
This repository was archived by the owner on Aug 16, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions .idea/runConfigurations.xml

This file was deleted.

36 changes: 19 additions & 17 deletions app-start/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 30
compileSdkVersion 32
defaultConfig {
applicationId 'com.example.android.people'
minSdkVersion 30
targetSdkVersion 30
targetSdkVersion 32
versionCode 1
versionName '1.0'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
Expand All @@ -39,30 +40,31 @@ android {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'androidx.fragment:fragment-ktx:1.2.4'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
implementation 'androidx.appcompat:appcompat:1.6.0-alpha01'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.recyclerview:recyclerview:1.2.1'

def lifecycle_version = '2.2.0'
def lifecycle_version = '2.4.1'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
testImplementation 'androidx.arch.core:core-testing:2.1.0'

implementation 'com.google.android.material:material:1.1.0'
implementation 'com.google.android.material:material:1.5.0'

implementation 'com.github.bumptech.glide:glide:4.9.0'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

testImplementation 'org.robolectric:robolectric:4.2'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation 'androidx.test.ext:truth:1.2.0'
testImplementation 'org.robolectric:robolectric:4.2.1'
testImplementation 'androidx.test.ext:junit:1.1.3'
testImplementation 'androidx.test.espresso:espresso-core:3.4.0'
testImplementation 'androidx.test.ext:truth:1.4.0'
testImplementation 'com.google.truth:truth:1.0'
}
6 changes: 4 additions & 2 deletions app-start/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
-->
<activity
android:name="com.example.android.people.MainActivity"
android:launchMode="singleTop">
android:launchMode="singleTop"
android:exported="true">
<!--
This activity is the one that's shown on the launcher.
-->
Expand Down Expand Up @@ -77,7 +78,8 @@

<!-- TODO 6: This is our extended Bubble. -->
<activity
android:name="com.example.android.people.BubbleActivity">
android:name="com.example.android.people.BubbleActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import android.content.Context
import android.content.Intent
import com.example.android.people.data.ChatRepository
import com.example.android.people.data.DefaultChatRepository
import com.example.android.people.data.Message
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.Default

/**
* Handles the "Reply" action in the chat notification.
Expand All @@ -32,7 +35,9 @@ class ReplyReceiver : BroadcastReceiver() {
}

override fun onReceive(context: Context, intent: Intent) {
val repository: ChatRepository = DefaultChatRepository.getInstance(context)
val repository: ChatRepository = DefaultChatRepository.getInstance(context,
CoroutineScope(Default)
)

val results = RemoteInput.getResultsFromIntent(intent) ?: return
// The message typed in the notification reply.
Expand All @@ -41,7 +46,11 @@ class ReplyReceiver : BroadcastReceiver() {
val chatId = uri.lastPathSegment?.toLong() ?: return

if (chatId > 0 && !input.isNullOrBlank()) {
repository.sendMessage(chatId, input.toString(), null, null)
repository.send(Message(
chatId,
sender = 0,
text = input.toString()
))
// We should update the notification so that the user can see that the reply has been
// sent.
repository.updateNotification(chatId)
Expand Down
10 changes: 7 additions & 3 deletions app-start/src/main/java/com/example/android/people/data/Chat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package com.example.android.people.data

typealias ChatThreadListener = (List<Message>) -> Unit

@Suppress("NAME_SHADOWING")
class Chat(val contact: Contact) {

private val listeners = mutableListOf<ChatThreadListener>()
Expand All @@ -36,9 +37,12 @@ class Chat(val contact: Contact) {
listeners.remove(listener)
}

fun addMessage(builder: Message.Builder) {
builder.id = _messages.last().id + 1
_messages.add(builder.build())
fun addMessage(message: Message) {
val message = message.copy(id = _messages.last().id+1)
require(message.isAvailable) {
"message is not available $message"
}
_messages.add(message)
listeners.forEach { listener -> listener(_messages) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@
package com.example.android.people.data

import android.content.Context
import android.net.Uri
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

interface ChatRepository {
fun getContacts(): LiveData<List<Contact>>
fun findContact(id: Long): LiveData<Contact?>
fun findMessages(id: Long): LiveData<List<Message>>
fun sendMessage(id: Long, text: String, photoUri: Uri?, photoMimeType: String?)
fun send(message: Message)
fun updateNotification(id: Long)
fun activateChat(id: Long)
fun deactivateChat(id: Long)
Expand All @@ -37,17 +38,17 @@ interface ChatRepository {

class DefaultChatRepository internal constructor(
private val notificationHelper: NotificationHelper,
private val executor: Executor
private val scope: CoroutineScope
) : ChatRepository {

companion object {
private var instance: DefaultChatRepository? = null

fun getInstance(context: Context): DefaultChatRepository {
fun getInstance(context: Context,scope:CoroutineScope): DefaultChatRepository {
return instance ?: synchronized(this) {
instance ?: DefaultChatRepository(
NotificationHelper(context),
Executors.newFixedThreadPool(4)
scope
).also {
instance = it
}
Expand Down Expand Up @@ -99,21 +100,18 @@ class DefaultChatRepository internal constructor(
}
}



@MainThread
override fun sendMessage(id: Long, text: String, photoUri: Uri?, photoMimeType: String?) {
val chat = chats.getValue(id)
chat.addMessage(Message.Builder().apply {
sender = 0L // User
this.text = text
timestamp = System.currentTimeMillis()
this.photo = photoUri
this.photoMimeType = photoMimeType
})
executor.execute {
override fun send(message: Message) {
val chat = chats.getValue(message.id)
chat.addMessage(message)

scope.launch (Dispatchers.Default){
// The animal is typing...
Thread.sleep(5000L)
delay(5000)
// Receive a reply.
chat.addMessage(chat.contact.reply(text))
chat.addMessage(chat.contact.reply(message.text))
// Show notification if the chat is not on the foreground.
if (chat.contact.id != currentChat) {
notificationHelper.showNotification(chat, false)
Expand All @@ -139,7 +137,7 @@ class DefaultChatRepository internal constructor(

override fun showAsBubble(id: Long) {
val chat = chats.getValue(id)
executor.execute {
scope.launch(Dispatchers.Main) {
notificationHelper.showNotification(chat, true)
}
}
Expand Down
74 changes: 41 additions & 33 deletions app-start/src/main/java/com/example/android/people/data/Contact.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,61 +17,69 @@ package com.example.android.people.data

import androidx.core.net.toUri

abstract class Contact(

sealed class Contact(
val id: Long,
val name: String,
val icon: String
) {
val name: String = this::class.simpleName!!
val iconUri = "content://com.example.android.people/icon/$id".toUri()
val shortcutId = "contact_$id"
protected val defaultMessage get() = Message(
sender = this.id,
timestamp =System.currentTimeMillis()
)
abstract fun reply(text: String): Message

companion object {
val CONTACTS = listOf(
object : Contact(1L, "Cat", "cat.jpg") {
override fun reply(text: String) = buildReply().apply { this.text = "Meow" }
},
object : Contact(2L, "Dog", "dog.jpg") {
override fun reply(text: String) = buildReply().apply { this.text = "Woof woof!!" }
},
object : Contact(3L, "Parrot", "parrot.jpg") {
override fun reply(text: String) = buildReply().apply { this.text = text }
},
object : Contact(4L, "Sheep", "sheep.jpg") {
override fun reply(text: String) = buildReply().apply {
this.text = "Look at me!"
photo = "content://com.example.android.people/photo/sheep_full.jpg".toUri()
photoMimeType = "image/jpeg"
}
}
)

object Cat:Contact(1,"cat.jpg") {
override fun reply(text: String): Message
= defaultMessage.copy(text = "Meow")
}

val iconUri = "content://com.example.android.people/icon/$id".toUri()
object Dog:Contact(2,"dog.jpg") {
override fun reply(text: String)
= defaultMessage.copy(text = "Woof woof!!")
}

val shortcutId = "contact_$id"
object Parrot:Contact(3,"parrot.jpg") {
override fun reply(text: String): Message
= defaultMessage.copy(text = text)
}

fun buildReply() = Message.Builder().apply {
sender = this@Contact.id
timestamp = System.currentTimeMillis()
object Sheep:Contact(4,"sheep.jpg") {
override fun reply(text: String): Message
= defaultMessage.copy(
text = "Look at me!",
photoUri = "content://com.example.android.people/photo/sheep_full.jpg".toUri(),
photoMimeType = "image/jpeg",
)
}

abstract fun reply(text: String): Message.Builder
companion object {
val CONTACTS = listOf(
Cat,Dog,Parrot,Sheep
)
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Contact
if (other !is Contact) return false

if (id != other.id) return false
if (name != other.name) return false
if (icon != other.icon) return false
if (name != other.name) return false
if (iconUri != other.iconUri) return false
if (shortcutId != other.shortcutId) return false

return true
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + icon.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + iconUri.hashCode()
result = 31 * result + shortcutId.hashCode()
return result
}
}
Loading