Skip to content
Open
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
6 changes: 5 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.devtools.ksp' version '2.3.4'
}

Expand Down Expand Up @@ -59,4 +58,9 @@ dependencies {

implementation "com.squareup.okhttp3:okhttp:5.3.2"
implementation "io.github.g00fy2:versioncompare:1.5.0"

def work_version = "2.9.1"
implementation "androidx.work:work-runtime-ktx:$work_version"

implementation 'com.github.bumptech.glide:glide:4.16.0'
}
25 changes: 25 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Theme.Memories.NoActionBar"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|density|screenSize|smallestScreenSize">
<intent-filter>
Expand All @@ -38,6 +39,30 @@
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>

<receiver
android:name=".widget.MemoriesWidget"
android:exported="true"
android:label="@string/widget_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="gallery.memories.widget.ACTION_REFRESH" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>

<activity
android:name=".widget.WidgetConfigActivity"
android:exported="true"
android:theme="@style/Theme.Memories"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
</application>

</manifest>
41 changes: 41 additions & 0 deletions android/app/src/main/java/gallery/memories/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ class MainActivity : AppCompatActivity() {
// Load JavaScript
initializeWebView()

// Handle widget deep link (open specific photo)
handleWidgetDeepLink(intent)

// Destroy video after 1 seconds (workaround for video not showing on first load)
binding.videoView.postDelayed({
binding.videoView.alpha = 1.0f
Expand Down Expand Up @@ -338,6 +341,44 @@ class MainActivity : AppCompatActivity() {
return false
}

/**
* Handle a widget deep link: if the intent has a photo subpath,
* navigate the webview to show that specific photo.
*/
private fun handleWidgetDeepLink(intent: Intent?) {
if (intent == null) return

// Server photo: navigate webview to the photo viewer
val subpath = intent.getStringExtra("gallery.memories.widget.EXTRA_PHOTO_SUBPATH")
if (!subpath.isNullOrBlank() && host != null) {
binding.webview.postDelayed({
nativex.http.loadWebView(binding.webview, subpath)
}, 1000) // delay to let the initial page load
return
}

// Local photo: open with system viewer
val localUri = intent.getStringExtra("gallery.memories.widget.EXTRA_LOCAL_PHOTO_URI")
if (!localUri.isNullOrBlank()) {
try {
val uri = Uri.parse(localUri)
val viewIntent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "image/*")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(viewIntent)
} catch (e: Exception) {
Log.w(TAG, "Failed to open local photo from widget", e)
}
}
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
handleWidgetDeepLink(intent)
}

fun initializePlayer(uris: Array<Uri>, uid: Long) {
if (player != null) {
if (playerUid == uid) return
Expand Down
15 changes: 15 additions & 0 deletions android/app/src/main/java/gallery/memories/dao/PhotoDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,24 @@ interface PhotoDao {
@Query("SELECT 1")
fun ping(): Int

@Query("SELECT COUNT(*) FROM photos")
fun getCount(): Int

@Query("SELECT dayid, COUNT(local_id) AS count FROM photos WHERE bucket_id IN (:bucketIds) AND has_remote = 0 GROUP BY dayid ORDER BY dayid DESC")
fun getDays(bucketIds: List<String>): List<Day>

@Query("SELECT * FROM photos WHERE bucket_id IN (:bucketIds) ORDER BY RANDOM() LIMIT 1")
fun getRandomPhoto(bucketIds: List<String>): Photo?

@Query("SELECT * FROM photos WHERE strftime('%m-%d', date_taken, 'unixepoch') = :date AND bucket_id IN (:bucketIds) ORDER BY date_taken DESC")
fun getOnThisDayPhotos(date: String, bucketIds: List<String>): List<Photo>

@Query("SELECT * FROM photos ORDER BY RANDOM() LIMIT 1")
fun getRandomPhotoAny(): Photo?

@Query("SELECT * FROM photos WHERE strftime('%m-%d', date_taken, 'unixepoch') = :date ORDER BY date_taken DESC")
fun getOnThisDayPhotosAny(date: String): List<Photo>

@Query("SELECT * FROM photos WHERE dayid=:dayId AND bucket_id IN (:buckets) AND has_remote = 0 ORDER BY date_taken DESC")
fun getPhotosByDay(dayId: Long, buckets: List<String>): List<Photo>

Expand Down
Loading