Skip to content
Merged
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
1 change: 1 addition & 0 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ kotlin{
androidMain.dependencies {
api(libs.androidx.metrics)
implementation(libs.androidx.compose.runtime)
implementation(libs.google.oss.licenses)
}
commonMain.dependencies {
api(projects.core.designsystem)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.ui.util

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.core.content.FileProvider
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.decodeToImageBitmap
import java.io.File
import java.io.FileOutputStream
import java.io.IOException

actual object ShareUtils {

private var activityProvider: () -> Activity = {
throw IllegalArgumentException(
"You need to implement the 'activityProvider' to provide the required Activity. " +
"Just make sure to set a valid activity using " +
"the 'setActivityProvider()' method.",
)
}

fun setActivityProvider(provider: () -> Activity) {
activityProvider = provider
}

actual fun shareText(text: String) {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, text)
}
val intentChooser = Intent.createChooser(intent, null)
activityProvider.invoke().startActivity(intentChooser)
}

actual suspend fun shareImage(title: String, image: ImageBitmap) {
val context = activityProvider.invoke().application.baseContext

val uri = saveImage(image.asAndroidBitmap(), context)

val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "image/png")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}

val shareIntent = Intent.createChooser(sendIntent, title)
activityProvider.invoke().startActivity(shareIntent)
}

@OptIn(ExperimentalResourceApi::class)
actual suspend fun shareImage(title: String, byte: ByteArray) {
Log.d("Sharing QR Code", " $title, size: ${byte.size} bytes")
val context = activityProvider.invoke().application.baseContext
val imageBitmap = byte.decodeToImageBitmap()

val uri = saveImage(imageBitmap.asAndroidBitmap(), context)

val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, uri)
setDataAndType(uri, "image/png")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}

val shareIntent = Intent.createChooser(sendIntent, title)
activityProvider.invoke().startActivity(shareIntent)
}

private suspend fun saveImage(image: Bitmap, context: Context): Uri? {
return withContext(Dispatchers.IO) {
try {
val imagesFolder = File(context.cacheDir, "images")
imagesFolder.mkdirs()
val file = File(imagesFolder, "shared_image.png")

val stream = FileOutputStream(file)
image.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.flush()
stream.close()

FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
} catch (e: IOException) {
Log.d("saving bitmap", "saving bitmap error ${e.message}")
null
}
}
}

actual fun callHelpline() {
val context = activityProvider.invoke().application.baseContext
val intent = Intent(Intent.ACTION_DIAL).apply {
data = Uri.parse("tel:8000000000")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}

context.startActivity(intent)
}

actual fun mailHelpline() {
val context = activityProvider.invoke().application.baseContext

val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, arrayOf("support@mifos.org"))
putExtra(Intent.EXTRA_SUBJECT, "User Query")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
try {
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(
context,
"There is no application that support this action",
Toast.LENGTH_SHORT,
).show()
}
}

actual fun openAppInfo() {
val context = activityProvider.invoke().application.baseContext
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${context.packageName}")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}

actual fun shareApp() {
val context = activityProvider.invoke().application.baseContext
val shareText = "Download Self Service app here: https://play.google" +
".com/store/apps/details?id=${context.packageName}"
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, shareText)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
val shareIntent = Intent.createChooser(intent, "Choose")
activityProvider.invoke().startActivity(shareIntent)
}

actual fun openUrl(url: String) {
val context = activityProvider.invoke().application.baseContext
val uri = url.let { Uri.parse(url) } ?: return
val intent = Intent(Intent.ACTION_VIEW).apply {
data = uri
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}

actual fun ossLicensesMenuActivity() {
val context = activityProvider.invoke().application.baseContext
val intent = Intent(context, OssLicensesMenuActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
}
}
33 changes: 33 additions & 0 deletions core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/ShareUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.ui.util

import androidx.compose.ui.graphics.ImageBitmap

expect object ShareUtils {

fun shareText(text: String)

suspend fun shareImage(title: String, image: ImageBitmap)

suspend fun shareImage(title: String, byte: ByteArray)

fun openAppInfo()

fun shareApp()

fun callHelpline()

fun mailHelpline()

fun openUrl(url: String)

fun ossLicensesMenuActivity()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.ui.util

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asSkiaBitmap
import io.github.vinceglb.filekit.core.FileKit
import java.awt.Desktop
import java.net.URI

actual object ShareUtils {
actual fun shareText(text: String) {
}

actual suspend fun shareImage(title: String, image: ImageBitmap) {
FileKit.saveFile(
bytes = image.asSkiaBitmap().readPixels(),
baseName = "MifosQrCode",
extension = "png",
)
}

actual suspend fun shareImage(title: String, byte: ByteArray) {
FileKit.saveFile(
bytes = byte,
baseName = "MifosQrCode",
extension = "png",
)
}

actual fun callHelpline() {
if (Desktop.isDesktopSupported()) {
val uri = URI("tel:8000000000")
try {
Desktop.getDesktop().browse(uri)
} catch (e: Exception) {
println("Calling is not supported on Desktop.")
}
} else {
println("Calling is not supported on this platform.")
}
}

actual fun mailHelpline() {
val uri = URI("mailto:support@example.com?subject=Help%20Request&body=Hello,%20I%20need%20assistance%20with...")
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.MAIL)) {
Desktop.getDesktop().mail(uri)
}
}

actual fun openAppInfo() {
}

actual fun shareApp() {
}

actual fun openUrl(url: String) {
try {
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(URI(url))
}
} catch (e: Exception) {
println("Error opening URL: ${e.message}")
}
}

actual fun ossLicensesMenuActivity() {
}
}
57 changes: 57 additions & 0 deletions core/ui/src/jsMain/kotlin/com/mifos/core/ui/util/ShareUtils.js.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
*/
package com.mifos.core.ui.util

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asSkiaBitmap
import io.github.vinceglb.filekit.core.FileKit
import kotlinx.browser.window

actual object ShareUtils {
actual fun shareText(text: String) {
}

actual suspend fun shareImage(title: String, image: ImageBitmap) {
FileKit.saveFile(
bytes = image.asSkiaBitmap().readPixels(),
baseName = "MifosQrCode",
extension = "png",
)
}

actual suspend fun shareImage(title: String, byte: ByteArray) {
FileKit.saveFile(
bytes = byte,
baseName = "MifosQrCode",
extension = "png",
)
}

actual fun callHelpline() {
window.alert("Calling is not supported on Web. Please contact support at 8000000000.")
}

actual fun mailHelpline() {
val url = "mailto:support@example.com?subject=Help%20Request&body=Hello,%20I%20need%20assistance%20with..."
window.open(url)
}

actual fun openAppInfo() {
}

actual fun shareApp() {
}

actual fun openUrl(url: String) {
}

actual fun ossLicensesMenuActivity() {
}
}
Loading