Initial commit

This commit is contained in:
Aryan 2026-02-24 17:37:40 +05:30
commit 6072b2ba29
844 changed files with 220532 additions and 0 deletions

View 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)
}
}

View 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"
}
}

View 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
}
}
}

View 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
}
}

View file

@ -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"))
}
}

View 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)
}
}

View 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) {}
}

View file

@ -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
}
}

View file

@ -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) { }
}

View file

@ -0,0 +1,8 @@
// src\oss
package com.aryan.reader.data
class RemoteConfigRepository {
fun init() {
// No-op for OSS
}
}

View 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()
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Episteme (OSS)</string>
</resources>