Skip to content

Commit b257055

Browse files
authored
Create WallpapersScreen.kt
1 parent b3080ef commit b257055

1 file changed

Lines changed: 269 additions & 0 deletions

File tree

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
package com.hackeros.app.ui.screens
2+
3+
import android.content.ContentValues
4+
import android.content.Context
5+
import android.os.Build
6+
import android.os.Environment
7+
import android.provider.MediaStore
8+
import android.widget.Toast
9+
import androidx.compose.foundation.background
10+
import androidx.compose.foundation.border
11+
import androidx.compose.foundation.clickable
12+
import androidx.compose.foundation.layout.*
13+
import androidx.compose.foundation.lazy.grid.GridCells
14+
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
15+
import androidx.compose.foundation.lazy.grid.items
16+
import androidx.compose.foundation.shape.RoundedCornerShape
17+
import androidx.compose.material.icons.Icons
18+
import androidx.compose.material.icons.filled.Close
19+
import androidx.compose.material.icons.filled.Download
20+
import androidx.compose.material.icons.filled.Fullscreen
21+
import androidx.compose.material3.*
22+
import androidx.compose.runtime.*
23+
import androidx.compose.ui.Alignment
24+
import androidx.compose.ui.Modifier
25+
import androidx.compose.ui.draw.clip
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.layout.ContentScale
28+
import androidx.compose.ui.platform.LocalContext
29+
import androidx.compose.ui.text.font.FontFamily
30+
import androidx.compose.ui.text.font.FontWeight
31+
import androidx.compose.ui.unit.dp
32+
import androidx.compose.ui.unit.sp
33+
import androidx.compose.ui.window.Dialog
34+
import androidx.compose.ui.window.DialogProperties
35+
import coil.compose.AsyncImage
36+
import com.hackeros.app.WallpaperItem
37+
import com.hackeros.app.ui.theme.LocalAppTheme
38+
import com.hackeros.app.ui.theme.backgroundColor
39+
import com.hackeros.app.ui.theme.cardColor
40+
import com.hackeros.app.ui.theme.mutedColor
41+
import com.hackeros.app.ui.theme.primaryColor
42+
import com.hackeros.app.ui.theme.textColor
43+
import com.hackeros.app.utils.Translations
44+
import kotlinx.coroutines.Dispatchers
45+
import kotlinx.coroutines.launch
46+
import kotlinx.coroutines.withContext
47+
import java.io.InputStream
48+
import java.net.URL
49+
50+
@Composable
51+
fun WallpapersScreen(
52+
wallpapers: List<WallpaperItem>,
53+
translations: Translations
54+
) {
55+
val theme = LocalAppTheme.current
56+
val t = translations
57+
val context = LocalContext.current
58+
val scope = rememberCoroutineScope()
59+
var selectedWallpaper by remember { mutableStateOf<WallpaperItem?>(null) }
60+
var downloading by remember { mutableStateOf(false) }
61+
62+
Column(modifier = Modifier.fillMaxSize()) {
63+
// Header
64+
Column(modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 8.dp, bottom = 20.dp)) {
65+
Text(
66+
text = t.header_wallpapers,
67+
fontFamily = FontFamily.Monospace,
68+
fontWeight = FontWeight.Bold,
69+
fontSize = 28.sp,
70+
color = Color.White
71+
)
72+
Text(
73+
text = t.sub_wallpapers,
74+
fontSize = 13.sp,
75+
color = theme.mutedColor(),
76+
modifier = Modifier.padding(top = 2.dp)
77+
)
78+
}
79+
80+
LazyVerticalGrid(
81+
columns = GridCells.Fixed(2),
82+
contentPadding = PaddingValues(start = 16.dp, end = 16.dp, bottom = 100.dp),
83+
horizontalArrangement = Arrangement.spacedBy(12.dp),
84+
verticalArrangement = Arrangement.spacedBy(12.dp)
85+
) {
86+
items(wallpapers) { wp ->
87+
WallpaperThumbnail(
88+
wallpaper = wp,
89+
hdLabel = t.hd_asset,
90+
primaryColor = theme.primaryColor(),
91+
onClick = { selectedWallpaper = wp }
92+
)
93+
}
94+
}
95+
}
96+
97+
// Fullscreen preview modal
98+
selectedWallpaper?.let { wp ->
99+
Dialog(
100+
onDismissRequest = { selectedWallpaper = null },
101+
properties = DialogProperties(usePlatformDefaultWidth = false)
102+
) {
103+
Box(
104+
modifier = Modifier
105+
.fillMaxSize()
106+
.background(Color.Black.copy(alpha = 0.96f)),
107+
contentAlignment = Alignment.Center
108+
) {
109+
// Close button
110+
IconButton(
111+
onClick = { selectedWallpaper = null },
112+
modifier = Modifier
113+
.align(Alignment.TopEnd)
114+
.padding(16.dp)
115+
.clip(androidx.compose.foundation.shape.CircleShape)
116+
.background(Color.White.copy(alpha = 0.1f))
117+
) {
118+
Icon(Icons.Default.Close, null, tint = Color.White.copy(alpha = 0.8f))
119+
}
120+
121+
// Image
122+
Box(
123+
modifier = Modifier
124+
.fillMaxWidth(0.9f)
125+
.aspectRatio(9f / 16f)
126+
.clip(RoundedCornerShape(20.dp))
127+
.border(1.dp, Color.White.copy(alpha = 0.1f), RoundedCornerShape(20.dp))
128+
) {
129+
AsyncImage(
130+
model = wp.url,
131+
contentDescription = wp.name,
132+
contentScale = ContentScale.Crop,
133+
modifier = Modifier.fillMaxSize()
134+
)
135+
}
136+
137+
// Download button
138+
Button(
139+
onClick = {
140+
scope.launch {
141+
downloading = true
142+
downloadImage(context, wp.url, wp.name)
143+
downloading = false
144+
Toast.makeText(context, "Saved to Downloads", Toast.LENGTH_SHORT).show()
145+
}
146+
},
147+
enabled = !downloading,
148+
colors = ButtonDefaults.buttonColors(containerColor = theme.primaryColor()),
149+
shape = RoundedCornerShape(14.dp),
150+
modifier = Modifier
151+
.align(Alignment.BottomCenter)
152+
.fillMaxWidth(0.85f)
153+
.padding(bottom = 40.dp)
154+
.height(56.dp)
155+
) {
156+
if (downloading) {
157+
CircularProgressIndicator(
158+
color = theme.backgroundColor(),
159+
modifier = Modifier.size(20.dp),
160+
strokeWidth = 2.dp
161+
)
162+
Spacer(Modifier.width(12.dp))
163+
Text(t.downloading, fontWeight = FontWeight.Bold, fontSize = 15.sp,
164+
color = theme.backgroundColor())
165+
} else {
166+
Icon(Icons.Default.Download, null, tint = theme.backgroundColor(),
167+
modifier = Modifier.size(22.dp))
168+
Spacer(Modifier.width(12.dp))
169+
Text(t.download_save, fontWeight = FontWeight.Bold, fontSize = 15.sp,
170+
color = theme.backgroundColor())
171+
}
172+
}
173+
}
174+
}
175+
}
176+
}
177+
178+
@Composable
179+
private fun WallpaperThumbnail(
180+
wallpaper: WallpaperItem,
181+
hdLabel: String,
182+
primaryColor: Color,
183+
onClick: () -> Unit
184+
) {
185+
Box(
186+
modifier = Modifier
187+
.fillMaxWidth()
188+
.aspectRatio(9f / 16f)
189+
.clip(RoundedCornerShape(16.dp))
190+
.border(1.dp, Color.White.copy(alpha = 0.05f), RoundedCornerShape(16.dp))
191+
.clickable(onClick = onClick)
192+
) {
193+
AsyncImage(
194+
model = wallpaper.url,
195+
contentDescription = wallpaper.name,
196+
contentScale = ContentScale.Crop,
197+
modifier = Modifier.fillMaxSize()
198+
)
199+
// Gradient overlay
200+
Box(
201+
modifier = Modifier
202+
.fillMaxSize()
203+
.background(
204+
androidx.compose.ui.graphics.Brush.verticalGradient(
205+
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.75f)),
206+
startY = 200f
207+
)
208+
)
209+
)
210+
Column(
211+
modifier = Modifier
212+
.align(Alignment.BottomStart)
213+
.padding(12.dp)
214+
) {
215+
Text(
216+
text = wallpaper.name,
217+
color = Color.White,
218+
fontWeight = FontWeight.Bold,
219+
fontSize = 12.sp
220+
)
221+
Spacer(Modifier.height(4.dp))
222+
Row(
223+
modifier = Modifier
224+
.clip(RoundedCornerShape(4.dp))
225+
.background(Color.Black.copy(alpha = 0.5f))
226+
.padding(horizontal = 6.dp, vertical = 3.dp),
227+
verticalAlignment = Alignment.CenterVertically,
228+
horizontalArrangement = Arrangement.spacedBy(4.dp)
229+
) {
230+
Icon(Icons.Default.Fullscreen, null,
231+
tint = primaryColor, modifier = Modifier.size(9.dp))
232+
Text(
233+
text = hdLabel,
234+
fontSize = 9.sp,
235+
fontFamily = FontFamily.Monospace,
236+
color = primaryColor
237+
)
238+
}
239+
}
240+
}
241+
}
242+
243+
private suspend fun downloadImage(context: Context, url: String, name: String) {
244+
withContext(Dispatchers.IO) {
245+
try {
246+
val stream: InputStream = URL(url).openStream()
247+
val bytes = stream.readBytes()
248+
stream.close()
249+
250+
val filename = "HackerOS_${name.replace(" ", "_")}_${System.currentTimeMillis()}.png"
251+
252+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
253+
val values = ContentValues().apply {
254+
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
255+
put(MediaStore.Images.Media.MIME_TYPE, "image/png")
256+
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
257+
}
258+
val uri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
259+
uri?.let {
260+
context.contentResolver.openOutputStream(it)?.use { os -> os.write(bytes) }
261+
}
262+
} else {
263+
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
264+
dir.mkdirs()
265+
java.io.File(dir, filename).writeBytes(bytes)
266+
}
267+
} catch (_: Exception) {}
268+
}
269+
}

0 commit comments

Comments
 (0)