From 7ecec593dbab9d1beef30f036d56398635088580 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:03:53 +0000 Subject: [PATCH 1/3] Initial plan From ef96c322c36981d0162555a16b8aa71b783eb46f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:11:10 +0000 Subject: [PATCH 2/3] Add User-Agent header to HttpClient for better metadata fetching Co-authored-by: yogeshpaliyal <9381846+yogeshpaliyal@users.noreply.github.com> --- .../yogeshpaliyal/deepr/DeeprApplication.kt | 4 +++ .../deepr/data/NetworkRepositoryTest.kt | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt b/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt index 338fb4fb..73aa732f 100644 --- a/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt +++ b/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt @@ -27,6 +27,7 @@ import com.yogeshpaliyal.deepr.viewmodel.LocalServerViewModel import com.yogeshpaliyal.deepr.viewmodel.TransferLinkLocalServerViewModel import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO +import io.ktor.client.plugins.UserAgent import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json @@ -78,6 +79,9 @@ class DeeprApplication : Application() { single { HttpClient(CIO) { + install(UserAgent) { + agent = "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36" + } install(ContentNegotiation) { json( Json { diff --git a/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt b/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt index fd325eec..d3166cc4 100644 --- a/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt +++ b/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt @@ -3,6 +3,7 @@ package com.yogeshpaliyal.deepr.data import io.ktor.client.HttpClient import io.ktor.client.engine.mock.MockEngine import io.ktor.client.engine.mock.respond +import io.ktor.client.plugins.UserAgent import io.ktor.http.HttpStatusCode import io.ktor.http.headersOf import kotlinx.coroutines.test.runTest @@ -152,4 +153,38 @@ class NetworkRepositoryTest { assertEquals("Test Title", linkInfo?.title) assertEquals("https://example.com/image.jpg", linkInfo?.image) } + + @Test + fun getLinkInfo_sendsUserAgentHeader() = + runTest { + // Create a mock HTTP client that captures the User-Agent header + var receivedUserAgent: String? = null + val mockEngine = + MockEngine { request -> + receivedUserAgent = request.headers["User-Agent"] + respond( + content = mockHtmlContent, + status = HttpStatusCode.OK, + headers = headersOf("Content-Type", "text/html"), + ) + } + + val httpClient = + HttpClient(mockEngine) { + install(UserAgent) { + agent = "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36" + } + } + val htmlParser = HtmlParser() + val repository = NetworkRepository(httpClient, htmlParser) + + val result = repository.getLinkInfo("example.com") + + // Verify the User-Agent header was sent + assertTrue(result.isSuccess) + assertEquals( + "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + receivedUserAgent, + ) + } } From 90aacdede9c24c2a4178ed170584966ee8a90810 Mon Sep 17 00:00:00 2001 From: Yogesh Choudhary Paliyal Date: Thu, 6 Nov 2025 22:43:49 +0530 Subject: [PATCH 3/3] Add network security configuration and enhance thumbnail handling in UI --- app/src/main/AndroidManifest.xml | 1 + .../yogeshpaliyal/deepr/DeeprApplication.kt | 6 +-- .../deepr/data/NetworkRepository.kt | 10 ++++- .../ui/screens/home/HomeBottomContent.kt | 40 +++++++++++++------ app/src/main/res/values/strings.xml | 3 +- .../main/res/xml/network_security_config.xml | 15 +++++++ .../deepr/data/NetworkRepositoryTest.kt | 3 +- 7 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 app/src/main/res/xml/network_security_config.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ecf14c69..a3c5cea9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Deepr"> diff --git a/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt b/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt index 73aa732f..171a227d 100644 --- a/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt +++ b/app/src/main/java/com/yogeshpaliyal/deepr/DeeprApplication.kt @@ -27,7 +27,6 @@ import com.yogeshpaliyal.deepr.viewmodel.LocalServerViewModel import com.yogeshpaliyal.deepr.viewmodel.TransferLinkLocalServerViewModel import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO -import io.ktor.client.plugins.UserAgent import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json @@ -79,9 +78,6 @@ class DeeprApplication : Application() { single { HttpClient(CIO) { - install(UserAgent) { - agent = "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36" - } install(ContentNegotiation) { json( Json { @@ -101,7 +97,7 @@ class DeeprApplication : Application() { } single { - NetworkRepository(get(), get()) + NetworkRepository(get()) } single { diff --git a/app/src/main/java/com/yogeshpaliyal/deepr/data/NetworkRepository.kt b/app/src/main/java/com/yogeshpaliyal/deepr/data/NetworkRepository.kt index 518aca8c..e4bb6ed7 100644 --- a/app/src/main/java/com/yogeshpaliyal/deepr/data/NetworkRepository.kt +++ b/app/src/main/java/com/yogeshpaliyal/deepr/data/NetworkRepository.kt @@ -2,17 +2,23 @@ package com.yogeshpaliyal.deepr.data import com.yogeshpaliyal.deepr.util.normalizeLink import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO import io.ktor.client.request.get import io.ktor.client.statement.bodyAsText class NetworkRepository( - val httpClient: HttpClient, val httpParser: HtmlParser, ) { + val httpClient: HttpClient by lazy { + HttpClient(CIO) + } + suspend fun getLinkInfo(url: String): Result { val normalizedUrl = normalizeLink(url) try { - val response = httpClient.get(normalizedUrl) + val response = + httpClient.get(normalizedUrl) { + } if (response.status.value != 200) { return Result.failure(Exception("Failed to fetch data from $normalizedUrl, status code: ${response.status.value}")) } diff --git a/app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/HomeBottomContent.kt b/app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/HomeBottomContent.kt index 987dc6af..fdc9b999 100644 --- a/app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/HomeBottomContent.kt +++ b/app/src/main/java/com/yogeshpaliyal/deepr/ui/screens/home/HomeBottomContent.kt @@ -256,18 +256,34 @@ fun HomeBottomContent( Spacer(modifier = Modifier.height(8.dp)) if (deeprInfo.thumbnail.isNotEmpty() && isThumbnailEnable) { - AsyncImage( - model = deeprInfo.thumbnail, - contentDescription = deeprInfo.name, - modifier = - Modifier - .fillMaxWidth() - .aspectRatio(1.91f) - .background(MaterialTheme.colorScheme.surfaceVariant), - placeholder = null, - error = null, - contentScale = ContentScale.Crop, - ) + Column { + AsyncImage( + model = deeprInfo.thumbnail, + contentDescription = deeprInfo.name, + modifier = + Modifier + .fillMaxWidth() + .aspectRatio(1.91f) + .background(MaterialTheme.colorScheme.surfaceVariant), + placeholder = null, + error = null, + contentScale = ContentScale.Crop, + ) + Spacer(modifier = Modifier.height(8.dp)) + OutlinedButton( + onClick = { + deeprInfo = deeprInfo.copy(thumbnail = "") + }, + modifier = Modifier.fillMaxWidth(), + ) { + Icon( + imageVector = TablerIcons.X, + contentDescription = stringResource(R.string.remove_thumbnail), + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(stringResource(R.string.remove_thumbnail)) + } + } } Spacer(modifier = Modifier.height(8.dp)) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index add0f234..8b4db7e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,7 +20,7 @@ Add to favourites Remove from favourites Add Link - Fetch name from link + Fetch meta from link Create Tag New tag (Optional) No links saved yet @@ -33,6 +33,7 @@ Try selecting different tags or clear filters. No favourites in selected tags Mark links with these tags as favourites or try different tags. + Remove thumbnail More options diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 00000000..8b18cdfa --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt b/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt index d3166cc4..bc9e5996 100644 --- a/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt +++ b/app/src/test/java/com/yogeshpaliyal/deepr/data/NetworkRepositoryTest.kt @@ -172,7 +172,8 @@ class NetworkRepositoryTest { val httpClient = HttpClient(mockEngine) { install(UserAgent) { - agent = "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36" + agent = + "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36" } } val htmlParser = HtmlParser()