### Basic Repository Setup in Kotlin Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo/README.md Demonstrates how to set up a base repository class by extending `GenericBaseRepo`. This involves providing implementations for `getRetrofitClient`, `logger` (FlexiLogger), and specifying the `loggingLevel`. ```kotlin abstract class BaseRepo : GenericBaseRepo( getRetrofitClient = { RetrofitClient }, logger = Log, //Your Implementation of FlexiLogger loggingLevel = LoggingLevel.V// Set desired logging level ) ``` -------------------------------- ### Initialize S3Uploader Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader-Multipart/README.md Initializes the S3Uploader with a token provider. This setup is necessary for the uploader to obtain authentication tokens when needed. ```kotlin S3Uploader.initS3Uploader( tokenProvider = { authManager.getToken() } ) ``` -------------------------------- ### Initialize S3Uploader in Application Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md Initialize the S3Uploader in your Application class's onCreate method. This setup requires a token provider for authentication, a logging level, and a custom logger implementation. ```kotlin class MyApp: Application() { override fun onCreate() { super.onCreate() initS3Uploader() } private fun initS3Uploader() { S3Uploader.initS3Uploader( tokenProvider = { // Provide your API auth token if needed }, loggingLevel = if (isDebug) LoggingLevel.W else LoggingLevel.NONE, logger = // Your implementation of FlexiLogger ) } } ``` -------------------------------- ### Install BaseRepo-S3Uploader with Dependencies Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-S3Uploader/README.md This Gradle Kotlin DSL snippet shows how to include the BaseRepo-S3Uploader module along with its required base modules (BaseRepo and S3Uploader) in your project. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:S3Uploader:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo-S3Uploader:1.2.4") ``` -------------------------------- ### Install S3Uploader Dependency Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md Add the S3Uploader library to your project's Gradle build file. This dependency enables the S3 file uploading functionality. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:S3Uploader:1.2.4") ``` -------------------------------- ### Customize Empty State UI with CompositionLocalProvider Source: https://github.com/appoly/appolydroid-toolbox/blob/main/PagingExtensions/README.md Provides an example of customizing the empty state UI using `CompositionLocalProvider` and `EmptyStateTextProvider`. This allows for defining a custom composable to display when the data list is empty. ```kotlin CompositionLocalProvider( LocalEmptyState provides object : EmptyStateTextProvider { @Composable override fun EmptyStateText(modifier: Modifier, text: String) { // Your custom empty state UI implementation Column( modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally ) { Icon( Icons.Filled.SearchOff, contentDescription = null, tint = MaterialTheme.colorScheme.secondary ) Text( text = text, style = MaterialTheme.typography.bodyLarge ) } } } ) { // Your composables that will use the custom empty state MyPagingList() } ``` -------------------------------- ### Install LazyGridPagingExtensions with PagingExtensions and Jetpack Paging Compose Source: https://github.com/appoly/appolydroid-toolbox/blob/main/LazyGridPagingExtensions/README.md This snippet shows the Gradle dependencies required to use the LazyGridPagingExtensions. It includes the base PagingExtensions module, the LazyGridPagingExtensions module itself, and the necessary Jetpack Paging Compose library. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:PagingExtensions:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:LazyGridPagingExtensions:1.2.4") // Make sure to include Jetpack Paging Compose implementation("androidx.paging:paging-compose:3.4.0") ``` -------------------------------- ### Kotlin Abstract Repository Setup for Appoly JSON Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-AppolyJson/README.md Defines an abstract repository class extending AppolyBaseRepo, which is part of the BaseRepo-AppolyJson module. It requires Retrofit client and a logger implementation, and allows setting the logging level. ```kotlin abstract class AppolyRepo : AppolyBaseRepo( getRetrofitClient = { RetrofitClient }, logger = Log, //Your Implementation of FlexiLogger loggingLevel = LoggingLevel.V // Set desired logging level ) ``` -------------------------------- ### Install PagingExtensions with Gradle Kotlin DSL Source: https://github.com/appoly/appolydroid-toolbox/blob/main/PagingExtensions/README.md This snippet shows how to add the PagingExtensions library to your project using Gradle's Kotlin DSL. It specifies the dependency for the PagingExtensions module. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:PagingExtensions:1.2.4") ``` -------------------------------- ### Install DateHelperUtil-Serialization and kotlinx.serialization Dependencies Source: https://github.com/appoly/appolydroid-toolbox/blob/main/DateHelperUtil-Serialization/README.md This snippet shows how to add the necessary dependencies for DateHelperUtil-Serialization and kotlinx.serialization to your project's build.gradle.kts file. It requires the base DateHelperUtil module and the kotlinx.serialization core and JSON modules. ```gradle // Requires base DateHelperUtil module implementation("com.github.appoly.AppolyDroid-Toolbox:DateHelperUtil:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:DateHelperUtil-Serialization:1.2.4") // Required kotlinx.serialization dependencies implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.10.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0") ``` -------------------------------- ### Repository Implementation with Date/Time Handling (Kotlin) Source: https://github.com/appoly/appolydroid-toolbox/blob/main/DateHelperUtil-Room/README.md This Kotlin repository example demonstrates how to interact with the `UserDao` using Java 8 date and time types. It shows creating users with current timestamps and querying users based on date ranges, with Room and the converters managing the underlying data transformations. ```kotlin class UserRepository(private val userDao: UserDao) { // Get all users registered in the past week fun getRecentUsers(): Flow> { val oneWeekAgo = LocalDateTime.now().minusWeeks(1) return userDao.getUsersRegisteredAfter(oneWeekAgo) } // Get users born in a specific year suspend fun getUsersBornInYear(year: Int): List { val startDate = LocalDate.of(year, 1, 1) val endDate = LocalDate.of(year, 12, 31) return userDao.getUsersBornBetween(startDate, endDate) } // Create a new user with current registration time suspend fun createUser(username: String, email: String, birthDate: LocalDate?): Long { val user = UserEntity( id = 0, // Room will assign the actual ID username = username, email = email, birthDate = birthDate, registrationDate = LocalDateTime.now(), lastLoginDate = ZonedDateTime.now() // Current time with timezone info ) return userDao.insertUser(user) } // Update a user's last login time suspend fun recordUserLogin(userId: Long) { userDao.updateLastLogin(userId, ZonedDateTime.now()) } } ``` -------------------------------- ### AppSnackBar Setup and Usage in Jetpack Compose Source: https://context7.com/appoly/appolydroid-toolbox/llms.txt Demonstrates how to set up AppSnackBar within a Jetpack Compose application, including providing custom colors via CompositionLocalProvider and displaying different snackbar types (Success, Error, Info) using a SnackbarHostState. It also shows how to integrate with UiState for automatic snackbar display based on state changes. ```kotlin import uk.co.appoly.droid.ui.snackbar.AppSnackBar import uk.co.appoly.droid.ui.snackbar.SnackBarType import uk.co.appoly.droid.ui.snackbar.showSnackbar import uk.co.appoly.droid.ui.snackbar.AppSnackBarColors import uk.co.appoly.droid.ui.snackbar.LocalAppSnackBarColors import uk.co.appoly.droid.ui.snackbar.snackBarType import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Text import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue // Basic setup with custom colors @Composable fun MyApp() { val snackbarHostState = remember { SnackbarHostState() } // Provide custom colors CompositionLocalProvider( LocalAppSnackBarColors provides AppSnackBarColors( info = Color(0xFF2196F3), success = Color(0xFF4CAF50), error = Color(0xFFF44336) ) ) { Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) { data -> AppSnackBar(snackbarData = data) } } ) { padding -> MainContent( modifier = Modifier.padding(padding), snackbarHostState = snackbarHostState ) } } } // Show typed snackbars @Composable fun MainContent( modifier: Modifier, snackbarHostState: SnackbarHostState ) { val scope = rememberCoroutineScope() Column(modifier) { Button(onClick = { scope.launch { snackbarHostState.showSnackbar( message = "Operation completed successfully!", type = SnackBarType.Success ) } }) { Text("Show Success") } Button(onClick = { scope.launch { snackbarHostState.showSnackbar( message = "Something went wrong", type = SnackBarType.Error, actionLabel = "Retry", withDismissAction = true ) } }) { Text("Show Error") } Button(onClick = { scope.launch { snackbarHostState.showSnackbar( message = "New update available", type = SnackBarType.Info ) } }) { Text("Show Info") } } } // Integration with UiState (requires AppSnackBar-UiState module) @Composable fun FormWithSnackbar(viewModel: FormViewModel, snackbarHostState: SnackbarHostState) { val state by viewModel.submitState.collectAsState() val scope = rememberCoroutineScope() LaunchedEffect(state) { when { state.isSuccess() -> { snackbarHostState.showSnackbar( message = "Form submitted!", type = state.snackBarType // Automatically maps to SnackBarType.Success ) } state.isError() -> { snackbarHostState.showSnackbar( message = state.message, type = state.snackBarType // Automatically maps to SnackBarType.Error ) } } } // Form content... } // Dummy ViewModel and UiState classes for compilation class FormViewModel { val submitState = androidx.compose.runtime.MutableStateFlow("", null) fun isSuccess() = false fun isError() = false val message = "" val snackBarType: SnackBarType? = null } interface UiState { fun isSuccess(): Boolean fun isError(): Boolean val message: String? val snackBarType: SnackBarType? } fun UiState.isSuccess(): Boolean = this.snackBarType == SnackBarType.Success fun UiState.isError(): Boolean = this.snackBarType == SnackBarType.Error ``` -------------------------------- ### Install AppolyDroid Toolbox using JitPack Source: https://context7.com/appoly/appolydroid-toolbox/llms.txt This snippet demonstrates how to add the AppolyDroid Toolbox library to your Android project by configuring the JitPack repository in your `settings.gradle.kts` and then including the desired modules using the Bill of Materials (BOM) in your `build.gradle.kts` for version alignment. ```kotlin // settings.gradle.kts dependencyResolutionManagement { repositories { maven { url = uri("https://jitpack.io") } } } // build.gradle.kts dependencies { // Import the BOM implementation(platform("com.github.appoly.AppolyDroid-Toolbox:AppolyDroid-Toolbox-bom:1.2.4")) // Add modules without specifying versions implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo") implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo-AppolyJson") implementation("com.github.appoly.AppolyDroid-Toolbox:UiState") implementation("com.github.appoly.AppolyDroid-Toolbox:AppSnackBar") implementation("com.github.appoly.AppolyDroid-Toolbox:DateHelperUtil") implementation("com.github.appoly.AppolyDroid-Toolbox:S3Uploader") implementation("com.github.appoly.AppolyDroid-Toolbox:PagingExtensions") implementation("com.github.appoly.AppolyDroid-Toolbox:LazyListPagingExtensions") implementation("com.github.appoly.AppolyDroid-Toolbox:SegmentedControl") } ``` -------------------------------- ### Get Pre-signed URL and Upload File Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md A repository method to first obtain a pre-signed URL from a backend API based on file type, and then upload the file using that URL. It handles potential exceptions during both the URL retrieval and the upload process. ```kotlin class FileUploadRepository { private val apiService: ApiService // Your API service interface suspend fun getPresignedUrl(fileType: String): String { val response = apiService.getPresignedUploadUrl(fileType) return response.presignedUrl } suspend fun uploadFile(file: File, progressFlow: MutableStateFlow? = null): Result { return try { val presignedUrl = getPresignedUrl(file.extension) val uploadResult = S3Uploader.uploadFile( presignedUrl = presignedUrl, file = file, progressMutableFlow = progressFlow ) Result.success(uploadResult) } catch (e: Exception) { Result.failure(e) } } } ``` -------------------------------- ### Advanced LazyColumn with Placeholders and Custom Keys Source: https://github.com/appoly/appolydroid-toolbox/blob/main/LazyListPagingExtensions/README.md An advanced example showcasing `lazyPagingItemsStates` with placeholder support, custom item keys, content types for recycling, and a custom retry action. This allows for more control over UI rendering and performance. ```kotlin @Composable fun AdvancedItemsList(viewModel: AdvancedViewModel) { val items = viewModel.itemsFlow.collectAsLazyPagingItems() LazyColumn { lazyPagingItemsStates( lazyPagingItems = items, usingPlaceholders = true, // Enable placeholders support emptyText = { "No items found" }, errorText = { error -> error.localizedMessage ?: "An error occurred" }, retry = { items.retry() }, // Custom retry action itemKey = { it.id }, // Custom key function itemContentType = { it.type }, // Content type for recycling itemPlaceholderContent = { // Custom placeholder UI ItemPlaceholder() } ) { item -> // Item UI ItemRow(item = item) } } } ``` -------------------------------- ### Advanced LazyVerticalGrid with Custom Spans and Placeholders Source: https://github.com/appoly/appolydroid-toolbox/blob/main/LazyGridPagingExtensions/README.md An advanced example showcasing `lazyPagingItemsWithStates` with custom configurations. It enables placeholder support, defines custom retry and item key functions, and implements item-specific spanning logic for full-width items. It also includes custom placeholder UI and content type definition. ```kotlin @Composable fun AdvancedItemsGrid(viewModel: AdvancedViewModel) { val items = viewModel.itemsFlow.collectAsLazyPagingItems() LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 128.dp) ) { lazyPagingItemsWithStates( lazyPagingItems = items, usingPlaceholders = true, // Enable placeholders support emptyText = { "No items found" }, errorText = { error -> error.localizedMessage ?: "An error occurred" }, retry = { items.retry() }, // Custom retry action itemKey = { it.id }, // Custom key function // Custom span based on item properties itemSpan = { item -> if (item != null && item.isFullWidth) { GridItemSpan(maxLineSpan) // Full width for featured items } else { GridItemSpan(1) // Default span } }, itemContentType = { it.type }, // Content type for recycling placeholderItemContent = { // Custom placeholder UI ItemPlaceholder() } ) { // Item UI ItemCard(item = item) } } } ``` -------------------------------- ### Configure and Manage Multipart S3 Uploads in Kotlin Source: https://context7.com/appoly/appolydroid-toolbox/llms.txt This Kotlin code demonstrates how to configure API endpoints, initialize and manage multipart S3 uploads using S3Uploader-Multipart. It includes functionalities for starting, pausing, resuming, and canceling uploads, along with observing upload progress. Dependencies include the S3Uploader library and Android Jetpack components like ViewModel and Coroutines. ```kotlin import uk.co.appoly.droid.s3upload.multipart.MultipartUploadManager import uk.co.appoly.droid.s3upload.multipart.MultipartApiUrls import uk.co.appoly.droid.s3upload.multipart.MultipartUploadConfig import uk.co.appoly.droid.s3upload.multipart.MultipartUploadProgress import uk.co.appoly.droid.s3upload.multipart.UploadSessionStatus import uk.co.appoly.droid.s3upload.multipart.worker.S3UploadWorkManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import java.io.File import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.foundation.layout.* import androidx.compose.material3.* // Assuming Material 3 import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import android.app.Application import android.content.Context // Configure API endpoints val apiUrls = MultipartApiUrls( initiateUrl = "https://api.example.com/s3/multipart/initiate", presignPartUrl = "https://api.example.com/s3/multipart/presign-part", completeUrl = "https://api.example.com/s3/multipart/complete", abortUrl = "https://api.example.com/s3/multipart/abort" ) // ViewModel with pause/resume support class MultipartUploadViewModel(application: Application) : AndroidViewModel(application) { private val uploadManager = MultipartUploadManager.getInstance( application, MultipartUploadConfig( chunkSize = 10 * 1024 * 1024L, // 10MB chunks maxConcurrentParts = 4, // Upload 4 parts simultaneously maxRetries = 5 ) ) private var sessionId: String? = null val progress: StateFlow = MutableStateFlow(null) fun startUpload(file: File) { viewModelScope.launch { uploadManager.startUpload(file, apiUrls) .onSuccess { id -> sessionId = id observeProgress(id) } .onFailure { error -> println("Failed to start: ${error.message}") } } } private fun observeProgress(sessionId: String) { viewModelScope.launch { uploadManager.observeProgress(sessionId).collect { (progress as MutableStateFlow).value = it } } } fun pause() { viewModelScope.launch { sessionId?.let { uploadManager.pauseUpload(it) } } } fun resume() { viewModelScope.launch { sessionId?.let { uploadManager.resumeUpload(it) } } } fun cancel() { viewModelScope.launch { sessionId?.let { uploadManager.cancelUpload(it) } } } } // Background uploads with WorkManager class BackgroundUploadService(private val context: Context) { fun scheduleUpload(file: File): String { return S3UploadWorkManager.scheduleUpload( context = context, file = file, apiUrls = apiUrls, requiresNetwork = true, requiresCharging = false ) } fun enableAutoRecovery() { S3UploadWorkManager.enableAutoRecovery(context) } } // Composable with full upload controls @Composable fun MultipartUploadScreen(viewModel: MultipartUploadViewModel) { val progress by viewModel.progress.collectAsState() Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { progress?.let { Text("Uploading: ${it.fileName}") LinearProgressIndicator( progress = { it.overallProgress / 100f }, modifier = Modifier.fillMaxWidth() ) Text("${it.overallProgress.toInt()}% - Parts: ${it.uploadedParts}/${it.totalParts}") Text("${it.uploadedBytes / 1024 / 1024}MB / ${it.totalBytes / 1024 / 1024}MB") Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { when (it.status) { UploadSessionStatus.IN_PROGRESS -> { Button(onClick = { viewModel.pause() }) { Text("Pause") } } UploadSessionStatus.PAUSED -> { Button(onClick = { viewModel.resume() }) { Text("Resume") } } UploadSessionStatus.FAILED -> { Text("Error: ${it.errorMessage}", color = Color.Red) Button(onClick = { viewModel.resume() }) { Text("Retry") } } UploadSessionStatus.COMPLETED -> { Text("Upload Complete!", color = Color.Green) } else -> {} } Button( onClick = { viewModel.cancel() }, colors = ButtonDefaults.buttonColors(containerColor = Color.Red) ) { Text("Cancel") } } } } } ``` -------------------------------- ### Basic AppSnackBar Setup in Jetpack Compose Source: https://github.com/appoly/appolydroid-toolbox/blob/main/AppSnackBar/README.md Integrate AppSnackBar into your Jetpack Compose UI by providing a custom SnackbarHost within your Scaffold. This allows you to use AppSnackBar for displaying messages. The example demonstrates showing a success snackbar. ```kotlin import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.material3.Button import androidx.compose.material3.Text import kotlinx.coroutines.launch @Composable fun MyScreen() { val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(hostState = snackbarHostState) { snackbarData -> // Use our custom AppSnackBar instead of default Snackbar AppSnackBar(snackbarData = snackbarData) } } ) { // Your content Button(onClick = { // Launch a coroutine to show the snackbar scope.launch { snackbarHostState.showSnackbar( message = "Operation successful!", type = SnackBarType.Success ) } }) { Text("Show Success Snackbar") } } } ``` -------------------------------- ### Backend API: Authentication Header Example Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader-Multipart/README.md Example of the Authorization header required for all backend API endpoints. It uses a Bearer token for authenticating requests from the mobile application. ```http Authorization: Bearer ``` -------------------------------- ### Initialize S3Uploader in Application Class Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-S3Uploader/README.md This Kotlin code demonstrates the initialization of the S3Uploader within your Application class. It requires a token provider for authentication, a logger implementation, and a logging level setting. ```kotlin class MyApp: Application() { override fun onCreate() { super.onCreate() // Initialize S3Uploader with your auth token provider S3Uploader.initS3Uploader( tokenProvider = { // Return your authentication token authManager.getToken() }, logger = YourFlexiLogImplementation, loggingLevel = if (BuildConfig.DEBUG) LoggingLevel.D else LoggingLevel.NONE ) } } ``` -------------------------------- ### Basic Date and Time Formatting in Kotlin Source: https://github.com/appoly/appolydroid-toolbox/blob/main/DateHelperUtil/README.md Demonstrates how to format local date and time objects into strings using the DateHelper utility. It also shows how to use extension functions for JSON and file string conversions. ```kotlin // Format local date time val dateString = DateHelper.formatLocalDateTime(localDateTime) // Format local date val dateString = DateHelper.formatLocalDate(localDate) // Format using extensions val jsonString = localDateTime.toJsonString() val fileString = localDateTime.toFileString() ``` -------------------------------- ### Build Commands for AppolyDroid Toolbox Source: https://github.com/appoly/appolydroid-toolbox/blob/main/CLAUDE.md This section details common Gradle build commands for the AppolyDroid Toolbox project. It covers building all modules, specific modules, running tests, publishing locally, and cleaning the build. ```bash # Build all modules ./gradlew build # Build specific module ./gradlew :BaseRepo:build # Run unit tests ./gradlew test # Run instrumented tests (requires device/emulator) ./gradlew connectedAndroidTest # Publish to local Maven (for testing) ./gradlew publishToMavenLocal # Clean build ./gradlew clean build ``` -------------------------------- ### Kotlin: Custom S3 Upload with Progress Tracking Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md Demonstrates how to upload a large file to S3 using a custom uploader class, including obtaining a pre-signed URL and tracking upload progress. It handles potential exceptions during the upload process. ```kotlin class CustomS3UploaderExample { suspend fun uploadLargeFile(file: File, progressFlow: MutableStateFlow): Result { return try { val presignedUrl = getPresignedUrl(file.name) val uploadResult = S3Uploader.uploadFile( presignedUrl = presignedUrl, file = file, progressMutableFlow = progressFlow, contentType = "application/octet-stream", chunkSize = 1024 * 1024, // 1MB chunks bufferSize = 8192 // 8KB buffer ) Result.success(uploadResult) } catch (e: Exception) { Result.failure(e) } } private suspend fun getPresignedUrl(fileName: String): String { // Implementation to get presigned URL return "" } } ``` -------------------------------- ### Complete File Upload with UI (Kotlin) Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md Demonstrates a complete file upload implementation including a ViewModel to manage upload state and progress, and a Composable UI screen to display the upload status. It utilizes StateFlow for reactive updates and handles different upload states like Idle, Uploading, Success, and Error. Dependencies include Jetpack Compose for UI and ViewModel for state management. ```kotlin class UploadViewModel : ViewModel() { private val repository: FileUploadRepository private val _uploadProgress = MutableStateFlow(0f) val uploadProgress: StateFlow = _uploadProgress private val _uploadState = MutableStateFlow(UploadState.Idle) val uploadState: StateFlow = _uploadState fun uploadImage(file: File) { _uploadState.value = UploadState.Uploading viewModelScope.launch { repository.uploadFile(file, _uploadProgress) .onSuccess { url -> _uploadState.value = UploadState.Success(url) } .onFailure { error -> _uploadState.value = UploadState.Error(error.message ?: "Upload failed") } } } sealed class UploadState { object Idle : UploadState() object Uploading : UploadState() data class Success(val downloadUrl: String) : UploadState() data class Error(val message: String) : UploadState() } } @Composable fun UploadScreen(viewModel: UploadViewModel) { val uploadProgress by viewModel.uploadProgress.collectAsState() val uploadState by viewModel.uploadState.collectAsState() Column( modifier = Modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { // Image picker or camera button Button(onClick = { /* Show image picker */ }) { Text("Select Image") } Spacer(modifier = Modifier.height(16.dp)) when (val state = uploadState) { is UploadViewModel.UploadState.Idle -> { // Idle state } is UploadViewModel.UploadState.Uploading -> { Text("Uploading... ${uploadProgress.toInt()}%") LinearProgressIndicator( progress = { uploadProgress / 100f }, modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp) ) } is UploadViewModel.UploadState.Success -> { Icon( imageVector = Icons.Default.CheckCircle, contentDescription = "Success", tint = Color.Green, modifier = Modifier.size(48.dp) ) Text("Upload Successful!") Text(state.downloadUrl, style = MaterialTheme.typography.bodySmall) } is UploadViewModel.UploadState.Error -> { Icon( imageVector = Icons.Default.Error, contentDescription = "Error", tint = Color.Red, modifier = Modifier.size(48.dp) ) Text("Upload Failed") Text(state.message, style = MaterialTheme.typography.bodySmall) Button(onClick = { /* Retry upload */ }) { Text("Retry") } } } } } ``` -------------------------------- ### Install SegmentedControl Library Source: https://github.com/appoly/appolydroid-toolbox/blob/main/SegmentedControl/README.md Add the SegmentedControl library to your project's Gradle dependencies to enable its use in your Jetpack Compose application. This is the primary step for integrating the component. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:SegmentedControl:1.2.4") ``` -------------------------------- ### Install AppSnackBar Dependency Source: https://github.com/appoly/appolydroid-toolbox/blob/main/AppSnackBar/README.md Add the AppSnackBar library to your project's Gradle dependencies. This line should be added to your app-level build.gradle file (or build.gradle.kts for Kotlin DSL). ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:AppSnackBar:1.2.4") ``` -------------------------------- ### Install DateHelperUtil Library Source: https://github.com/appoly/appolydroid-toolbox/blob/main/DateHelperUtil/README.md Add the DateHelperUtil library to your Android project using Gradle Kotlin DSL. This dependency provides the core functionality for date and time operations. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:DateHelperUtil:1.2.4") ``` -------------------------------- ### Install BaseRepo-S3Uploader-Multipart with Gradle Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-S3Uploader-Multipart/README.md This snippet shows how to add the BaseRepo-S3Uploader-Multipart module and its dependencies to your Android project using Gradle Kotlin DSL. Ensure you have the base modules and S3Uploader-Multipart already included. ```gradle.kts implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:S3Uploader-Multipart:1.2.4") implementation("com.github.appoly.AppolyDroid-Toolbox:BaseRepo-S3Uploader-Multipart:1.2.4") ``` -------------------------------- ### MultipartUploadManager API Reference Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader-Multipart/README.md Defines the interface for managing multipart uploads directly. It includes methods for starting, pausing, resuming, canceling uploads, observing progress, and handling recovery. ```kotlin class MultipartUploadManager { // Start a new upload suspend fun startUpload(file: File, apiUrls: MultipartApiUrls): Result // Control operations suspend fun pauseUpload(sessionId: String): Result suspend fun resumeUpload(sessionId: String): Result suspend fun cancelUpload(sessionId: String): Result // Progress observation fun observeProgress(sessionId: String): Flow fun observeAllUploads(): Flow> // Recovery suspend fun recoverInterruptedUploads(): List suspend fun cleanupOldSessions(olderThanMs: Long = 7 days): Int } ``` -------------------------------- ### Using UiState Smart Cast Extensions in Composable Source: https://github.com/appoly/appolydroid-toolbox/blob/main/UiState/README.md Illustrates how to leverage UiState's extension functions for smart casting within an Android Composable function. This simplifies checking the current UI state and accessing its properties. ```kotlin // In your Composable val uiState = viewModel.uiState.collectAsState().value // Smart casting with extension functions if (uiState.isError()) { // uiState is automatically cast to UiState.Error Text("Error: ${uiState.message}") } else if (uiState.isLoading()) { // uiState is automatically cast to UiState.Loading CircularProgressIndicator() } else if (uiState.isSuccess()) { // uiState is automatically cast to UiState.Success Text("Data loaded successfully!") } ``` -------------------------------- ### Custom Empty and Error State Spans in LazyVerticalGrid Source: https://github.com/appoly/appolydroid-toolbox/blob/main/LazyGridPagingExtensions/README.md This example demonstrates how to make the empty and error states span the entire width of the LazyVerticalGrid. It utilizes the `emptyTextSpan` and `errorTextSpan` parameters within `lazyPagingItemsWithStates` to achieve this full-width behavior. ```kotlin @Composable fun CustomEmptyStateGrid(viewModel: ItemsViewModel) { val items = viewModel.itemsFlow.collectAsLazyPagingItems() LazyVerticalGrid( columns = GridCells.Fixed(3) ) { lazyPagingItemsWithStates( lazyPagingItems = items, emptyText = { "No items found" }, // Make empty state span all columns emptyTextSpan = { GridItemSpan(maxLineSpan) }, errorText = { error -> error.localizedMessage ?: "An error occurred" }, // Make error state span all columns errorTextSpan = { GridItemSpan(maxLineSpan) } ) { ItemCard(item = item) } } } ``` -------------------------------- ### Initialize S3Uploader in Application Class (Kotlin) Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader-Multipart/README.md Initializes the S3Uploader singleton in the Application class. It requires a token provider function to obtain authentication tokens and allows configuration of the logging level based on build type. ```kotlin class MyApp : Application() { override fun onCreate() { super.onCreate() S3Uploader.initS3Uploader( tokenProvider = { authManager.getToken() }, loggingLevel = if (BuildConfig.DEBUG) LoggingLevel.D else LoggingLevel.NONE ) } } ``` -------------------------------- ### Date Calculations and Checks in Kotlin Source: https://github.com/appoly/appolydroid-toolbox/blob/main/DateHelperUtil/README.md Provides examples of performing date calculations and checks using extension functions. This includes verifying if a date is in the future or past, and converting between `LocalDateTime`/`LocalDate` and milliseconds. ```kotlin // Check if date is in the future val isFuture = localDateTime.isFuture() val isFuture = zonedDateTime.isFuture() // Check if date is in the past val isPassed = localDateTime.isPassed() val isPassed = zonedDateTime.isPassed() // Convert to/from milliseconds val millis = localDateTime.toMillis() val localDateTime = millis.millisToLocalDateTime() val localDate = millis.millisToLocalDate() ``` -------------------------------- ### Custom Loading State Provider with Jetpack Compose Source: https://context7.com/appoly/appolydroid-toolbox/llms.txt Provides a custom loading indicator UI using Jetpack Compose. It utilizes `CompositionLocalProvider` to supply a custom `LoadingStateProvider` implementation, allowing developers to define the appearance of the loading state, such as a `CircularProgressIndicator` with custom text. This requires the Jetpack Compose UI libraries and the `uk.co.appoly.droid.util.paging` library. ```kotlin import uk.co.appoly.droid.util.paging.isLoading import uk.co.appoly.droid.util.paging.isError import uk.co.appoly.droid.util.paging.PagingErrorType import uk.co.appoly.droid.util.paging.LocalLoadingState import uk.co.appoly.droid.util.paging.LocalErrorState import uk.co.appoly.droid.util.paging.LocalEmptyState import uk.co.appoly.droid.util.paging.LoadingStateProvider import uk.co.appoly.droid.util.paging.ErrorStateProvider import uk.co.appoly.droid.util.paging.EmptyStateTextProvider import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.paging.LoadState // Custom loading state provider @Composable fun CustomLoadingProvider(content: @Composable () -> Unit) { CompositionLocalProvider( LocalLoadingState provides object : LoadingStateProvider { @Composable override fun LoadingState(modifier: Modifier) { Column( modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally ) { CircularProgressIndicator(color = MaterialTheme.colorScheme.primary) Spacer(Modifier.height(8.dp)) Text("Loading...", style = MaterialTheme.typography.bodySmall) } } } ) { content() } } ``` -------------------------------- ### Customize Loading State UI with CompositionLocalProvider Source: https://github.com/appoly/appolydroid-toolbox/blob/main/PagingExtensions/README.md Shows how to customize the loading indicator's appearance using `CompositionLocalProvider` and a custom `LoadingStateProvider`. This allows for defining your own composable UI for the loading state. ```kotlin CompositionLocalProvider( LocalLoadingState provides object : LoadingStateProvider { @Composable override fun LoadingState(modifier: Modifier) { // Your custom loading UI implementation Column( modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally ) { CircularProgressIndicator() Text("Loading...", style = MaterialTheme.typography.bodySmall) } } } ) { // Your composables that will use the custom loading state MyPagingList() } ``` -------------------------------- ### Basic File Upload with Progress Tracking Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader/README.md Upload a file to S3 using a pre-signed URL and track the upload progress. This function returns a Result containing the upload URL on success or an exception on failure. ```kotlin class FileUploadViewModel : ViewModel() { val uploadProgress = MutableStateFlow(0f) suspend fun uploadFile(file: File): Result { return try { val uploadResult = S3Uploader.uploadFile( presignedUrl = "https://your-s3-presigned-url", file = file, progressMutableFlow = uploadProgress ) Result.success(uploadResult) } catch (e: Exception) { Result.failure(e) } } } ``` -------------------------------- ### AppSnackBar Integration with Material 3 Scaffold Source: https://github.com/appoly/appolydroid-toolbox/blob/main/AppSnackBar/README.md A complete example showing how to integrate AppSnackBar within a Material 3 `Scaffold`. This includes setting up the `SnackbarHost` and demonstrating how to trigger different types of snackbars via button clicks. ```kotlin import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun MyScreen() { val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(hostState = snackbarHostState) { AppSnackBar(snackbarData = it) } } ) { Column( modifier = Modifier .fillMaxSize() .padding(it) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { scope.launch { snackbarHostState.showSnackbar( message = "This is an information message", type = SnackBarType.Info ) } } ) { Text("Show Info Snackbar") } Button( onClick = { scope.launch { snackbarHostState.showSnackbar( message = "Operation successful!", type = SnackBarType.Success ) } } ) { Text("Show Success Snackbar") } Button( onClick = { scope.launch { snackbarHostState.showSnackbar( message = "An error occurred", type = SnackBarType.Error ) } } ) { Text("Show Error Snackbar") } } } } ``` -------------------------------- ### Basic UI State Management in ViewModel Source: https://github.com/appoly/appolydroid-toolbox/blob/main/UiState/README.md Demonstrates how to use UiState within an Android ViewModel to manage loading, success, and error states for data fetching. It utilizes MutableStateFlow to expose the UI state. ```kotlin class MyViewModel : ViewModel() { private val _uiState = MutableStateFlow(UiState.Idle()) val uiState = _uiState.asStateFlow() fun loadData() { // Update state to Loading _uiState.value = UiState.Loading() viewModelScope.launch { try { val result = repository.fetchData() // Update state to Success _uiState.value = UiState.Success() } catch (e: Exception) { // Update state to Error with message _uiState.value = UiState.Error("Failed to load data: ${e.message}") } } } } ``` -------------------------------- ### Manage Multipart Uploads (Kotlin) Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-S3Uploader-Multipart/README.md Provides extension functions for managing multipart uploads. These include starting a new upload, pausing, resuming, and canceling active uploads. They operate on a GenericBaseRepo and return an APIResult indicating success or failure. ```kotlin suspend fun GenericBaseRepo.startMultipartUpload( context: Context, file: File, apiUrls: MultipartApiUrls, config: MultipartUploadConfig = MultipartUploadConfig.DEFAULT ): APIResult suspend fun GenericBaseRepo.pauseMultipartUpload( context: Context, sessionId: String ): APIResult suspend fun GenericBaseRepo.resumeMultipartUpload( context: Context, sessionId: String ): APIResult suspend fun GenericBaseRepo.cancelMultipartUpload( context: Context, sessionId: String ): APIResult ``` -------------------------------- ### Basic Multipart Upload with MultipartUploadManager (Kotlin) Source: https://github.com/appoly/appolydroid-toolbox/blob/main/S3Uploader-Multipart/README.md Demonstrates how to initiate a basic multipart upload using MultipartUploadManager. It requires the application context, a File object to upload, and a configuration of API URLs for different upload stages. The upload progress can be observed via a session ID. ```kotlin class UploadViewModel(application: Application) : AndroidViewModel(application) { private val uploadManager = MultipartUploadManager.getInstance(application) fun startUpload(file: File) { viewModelScope.launch { val apiUrls = MultipartApiUrls( initiateUrl = "https://api.example.com/s3/multipart/initiate", presignPartUrl = "https://api.example.com/s3/multipart/presign-part", completeUrl = "https://api.example.com/s3/multipart/complete", abortUrl = "https://api.example.com/s3/multipart/abort" ) val result = uploadManager.startUpload(file, apiUrls) result.onSuccess { sessionId -> // Upload started, observe progress observeProgress(sessionId) }.onFailure { error -> // Handle error Log.e("Upload", "Failed to start upload", error) } } } private fun observeProgress(sessionId: String) { viewModelScope.launch { uploadManager.observeProgress(sessionId).collect { it?.let { Log.d("Upload", "Progress: ${it.overallProgress}%") Log.d("Upload", "Parts: ${it.uploadedParts}/${it.totalParts}") } } } } } ``` -------------------------------- ### Indexed Paging List Access (Kotlin) Source: https://context7.com/appoly/appolydroid-toolbox/llms.txt Provides an example of using `lazyPagingItemsIndexedStates` for Jetpack Paging 3 integration with LazyColumn, allowing access to both the index and the item. This is useful for displaying item numbers or performing index-based operations. Requires `collectAsLazyPagingItems`. ```kotlin import uk.co.appoly.droid.util.paging.lazyPagingItemsIndexedStates import androidx.compose.foundation.layout.Row import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.paging.compose.collectAsLazyPagingItems // Assuming ItemsViewModel and ItemRow are defined elsewhere // @Composable // fun ItemRow(item: YourItemType) { /* ... */ } // class ItemsViewModel { val itemsFlow: Flow> = ... } @Composable fun NumberedList(viewModel: ItemsViewModel) { val items = viewModel.itemsFlow.collectAsLazyPagingItems() LazyColumn { lazyPagingItemsIndexedStates( lazyPagingItems = items, emptyText = { "No items" }, errorText = { it.localizedMessage ?: "Error" } ) { index, item -> Row { Text("${index + 1}.") ItemRow(item = item) } } } } ``` -------------------------------- ### Simple File Upload to S3 Source: https://github.com/appoly/appolydroid-toolbox/blob/main/BaseRepo-S3Uploader/README.md This Kotlin code snippet shows a simple file upload to S3 using the `uploadFileToS3` method within a `MediaRepository`. It requires the S3Uploader to be initialized and takes a file and the URL for generating pre-signed URLs as input. ```kotlin class MediaRepository : AppolyBaseRepo({ YourRetrofitClient }) { /** * Upload a file to S3 and return the S3 file path */ suspend fun uploadImage(imageFile: File): APIResult = uploadFileToS3( generatePresignedURL = "https://api.example.com/uploads/generate-presigned-url", file = imageFile ) } ```