Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class CsvBookmarkImporter(
isFavourite = isFavourite,
createdAt = createdAt,
profileId = profileID,
openWithPackage = "",
)
val linkId = deeprQueries.lastInsertRowId().executeAsOne()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ interface LinkRepository {
notes: String,
thumbnail: String,
profileId: Long,
openWithPackage: String = "",
)

suspend fun lastInsertRowId(): Long?
Expand All @@ -99,6 +100,7 @@ interface LinkRepository {
notes: String,
thumbnail: String,
profileId: Long,
openWithPackage: String,
id: Long,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ class LinkRepositoryImpl(
notes: String,
thumbnail: String,
profileId: Long,
openWithPackage: String,
) {
withContext(Dispatchers.IO) {
deeprQueries.insertDeepr(link, name, openedCount, notes, thumbnail, profileId)
deeprQueries.insertDeepr(link, name, openedCount, notes, thumbnail, profileId, openWithPackage)
}
scheduleAutoBackup()
}
Expand All @@ -201,10 +202,11 @@ class LinkRepositoryImpl(
notes: String,
thumbnail: String,
profileId: Long,
openWithPackage: String,
id: Long,
) {
withContext(Dispatchers.IO) {
deeprQueries.updateDeeplink(newLink, newName, notes, thumbnail, profileId, id)
deeprQueries.updateDeeplink(newLink, newName, notes, thumbnail, profileId, openWithPackage, id)
}
scheduleAutoBackup()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ open class LocalServerRepositoryImpl(
isFavourite = deeplink.isFavourite,
createdAt = deeplink.createdAt,
profileId = 1L, // Default profile
openWithPackage = "",
)

val insertedId = deeprQueries.lastInsertRowId().executeAsOne()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.yogeshpaliyal.deepr.ui.screens.addlink

import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.animation.AnimatedVisibility
Expand Down Expand Up @@ -63,6 +66,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
import com.journeyapps.barcodescanner.ScanOptions
Expand All @@ -81,6 +85,7 @@ import compose.icons.TablerIcons
import compose.icons.tablericons.ArrowLeft
import compose.icons.tablericons.Check
import compose.icons.tablericons.Download
import compose.icons.tablericons.ExternalLink
import compose.icons.tablericons.Link
import compose.icons.tablericons.Note
import compose.icons.tablericons.Photo
Expand Down Expand Up @@ -213,6 +218,7 @@ fun AddLinkScreen(
deeprInfo.notes,
deeprInfo.thumbnail,
selectedProfileId,
deeprInfo.openWithPackage,
)
} else {
// Edit
Expand All @@ -224,10 +230,11 @@ fun AddLinkScreen(
deeprInfo.notes,
deeprInfo.thumbnail,
selectedProfileId,
deeprInfo.openWithPackage,
)
}
if (executeAfterSave) {
openDeeplink(context, normalizedLink)
openDeeplink(context, normalizedLink, deeprInfo.openWithPackage)
}
navigator.removeLast()
}
Expand Down Expand Up @@ -637,6 +644,144 @@ fun AddLinkScreen(
}
}

// Open With App Section
val resolvedApps = remember(deeprInfo.link) {
if (isValidDeeplink(deeprInfo.link)) {
val normalizedLink = normalizeLink(deeprInfo.link)
val intent = Intent(Intent.ACTION_VIEW, normalizedLink.toUri())
context.packageManager
.queryIntentActivities(intent, PackageManager.MATCH_ALL)
.distinctBy { it.activityInfo.packageName }
} else {
emptyList()
}
}

if (resolvedApps.isNotEmpty()) {
ElevatedCard(
modifier = Modifier.fillMaxWidth(),
colors =
CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.surface,
),
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Icon(
imageVector = TablerIcons.ExternalLink,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
Text(
text = stringResource(R.string.open_with_app),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
)
}

var openWithExpanded by remember { mutableStateOf(false) }
val appNotInstalledText = stringResource(R.string.app_not_installed)
val selectedAppLabel = remember(deeprInfo.openWithPackage, resolvedApps) {
if (deeprInfo.openWithPackage.isEmpty()) {
""
} else {
resolvedApps
.firstOrNull { it.activityInfo.packageName == deeprInfo.openWithPackage }
?.loadLabel(context.packageManager)
?.toString()
?: appNotInstalledText
}
}

ExposedDropdownMenuBox(
expanded = openWithExpanded,
onExpandedChange = { openWithExpanded = !openWithExpanded },
modifier = Modifier.fillMaxWidth(),
) {
OutlinedTextField(
value = selectedAppLabel,
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.select_app)) },
placeholder = { Text(stringResource(R.string.system_default)) },
modifier =
Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable, true),
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = openWithExpanded) },
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
shape = RoundedCornerShape(12.dp),
)

