Skip to content
Merged
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
10 changes: 10 additions & 0 deletions jetpacker/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@
<property
android:name="android.app.appfunctions.app_metadata"
android:resource="@xml/app_metadata" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

</manifest>
20 changes: 20 additions & 0 deletions jetpacker/android/app/src/main/res/xml/file_paths.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<paths>
<cache-path name="camera_captures" path="camera_captures/" />
<files-path name="documents" path="travel_documents/" />
</paths>
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import com.example.jetpacker.data.itinerary.DayThemeDao
import com.example.jetpacker.data.itinerary.EventDao
import com.example.jetpacker.data.itinerary.ExpenseDao
import com.example.jetpacker.data.itinerary.TourDetailDao
import com.example.jetpacker.data.itinerary.TravelDocumentDao
import com.example.jetpacker.data.itinerary.TravelDocumentManager
import com.example.jetpacker.data.trips.TripDao
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -68,8 +70,22 @@ object DatabaseModule {
return db.expenseDao()
}

@Provides
fun provideTravelDocumentDao(db: JetPackerDatabase): TravelDocumentDao {
return db.travelDocumentDao()
}

@Provides
fun provideAssistantConversationDao(db: JetPackerDatabase): AssistantConversationDao {
return db.assistantConversationDao()
}

@Provides
@Singleton
fun provideTravelDocumentManager(
@ApplicationContext context: Context,
travelDocumentDao: TravelDocumentDao,
): TravelDocumentManager {
return TravelDocumentManager(context, travelDocumentDao)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import com.example.jetpacker.data.itinerary.MuseumDetail
import com.example.jetpacker.data.itinerary.TimelineEvent
import com.example.jetpacker.data.itinerary.TourDetail
import com.example.jetpacker.data.itinerary.TourDetailDao
import com.example.jetpacker.data.itinerary.TravelDocument
import com.example.jetpacker.data.itinerary.TravelDocumentDao
import com.example.jetpacker.data.itinerary.VoiceNoteEntity
import com.example.jetpacker.data.trips.Trip
import com.example.jetpacker.data.trips.TripDao
Expand All @@ -51,6 +53,7 @@ import com.example.jetpacker.data.trips.TripDao
MuseumDetail::class,
Expense::class,
VoiceNoteEntity::class,
TravelDocument::class,
AssistantConversation::class,
],
version = 4,
Expand All @@ -67,5 +70,7 @@ abstract class JetPackerDatabase : RoomDatabase() {

abstract fun expenseDao(): ExpenseDao

abstract fun travelDocumentDao(): TravelDocumentDao

abstract fun assistantConversationDao(): AssistantConversationDao
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetpacker.data.itinerary

/**
* Provider interface for on-device semantic document search using ML Kit Embedding API
* (Embedding Gemma v2) and Jetpack AppSearch hybrid vector retrieval.
*/
interface DocumentSemanticSearchProvider {
/**
* Finds travel documents semantically relevant to [query].
*
* @param query The natural language user query.
* @param tripId Optional trip ID filter.
* @param maxResults Maximum number of documents to return.
*/
suspend fun findRelevantDocuments(
query: String,
tripId: String? = null,
maxResults: Int = 10,
): List<TravelDocument>

/** Clears any cached search indexes, vectors, or metadata. */
suspend fun clearIndex() {}

/** Synchronizes the search index with current stored documents. */
suspend fun syncIndex() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetpacker.data.itinerary

import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.UUID

/** Room entity representing a travel document such as a passport or visa. */
@Entity(tableName = "travel_documents")
data class TravelDocument(
@PrimaryKey @JvmField val id: String = UUID.randomUUID().toString(),
@JvmField val documentId: String = "",
@JvmField val documentType: String = "PASSPORT",
@JvmField val issuingCountry: String = "",
@JvmField val expiryDate: Long = 0L,
@JvmField val documentNumberMasked: String = "",
@JvmField val notes: String = "",
@JvmField val title: String = "",
@JvmField val filePath: String = "",
@JvmField val mimeType: String = "",
@JvmField val tripId: String? = null,
@JvmField val eventId: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetpacker.data.itinerary

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

/** Data Access Object (DAO) for managing travel documents such as passports and visas. */
@Dao
interface TravelDocumentDao {
@Query("SELECT * FROM travel_documents ORDER BY expiryDate ASC")
fun getAllDocuments(): Flow<List<TravelDocument>>

@Query(
"SELECT * FROM travel_documents WHERE UPPER(documentType) = UPPER(:documentType) ORDER BY expiryDate ASC"
)
fun getDocumentsByType(documentType: String): Flow<List<TravelDocument>>

@Query("SELECT * FROM travel_documents WHERE tripId = :tripId ORDER BY expiryDate ASC")
fun getDocumentsForTrip(tripId: String): Flow<List<TravelDocument>>

@Query("SELECT * FROM travel_documents WHERE eventId = :eventId ORDER BY expiryDate ASC")
fun getDocumentsForEvent(eventId: String): Flow<List<TravelDocument>>

@Query("SELECT * FROM travel_documents WHERE documentId = :documentId OR id = :documentId LIMIT 1")
suspend fun getDocumentById(documentId: String): TravelDocument?

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertDocument(document: TravelDocument)

@Query(
"UPDATE travel_documents SET eventId = :eventId, tripId = CASE WHEN :tripId IS NOT NULL THEN :tripId ELSE tripId END WHERE documentId = :documentId OR id = :documentId"
)
suspend fun linkDocumentToEvent(documentId: String, eventId: String?, tripId: String? = null)

@Query(
"UPDATE travel_documents SET tripId = :tripId WHERE documentId = :documentId OR id = :documentId"
)
suspend fun linkDocumentToTrip(documentId: String, tripId: String?)

@Delete suspend fun deleteDocument(document: TravelDocument)

@Query("DELETE FROM travel_documents WHERE id = :id") suspend fun deleteDocumentById(id: String)

@Query("DELETE FROM travel_documents") suspend fun deleteAllDocuments()
}
Loading
Loading