Skip to content

Commit b1f224e

Browse files
committed
feat(feature:send-money): sharing invite for contacts not found on upi
1 parent bca89e6 commit b1f224e

File tree

11 files changed

+751
-5
lines changed

11 files changed

+751
-5
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.feature.send.money
11+
12+
import android.content.Context
13+
14+
/**
15+
* Android-specific context provider for sharing functionality
16+
*/
17+
object ContextProvider {
18+
private var applicationContext: Context? = null
19+
20+
/**
21+
* Initialize the context provider with the application context
22+
* This should be called from the Application class or MainActivity
23+
*/
24+
fun init(context: Context) {
25+
applicationContext = context.applicationContext
26+
}
27+
28+
/**
29+
* Get the application context
30+
*/
31+
fun getContext(): Context? = applicationContext
32+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.feature.send.money
11+
12+
import android.content.Context
13+
import android.content.Intent
14+
import android.net.Uri
15+
import androidx.core.net.toUri
16+
17+
/**
18+
* Android-specific implementation for sharing invite messages via SMS and WhatsApp
19+
*/
20+
class AndroidSharingHelper(private val context: android.content.Context) : SharingHelper {
21+
22+
/**
23+
* Shares the invite message via the selected sharing option
24+
*
25+
* @param sharingOption The selected sharing method (SMS or WhatsApp)
26+
* @param phoneNumber The target phone number
27+
* @param message The invite message to share
28+
*/
29+
override fun shareInviteMessage(
30+
sharingOption: SharingOption,
31+
phoneNumber: String,
32+
message: String,
33+
) {
34+
when (sharingOption) {
35+
SharingOption.SMS -> shareViaSms(phoneNumber, message)
36+
SharingOption.WHATSAPP -> shareViaWhatsApp(phoneNumber, message)
37+
}
38+
}
39+
40+
/**
41+
* Shares the invite message via SMS using Android's SMS intent
42+
*/
43+
private fun shareViaSms(phoneNumber: String, message: String) {
44+
try {
45+
val intent = Intent(Intent.ACTION_SENDTO).apply {
46+
data = "smsto:$phoneNumber".toUri()
47+
putExtra("sms_body", message)
48+
}
49+
50+
if (intent.resolveActivity(context.packageManager) != null) {
51+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
52+
context.startActivity(intent)
53+
} else {
54+
val fallbackIntent = Intent(Intent.ACTION_SEND).apply {
55+
type = "text/plain"
56+
putExtra(Intent.EXTRA_TEXT, message)
57+
putExtra("address", phoneNumber)
58+
}
59+
fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
60+
context.startActivity(Intent.createChooser(fallbackIntent, "Send SMS"))
61+
}
62+
} catch (e: Exception) {
63+
println("Error sharing via SMS: ${e.message}")
64+
}
65+
}
66+
67+
/**
68+
* Shares the invite message via WhatsApp using WhatsApp's intent
69+
*/
70+
private fun shareViaWhatsApp(phoneNumber: String, message: String) {
71+
try {
72+
val cleanPhoneNumber = phoneNumber.replace("+", "").replace(" ", "")
73+
74+
val intent = Intent(Intent.ACTION_VIEW).apply {
75+
data =
76+
"https://api.whatsapp.com/send?phone=$cleanPhoneNumber&text=${Uri.encode(message)}".toUri()
77+
}
78+
79+
// Check if WhatsApp is installed
80+
if (intent.resolveActivity(context.packageManager) != null) {
81+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
82+
context.startActivity(intent)
83+
} else {
84+
// Fallback to generic sharing if WhatsApp is not installed
85+
val fallbackIntent = Intent(Intent.ACTION_SEND).apply {
86+
type = "text/plain"
87+
putExtra(Intent.EXTRA_TEXT, message)
88+
putExtra("address", phoneNumber)
89+
}
90+
fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
91+
context.startActivity(Intent.createChooser(fallbackIntent, "Share via"))
92+
}
93+
} catch (e: Exception) {
94+
// Handle any exceptions (e.g., WhatsApp not installed)
95+
println("Error sharing via WhatsApp: ${e.message}")
96+
}
97+
}
98+
}
99+
100+
/**
101+
* Android-specific implementation of the factory function
102+
*/
103+
actual fun getSharingHelper(): SharingHelper {
104+
val context = ContextProvider.getContext()
105+
return if (context != null) {
106+
AndroidSharingHelper(context)
107+
} else {
108+
DefaultSharingHelper
109+
}
110+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Copyright 2024 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
9+
*/
10+
package org.mifospay.feature.send.money
11+
12+
import androidx.compose.foundation.layout.Arrangement
13+
import androidx.compose.foundation.layout.Column
14+
import androidx.compose.foundation.layout.Spacer
15+
import androidx.compose.foundation.layout.fillMaxWidth
16+
import androidx.compose.foundation.layout.height
17+
import androidx.compose.foundation.layout.padding
18+
import androidx.compose.foundation.layout.size
19+
import androidx.compose.material3.AlertDialog
20+
import androidx.compose.material3.ButtonDefaults
21+
import androidx.compose.material3.Card
22+
import androidx.compose.material3.CardDefaults
23+
import androidx.compose.material3.Icon
24+
import androidx.compose.material3.IconButton
25+
import androidx.compose.material3.IconButtonDefaults
26+
import androidx.compose.material3.ListItem
27+
import androidx.compose.material3.ListItemDefaults
28+
import androidx.compose.material3.Text
29+
import androidx.compose.material3.TextButton
30+
import androidx.compose.runtime.Composable
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.text.style.TextAlign
33+
import androidx.compose.ui.unit.dp
34+
import org.mifospay.core.designsystem.component.MifosButton
35+
import org.mifospay.core.designsystem.icon.MifosIcons
36+
import template.core.base.designsystem.theme.KptTheme
37+
38+
@Composable
39+
fun ContactNotFoundDialog(
40+
showDialog: Boolean,
41+
selectedOption: SharingOption,
42+
onOptionSelected: (SharingOption) -> Unit,
43+
onNotNowClick: () -> Unit,
44+
onSetAsDefaultClick: () -> Unit,
45+
onDismiss: () -> Unit,
46+
modifier: Modifier = Modifier,
47+
) {
48+
if (showDialog) {
49+
AlertDialog(
50+
onDismissRequest = onDismiss,
51+
modifier = modifier,
52+
title = {
53+
Text(
54+
text = "Set preferred ways to share",
55+
style = KptTheme.typography.headlineSmall,
56+
color = KptTheme.colorScheme.onSurface,
57+
)
58+
},
59+
text = {
60+
Column(
61+
modifier = Modifier.fillMaxWidth(),
62+
verticalArrangement = Arrangement.spacedBy(KptTheme.spacing.md),
63+
) {
64+
Text(
65+
text = "Your choice will be recommended for future sharing of any contact.",
66+
style = KptTheme.typography.bodyMedium,
67+
color = KptTheme.colorScheme.onSurface.copy(alpha = 0.7f),
68+
textAlign = TextAlign.Start,
69+
)
70+
71+
Spacer(modifier = Modifier.height(KptTheme.spacing.sm))
72+
73+
SharingOptionItem(
74+
option = SharingOption.WHATSAPP,
75+
title = "WhatsApp",
76+
subtitle = "Share via WhatsApp",
77+
isSelected = selectedOption == SharingOption.WHATSAPP,
78+
onClick = { onOptionSelected(SharingOption.WHATSAPP) },
79+
)
80+
81+
SharingOptionItem(
82+
option = SharingOption.SMS,
83+
title = "SMS",
84+
subtitle = "Share via SMS",
85+
isSelected = selectedOption == SharingOption.SMS,
86+
onClick = { onOptionSelected(SharingOption.SMS) },
87+
)
88+
}
89+
},
90+
confirmButton = {
91+
MifosButton(
92+
onClick = onSetAsDefaultClick,
93+
modifier = Modifier.fillMaxWidth(),
94+
colors = ButtonDefaults.buttonColors(
95+
containerColor = KptTheme.colorScheme.primary,
96+
contentColor = KptTheme.colorScheme.onPrimary,
97+
),
98+
) {
99+
Text(
100+
text = "Set as default",
101+
style = KptTheme.typography.labelLarge,
102+
)
103+
}
104+
},
105+
dismissButton = {
106+
TextButton(
107+
onClick = onNotNowClick,
108+
modifier = Modifier.fillMaxWidth(),
109+
) {
110+
Text(
111+
text = "Not now",
112+
style = KptTheme.typography.labelLarge,
113+
color = KptTheme.colorScheme.primary,
114+
)
115+
}
116+
},
117+
containerColor = KptTheme.colorScheme.surface,
118+
shape = KptTheme.shapes.large,
119+
)
120+
}
121+
}
122+
123+
@Composable
124+
private fun SharingOptionItem(
125+
option: SharingOption,
126+
title: String,
127+
subtitle: String,
128+
isSelected: Boolean,
129+
onClick: () -> Unit,
130+
modifier: Modifier = Modifier,
131+
) {
132+
Card(
133+
modifier = modifier
134+
.fillMaxWidth()
135+
.padding(vertical = 2.dp),
136+
shape = KptTheme.shapes.small,
137+
colors = CardDefaults.cardColors(
138+
containerColor = if (isSelected) {
139+
KptTheme.colorScheme.primaryContainer
140+
} else {
141+
KptTheme.colorScheme.surface
142+
},
143+
),
144+
elevation = CardDefaults.cardElevation(
145+
defaultElevation = if (isSelected) 2.dp else 0.dp,
146+
),
147+
) {
148+
ListItem(
149+
headlineContent = {
150+
Text(
151+
text = title,
152+
style = KptTheme.typography.bodyLarge,
153+
color = if (isSelected) {
154+
KptTheme.colorScheme.onPrimaryContainer
155+
} else {
156+
KptTheme.colorScheme.onSurface
157+
},
158+
)
159+
},
160+
supportingContent = {
161+
Text(
162+
text = subtitle,
163+
style = KptTheme.typography.bodyMedium,
164+
color = if (isSelected) {
165+
KptTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f)
166+
} else {
167+
KptTheme.colorScheme.onSurface.copy(alpha = 0.7f)
168+
},
169+
)
170+
},
171+
leadingContent = {
172+
Icon(
173+
imageVector = when (option) {
174+
SharingOption.WHATSAPP -> MifosIcons.Share
175+
SharingOption.SMS -> MifosIcons.Share
176+
},
177+
contentDescription = title,
178+
modifier = Modifier.size(24.dp),
179+
tint = if (isSelected) {
180+
KptTheme.colorScheme.onPrimaryContainer
181+
} else {
182+
KptTheme.colorScheme.onSurface.copy(alpha = 0.7f)
183+
},
184+
)
185+
},
186+
trailingContent = {
187+
IconButton(
188+
onClick = onClick,
189+
colors = IconButtonDefaults.iconButtonColors(
190+
contentColor = if (isSelected) {
191+
KptTheme.colorScheme.onPrimaryContainer
192+
} else {
193+
KptTheme.colorScheme.onSurface.copy(alpha = 0.7f)
194+
},
195+
),
196+
) {
197+
Icon(
198+
imageVector = if (isSelected) {
199+
MifosIcons.RadioButtonChecked
200+
} else {
201+
MifosIcons.RadioButtonUnchecked
202+
},
203+
contentDescription = if (isSelected) "Selected" else "Not selected",
204+
modifier = Modifier.size(20.dp),
205+
)
206+
}
207+
},
208+
colors = ListItemDefaults.colors(
209+
containerColor = androidx.compose.ui.graphics.Color.Transparent,
210+
),
211+
)
212+
}
213+
}

0 commit comments

Comments
 (0)