Skip to content

Commit f32b4e2

Browse files
committed
Implement QR code generator for link url
1 parent 6d79a77 commit f32b4e2

6 files changed

Lines changed: 98 additions & 8 deletions

File tree

app/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ dependencies {
9696
implementation(libs.timber)
9797
debugImplementation(libs.leakcanary.android)
9898

99+
implementation("com.google.zxing:core:3.5.4")
100+
99101
testImplementation(libs.junit)
100102
testImplementation(libs.truth)
101103

@@ -106,4 +108,4 @@ dependencies {
106108
androidTestImplementation(libs.kotlinx.coroutines.testing)
107109
androidTestImplementation(libs.androidx.espresso.core)
108110
androidTestImplementation(libs.androidx.espresso.intents)
109-
}
111+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DGOPSALSAZMY6AAAAAAAAAAAAA=

app/src/main/java/com/amrdeveloper/linkhub/ui/components/Folders.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ fun FolderContent(
142142
}
143143

144144
private val folderLongClickOptions = listOf(
145-
DropDownOption("Edit", R.drawable.ic_pin_edit),
145+
DropDownOption("Info", R.drawable.ic_pin_edit),
146146
DropDownOption("Shortcut", R.drawable.ic_shortcut)
147147
)
148148

@@ -160,7 +160,7 @@ fun FolderWithActions(
160160
options = folderLongClickOptions,
161161
onOptionSelected = { option ->
162162
when (option.text) {
163-
"Edit" -> {
163+
"Info" -> {
164164
val bundle = bundleOf("folder" to folder)
165165
navController.navigate(R.id.folderFragment, bundle)
166166
}
@@ -182,4 +182,4 @@ fun FolderWithActions(
182182
)
183183
}
184184
)
185-
}
185+
}

app/src/main/java/com/amrdeveloper/linkhub/ui/components/Links.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ fun LinkContent(
165165
}
166166

167167
private val linkLongClickOptions = listOf(
168-
DropDownOption("Edit", R.drawable.ic_shortcut),
168+
DropDownOption("Info", R.drawable.ic_shortcut),
169169
DropDownOption("Copy", R.drawable.ic_shortcut),
170170
DropDownOption("Shortcut", R.drawable.ic_shortcut),
171171
DropDownOption("Share", R.drawable.ic_shortcut)
@@ -187,7 +187,7 @@ fun LinkWithActions(
187187
options = linkLongClickOptions,
188188
onOptionSelected = { option ->
189189
when (option.text) {
190-
"Edit" -> {
190+
"Info" -> {
191191
val bundle = bundleOf("link" to link)
192192
navController.navigate(R.id.linkFragment, bundle)
193193
}
@@ -242,4 +242,4 @@ private fun findPlatformDomainIcon(url: String): Int {
242242
}
243243
}
244244
return R.drawable.ic_link
245-
}
245+
}

app/src/main/java/com/amrdeveloper/linkhub/ui/link/LinkScreen.kt

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
11
package com.amrdeveloper.linkhub.ui.link
22

3+
import android.graphics.Bitmap
34
import android.webkit.URLUtil
45
import androidx.activity.compose.BackHandler
6+
import androidx.compose.foundation.Image
7+
import androidx.compose.foundation.border
58
import androidx.compose.foundation.layout.Column
69
import androidx.compose.foundation.layout.Row
710
import androidx.compose.foundation.layout.Spacer
11+
import androidx.compose.foundation.layout.fillMaxSize
812
import androidx.compose.foundation.layout.fillMaxWidth
913
import androidx.compose.foundation.layout.height
1014
import androidx.compose.foundation.layout.padding
1115
import androidx.compose.foundation.layout.size
1216
import androidx.compose.foundation.rememberScrollState
1317
import androidx.compose.foundation.verticalScroll
18+
import androidx.compose.material3.CircularProgressIndicator
1419
import androidx.compose.material3.Icon
1520
import androidx.compose.material3.IconButton
21+
import androidx.compose.material3.MaterialTheme
1622
import androidx.compose.material3.OutlinedTextField
1723
import androidx.compose.material3.OutlinedTextFieldDefaults
1824
import androidx.compose.material3.Scaffold
1925
import androidx.compose.material3.Text
2026
import androidx.compose.runtime.Composable
27+
import androidx.compose.runtime.LaunchedEffect
2128
import androidx.compose.runtime.getValue
2229
import androidx.compose.runtime.mutableStateOf
2330
import androidx.compose.runtime.remember
2431
import androidx.compose.runtime.setValue
2532
import androidx.compose.ui.Alignment
2633
import androidx.compose.ui.Modifier
2734
import androidx.compose.ui.graphics.Color
35+
import androidx.compose.ui.graphics.asImageBitmap
2836
import androidx.compose.ui.res.colorResource
2937
import androidx.compose.ui.res.painterResource
3038
import androidx.compose.ui.res.stringResource
@@ -44,6 +52,9 @@ import com.amrdeveloper.linkhub.util.CREATED_FOLDER_NAME_KEY
4452
import com.amrdeveloper.linkhub.util.CREATE_FOLDER_ID
4553
import com.amrdeveloper.linkhub.util.FOLDER_NONE_ID
4654
import com.amrdeveloper.linkhub.util.UiPreferences
55+
import com.amrdeveloper.linkhub.util.generateQrCodeBitmap
56+
import kotlinx.coroutines.Dispatchers
57+
import kotlinx.coroutines.withContext
4758
import java.net.URI
4859
import java.text.DateFormat
4960

@@ -224,7 +235,11 @@ fun LinkScreen(
224235
}
225236

226237
// Link metadata
227-
currentLink?.let { LinkCreatedAndUpdatedTime(it) }
238+
currentLink?.let {
239+
LinkCreateQRCodeImage(it)
240+
241+
LinkCreatedAndUpdatedTime(it)
242+
}
228243

229244
when (taskState) {
230245
TaskState.Success -> {
@@ -364,3 +379,37 @@ private fun LinkCreatedAndUpdatedTime(link: Link) {
364379
)
365380
}
366381
}
382+
383+
@Composable
384+
private fun LinkCreateQRCodeImage(link: Link) {
385+
var qrBitmap by remember { mutableStateOf<Bitmap?>(value = null) }
386+
LaunchedEffect(link) {
387+
withContext(Dispatchers.Default) {
388+
qrBitmap = generateQrCodeBitmap(text = link.url)
389+
}
390+
}
391+
392+
if (qrBitmap == null) return
393+
394+
Column(
395+
modifier = Modifier
396+
.fillMaxSize()
397+
.padding(8.dp)
398+
.border(1.dp, colorResource(R.color.light_blue_600)),
399+
horizontalAlignment = Alignment.CenterHorizontally
400+
) {
401+
Text(
402+
text = "Generated QR Code",
403+
color = colorResource(R.color.light_blue_600),
404+
style = MaterialTheme.typography.titleMedium,
405+
)
406+
407+
qrBitmap?.let { bitmap ->
408+
Image(
409+
bitmap = bitmap.asImageBitmap(),
410+
contentDescription = "Generated QR Code",
411+
modifier = Modifier.size(250.dp)
412+
)
413+
} ?: CircularProgressIndicator()
414+
}
415+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.amrdeveloper.linkhub.util
2+
3+
import android.graphics.Bitmap
4+
import android.graphics.Color
5+
import androidx.core.graphics.createBitmap
6+
import androidx.core.graphics.set
7+
import com.google.zxing.BarcodeFormat
8+
import com.google.zxing.MultiFormatWriter
9+
import com.google.zxing.WriterException
10+
11+
fun generateQrCodeBitmap(
12+
text: String,
13+
width: Int = 512,
14+
height: Int = 512,
15+
dataColor: Int = Color.rgb(0x03, 0x9B, 0xE5),
16+
emptyColor: Int = Color.WHITE
17+
): Bitmap? {
18+
return try {
19+
// Encode the text into a 2D BitMatrix
20+
val bitMatrix = MultiFormatWriter().encode(
21+
text,
22+
BarcodeFormat.QR_CODE,
23+
width,
24+
height
25+
)
26+
27+
val bitmap = createBitmap(width, height, config = Bitmap.Config.RGB_565)
28+
29+
for (x in 0 until width) {
30+
for (y in 0 until height) {
31+
bitmap[x, y] = if (bitMatrix.get(x, y)) dataColor else emptyColor
32+
}
33+
}
34+
bitmap
35+
} catch (_: WriterException) {
36+
null
37+
}
38+
}

0 commit comments

Comments
 (0)