Skip to content

Commit

Permalink
Merge pull request #30 from viethua99/feature/project-list
Browse files Browse the repository at this point in the history
[KMP] Implement project list
  • Loading branch information
viethua99 authored Mar 10, 2024
2 parents 094709e + 0df09e2 commit f48635c
Show file tree
Hide file tree
Showing 112 changed files with 2,374 additions and 845 deletions.
2 changes: 2 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ kotlin {
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.androidx.activity.compose)
implementation(libs.accompanist.permissions)

// Ktor Dependencies for API network
implementation(libs.ktor.client.cio)
Expand Down Expand Up @@ -98,6 +99,7 @@ kotlin {
// Voyager Dependencies for screen models
implementation(libs.voyager.navigator)
implementation(libs.voyager.tab.navigator)
implementation(libs.voyager.transitions)
implementation(libs.voyager.koin)

// Kamel Dependencies for images loading
Expand Down
15 changes: 15 additions & 0 deletions composeApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name=".StableDiffusionApplication"
android:allowBackup="true"
Expand All @@ -22,6 +28,15 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name=".ComposeFileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/path_provider" />
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.vproject.stablediffusion

import android.content.Context
import android.net.Uri
import androidx.core.content.FileProvider
import java.io.File
import java.util.Objects

class ComposeFileProvider : FileProvider(
R.xml.path_provider
) {
companion object {
fun getImageUri(context: Context): Uri {
// 1
val tempFile = File.createTempFile(
"picture_${System.currentTimeMillis()}", ".png", context.cacheDir
).apply {
createNewFile()
}
// 2
val authority = context.applicationContext.packageName + ".provider"
// 3
println("getImageUri: ${tempFile.absolutePath}")
return getUriForFile(
Objects.requireNonNull(context),
authority,
tempFile,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
package com.vproject.stablediffusion

import App
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import com.vproject.stablediffusion.app.App
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.vproject.stablediffusion.di

import com.vproject.stablediffusion.util.TestUtil
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.HttpTimeout
Expand All @@ -10,8 +9,6 @@ import kotlinx.serialization.json.Json
import org.koin.dsl.module

actual fun platformModule() = module {
single { TestUtil(get()) }

single {
HttpClient(OkHttp) {
install(ContentNegotiation) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,72 +1,9 @@
package com.vproject.stablediffusion.util

import android.content.Context
import android.graphics.BitmapFactory
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.util.Log
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL

actual fun imageBitmapFromBytes(encodedImageData: ByteArray): ImageBitmap {
return BitmapFactory.decodeByteArray(encodedImageData, 0, encodedImageData.size).asImageBitmap()
}

actual class TestUtil(private val context: Context) {
actual fun isInternetAvailable(): Boolean {
CoroutineScope(Dispatchers.IO).launch {
try {
val url = URL("https://api.stability.ai")
val urlc = url.openConnection() as HttpURLConnection;
urlc.connectTimeout = 4000
urlc.connect();
if (urlc.responseCode == 200) {
Log.d("HELLO", "Success !")
} else {
Log.d("HELLO", "Failed !")
}
} catch (e1: MalformedURLException) {
Log.d("HELLO", "MalformedURLException !")

e1.printStackTrace()
} catch (e: IOException) {
Log.d("HELLO", "IOException !")
e.printStackTrace()
}
}

/////
var result = false
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val networkCapabilities = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
result = when {
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
} else {
connectivityManager.run {
connectivityManager.activeNetworkInfo?.run {
result = when (type) {
ConnectivityManager.TYPE_WIFI -> true
ConnectivityManager.TYPE_MOBILE -> true
ConnectivityManager.TYPE_ETHERNET -> true
else -> false
}
}
}
}
return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.vproject.stablediffusion.util

import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import java.io.InputStream

object BitmapUtils {
fun getBitmapFromUri(uri: Uri, contentResolver: ContentResolver): Bitmap? {
var inputStream: InputStream?
try {
inputStream = contentResolver.openInputStream(uri)
val s = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
return Bitmap.createScaledBitmap(s, 1024, 1024, true)
} catch (e: Exception) {
e.printStackTrace()
println("getBitmapFromUri Exception: ${e.message}")
println("getBitmapFromUri Exception: ${e.localizedMessage}")
return null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.vproject.stablediffusion.util

import android.content.ContentResolver
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.vproject.stablediffusion.ComposeFileProvider

@Composable
actual fun rememberCameraManager(onResult: (SharedImage?) -> Unit): CameraManager {
val context = LocalContext.current
val contentResolver: ContentResolver = context.contentResolver
var tempPhotoUri by remember { mutableStateOf(value = Uri.EMPTY) }
val cameraLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicture(),
onResult = { success ->
if (success) {
onResult.invoke(SharedImage(BitmapUtils.getBitmapFromUri(tempPhotoUri, contentResolver)))
}
}
)
return remember {
CameraManager(
onLaunch = {
tempPhotoUri = ComposeFileProvider.getImageUri(context)
cameraLauncher.launch(tempPhotoUri)
}
)
}
}

actual class CameraManager actual constructor(
private val onLaunch: () -> Unit
) {
actual fun launch() {
onLaunch()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.vproject.stablediffusion.util

import android.content.ContentResolver
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext

@Composable
actual fun rememberGalleryManager(onResult: (SharedImage?) -> Unit): GalleryManager {
val context = LocalContext.current
val contentResolver: ContentResolver = context.contentResolver
val galleryLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
uri?.let {
onResult.invoke(SharedImage(BitmapUtils.getBitmapFromUri(uri, contentResolver)))
}
}
return remember {
GalleryManager(onLaunch = {
galleryLauncher.launch(
PickVisualMediaRequest(
mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
})
}
}

actual class GalleryManager actual constructor(private val onLaunch: () -> Unit) {
actual fun launch() {
onLaunch()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.vproject.stablediffusion.util

import android.Manifest
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import kotlinx.coroutines.launch

@Composable
actual fun createPermissionsManager(callback: PermissionCallback): PermissionsManager {
return remember { PermissionsManager(callback) }
}

actual class PermissionsManager actual constructor(private val callback: PermissionCallback) :
PermissionHandler {
@OptIn(ExperimentalPermissionsApi::class)
@Composable
override fun askPermission(permission: PermissionType) {
val lifecycleOwner = LocalLifecycleOwner.current

when (permission) {
PermissionType.CAMERA -> {
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)
LaunchedEffect(cameraPermissionState) {
val permissionResult = cameraPermissionState.status
if (!permissionResult.isGranted) {
if (permissionResult.shouldShowRationale) {
callback.onPermissionStatus(
permission, PermissionStatus.SHOW_RATIONAL
)
} else {
lifecycleOwner.lifecycleScope.launch {
cameraPermissionState.launchPermissionRequest()
}
}
} else {
callback.onPermissionStatus(
permission, PermissionStatus.GRANTED
)
}
}
}

PermissionType.GALLERY -> {
// Granted by default because in Android GetContent API does not require any
// runtime permissions, and i am using it to access gallery in my app
callback.onPermissionStatus(
permission, PermissionStatus.GRANTED
)
}
}
}


@OptIn(ExperimentalPermissionsApi::class)
@Composable
override fun isPermissionGranted(permission: PermissionType): Boolean {
return when (permission) {
PermissionType.CAMERA -> {
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)
cameraPermissionState.status.isGranted
}

PermissionType.GALLERY -> {
// Granted by default because in Android GetContent API does not require any
// runtime permissions, and i am using it to access gallery in my app
true
}
}
}

@Composable
override fun launchSettings() {
val context = LocalContext.current
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", context.packageName, null)
).also {
context.startActivity(it)
}
}
}
Loading

0 comments on commit f48635c

Please sign in to comment.