Add InviteFriendFragment

This commit is contained in:
Andrew 2024-05-04 20:07:20 +09:30
parent ddf0b027d7
commit 75ea086032
9 changed files with 171 additions and 47 deletions

View File

@ -0,0 +1,115 @@
package org.thoughtcrime.securesms.conversation.start
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.preferences.copyPublicKey
import org.thoughtcrime.securesms.preferences.sendInvitation
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.components.AppBar
import org.thoughtcrime.securesms.ui.components.OnPrimaryButtons
import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.SmallButtons
import org.thoughtcrime.securesms.ui.components.TemporaryStateButton
@AndroidEntryPoint
class InviteFriendFragment : Fragment() {
lateinit var delegate: NewConversationDelegate
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
setContent {
AppTheme {
InviteFriend()
}
}
}
@Composable
private fun InviteFriend() {
Column(modifier = Modifier.background(MaterialTheme.colors.surface)) {
AppBar("Invite a Friend", onBack = { delegate.onDialogBackPressed() }, onClose = { delegate.onDialogClosePressed() })
Column(
modifier = Modifier.padding(horizontal = 24.dp),
verticalArrangement = spacedBy(10.dp)
) {
Box(
modifier = Modifier
.border(
width = 1.dp,
color = classicDarkColors[5],
shape = RoundedCornerShape(size = 13.dp)
)
.fillMaxWidth()
.wrapContentHeight()
) {
Text(
TextSecurePreferences.getLocalNumber(LocalContext.current)!!,
textAlign = TextAlign.Center,
modifier = Modifier
.align(Alignment.Center)
.padding(22.dp)
)
}
Text(
"Invite your friend to chat with you on Session by sharing your Account ID with them.",
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 8.dp)
)
OnPrimaryButtons {
SmallButtons {
Row(horizontalArrangement = spacedBy(20.dp)) {
OutlineButton(
modifier = Modifier.weight(1f),
onClick = { requireContext().sendInvitation() }
) {
Text(stringResource(R.string.share))
}
TemporaryStateButton { source, temporary ->
OutlineButton(
modifier = Modifier.weight(1f),
interactionSource = source,
onClick = { requireContext().copyPublicKey() },
) {
AnimatedVisibility(temporary) { Text(stringResource(R.string.copied)) }
AnimatedVisibility(!temporary) { Text(stringResource(R.string.copy)) }
}
}
}
}
}
}
}
}
}

View File

