Skip to content

Commit 3ac0450

Browse files
committed
GH-177 Add dynamic shortcuts for bookmarks
1 parent a49ed98 commit 3ac0450

File tree

8 files changed

+144
-14
lines changed

8 files changed

+144
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Ability to switch the order of items when viewing an album or a folder
13+
- App shortcuts for search bookmarks – by long pressing the gallery icon in the launcher,
14+
you'll see shortcuts which let you quickly open the gallery at a particular bookmark
1315

1416
### Fixed
1517

app/src/main/java/ua/com/radiokot/photoprism/PhotoPrismGallery.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import ua.com.radiokot.photoprism.features.ext.memories.logic.CancelDailyMemorie
3434
import ua.com.radiokot.photoprism.features.ext.memories.logic.ScheduleDailyMemoriesUpdatesUseCase
3535
import ua.com.radiokot.photoprism.features.ext.store.galleryExtensionStoreModule
3636
import ua.com.radiokot.photoprism.features.gallery.galleryFeatureModule
37+
import ua.com.radiokot.photoprism.features.gallery.logic.SearchBookmarkShortcutsManager
3738
import ua.com.radiokot.photoprism.features.importt.importFeatureModule
3839
import ua.com.radiokot.photoprism.features.importt.view.ImportActivity
3940
import ua.com.radiokot.photoprism.features.viewer.mediaViewerFeatureModule
@@ -88,6 +89,7 @@ class PhotoPrismGallery : Application() {
8889

8990
initRxErrorHandler()
9091
initLogging()
92+
initShortcuts()
9193
clearInternalDownloads()
9294

9395
loadSessionIfPresent()
@@ -147,6 +149,13 @@ class PhotoPrismGallery : Application() {
147149
get<UpdatePhotoFrameWidgetManifestComponentsUseCase>().invoke()
148150
}
149151

152+
private fun initShortcuts() {
153+
SearchBookmarkShortcutsManager(this)
154+
.syncShortcutsWithBookmarks(
155+
bookmarksRepository = get(),
156+
)
157+
}
158+
150159
override fun attachBaseContext(base: Context) =
151160
super.attachBaseContext(
152161
LocalizedContextFactory(base)

app/src/main/java/ua/com/radiokot/photoprism/features/gallery/data/storage/SearchBookmarksRepository.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ class SearchBookmarksRepository(
147147
fun findByConfig(config: SearchConfig): SearchBookmark? =
148148
itemsList.find { it.searchConfig == config }
149149

150+
fun findById(id: Long): SearchBookmark? =
151+
itemsList.find { it.id == id }
152+
150153
/**
151154
* Replaces all the bookmarks with given.
152155
*/
@@ -159,4 +162,4 @@ class SearchBookmarksRepository(
159162
.toCompletable()
160163
.subscribeOn(Schedulers.io())
161164
.andThen(updateDeferred())
162-
}
165+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package ua.com.radiokot.photoprism.features.gallery.logic
2+
3+
import android.app.Application
4+
import android.content.Intent
5+
import androidx.core.content.pm.ShortcutInfoCompat
6+
import androidx.core.content.pm.ShortcutManagerCompat
7+
import androidx.core.graphics.drawable.IconCompat
8+
import io.reactivex.rxjava3.disposables.Disposable
9+
import ua.com.radiokot.photoprism.R
10+
import ua.com.radiokot.photoprism.features.gallery.data.storage.SearchBookmarksRepository
11+
import ua.com.radiokot.photoprism.features.gallery.view.GalleryActivity
12+
13+
class SearchBookmarkShortcutsManager(
14+
private val application: Application,
15+
) {
16+
17+
/**
18+
* Synchronizes app dynamic shortcuts (ones appear on icon long press)
19+
* with the search bookmarks.
20+
*/
21+
fun syncShortcutsWithBookmarks(
22+
bookmarksRepository: SearchBookmarksRepository,
23+
): Disposable = bookmarksRepository
24+
.items
25+
.subscribe { bookmarks ->
26+
27+
ShortcutManagerCompat.setDynamicShortcuts(
28+
application,
29+
bookmarks.map { bookmark ->
30+
ShortcutInfoCompat.Builder(application, bookmark.id.toString())
31+
.setShortLabel(bookmark.name)
32+
.setIcon(
33+
IconCompat.createWithResource(
34+
application,
35+
R.drawable.ic_bookmark_shortcut
36+
)
37+
)
38+
.setIntent(
39+
Intent(application, GalleryActivity::class.java)
40+
.setAction(GalleryActivity.ACTION_BOOKMARK_SHORTCUT)
41+
.putExtra(GalleryActivity.BOOKMARK_ID_EXTRA, bookmark.id)
42+
)
43+
.build()
44+
}
45+
)
46+
}
47+
}

app/src/main/java/ua/com/radiokot/photoprism/features/gallery/search/view/model/GallerySearchViewModel.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import ua.com.radiokot.photoprism.features.gallery.data.model.GalleryMedia
1515
import ua.com.radiokot.photoprism.features.gallery.data.model.SearchBookmark
1616
import ua.com.radiokot.photoprism.features.gallery.data.model.SearchConfig
1717
import ua.com.radiokot.photoprism.features.gallery.data.storage.SearchBookmarksRepository
18-
import ua.com.radiokot.photoprism.features.gallery.search.data.storage.SearchPreferences
1918
import ua.com.radiokot.photoprism.features.gallery.search.albums.view.model.GallerySearchAlbumsViewModel
19+
import ua.com.radiokot.photoprism.features.gallery.search.data.storage.SearchPreferences
2020
import ua.com.radiokot.photoprism.features.gallery.search.people.view.model.GallerySearchPeopleViewModel
2121

2222
class GallerySearchViewModel(
@@ -99,6 +99,33 @@ class GallerySearchViewModel(
9999
&& !bookmarksRepository.isLoading
100100
&& areBookmarksCurrentlyMoving.value == false
101101

102+
fun applyBookmarkByIdAsync(
103+
bookmarkId: Long,
104+
) = bookmarksRepository
105+
.updateIfNotFreshDeferred()
106+
.doOnComplete {
107+
val bookmark = bookmarksRepository.findById(bookmarkId)
108+
109+
if (bookmark == null) {
110+
log.warn {
111+
"applyBookmarkByIdAsync(): bookmark_not_found:" +
112+
"\nbookmarkId=$bookmarkId"
113+
}
114+
return@doOnComplete
115+
}
116+
117+
log.debug {
118+
"applyBookmarkByIdAsync(): applying_bookmark:" +
119+
"\nbookmark=$bookmark"
120+
}
121+
122+
applySearchConfig(
123+
config = bookmark.searchConfig,
124+
)
125+
}
126+
.subscribeBy()
127+
.autoDispose(this)
128+
102129
private fun subscribeToBookmarks() {
103130
bookmarksRepository.items
104131
.observeOn(AndroidSchedulers.mainThread())

app/src/main/java/ua/com/radiokot/photoprism/features/gallery/view/GalleryActivity.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ class GalleryActivity : BaseActivity() {
190190
return
191191
}
192192

193-
viewModel.initViewingOnce()
193+
viewModel.initViewingOnce(
194+
requestedBookmarkId = getBookmarkId(intent),
195+
)
194196
}
195197

196198
rootView = ActivityGalleryBinding.inflate(layoutInflater)
@@ -1031,6 +1033,15 @@ class GalleryActivity : BaseActivity() {
10311033
.show()
10321034
}
10331035

1036+
override fun onNewIntent(intent: Intent?) {
1037+
super.onNewIntent(intent)
1038+
1039+
if (intent?.action == ACTION_BOOKMARK_SHORTCUT) {
1040+
getBookmarkId(intent)
1041+
?.also(viewModel.searchViewModel::applyBookmarkByIdAsync)
1042+
}
1043+
}
1044+
10341045
private var backPressResetDisposable: Disposable? = null
10351046
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
10361047
if (keyCode == KeyEvent.KEYCODE_BACK) {
@@ -1098,7 +1109,13 @@ class GalleryActivity : BaseActivity() {
10981109
)
10991110
}
11001111

1101-
private companion object {
1112+
companion object {
11021113
private const val FALLBACK_LIST_SIZE = 100
1114+
const val ACTION_BOOKMARK_SHORTCUT = "bookmark-shortcut"
1115+
const val BOOKMARK_ID_EXTRA = "bookmark-id"
1116+
1117+
private fun getBookmarkId(intent: Intent): Long? = intent
1118+
.getLongExtra(BOOKMARK_ID_EXTRA, Long.MIN_VALUE)
1119+
.takeIf { it != Long.MIN_VALUE }
11031120
}
11041121
}

app/src/main/java/ua/com/radiokot/photoprism/features/gallery/view/model/GalleryViewModel.kt

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,6 @@ class GalleryViewModel(
116116
searchViewModel.availableMediaTypes.value = allowedMediaTypes
117117
}
118118

119-
log.debug {
120-
"initSelectionForAppOnce(): initialized_selection:" +
121-
"\nrequestedMimeType=$requestedMimeType," +
122-
"\nallowMultiple=$allowMultiple," +
123-
"\nallowedMediaTypes=$allowedMediaTypes"
124-
}
125-
126119
stateSubject.onNext(
127120
State.Selecting.ForOtherApp(
128121
allowedMediaTypes = allowedMediaTypes,
@@ -148,12 +141,22 @@ class GalleryViewModel(
148141
},
149142
)
150143
}
144+
151145
initCommon()
152146

147+
log.debug {
148+
"initSelectionForAppOnce(): initialized_selection:" +
149+
"\nrequestedMimeType=$requestedMimeType," +
150+
"\nallowMultiple=$allowMultiple," +
151+
"\nallowedMediaTypes=$allowedMediaTypes"
152+
}
153+
153154
isInitialized = true
154155
}
155156

156-
fun initViewingOnce() {
157+
fun initViewingOnce(
158+
requestedBookmarkId: Long?,
159+
) {
157160
if (isInitialized) {
158161
log.debug {
159162
"initViewingOnce(): already_initialized"
@@ -162,8 +165,10 @@ class GalleryViewModel(
162165
return
163166
}
164167

165-
log.debug {
166-
"initViewingOnce(): initialized_viewing"
168+
if (requestedBookmarkId != null) {
169+
searchViewModel.applyBookmarkByIdAsync(
170+
bookmarkId = requestedBookmarkId,
171+
)
167172
}
168173

169174
stateSubject.onNext(State.Viewing)
@@ -181,15 +186,22 @@ class GalleryViewModel(
181186
repositoryToPostFrom == currentMediaRepository
182187
},
183188
)
189+
184190
initCommon()
185191

192+
log.debug {
193+
"initViewingOnce(): initialized_viewing:" +
194+
"\nrequestedBookmarkId=$requestedBookmarkId"
195+
}
196+
186197
isInitialized = true
187198
}
188199

189200
private fun initCommon() {
190201
subscribeToSearch()
191202
subscribeToFastScroll()
192203
subscribeToRepositoryChanges()
204+
193205
resetRepositoryAndSearchConfig()
194206
}
195207

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="478" android:viewportWidth="478" android:width="24dp">
2+
3+
<group>
4+
5+
<clip-path android:pathData="M0,0h478v478H0z"/>
6+
7+
<path android:fillColor="#E1E0FF" android:pathData="M239,239m-239,0a239,239 0,1 1,478 0a239,239 0,1 1,-478 0"/>
8+
9+
<path android:fillColor="#5256A9" android:fillType="evenOdd" android:pathData="M311.7,120.8a29.6,29.6 0,0 1,29.5 29.6v177.1c0,11 -5.7,20.7 -15.3,26 -9.7,5.2 -21,4.8 -30.2,-1.1l-50.5,-32.5a7.8,7.8 0,0 0,-8.4 0l-50.5,32.5a29.2,29.2 0,0 1,-30.1 1,29.3 29.3,0 0,1 -15.4,-25.9v-177a29.6,29.6 0,0 1,29.5 -29.7h141.4ZM241,164a6.4,6.4 0,0 0,-5.8 3.6l-13.8,29 -32,4a6.4,6.4 0,0 0,-3.5 11l23.4,22 -6,31.4a6.4,6.4 0,0 0,9.4 6.8l28.3,-15.3 28.2,15.3c1,0.5 2,0.8 3.1,0.8a6.4,6.4 0,0 0,6.3 -7.6l-5.9,-31.4 23.4,-22a6.4,6.4 0,0 0,-3.6 -11l-31.9,-4 -13.8,-29a6.4,6.4 0,0 0,-5.8 -3.6Z"/>
10+
11+
</group>
12+
13+
</vector>

0 commit comments

Comments
 (0)