Skip to content

Commit

Permalink
--wip-- [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
TobyBridle committed Feb 28, 2024
1 parent 1c80c36 commit d21db0c
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 40 deletions.
71 changes: 45 additions & 26 deletions app/src/main/java/com/chouten/app/common/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.chouten.app.common

import android.app.Activity
import android.content.ContentResolver
import android.content.Context
import android.content.ContextWrapper
import android.content.res.Configuration
import android.net.Uri
import android.provider.DocumentsContract
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
Expand All @@ -15,7 +19,9 @@ import androidx.compose.ui.graphics.Color
import com.chouten.app.domain.model.Version
import com.chouten.app.domain.proto.AppearancePreferences
import com.chouten.app.domain.proto.appearanceDatastore
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit


Expand All @@ -33,12 +39,9 @@ fun ColorScheme.animate(): ColorScheme {
}

@Composable
fun animateColor(color: Color): Color =
animateColorAsState(
targetValue = color,
animationSpec = animSpec,
label = "Theme Color Transition"
).value
fun animateColor(color: Color): Color = animateColorAsState(
targetValue = color, animationSpec = animSpec, label = "Theme Color Transition"
).value

return ColorScheme(
primary = animateColor(this.primary),
Expand Down Expand Up @@ -143,17 +146,20 @@ fun Long.formatMinSec(): String {
String.format(
"%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(this),
TimeUnit.MILLISECONDS.toMinutes(this) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(this)),
TimeUnit.MILLISECONDS.toSeconds(this) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(this))
TimeUnit.MILLISECONDS.toMinutes(this) - TimeUnit.HOURS.toMinutes(
TimeUnit.MILLISECONDS.toHours(this)
),
TimeUnit.MILLISECONDS.toSeconds(this) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(this)
)
)
} else {
String.format(
"%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(this),
TimeUnit.MILLISECONDS.toSeconds(this) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(this))
TimeUnit.MILLISECONDS.toSeconds(this) - TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(this)
)
)
}
}
Expand All @@ -180,19 +186,10 @@ internal fun scale(start1: Float, end1: Float, pos: Float, start2: Float, end2:
* Scale x.start, x.endInclusive from a1..b1 range to a2..b2 range
*/
internal fun scale(
start1: Float,
end1: Float,
range: ClosedFloatingPointRange<Float>,
start2: Float,
end2: Float
) =
scale(start1, end1, range.start, start2, end2)..scale(
start1,
end1,
range.endInclusive,
start2,
end2
)
start1: Float, end1: Float, range: ClosedFloatingPointRange<Float>, start2: Float, end2: Float
) = scale(start1, end1, range.start, start2, end2)..scale(
start1, end1, range.endInclusive, start2, end2
)

/**
* Calculate fraction for value between a range [end] and [start] coerced into 0f-1f range
Expand All @@ -208,4 +205,26 @@ fun calculateFraction(start: Float, end: Float, pos: Float) =
* @return The parsed [Version] object.
* @throws IllegalArgumentException If the version string is not valid.
*/
fun String.toVersion(useRegex: Boolean = false) = Version(this, useRegex)
fun String.toVersion(useRegex: Boolean = false) = Version(this, useRegex)