@ -35,7 +35,7 @@ class NewConversationFragment : BottomSheetDialogFragment(), NewConversationDele
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
replaceFragment( replaceFragment(
fragment = NewConversationHomeFragment().apply { delegate = this@NewConversationFragment }, fragment = NewConversationHomeFragment().also { it.delegate = this },
fragmentKey = NewConversationHomeFragment::class.java.simpleName fragmentKey = NewConversationHomeFragment::class.java.simpleName
) )
} }
@ -44,8 +44,7 @@ class NewConversationFragment : BottomSheetDialogFragment(), NewConversationDele
val dialog = BottomSheetDialog(requireContext(), R.style.Theme_Session_BottomSheet) val dialog = BottomSheetDialog(requireContext(), R.style.Theme_Session_BottomSheet)
dialog.setOnShowListener { dialog.setOnShowListener {
val bottomSheetDialog = it as BottomSheetDialog val bottomSheetDialog = it as BottomSheetDialog
val parentLayout = val parentLayout = bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
parentLayout?.let { parentLayout?.let {
val behaviour = BottomSheetBehavior.from(it) val behaviour = BottomSheetBehavior.from(it)
val layoutParams = it.layoutParams val layoutParams = it.layoutParams
@ -58,15 +57,15 @@ class NewConversationFragment : BottomSheetDialogFragment(), NewConversationDele
} }
override fun onNewMessageSelected() { override fun onNewMessageSelected() {
replaceFragment(NewMessageFragment().apply { delegate = this@NewConversationFragment }) replaceFragment(NewMessageFragment().also { it.delegate = this })
} }
override fun onCreateGroupSelected() { override fun onCreateGroupSelected() {
replaceFragment(CreateGroupFragment().apply { delegate = this@NewConversationFragment }) replaceFragment(CreateGroupFragment().also { it.delegate = this })
} }
override fun onJoinCommunitySelected() { override fun onJoinCommunitySelected() {
replaceFragment(JoinCommunityFragment().apply { delegate = this@NewConversationFragment }) replaceFragment(JoinCommunityFragment().also { it.delegate = this })
} }
override fun onContactSelected(address: String) { override fun onContactSelected(address: String) {
@ -81,7 +80,7 @@ class NewConversationFragment : BottomSheetDialogFragment(), NewConversationDele
} }
override fun onInviteFriend() { override fun onInviteFriend() {
replaceFragment(InviteFriendFragment().also { it.delegate = this })
} }
override fun onDialogClosePressed() { override fun onDialogClosePressed() {

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@ -51,7 +52,7 @@ class NewConversationHomeFragment : Fragment() {
@Composable @Composable
fun NewConversationScreen() { fun NewConversationScreen() {
Column { Column(modifier = Modifier.background(MaterialTheme.colors.surface)) {
AppBar(stringResource(R.string.dialog_new_conversation_title), onClose = { delegate.onDialogClosePressed() }) AppBar(stringResource(R.string.dialog_new_conversation_title), onClose = { delegate.onDialogClosePressed() })
ItemButton(textId = R.string.messageNew, icon = R.drawable.ic_message) { delegate.onNewMessageSelected() } ItemButton(textId = R.string.messageNew, icon = R.drawable.ic_message) { delegate.onNewMessageSelected() }
Divider(modifier = Modifier.padding(start = 80.dp)) Divider(modifier = Modifier.padding(start = 80.dp))

View File

@ -30,6 +30,8 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
@ -61,10 +63,8 @@ class NewMessageFragment : Fragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lifecycleScope.launch { lifecycleScope.launch {
viewModel.event.collect { viewModel.event.filterIsInstance<Event.Success>().collect {
when (it) { createPrivateChat(it.key)
is Event.Success -> createPrivateChat(it.key)
}
} }
} }
} }
@ -121,7 +121,7 @@ private fun NewMessage(
) { ) {
val pagerState = rememberPagerState { TITLES.size } val pagerState = rememberPagerState { TITLES.size }
Column(modifier = Modifier.background(MaterialTheme.colors.background)) { Column(modifier = Modifier.background(MaterialTheme.colors.surface)) {
AppBar(stringResource(R.string.messageNew), onClose = { onClose() }, onBack = { onBack() }) AppBar(stringResource(R.string.messageNew), onClose = { onClose() }, onBack = { onBack() })
SessionTabRow(pagerState, TITLES) SessionTabRow(pagerState, TITLES)
HorizontalPager(pagerState) { HorizontalPager(pagerState) {

View File

@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.ui.SessionShieldIcon
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.components.DestructiveButtons
import org.thoughtcrime.securesms.ui.components.OutlineButton import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.QrImage import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.components.SmallButtons import org.thoughtcrime.securesms.ui.components.SmallButtons
@ -205,9 +206,7 @@ fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8) Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPassword), style = MaterialTheme.typography.h8)
Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription)) Text(text = stringResource(R.string.recoveryPasswordHideRecoveryPasswordDescription))
} }
CompositionLocalProvider( DestructiveButtons {
LocalButtonColor provides colorDestructive,
) {
OutlineButton( OutlineButton(
textId = R.string.hide, textId = R.string.hide,
modifier = Modifier modifier = Modifier

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.preferences
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
@ -353,32 +351,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
} }
.execute() .execute()
} }
private fun copyPublicKey() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", hexEncodedPublicKey)
clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
private fun sharePublicKey() {
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, hexEncodedPublicKey)
type = "text/plain"
}.let { Intent.createChooser(it, getString(R.string.share)) }
.let(::startActivity)
}
private fun sendInvitation() {
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is $hexEncodedPublicKey !")
type = "text/plain"
}.let { Intent.createChooser(it, getString(R.string.activity_settings_invite_button_title)) }
.let(::startActivity)
}
// endregion // endregion
private inner class DisplayNameEditActionModeCallback: ActionMode.Callback { private inner class DisplayNameEditActionModeCallback: ActionMode.Callback {
@ -420,7 +392,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
) { ) {
OutlineButton( OutlineButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
onClick = ::sharePublicKey onClick = { sendInvitation() }
) { ) {
Text(stringResource(R.string.share)) Text(stringResource(R.string.share))
} }
@ -499,4 +471,4 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
fun createReceiver(update: () -> Unit) = object : BroadcastReceiver() { fun createReceiver(update: () -> Unit) = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { update() } override fun onReceive(context: Context, intent: Intent) { update() }
} }

View File

@ -0,0 +1,32 @@
package org.thoughtcrime.securesms.preferences
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.widget.Toast
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
fun Context.sendInvitation() {
Intent().apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_TEXT,
"""Hey, I've been using Session to chat with complete privacy and security. Come join me! My Account ID is
${TextSecurePreferences.getLocalNumber(this@sendInvitation)}
Download it at https://getsession.org/"""
)
type = "text/plain"
}.let { Intent.createChooser(it, getString(R.string.activity_settings_invite_button_title)) }
.let(::startActivity)
}
fun Context.copyPublicKey() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Session ID", TextSecurePreferences.getLocalNumber(this))
clipboard.setPrimaryClip(clip)
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}

View File

@ -80,6 +80,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.runIf import org.session.libsession.utilities.runIf
import org.thoughtcrime.securesms.components.ProfilePictureView import org.thoughtcrime.securesms.components.ProfilePictureView
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
import org.thoughtcrime.securesms.ui.components.OnPrimaryButtons
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -219,7 +220,7 @@ fun CellWithPaddingAndMargin(
margin: Dp = 32.dp, margin: Dp = 32.dp,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
CompositionLocalProvider(LocalButtonColor provides MaterialTheme.colors.onPrimary) { OnPrimaryButtons {
Card( Card(
backgroundColor = LocalCellColor.current, backgroundColor = LocalCellColor.current,
shape = RoundedCornerShape(16.dp), shape = RoundedCornerShape(16.dp),

View File

@ -172,3 +172,8 @@ fun SmallButtons(content: @Composable () -> Unit) {
fun DestructiveButtons(content: @Composable () -> Unit) { fun DestructiveButtons(content: @Composable () -> Unit) {
CompositionLocalProvider(LocalButtonColor provides colorDestructive) { content() } CompositionLocalProvider(LocalButtonColor provides colorDestructive) { content() }
} }
@Composable
fun OnPrimaryButtons(content: @Composable () -> Unit) {
CompositionLocalProvider(LocalButtonColor provides MaterialTheme.colors.onPrimary) { content() }
}