Initial commit
This commit is contained in:
commit
6072b2ba29
844 changed files with 220532 additions and 0 deletions
25
app/src/oss/java/com/aryan/reader/Auth.kt
Normal file
25
app/src/oss/java/com/aryan/reader/Auth.kt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// src\oss
|
||||
package com.aryan.reader
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
|
||||
class AuthRepository(private val applicationContext: Context) {
|
||||
|
||||
fun getSignedInUser(): UserData? {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun signIn(activityContext: Context): UserData? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun signOut() {
|
||||
}
|
||||
|
||||
fun observeAuthState(): Flow<UserData?> {
|
||||
return flowOf(null)
|
||||
}
|
||||
}
|
||||
52
app/src/oss/java/com/aryan/reader/BillingClientWrapper.kt
Normal file
52
app/src/oss/java/com/aryan/reader/BillingClientWrapper.kt
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// src\oss
|
||||
package com.aryan.reader
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.aryan.reader.data.ProductDetailsEntity
|
||||
import com.aryan.reader.data.PurchaseEntity
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
data class ProUpgradeState(
|
||||
val productDetails: ProductDetailsEntity? = null,
|
||||
val hasValidPurchase: Boolean = false,
|
||||
val activePurchases: List<PurchaseEntity> = emptyList(),
|
||||
val billingClientReady: Boolean = false,
|
||||
val error: String? = null,
|
||||
val isVerifying: Boolean = false
|
||||
)
|
||||
|
||||
class BillingClientWrapper(
|
||||
context: Context,
|
||||
private val externalScope: CoroutineScope,
|
||||
private val onPurchaseVerified: (PurchaseEntity) -> Unit
|
||||
) {
|
||||
private val _proUpgradeState = MutableStateFlow(ProUpgradeState())
|
||||
val proUpgradeState = _proUpgradeState.asStateFlow()
|
||||
|
||||
fun initializeConnection() {
|
||||
// No-op for OSS
|
||||
}
|
||||
|
||||
fun refreshPurchasesAsync() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
fun launchPurchaseFlow(activity: Activity) {
|
||||
_proUpgradeState.value = _proUpgradeState.value.copy(error = "Not available in Open Source version")
|
||||
}
|
||||
|
||||
fun clearError() {
|
||||
_proUpgradeState.value = _proUpgradeState.value.copy(error = null)
|
||||
}
|
||||
|
||||
fun clearVerificationState() {
|
||||
_proUpgradeState.value = _proUpgradeState.value.copy(isVerifying = false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PRO_LIFETIME_PRODUCT_ID = "episteme_pro_lifetime"
|
||||
}
|
||||
}
|
||||
20
app/src/oss/java/com/aryan/reader/GoogleDriveAuthManager.kt
Normal file
20
app/src/oss/java/com/aryan/reader/GoogleDriveAuthManager.kt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// src/oss
|
||||
package com.aryan.reader
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class GoogleDriveAuthManager(context: Context) {
|
||||
|
||||
val client = DummyClient()
|
||||
|
||||
class DummyClient {
|
||||
val signInIntent: Intent = Intent()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun hasDrivePermissions(account: Any?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/src/oss/java/com/aryan/reader/OcrEngine.kt
Normal file
19
app/src/oss/java/com/aryan/reader/OcrEngine.kt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package com.aryan.reader
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.aryan.reader.pdf.OcrLanguage
|
||||
import com.aryan.reader.pdf.ocr.OcrResult
|
||||
import timber.log.Timber
|
||||
|
||||
object OcrEngine {
|
||||
fun init(language: OcrLanguage) { Timber.d("OCR Init called in OSS flavor (No-op)")
|
||||
}
|
||||
|
||||
suspend fun extractTextFromBitmap(
|
||||
bitmap: Bitmap,
|
||||
onModelDownloading: () -> Unit
|
||||
): OcrResult? {
|
||||
Timber.d("OCR Extract called in OSS flavor (No-op)")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// src\oss
|
||||
package com.aryan.reader.data
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PurchaseVerificationRequest(
|
||||
val purchaseToken: String,
|
||||
val idToken: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VerificationResponse(
|
||||
val status: String,
|
||||
val message: String
|
||||
)
|
||||
|
||||
class CloudflareRepository {
|
||||
suspend fun verifyPurchase(purchaseToken: String): Result<VerificationResponse> {
|
||||
return Result.failure(Exception("Not available in OSS version"))
|
||||
}
|
||||
}
|
||||
67
app/src/oss/java/com/aryan/reader/data/FeedbackRepository.kt
Normal file
67
app/src/oss/java/com/aryan/reader/data/FeedbackRepository.kt
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// src\oss
|
||||
package com.aryan.reader.data
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FeedbackTextPayload(
|
||||
val message: String,
|
||||
val category: String,
|
||||
val context: Map<String, String>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FeedbackTextResponse(
|
||||
val status: String,
|
||||
val thread_ts: String? = null,
|
||||
val message: String? = null
|
||||
)
|
||||
|
||||
class FeedbackRepository(private val context: Context) {
|
||||
|
||||
private val firestoreRepository = FirestoreRepository()
|
||||
|
||||
fun sendFeedbackText(payload: FeedbackTextPayload): Result<FeedbackTextResponse> {
|
||||
return Result.failure(Exception("Feedback submission not available in OSS version"))
|
||||
}
|
||||
|
||||
fun sendFeedbackImage(imageUri: Uri, threadTs: String): Result<Unit> {
|
||||
return Result.failure(Exception("Feedback submission not available in OSS version"))
|
||||
}
|
||||
|
||||
// --- Feedback / Support System Stubs ---
|
||||
|
||||
fun listenForUnreadFeedback(userId: String, onUpdate: (Boolean) -> Unit): Any? {
|
||||
return firestoreRepository.listenForUnreadFeedback(userId, onUpdate)
|
||||
}
|
||||
|
||||
fun listenToFeedbackThreads(userId: String, onUpdate: (List<FeedbackThread>) -> Unit): Any? {
|
||||
return firestoreRepository.listenToFeedbackThreads(userId, onUpdate)
|
||||
}
|
||||
|
||||
fun listenToMessages(threadId: String, onUpdate: (List<FeedbackMessage>) -> Unit): Any? {
|
||||
return firestoreRepository.listenToFeedbackMessages(threadId, onUpdate)
|
||||
}
|
||||
|
||||
fun generateMessageId(): String {
|
||||
return firestoreRepository.generateMessageId()
|
||||
}
|
||||
|
||||
suspend fun createThread(userId: String, category: String, message: String, attachmentUris: List<Uri>): String {
|
||||
return firestoreRepository.createFeedbackThread(userId, category, message, emptyList())
|
||||
}
|
||||
|
||||
suspend fun addMessage(threadId: String, messageId: String, uid: String, message: String, sender: String, attachmentUris: List<Uri> = emptyList()) {
|
||||
firestoreRepository.addMessageToThread(threadId, messageId, uid, message, sender, emptyList())
|
||||
}
|
||||
|
||||
suspend fun markThreadAsRead(threadId: String) {
|
||||
firestoreRepository.markThreadAsRead(threadId)
|
||||
}
|
||||
|
||||
fun removeListener(listener: Any?) {
|
||||
firestoreRepository.removeListener(listener)
|
||||
}
|
||||
}
|
||||
137
app/src/oss/java/com/aryan/reader/data/FirestoreRepository.kt
Normal file
137
app/src/oss/java/com/aryan/reader/data/FirestoreRepository.kt
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// src\oss
|
||||
@file:Suppress("unused", "RedundantSuspendModifier")
|
||||
|
||||
package com.aryan.reader.data
|
||||
|
||||
import java.util.Date
|
||||
|
||||
// --- Data Classes used by ViewModel ---
|
||||
data class BookMetadata(
|
||||
val bookId: String = "",
|
||||
val title: String? = null,
|
||||
val author: String? = null,
|
||||
val displayName: String = "",
|
||||
val type: String = "",
|
||||
val lastPositionCfi: String? = null,
|
||||
val lastChapterIndex: Int? = null,
|
||||
val locatorBlockIndex: Int? = null,
|
||||
val locatorCharOffset: Int? = null,
|
||||
val lastPage: Int? = null,
|
||||
val progressPercentage: Float? = null,
|
||||
var isRecent: Boolean = true,
|
||||
var isDeleted: Boolean = false,
|
||||
val lastModifiedTimestamp: Long = 0L,
|
||||
val bookmarksJson: String? = null,
|
||||
val hasAnnotations: Boolean = false
|
||||
)
|
||||
|
||||
data class DeviceItem(
|
||||
val deviceId: String = "",
|
||||
val deviceName: String = "",
|
||||
val firstRegistered: Date? = null,
|
||||
val lastSeen: Date? = null,
|
||||
val status: String = "active"
|
||||
)
|
||||
|
||||
sealed class DeviceStatus {
|
||||
data object Active : DeviceStatus()
|
||||
data object Revoked : DeviceStatus()
|
||||
data object NotFound : DeviceStatus()
|
||||
data class Error(val exception: Exception) : DeviceStatus()
|
||||
}
|
||||
|
||||
data class FontMetadata(
|
||||
val id: String = "",
|
||||
val displayName: String = "",
|
||||
val fileName: String = "",
|
||||
val fileExtension: String = "",
|
||||
val timestamp: Long = 0L,
|
||||
var isDeleted: Boolean = false
|
||||
)
|
||||
|
||||
// --- Feedback Models Stubs ---
|
||||
data class FeedbackThread(
|
||||
val id: String = "",
|
||||
val uid: String = "",
|
||||
val category: String = "",
|
||||
val status: String = "open",
|
||||
val lastUpdated: Date? = null,
|
||||
var hasUnreadAdminReply: Boolean = false,
|
||||
val slackThreadTs: String? = null,
|
||||
val preview: String = ""
|
||||
)
|
||||
|
||||
data class FeedbackMessage(
|
||||
val id: String = "",
|
||||
val text: String = "",
|
||||
val sender: String = "",
|
||||
val timestamp: Date? = null,
|
||||
val attachments: List<String> = emptyList()
|
||||
)
|
||||
|
||||
|
||||
// --- Stub Class ---
|
||||
class FirestoreRepository {
|
||||
|
||||
// Helper to safely ignore listener removal in ViewModel
|
||||
fun removeListener(listener: Any?) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
fun listenToUserProfile(userId: String, onUpdate: (isPro: Boolean) -> Unit): Any? {
|
||||
// In OSS, user is never Pro. Return null as the "listener"
|
||||
onUpdate(false)
|
||||
return null
|
||||
}
|
||||
|
||||
fun getFcmToken(onComplete: (String?) -> Unit) {
|
||||
onComplete(null)
|
||||
}
|
||||
|
||||
suspend fun getDeviceStatus(userId: String, deviceId: String): DeviceStatus = DeviceStatus.NotFound
|
||||
suspend fun getRegisteredDevices(userId: String): List<DeviceItem> = emptyList()
|
||||
suspend fun updateDeviceLastSeen(userId: String, deviceId: String) {}
|
||||
suspend fun deleteDevice(userId: String, deviceId: String) {}
|
||||
suspend fun registerOrUpdateDevice(userId: String, deviceId: String, deviceName: String, fcmToken: String) {}
|
||||
suspend fun replaceDevice(userId: String, deviceToRemoveId: String, newDeviceId: String, newDeviceName: String, originDeviceId: String): Boolean = false
|
||||
|
||||
suspend fun syncFontMetadata(userId: String, font: FontMetadata) {}
|
||||
suspend fun getAllFonts(userId: String): List<FontMetadata> = emptyList()
|
||||
suspend fun deleteFontMetadata(userId: String, fontId: String) {}
|
||||
|
||||
suspend fun syncBookMetadata(userId: String, book: BookMetadata, originDeviceId: String) {}
|
||||
suspend fun getAllBooks(userId: String): List<BookMetadata> = emptyList()
|
||||
suspend fun getBookMetadata(userId: String, bookId: String): BookMetadata? = null
|
||||
|
||||
suspend fun syncShelf(userId: String, shelf: ShelfMetadata, originDeviceId: String) {}
|
||||
suspend fun getAllShelves(userId: String): List<ShelfMetadata> = emptyList()
|
||||
suspend fun getShelf(userId: String, shelfName: String): ShelfMetadata? = null
|
||||
|
||||
suspend fun deleteAllUserFirestoreData(userId: String) {}
|
||||
suspend fun updateFcmToken(userId: String, deviceId: String, token: String) {}
|
||||
|
||||
// --- Feedback Stubs ---
|
||||
fun listenForUnreadFeedback(userId: String, onUpdate: (Boolean) -> Unit): Any? {
|
||||
onUpdate(false)
|
||||
return null
|
||||
}
|
||||
|
||||
fun listenToFeedbackThreads(userId: String, onUpdate: (List<FeedbackThread>) -> Unit): Any? {
|
||||
onUpdate(emptyList())
|
||||
return null
|
||||
}
|
||||
|
||||
fun listenToFeedbackMessages(threadId: String, onUpdate: (List<FeedbackMessage>) -> Unit): Any? {
|
||||
onUpdate(emptyList())
|
||||
return null
|
||||
}
|
||||
|
||||
fun generateMessageId(): String {
|
||||
return java.util.UUID.randomUUID().toString()
|
||||
}
|
||||
|
||||
suspend fun createFeedbackThread(userId: String, category: String, initialMessage: String, attachments: List<String> = emptyList()): String = ""
|
||||
|
||||
suspend fun addMessageToThread(threadId: String, messageId: String, uid: String, text: String, sender: String, attachments: List<String>) {}
|
||||
suspend fun markThreadAsRead(threadId: String) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// src\oss
|
||||
package com.aryan.reader.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.aryan.reader.FileType
|
||||
import java.io.File
|
||||
|
||||
data class DriveFileList(
|
||||
val files: List<DriveFile> = emptyList()
|
||||
)
|
||||
|
||||
data class DriveFile(
|
||||
val id: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
data class ShelfMetadata(
|
||||
val name: String = "",
|
||||
val bookIds: List<String> = emptyList(),
|
||||
val lastModifiedTimestamp: Long = 0L,
|
||||
var isDeleted: Boolean = false
|
||||
)
|
||||
|
||||
class GoogleDriveRepository {
|
||||
|
||||
fun hasDrivePermissions(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun getAccessToken(context: Context): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun getFiles(accessToken: String): DriveFileList? {
|
||||
return DriveFileList(emptyList())
|
||||
}
|
||||
|
||||
suspend fun uploadAnnotationFile(accessToken: String, bookId: String, file: File): DriveFile? {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun downloadAnnotationFile(accessToken: String, bookId: String, destination: File): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun uploadFont(accessToken: String, fileName: String, file: File, extension: String): DriveFile? {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun uploadFile(accessToken: String, bookId: String, file: File, type: FileType): DriveFile? {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun downloadFile(accessToken: String, fileId: String, destination: File): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun deleteAllFiles(accessToken: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun deleteDriveFile(accessToken: String, fileId: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun getSignInIntent(context: Context): Intent {
|
||||
return Intent()
|
||||
}
|
||||
|
||||
fun isUserSignedInToDrive(context: Context): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun handleSignInResult(data: Intent?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.aryan.reader.data
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
|
||||
class PlatformFeaturesRepository(private val context: Context) {
|
||||
fun checkForUpdates(activity: Activity, launcher: ActivityResultLauncher<IntentSenderRequest>) {}
|
||||
|
||||
suspend fun requestReview(activity: Activity) { }
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// src\oss
|
||||
package com.aryan.reader.data
|
||||
|
||||
class RemoteConfigRepository {
|
||||
fun init() {
|
||||
// No-op for OSS
|
||||
}
|
||||
}
|
||||
17
app/src/oss/java/com/aryan/reader/data/SyncWorker.kt
Normal file
17
app/src/oss/java/com/aryan/reader/data/SyncWorker.kt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// src\oss
|
||||
package com.aryan.reader.data
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
|
||||
class SyncWorker(
|
||||
appContext: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : CoroutineWorker(appContext, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
// OSS version does not perform background cloud sync
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
4
app/src/oss/res/values/strings.xml
Normal file
4
app/src/oss/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Episteme (OSS)</string>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue