/* * Copyright (C) 2025 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.android.privatespace import android.content.Context import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.window.DialogProperties internal const val CIRCULAR_PROGRESS_INDICATOR_TEST_TAG = "CIRCULAR_PROGRESS_INDICATOR" @Composable fun PrivateSpaceActivityScreen( viewModel: PrivateSpaceViewModel, context: Context, onOpenDocumentsPicker: () -> Unit, onFinished: () -> Unit, ) { when (viewModel.uiState) { PrivateSpaceUiState.STARTED -> { // Show nothing, wait for the results of the document picker. } PrivateSpaceUiState.CHECKING_PROGRESS_INDICATOR -> Box(modifier = Modifier.fillMaxSize()) { CircularProgressIndicator( modifier = Modifier.align(Alignment.Center) .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG) ) } PrivateSpaceUiState.SHOW_DOCUMENT_PICKER -> onOpenDocumentsPicker() PrivateSpaceUiState.SHOW_MOVE_FILES_DIALOG -> { ThreeButtonAlertDialog( onDismissRequest = { logDialogUserInteraction( PrivateSpaceStatsLog .PRIVATE_SPACE_MOVE_CONTENT_EVENT__OPERATION_TYPE__OPERATION_DIALOG_DISMISSED ) viewModel.finishFlow() }, title = stringResource(R.string.move_files_dialog_title), message = stringResource(R.string.move_files_dialog_summary), primaryButton = { Button( onClick = { logDialogUserInteraction( PrivateSpaceStatsLog .PRIVATE_SPACE_MOVE_CONTENT_EVENT__OPERATION_TYPE__OPERATION_MOVE ) viewModel.moveFiles(context) } ) { Text(stringResource(R.string.move_files_dialog_button_label_move)) } }, secondaryButton = { OutlinedButton( onClick = { logDialogUserInteraction( PrivateSpaceStatsLog .PRIVATE_SPACE_MOVE_CONTENT_EVENT__OPERATION_TYPE__OPERATION_COPY ) viewModel.copyFiles(context) } ) { Text(stringResource(R.string.move_files_dialog_button_label_copy)) } }, dismissButton = { TextButton( onClick = { logDialogUserInteraction( PrivateSpaceStatsLog .PRIVATE_SPACE_MOVE_CONTENT_EVENT__OPERATION_TYPE__OPERATION_CANCEL_BY_USER ) viewModel.finishFlow() } ) { Text(stringResource(R.string.move_files_dialog_button_label_cancel)) } }, ) } PrivateSpaceUiState.SHOW_NOT_ENOUGH_SPACE_DIALOG -> { buildErrorDialog( viewModel, stringResource(R.string.filetransfer_notification_transfer_error_title), stringResource( R.string.filetransfer_notification_insufficient_storage_error_message ), ) } PrivateSpaceUiState.SHOW_ABOVE_FILE_SIZE_LIMITS_DIALOG -> { buildErrorDialog( viewModel, stringResource(R.string.filetransfer_large_files_size_error_title), stringResource(R.string.filetransfer_large_files_size_error_message), ) } PrivateSpaceUiState.SHOW_TOO_MANY_FILES_SELECTED_DIALOG -> { buildErrorDialog( viewModel, stringResource(R.string.filetransfer_too_many_files_error_title), stringResource(R.string.filetransfer_too_many_files_error_message), ) } PrivateSpaceUiState.SHOW_TRANSFER_IN_PROGRESS_DIALOG -> { buildErrorDialog( viewModel, stringResource( if (viewModel.isCopyOperationForErrorDialog()) R.string.filetransfer_dialog_still_copying_error_title else R.string.filetransfer_dialog_still_moving_error_title ), stringResource(R.string.filetransfer_dialog_still_transferring_error_message), ) } PrivateSpaceUiState.FINISHED -> { onFinished() } } } @Composable private fun buildErrorDialog(viewModel: PrivateSpaceViewModel, title: String, message: String) { AlertDialog( onDismissRequest = { viewModel.finishFlow() }, title = { Text(text = title, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center) }, text = { Text(text = message, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center) }, confirmButton = {}, dismissButton = { Button(onClick = { viewModel.finishFlow() }) { Text(stringResource(R.string.filetransfer_error_dialog_button)) } }, properties = DialogProperties(usePlatformDefaultWidth = false), modifier = Modifier.padding( horizontal = dimensionResource(id = R.dimen.private_space_alert_error_padding_horizontal) ), ) } fun logDialogUserInteraction(operationType: Int) { PrivateSpaceStatsLog.write( PrivateSpaceStatsLog.PRIVATE_SPACE_MOVE_CONTENT_EVENT, operationType, /* files_selected_count */ 0, /* transfer_result */ 0, /* files_success_count */ 0, /* files_failure_count */ 0, /* total_success_files_size_bucket */ 0, /* total_failure_files_size_bucket */ 0, /* failed_file_error_codes */ null, /* failed_file_mime_types */ null, /* success_file_mime_types */ null, /* notification_event */ 0, /* duration_ms */ 0, ) }