Skip to content

Commit

Permalink
get the example working with groups
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Jan 26, 2024
1 parent d89f11c commit ac8a2d5
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 44 deletions.
16 changes: 13 additions & 3 deletions example/src/main/java/org/xmtp/android/example/ClientManager.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.xmtp.android.example

import android.content.Context
import androidx.annotation.UiThread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
Expand All @@ -13,7 +14,16 @@ import org.xmtp.android.library.messages.PrivateKeyBundleV1Builder

object ClientManager {

val CLIENT_OPTIONS = ClientOptions(api = ClientOptions.Api(XMTPEnvironment.DEV, appVersion = "XMTPAndroidExample/v1.0.0"))
fun clientOptions(appContext: Context?): ClientOptions {
return ClientOptions(
api = ClientOptions.Api(
XMTPEnvironment.DEV,
appVersion = "XMTPAndroidExample/v1.0.0"
),
enableLibXmtpV3 = true,
appContext = appContext
)
}

private val _clientState = MutableStateFlow<ClientState>(ClientState.Unknown)
val clientState: StateFlow<ClientState> = _clientState
Expand All @@ -28,13 +38,13 @@ object ClientManager {
}

@UiThread
fun createClient(encodedPrivateKeyData: String) {
fun createClient(encodedPrivateKeyData: String, appContext: Context) {
if (clientState.value is ClientState.Ready) return
GlobalScope.launch(Dispatchers.IO) {
try {
val v1Bundle =
PrivateKeyBundleV1Builder.fromEncodedData(data = encodedPrivateKeyData)
_client = Client().buildFrom(v1Bundle, CLIENT_OPTIONS)
_client = Client().buildFrom(v1Bundle, clientOptions(appContext))
_clientState.value = ClientState.Ready
} catch (e: Exception) {
_clientState.value = ClientState.Error(e.localizedMessage.orEmpty())
Expand Down
17 changes: 16 additions & 1 deletion example/src/main/java/org/xmtp/android/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.xmtp.android.example.conversation.ConversationDetailActivity
import org.xmtp.android.example.conversation.ConversationsAdapter
import org.xmtp.android.example.conversation.ConversationsClickListener
import org.xmtp.android.example.conversation.NewConversationBottomSheet
import org.xmtp.android.example.conversation.NewGroupBottomSheet
import org.xmtp.android.example.databinding.ActivityMainBinding
import org.xmtp.android.example.pushnotifications.PushNotificationTokenManager
import org.xmtp.android.example.utils.KeyUtil
Expand All @@ -35,6 +36,7 @@ class MainActivity : AppCompatActivity(),
private lateinit var accountManager: AccountManager
private lateinit var adapter: ConversationsAdapter
private var bottomSheet: NewConversationBottomSheet? = null
private var groupBottomSheet: NewGroupBottomSheet? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -48,7 +50,7 @@ class MainActivity : AppCompatActivity(),
return
}

ClientManager.createClient(keys)
ClientManager.createClient(keys, this)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Expand All @@ -67,6 +69,10 @@ class MainActivity : AppCompatActivity(),
openConversationDetail()
}

binding.groupFab.setOnClickListener {
openGroupDetail()
}

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
ClientManager.clientState.collect(::ensureClientState)
Expand All @@ -86,6 +92,7 @@ class MainActivity : AppCompatActivity(),

override fun onDestroy() {
bottomSheet?.dismiss()
groupBottomSheet?.dismiss()
super.onDestroy()
}

Expand Down Expand Up @@ -127,6 +134,7 @@ class MainActivity : AppCompatActivity(),
is ClientManager.ClientState.Ready -> {
viewModel.fetchConversations()
binding.fab.visibility = View.VISIBLE
binding.groupFab.visibility = View.VISIBLE
}
is ClientManager.ClientState.Error -> showError(clientState.message)
is ClientManager.ClientState.Unknown -> Unit
Expand Down Expand Up @@ -193,4 +201,11 @@ class MainActivity : AppCompatActivity(),
NewConversationBottomSheet.TAG
)
}
private fun openGroupDetail() {
groupBottomSheet = NewGroupBottomSheet.newInstance()
groupBottomSheet?.show(
supportFragmentManager,
NewGroupBottomSheet.TAG
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class MainViewModel : ViewModel() {
viewModelScope.launch(Dispatchers.IO) {
val listItems = mutableListOf<MainListItem>()
try {
val conversations = ClientManager.client.conversations.list()
val conversations = ClientManager.client.conversations.list(includeGroups = true)
PushNotificationTokenManager.xmtpPush.subscribe(conversations.map { it.topic })
listItems.addAll(
conversations.map { conversation ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.xmtp.android.example.connect

import android.app.Application
import android.content.Context
import android.net.Uri
import androidx.annotation.UiThread
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.walletconnect.wcmodal.client.Modal
Expand All @@ -21,7 +24,7 @@ import org.xmtp.android.library.XMTPException
import org.xmtp.android.library.messages.PrivateKeyBuilder
import org.xmtp.android.library.messages.PrivateKeyBundleV1Builder

class ConnectWalletViewModel : ViewModel() {
class ConnectWalletViewModel(application: Application) : AndroidViewModel(application) {

private val chains: List<ChainSelectionUi> =
Chains.values().map { it.toChainUiState() }
Expand Down Expand Up @@ -84,7 +87,7 @@ class ConnectWalletViewModel : ViewModel() {
_uiState.value = ConnectUiState.Loading
try {
val wallet = PrivateKeyBuilder()
val client = Client().create(wallet, ClientManager.CLIENT_OPTIONS)
val client = Client().create(wallet, ClientManager.clientOptions(getApplication()))
_uiState.value = ConnectUiState.Success(
wallet.getAddress(),
PrivateKeyBundleV1Builder.encodeData(client.privateKeyBundleV1)
Expand All @@ -108,7 +111,7 @@ class ConnectWalletViewModel : ViewModel() {
it.copy(showWallet = true, uri = uri)
}
}
val client = Client().create(wallet, ClientManager.CLIENT_OPTIONS)
val client = Client().create(wallet, ClientManager.clientOptions(getApplication()))
_uiState.value = ConnectUiState.Success(
wallet.getAddress(),
PrivateKeyBundleV1Builder.encodeData(client.privateKeyBundleV1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ class NewConversationViewModel : ViewModel() {
}
}

@UiThread
fun createGroup(addresses: List<String>) {
_uiState.value = UiState.Loading
viewModelScope.launch(Dispatchers.IO) {
try {
val group = ClientManager.client.conversations.newGroup(addresses)
_uiState.value = UiState.Success(Conversation.Group(group))
} catch (e: Exception) {
_uiState.value = UiState.Error(e.localizedMessage.orEmpty())
}
}
}

sealed class UiState {
object Unknown : UiState()
object Loading : UiState()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.xmtp.android.example.conversation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.launch
import org.xmtp.android.example.R
import org.xmtp.android.example.databinding.BottomSheetNewConversationBinding
import java.util.regex.Pattern

class NewGroupBottomSheet : BottomSheetDialogFragment() {

private val viewModel: NewConversationViewModel by viewModels()
private var _binding: BottomSheetNewConversationBinding? = null
private val binding get() = _binding!!

companion object {
const val TAG = "NewGroupBottomSheet"

private val ADDRESS_PATTERN = Pattern.compile("^0x[a-fA-F0-9]{40}\$")

fun newInstance(): NewGroupBottomSheet {
return NewGroupBottomSheet()
}
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetNewConversationBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect(::ensureUiState)
}
}

binding.addressInput.addTextChangedListener {
if (viewModel.uiState.value is NewConversationViewModel.UiState.Loading) return@addTextChangedListener
val input = binding.addressInput.text.trim()
val matcher = ADDRESS_PATTERN.matcher(input)
if (matcher.matches()) {
viewModel.createGroup(listOf(input.toString()))
}
}
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

private fun ensureUiState(uiState: NewConversationViewModel.UiState) {
when (uiState) {
is NewConversationViewModel.UiState.Error -> {
binding.addressInput.isEnabled = true
binding.progress.visibility = View.GONE
showError(uiState.message)
}
NewConversationViewModel.UiState.Loading -> {
binding.addressInput.isEnabled = false
binding.progress.visibility = View.VISIBLE
}
is NewConversationViewModel.UiState.Success -> {
startActivity(
ConversationDetailActivity.intent(
requireContext(),
topic = uiState.conversation.topic,
peerAddress = uiState.conversation.peerAddress
)
)
dismiss()
}
NewConversationViewModel.UiState.Unknown -> Unit
}
}

private fun showError(message: String) {
val error = message.ifBlank { resources.getString(R.string.error) }
Toast.makeText(requireContext(), error, Toast.LENGTH_SHORT).show()
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.xmtp.android.example.pushnotifications

import android.Manifest
import android.app.PendingIntent
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
Expand Down Expand Up @@ -51,7 +54,7 @@ class PushNotificationsService : FirebaseMessagingService() {
}

GlobalScope.launch(Dispatchers.Main) {
ClientManager.createClient(keysData)
ClientManager.createClient(keysData, applicationContext)
}
val conversation = ClientManager.client.fetchConversation(topic)
if (conversation == null) {
Expand Down Expand Up @@ -88,6 +91,20 @@ class PushNotificationsService : FirebaseMessagingService() {

// Use the URL as the ID for now until one is passed back from the server.
NotificationManagerCompat.from(this).apply {
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
notify(topic.hashCode(), builder.build())
}
}
Expand Down
9 changes: 9 additions & 0 deletions example/src/main/res/drawable/ic_group_add_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M22,9l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0l0,-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M8,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4S4,5.79 4,8S5.79,12 8,12z"/>
<path android:fillColor="@android:color/white" android:pathData="M8,13c-2.67,0 -8,1.34 -8,4v3h16v-3C16,14.34 10.67,13 8,13z"/>
<path android:fillColor="@android:color/white" android:pathData="M12.51,4.05C13.43,5.11 14,6.49 14,8s-0.57,2.89 -1.49,3.95C14.47,11.7 16,10.04 16,8S14.47,4.3 12.51,4.05z"/>
<path android:fillColor="@android:color/white" android:pathData="M16.53,13.83C17.42,14.66 18,15.7 18,17v3h2v-3C20,15.55 18.41,14.49 16.53,13.83z"/>
</vector>
12 changes: 12 additions & 0 deletions example/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/group_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/padding"
android:contentDescription="@string/new_message"
android:src="@drawable/ic_group_add_24"
android:tint="@android:color/white"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/fab"
app:layout_constraintEnd_toEndOf="parent" />

<ProgressBar
android:id="@+id/progress"
style="@style/Widget.AppCompat.ProgressBar"
Expand Down
Loading

0 comments on commit ac8a2d5

Please sign in to comment.