suspend fun Uri.findDocument(
contentResolver: ContentResolver, document: String
): Uri? = withContext(Dispatchers.IO) {
contentResolver.query(
this@findDocument, arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
), null, null, null, null
)?.use { cursor ->
val nameColumn =
cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
val idColumn = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
while (cursor.moveToNext()) {
Log.d("FindDocument", "Found ${cursor.getString(nameColumn)}")
if (cursor.getString(nameColumn) == document) {
return@use DocumentsContract.buildTreeDocumentUri(this@findDocument.authority, cursor.getString(idColumn))
}
}
null
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.chouten.app.data.data_source.module

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
Expand All @@ -18,4 +19,10 @@ interface ModuleDao {

@Update
suspend fun updateModule(moduleModel: ModuleModel)

@Query("DELETE FROM ModuleModel WHERE id = :moduleId")
suspend fun removeModule(moduleId: String)

@Delete
suspend fun removeModule(moduleModel: ModuleModel)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package com.chouten.app.data.repository
import android.content.Context
import com.chouten.app.data.data_source.module.ModuleDatabase
import com.chouten.app.domain.model.ModuleModel
import com.chouten.app.domain.proto.filepathDatastore
import com.chouten.app.domain.proto.moduleDatastore
import com.chouten.app.domain.repository.ModuleRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject

class ModuleRepositoryImpl @Inject constructor(
Expand All @@ -13,22 +17,26 @@ class ModuleRepositoryImpl @Inject constructor(
override suspend fun getModules() = moduleDatabase.moduleDao.getModules()

/**
* Adds a module to the module folder
* Adds a module to the module database
* @param moduleModel: [ModuleModel] - The module (model)
*/
override suspend fun addModule(moduleModel: ModuleModel) {
moduleDatabase.moduleDao.addModule(moduleModel)
}

/**
* Update a module entry in the module database
* @param moduleModel: [ModuleModel] - The module (model)
*/
override suspend fun updateModule(moduleModel: ModuleModel) {
moduleDatabase.moduleDao.updateModule(moduleModel)
}

/**
* Removes a module from the module folder
* Removes a module from the module database
* @param moduleId: [String] - The ID of the module to remove
*/
override suspend fun removeModule(moduleId: String) {
TODO("Not yet implemented")
moduleDatabase.moduleDao.removeModule(moduleId)
}
}
3 changes: 1 addition & 2 deletions app/src/main/java/com/chouten/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,7 @@ object AppModule {
moduleRepository,
httpClient,
log,
moduleJsonParser,
moduleDirGetter
moduleJsonParser
), getModuleDir = moduleDirUsecase, removeModule = RemoveModuleUseCase(
app.applicationContext,
moduleRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import kotlinx.serialization.encoding.Encoder
* Data class representing the user's file preferences.
* @param CHOUTEN_ROOT_DIR The root directory of the Chouten app. (e.g /storage/emulated/0/Documents/Chouten).
* @param IS_CHOUTEN_MODULE_DIR_SET Whether the module directory of the Chouten app has been set.
* @param SAVE_MODULE_ARTIFACTS Whether to save .module artifacts during module installation
*/
@Serializable
data class FilePreferences(
@Serializable(with = UriSerializer::class) val CHOUTEN_ROOT_DIR: Uri,
val IS_CHOUTEN_MODULE_DIR_SET: Boolean = false,
val SAVE_MODULE_ARTIFACTS: Boolean = true
) {
companion object {
val DEFAULT = FilePreferences(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.DocumentsContract
import android.util.Log
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.text.toUpperCase
import androidx.core.content.FileProvider
import com.chouten.app.common.OutOfDateAppException
import com.chouten.app.common.OutOfDateModuleException
import com.chouten.app.common.findDocument
import com.chouten.app.domain.model.ModuleModel
import com.chouten.app.domain.proto.filepathDatastore
import com.chouten.app.domain.repository.ModuleRepository
import com.lagradost.nicehttp.Requests
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.withContext
import okio.buffer
import okio.sink
import okio.source
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileNotFoundException
Expand Down Expand Up @@ -149,9 +155,6 @@ class AddModuleUseCase @Inject constructor(
}
}

// We have unzipped and can delete the cached .module file
cachedModule?.delete()

// Get the metadata file
val metadataInputStream = destinationDir.resolve("metadata.json").also {
if (!it.exists()) safeException(
Expand Down Expand Up @@ -309,12 +312,55 @@ class AddModuleUseCase @Inject constructor(
}

val os = ByteArrayOutputStream()
BitmapFactory.decodeStream(inputStream).apply {
BitmapFactory.decodeFile(this@apply.absolutePath)?.apply {
compress(Bitmap.CompressFormat.PNG, 80, os)
}
} ?: log("Could not parse icon.png")
module = module.copy(metadata = module.metadata.copy(icon = os.toByteArray()))
}

val preferences = mContext.filepathDatastore.data.firstOrNull()
preferences?.CHOUTEN_ROOT_DIR?.let {
if (it == Uri.EMPTY) {
log("CHOUTEN_ROOT_DIR is empty. Cannot copy module artifact")
return@let
} else if (!preferences.SAVE_MODULE_ARTIFACTS) {
log("SAVE_MODULE_ARTIFACTS is false. Not saving module artifact")
return@let
}

val childDocumentsUri = DocumentsContract.buildChildDocumentsUriUsingTree(
it, DocumentsContract.getTreeDocumentId(
DocumentsContract.buildChildDocumentsUriUsingTree(
it, DocumentsContract.getTreeDocumentId(it)
).findDocument(contentResolver, "Modules") ?: safeException(
IOException(
"Could not find Module Dir at $it"
), destinationDir
)
)
)

val displayName = "${module.name}_v${module.version}_${module.id}.module"
log("Adding artifact $displayName to $childDocumentsUri")
childDocumentsUri.findDocument(contentResolver, displayName)?.let { _ ->
log("Existing artifact exists within $childDocumentsUri")
} ?: run {
val moduleUri = DocumentsContract.createDocument(
contentResolver, childDocumentsUri, "application/octet-stream", displayName
) ?: throw IOException("Could not save module artifact")
val stream = if (isRemote) {
cachedModule?.inputStream()
} else contentResolver.openInputStream(uri)
stream?.apply {
contentResolver.openOutputStream(moduleUri)?.sink()?.buffer()
?.use { buffer ->
buffer.writeAll(source())
}
close()
}
}
}

log("Adding Module $module")
moduleRepository.addModule(module)
destinationDir.delete()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.chouten.app.presentation.ui.components.preferences

import androidx.compose.foundation.clickable
import androidx.compose.material3.ListItem
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun PreferenceItem(
modifier: Modifier = Modifier,
headlineContent: @Composable () -> Unit,
supportingContent: @Composable (() -> Unit) = {},
icon: @Composable (() -> Unit) = {},
callback: () -> Unit
) {
ListItem(
modifier = Modifier
.clickable {
callback()
}
.then(modifier),
headlineContent = headlineContent,
supportingContent = supportingContent,
leadingContent = icon
)
}
Loading

0 comments on commit d21db0c

Please sign in to comment.