Skip to content

Commit

Permalink
Now template qr code will redirect to github page if scanned not in I…
Browse files Browse the repository at this point in the history
…mageToolbox
  • Loading branch information
T8RIN committed May 25, 2024
1 parent 4404f14 commit 3ee0b85
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 84 deletions.
2 changes: 2 additions & 0 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,6 @@ dependencies {
"marketImplementation"(libs.play.services.code.scanner)

api(libs.zxing.android.embedded)

api(libs.capturable)
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,23 @@ internal class FavoriteFiltersInteractorImpl @Inject constructor(

override fun getTemplateFilters(): Flow<List<TemplateFilter<Bitmap>>> =
dataStore.data.map { prefs ->
prefs[TEMPLATE_FILTERS]?.toTemplateFiltersList() ?: emptyList()
prefs[TEMPLATE_FILTERS]?.takeIf { it.isNotEmpty() }?.toTemplateFiltersList()
?: emptyList()
}

override suspend fun addTemplateFilterFromString(
string: String,
onSuccess: suspend (filterName: String, filtersCount: Int) -> Unit,
onError: suspend () -> Unit
) {
if (context.applicationInfo.packageName in string && "Filter" in string) {
string.toTemplateFiltersList().firstOrNull()?.let {
addTemplateFilter(it)
onSuccess(it.name, it.filters.size)
} ?: onError()
} else onError()
runCatching {
if (context.applicationInfo.packageName in string && "Filter" in string && LINK_HEADER in string) {
string.removePrefix(LINK_HEADER).toTemplateFiltersList().firstOrNull()?.let {
addTemplateFilter(it)
onSuccess(it.name, it.filters.size)
} ?: onError()
} else onError()
}.onFailure { onError() }
}

override suspend fun addTemplateFilterFromUri(
Expand Down Expand Up @@ -117,7 +120,7 @@ internal class FavoriteFiltersInteractorImpl @Inject constructor(

override suspend fun convertTemplateFilterToString(
templateFilter: TemplateFilter<Bitmap>
): String = listOf(templateFilter).toDatastoreString()
): String = "$LINK_HEADER${listOf(templateFilter).toDatastoreString()}"

override suspend fun addTemplateFilter(templateFilter: TemplateFilter<Bitmap>) {
val currentFilters = getTemplateFilters().first()
Expand All @@ -132,9 +135,10 @@ internal class FavoriteFiltersInteractorImpl @Inject constructor(

private fun List<Filter<Bitmap, *>>.toDatastoreString(
includeValue: Boolean = false
): String = joinToString(separator = ",") { filter ->
): String = joinToString(separator = FILTERS_SEPARATOR) { filter ->
filter::class.qualifiedName!! + if (includeValue && filter.value != null) {
":" + filter.value!!.toPair()?.let { it.first + ":" + it.second }
VALUE_SEPARATOR + filter.value!!.toPair()
?.let { it.first + VALUE_SEPARATOR + it.second }
} else ""
}.trim()

Expand Down Expand Up @@ -340,12 +344,12 @@ internal class FavoriteFiltersInteractorImpl @Inject constructor(

private fun String.toFiltersList(
includeValue: Boolean
): List<Filter<Bitmap, *>> = split(",").mapNotNull { line ->
): List<Filter<Bitmap, *>> = split(FILTERS_SEPARATOR).mapNotNull { line ->
if (line.trim().isEmpty()) return@mapNotNull null

val (name, value) = if (includeValue) {
runCatching {
val splitData = line.split(":")
val splitData = line.split(VALUE_SEPARATOR)
val className = splitData[1]
val valueString = splitData[2]

Expand All @@ -364,25 +368,33 @@ internal class FavoriteFiltersInteractorImpl @Inject constructor(
}.getOrNull()
}

private fun String.toTemplateFiltersList(): List<TemplateFilter<Bitmap>> = split("/").map {
val splitData = it.split("+")
val name = splitData[0]
val filters = splitData[1].toFiltersList(true)
private fun String.toTemplateFiltersList(): List<TemplateFilter<Bitmap>> =
split(TEMPLATES_SEPARATOR).map {
val splitData = it.split(TEMPLATE_CONTENT_SEPARATOR)
val name = splitData[0]
val filters = splitData[1].toFiltersList(true)

TemplateFilter(
name = name,
filters = filters
)
}
TemplateFilter(
name = name,
filters = filters
)
}

private fun List<TemplateFilter<Bitmap>>.toDatastoreString(): String =
joinToString(separator = "/") {
it.name + "+" + it.filters.toDatastoreString(true)
joinToString(separator = TEMPLATES_SEPARATOR) {
it.name + TEMPLATE_CONTENT_SEPARATOR + it.filters.toDatastoreString(true)
}


private operator fun <E> List<E>.component6(): E = get(5)
}

private const val LINK_HEADER: String = "https://github.com/T8RIN/ImageToolbox?"

private const val FILTERS_SEPARATOR = ","
private const val TEMPLATES_SEPARATOR = "\\"
private const val TEMPLATE_CONTENT_SEPARATOR = "+"
private const val VALUE_SEPARATOR = ":"

private val FAVORITE_FILTERS = stringPreferencesKey("FAVORITE_FILTERS")
private val TEMPLATE_FILTERS = stringPreferencesKey("TEMPLATE_FILTERS")
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ru.tech.imageresizershrinker.core.domain.image.ImageTransformer
import ru.tech.imageresizershrinker.core.domain.image.ShareProvider
import ru.tech.imageresizershrinker.core.domain.image.model.ImageFormat
import ru.tech.imageresizershrinker.core.domain.image.model.ImageInfo
import ru.tech.imageresizershrinker.core.domain.model.IntegerSize
import ru.tech.imageresizershrinker.core.filters.domain.FilterProvider
import ru.tech.imageresizershrinker.core.filters.domain.model.Filter
Expand All @@ -123,6 +126,7 @@ import ru.tech.imageresizershrinker.core.filters.presentation.utils.getTemplateF
import ru.tech.imageresizershrinker.core.resources.R
import ru.tech.imageresizershrinker.core.resources.icons.BookmarkOff
import ru.tech.imageresizershrinker.core.resources.icons.Cube
import ru.tech.imageresizershrinker.core.ui.utils.confetti.LocalConfettiHostState
import ru.tech.imageresizershrinker.core.ui.utils.helper.ContextUtils.getStringLocalized
import ru.tech.imageresizershrinker.core.ui.utils.helper.ImageUtils.safeAspectRatio
import ru.tech.imageresizershrinker.core.ui.utils.helper.isPortraitOrientationAsState
Expand Down Expand Up @@ -154,7 +158,8 @@ import javax.inject.Inject
@HiltViewModel
private class AddFiltersSheetViewModel @Inject constructor(
private val filterProvider: FilterProvider<Bitmap>,
private val imageTransformer: ImageTransformer<Bitmap>
private val imageTransformer: ImageTransformer<Bitmap>,
private val shareProvider: ShareProvider<Bitmap>
) : ViewModel() {
private val _previewData: MutableState<List<UiFilter<*>>?> = mutableStateOf(null)
val previewData by _previewData
Expand Down Expand Up @@ -215,6 +220,23 @@ private class AddFiltersSheetViewModel @Inject constructor(
}
}

fun shareImage(
bitmap: Bitmap,
onComplete: () -> Unit
) {
viewModelScope.launch {
shareProvider.shareImage(
imageInfo = ImageInfo(
width = bitmap.width,
height = bitmap.height,
imageFormat = ImageFormat.Png.Lossless
),
image = bitmap,
onComplete = onComplete
)
}
}

}

@OptIn(ExperimentalMaterial3Api::class)
Expand All @@ -233,6 +255,12 @@ fun AddFiltersSheet(
val onRequestFilterMapping = viewModel::filterToTransformation

val scope = rememberCoroutineScope()
val confettiHostState = LocalConfettiHostState.current
val showConfetti: () -> Unit = {
scope.launch {
confettiHostState.showConfetti()
}
}

val favoriteFilters by LocalFavoriteFiltersInteractor.getFavoriteFiltersAsUiState()
val templateFilters by LocalFavoriteFiltersInteractor.getTemplateFiltersAsUiState()
Expand Down Expand Up @@ -493,6 +521,9 @@ fun AddFiltersSheet(
contentPadding = PaddingValues(16.dp)
) {
itemsIndexed(templateFilters) { index, filter ->
var showFilterTemplateInfoSheet by rememberSaveable {
mutableStateOf(false)
}
TemplateFilterSelectionItem(
templateFilter = filter,
onClick = {
Expand All @@ -504,13 +535,27 @@ fun AddFiltersSheet(
onLongClick = {
viewModel.setPreviewData(filter.filters)
},
onInfoClick = {
showFilterTemplateInfoSheet = true
},
onRequestFilterMapping = onRequestFilterMapping,
shape = ContainerShapeDefaults.shapeForIndex(
index = index,
size = templateFilters.size
),
modifier = Modifier.animateItem()
)
FilterTemplateInfoSheet(
visible = showFilterTemplateInfoSheet,
onDismiss = {
showFilterTemplateInfoSheet = it
},
templateFilter = filter,
onRequestFilterMapping = onRequestFilterMapping,
onShareImage = {
viewModel.shareImage(it, showConfetti)
}
)
}
item {
FilterTemplateAddingGroup()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -59,8 +60,10 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
Expand All @@ -72,6 +75,8 @@ import androidx.compose.ui.unit.sp
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import coil.transform.Transformation
import dev.shreyaspatil.capturable.capturable
import dev.shreyaspatil.capturable.controller.rememberCaptureController
import kotlinx.coroutines.launch
import ru.tech.imageresizershrinker.core.filters.domain.model.TemplateFilter
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiFilter
Expand All @@ -89,11 +94,13 @@ import ru.tech.imageresizershrinker.core.ui.widget.preferences.PreferenceItem
import ru.tech.imageresizershrinker.core.ui.widget.sheets.SimpleSheet
import ru.tech.imageresizershrinker.core.ui.widget.text.TitleItem

@OptIn(ExperimentalComposeUiApi::class, ExperimentalComposeApi::class)
@Composable
internal fun FilterTemplateInfoSheet(
visible: Boolean,
onDismiss: (Boolean) -> Unit,
templateFilter: TemplateFilter<Bitmap>,
onShareImage: (Bitmap) -> Unit,
onRequestFilterMapping: ((UiFilter<*>) -> Transformation)?
) {
SimpleSheet(
Expand Down Expand Up @@ -139,6 +146,7 @@ internal fun FilterTemplateInfoSheet(
}

val scope = rememberCoroutineScope()
val captureController = rememberCaptureController()

LazyColumn(
modifier = Modifier.fillMaxWidth(),
Expand All @@ -147,53 +155,55 @@ internal fun FilterTemplateInfoSheet(
horizontalAlignment = Alignment.CenterHorizontally
) {
item {
if (onRequestFilterMapping != null) {
Spacer(modifier = Modifier.height(36.dp))
}
BoxWithConstraints(
modifier = Modifier.then(
if (onRequestFilterMapping != null) {
Modifier
.background(
color = MaterialTheme.colorScheme.surfaceContainerLowest,
shape = RoundedCornerShape(16.dp)
)
.padding(16.dp)
} else Modifier
)
) {
val targetSize = min(min(maxWidth, maxHeight), 300.dp)
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
QrCode(
content = filterContent,
modifier = Modifier
.then(
if (onRequestFilterMapping != null) {
Modifier.padding(top = 36.dp, bottom = 16.dp)
} else Modifier
)
.size(targetSize)
Column(Modifier.capturable(captureController)) {
if (onRequestFilterMapping != null) {
Spacer(modifier = Modifier.height(32.dp))
}
BoxWithConstraints(
modifier = Modifier.then(
if (onRequestFilterMapping != null) {
Modifier
.background(
color = MaterialTheme.colorScheme.surfaceContainerLowest,
shape = RoundedCornerShape(16.dp)
)
.padding(16.dp)
} else Modifier
)
) {
val targetSize = min(min(maxWidth, maxHeight), 300.dp)
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
QrCode(
content = filterContent,
modifier = Modifier
.then(
if (onRequestFilterMapping != null) {
Modifier.padding(top = 36.dp, bottom = 16.dp)
} else Modifier
)
.size(targetSize)
)

Text(
text = templateFilter.name,
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center,
modifier = Modifier.width(targetSize)
)
}
Text(
text = templateFilter.name,
style = MaterialTheme.typography.headlineSmall,
textAlign = TextAlign.Center,
modifier = Modifier.width(targetSize)
)
}

if (onRequestFilterMapping != null) {
TemplateFilterPreviewItem(
modifier = Modifier
.align(Alignment.TopCenter)
.offset(y = (-48).dp)
.size(64.dp),
templateFilter = templateFilter,
onRequestFilterMapping = onRequestFilterMapping
)
if (onRequestFilterMapping != null) {
TemplateFilterPreviewItem(
modifier = Modifier
.align(Alignment.TopCenter)
.offset(y = (-48).dp)
.size(64.dp),
templateFilter = templateFilter,
onRequestFilterMapping = onRequestFilterMapping
)
}
}
}

Expand Down Expand Up @@ -317,7 +327,12 @@ internal fun FilterTemplateInfoSheet(
startIcon = Icons.Rounded.QrCode,
onClick = {
showShareDialog = false
TODO()
scope.launch {
captureController.captureAsync()
.await()
.asAndroidBitmap()
.let(onShareImage)
}
},
titleFontStyle = LocalTextStyle.current.copy(
fontSize = 16.sp,
Expand Down
Loading

0 comments on commit 3ee0b85

Please sign in to comment.