ExposedDropdownMenu(
expanded = openWithExpanded,
onDismissRequest = { openWithExpanded = false },
) {
// System default (no specific app)
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
if (deeprInfo.openWithPackage.isEmpty()) {
Icon(
imageVector = TablerIcons.Check,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.primary,
)
}
Text(stringResource(R.string.system_default))
}
},
onClick = {
deeprInfo = deeprInfo.copy(openWithPackage = "")
openWithExpanded = false
},
)

HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))

resolvedApps.forEach { resolveInfo ->
val packageName = resolveInfo.activityInfo.packageName
val appLabel = resolveInfo.loadLabel(context.packageManager).toString()

DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
if (packageName == deeprInfo.openWithPackage) {
Icon(
imageVector = TablerIcons.Check,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colorScheme.primary,
)
}
Text(appLabel)
}
},
onClick = {
deeprInfo = deeprInfo.copy(openWithPackage = packageName)
openWithExpanded = false
},
)
}
}
}
}
}
}

// Tags Section
ElevatedCard(
modifier = Modifier.fillMaxWidth(),
Expand Down Expand Up @@ -889,7 +1034,7 @@ fun AddLinkScreen(
FilledTonalButton(
modifier = Modifier.weight(1f),
onClick = {
isError = !openDeeplink(context, deeprInfo.link)
isError = !openDeeplink(context, deeprInfo.link, deeprInfo.openWithPackage)
},
shape = RoundedCornerShape(12.dp),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ fun Content(
when (it) {
is Click -> {
viewModel.incrementOpenedCount(it.item.id)
openDeeplink(context, it.item.link)
openDeeplink(context, it.item.link, it.item.openWithPackage)
analyticsManager.logEvent(
AnalyticsEvents.OPEN_LINK,
mapOf(AnalyticsParams.LINK_ID to it.item.id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fun createDeeprObject(
notes: String = "",
thumbnail: String = "",
profileId: Long = 1L,
openWithPackage: String = "",
): GetLinksAndTags =
GetLinksAndTags(
id = 0,
Expand All @@ -28,4 +29,5 @@ fun createDeeprObject(
notes = notes,
thumbnail = thumbnail,
profileId = profileId,
openWithPackage = openWithPackage,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ fun createShortcut(
useLinkBasedIcon: Boolean,
) {
if (isShortcutSupported(context)) {
val intent = Intent(Intent.ACTION_VIEW, deepr.link.toUri())
if (deepr.openWithPackage.isNotEmpty()) {
intent.setPackage(deepr.openWithPackage)
}
val shortcutInfo =
ShortcutInfoCompat
.Builder(context, "deepr_${deepr.id}")
.setShortLabel(shortcutName)
.setLongLabel(shortcutName)
.setIcon(getShortcutAppIcon(context, deepr.link, useLinkBasedIcon))
.setIntent(
Intent(Intent.ACTION_VIEW, deepr.link.toUri()),
).build()
.setIntent(intent)
.build()
if (alreadyExists) {
// If the shortcut already exists, we update it
ShortcutManagerCompat.updateShortcuts(context, listOf(shortcutInfo))
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/yogeshpaliyal/deepr/util/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ import java.util.Locale
fun openDeeplink(
context: Context,
link: String,
packageName: String = "",
): Boolean {
if (!isValidDeeplink(link)) return false
val normalizedLink = normalizeLink(link)
return try {
val intent = Intent(Intent.ACTION_VIEW, normalizedLink.toUri())
if (packageName.isNotEmpty()) {
intent.setPackage(packageName)
}
context.startActivity(intent)
true
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,11 @@ class AccountViewModel(
notes: String = "",
thumbnail: String = "",
profileId: Long? = null,
openWithPackage: String = "",
) {
viewModelScope.launch(Dispatchers.IO) {
val targetProfileId = profileId ?: selectedProfileId.first()
linkRepository.insertDeepr(link = link, name, if (executed) 1 else 0, notes, thumbnail, targetProfileId)
linkRepository.insertDeepr(link = link, name, if (executed) 1 else 0, notes, thumbnail, targetProfileId, openWithPackage)
linkRepository.lastInsertRowId()?.let {
modifyTagsForLink(it, tagsList)
analyticsManager.logEvent(
Expand Down Expand Up @@ -520,10 +521,11 @@ class AccountViewModel(
notes: String = "",
thumbnail: String = "",
profileId: Long? = null,
openWithPackage: String = "",
) {
viewModelScope.launch(Dispatchers.IO) {
val targetProfileId = profileId ?: selectedProfileId.first()
linkRepository.updateDeeplink(newLink, newName, notes, thumbnail, targetProfileId, id)
linkRepository.updateDeeplink(newLink, newName, notes, thumbnail, targetProfileId, openWithPackage, id)
modifyTagsForLink(id, tagsList)
syncToMarkdown()
analyticsManager.logEvent(
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
<string name="edit_shortcut">Edit shortcut</string>
<string name="show_qr_code">Show QR Code</string>
<string name="open_with">Open With</string>
<string name="open_with_app">Open with App</string>
<string name="select_app">Select app</string>
<string name="app_not_installed">App not installed</string>
<string name="share_link">Share link</string>
<string name="deeplink_already_exists">Link already exists</string>

Expand Down
8 changes: 5 additions & 3 deletions app/src/main/sqldelight/com/yogeshpaliyal/deepr/Deepr.sq
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ isFavourite INTEGER NOT NULL DEFAULT 0,
notes TEXT NOT NULL DEFAULT '',
thumbnail TEXT NOT NULL DEFAULT '',
profileId INTEGER NOT NULL DEFAULT 1,
openWithPackage TEXT NOT NULL DEFAULT '',
FOREIGN KEY (profileId) REFERENCES Profile(id) ON DELETE CASCADE
);

Expand Down Expand Up @@ -43,10 +44,10 @@ lastInsertRowId:
SELECT last_insert_rowid();

insertDeepr:
INSERT INTO Deepr (link, name, openedCount, notes, thumbnail, profileId) VALUES (?, ?, ?, ?, ?, ?);
INSERT INTO Deepr (link, name, openedCount, notes, thumbnail, profileId, openWithPackage) VALUES (?, ?, ?, ?, ?, ?, ?);

importDeepr:
INSERT INTO Deepr (link, name, openedCount, notes, thumbnail, isFavourite, createdAt, profileId) VALUES (?, ?, ?, ?, ?, ?, ?, ?);
INSERT INTO Deepr (link, name, openedCount, notes, thumbnail, isFavourite, createdAt, profileId, openWithPackage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);

getLinksAndTags:
SELECT
Expand All @@ -59,6 +60,7 @@ SELECT
Deepr.notes,
Deepr.thumbnail,
Deepr.profileId,
Deepr.openWithPackage,
DOL_Max.lastOpenedAt,
GROUP_CONCAT(Tags.name, ', ') AS tagsNames,
GROUP_CONCAT(Tags.id, ', ') AS tagsIds
Expand Down Expand Up @@ -187,7 +189,7 @@ resetOpenedCount:
UPDATE Deepr SET openedCount = 0 WHERE id = ?;

updateDeeplink:
UPDATE Deepr SET link = ? , name = ?, notes = ?, thumbnail = ?, profileId = ? WHERE id = ?;
UPDATE Deepr SET link = ? , name = ?, notes = ?, thumbnail = ?, profileId = ?, openWithPackage = ? WHERE id = ?;

countDeepr:
SELECT COUNT(*) FROM Deepr;
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/sqldelight/migrations/10.sqm
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add openWithPackage column to Deepr table to store preferred app package name for opening links
ALTER TABLE Deepr ADD COLUMN openWithPackage TEXT NOT NULL DEFAULT '';
Loading