diff --git a/.gitignore b/.gitignore index a070f811..5246cd2d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ fastlane/.env /app/release/baselineProfiles/1/save-unspecified-release.dm /app/release/output-metadata.json /app/src/main/assets/.env +/.kotlin/sessions/kotlin-compiler-1215430679833621634.salive +/.kotlin/ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 617f846e..a7143dbf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -299,6 +299,7 @@ dependencies { detektPlugins(libs.detekt.rules.authors) detektPlugins(libs.detekt.rules.libraries) detektPlugins(libs.detekt.compose) + detektPlugins(libs.detekt.rules.compose) } configurations.all { diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index 824fccc8..e46e32ce 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -221,7 +221,6 @@ FinalNewline:BaseComposeActivity.kt$net.opendasharchive.openarchive.features.core.BaseComposeActivity.kt FinalNewline:BaseDialog.kt$net.opendasharchive.openarchive.features.core.dialog.BaseDialog.kt FinalNewline:BaseFragment.kt$net.opendasharchive.openarchive.features.core.BaseFragment.kt - FinalNewline:BaseSnowbirdFragment.kt$net.opendasharchive.openarchive.services.snowbird.BaseSnowbirdFragment.kt FinalNewline:BaseViewModel.kt$net.opendasharchive.openarchive.util.BaseViewModel.kt FinalNewline:BasicAuthInterceptor.kt$net.opendasharchive.openarchive.services.webdav.BasicAuthInterceptor.kt FinalNewline:BiometricAuthenticator.kt$net.opendasharchive.openarchive.features.settings.passcode.BiometricAuthenticator.kt diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/core/BaseFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/features/core/BaseFragment.kt index d0949996..18a80906 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/core/BaseFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/core/BaseFragment.kt @@ -10,11 +10,11 @@ import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.extensions.androidViewModel import net.opendasharchive.openarchive.features.core.dialog.DialogStateManager +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.features.onboarding.SpaceSetupActivity import net.opendasharchive.openarchive.services.snowbird.SnowbirdGroupViewModel import net.opendasharchive.openarchive.services.snowbird.SnowbirdRepoViewModel import net.opendasharchive.openarchive.util.FullScreenOverlayManager -import net.opendasharchive.openarchive.util.Utility abstract class BaseFragment : Fragment(), ToolbarConfigurable { @@ -44,10 +44,13 @@ abstract class BaseFragment : Fragment(), ToolbarConfigurable { } open fun handleError(error: SnowbirdError) { - Utility.showMaterialWarning( - requireContext(), - error.friendlyMessage - ) + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + title = UiText.DynamicString("Oops") + message = UiText.DynamicString(error.friendlyMessage) + positiveButton { + text = UiText.StringResource(R.string.lbl_ok) + } + } } open fun handleLoadingStatus(isLoading: Boolean) { diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt index bb9a18d2..8dd70428 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt @@ -9,7 +9,6 @@ import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Build import android.os.Bundle -import android.provider.MediaStore import android.view.Gravity import android.view.LayoutInflater import android.view.Menu @@ -19,12 +18,8 @@ import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.LinearLayout import android.widget.PopupWindow -import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi -import androidx.appcompat.content.res.AppCompatResources -import androidx.compose.material3.MaterialTheme -import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.drawerlayout.widget.DrawerLayout @@ -76,12 +71,10 @@ import net.opendasharchive.openarchive.upload.UploadManagerFragment import net.opendasharchive.openarchive.upload.UploadService import net.opendasharchive.openarchive.util.Prefs import net.opendasharchive.openarchive.util.ProofModeHelper -import net.opendasharchive.openarchive.util.Utility import net.opendasharchive.openarchive.util.extensions.Position import net.opendasharchive.openarchive.util.extensions.cloak import net.opendasharchive.openarchive.util.extensions.hide import net.opendasharchive.openarchive.util.extensions.scaleAndTintDrawable -import net.opendasharchive.openarchive.util.extensions.scaled import net.opendasharchive.openarchive.util.extensions.show import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel @@ -226,10 +219,10 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda override fun onStart() { super.onStart() - if(Prefs.useProofMode){ + if (Prefs.useProofMode) { Prefs.proofModeLocation = true Prefs.proofModeNetwork = true - }else{ + } else { Prefs.proofModeLocation = false Prefs.proofModeNetwork = false } @@ -334,9 +327,15 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda if (Picker.canPickFiles(this@MainActivity)) { setAddButtonLongClickEnabled() onAddLongClick = { - val addMediaBottomSheet = - ContentPickerFragment { actionType -> addClicked(actionType) } - addMediaBottomSheet.show(supportFragmentManager, ContentPickerFragment.TAG) + if (Space.current == null) { + navigateToAddServer() + } else if (getSelectedProject() == null) { + navigateToAddFolder() + } else { + val addMediaBottomSheet = + ContentPickerFragment { actionType -> addClicked(actionType) } + addMediaBottomSheet.show(supportFragmentManager, ContentPickerFragment.TAG) + } } supportFragmentManager.setFragmentResultListener( AddMediaDialogFragment.RESP_TAKE_PHOTO, this@MainActivity @@ -763,7 +762,7 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda getSelectedProject() != null -> { if (Prefs.addMediaHint) { when (mediaType) { - AddMediaType.CAMERA -> Picker.takePhoto(this, mediaLaunchers.cameraLauncher) + AddMediaType.CAMERA -> Picker.takePhoto(this@MainActivity, mediaLaunchers.cameraLauncher) AddMediaType.GALLERY -> Picker.pickMedia( this, mediaLaunchers.imagePickerLauncher @@ -778,6 +777,7 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda message = R.string.press_and_hold_options_media_screen_message.asUiText(), onDone = { Prefs.addMediaHint = true + addClicked(mediaType) } ) } @@ -831,7 +831,16 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda } private fun showNotificationPermissionRationale() { - Utility.showMaterialWarning(this, "Accept!") { Timber.d("thing") } + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + title = UiText.DynamicString("Notification Permission") + message = UiText.DynamicString("We need permission to post notifications") + positiveButton { + text = UiText.DynamicString("Accept") + action = { + Timber.d("thing") + } + } + } } private fun handleIntent(intent: Intent) { @@ -879,14 +888,8 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { - 2 -> Picker.pickMedia(this, mediaLaunchers.imagePickerLauncher) - REQUEST_CAMERA_PERMISSION -> { - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - takePhoto() // ✅ Permission granted, retry camera - } else { - Toast.makeText(this, "Camera permission denied", Toast.LENGTH_LONG).show() - } - } + REQUEST_FILE_MEDIA -> Picker.pickMedia(this, mediaLaunchers.imagePickerLauncher) + REQUEST_CAMERA_PERMISSION -> Picker.takePhoto(this, mediaLaunchers.cameraLauncher) } } @@ -971,28 +974,9 @@ class MainActivity : BaseActivity(), SpaceDrawerAdapterListener, FolderDrawerAda } } - - private fun takePhoto() { - // Check if CAMERA permission is granted - if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - // Request permission - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) - return - } - - // If permission is already granted, start the camera intent - val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) - if (takePictureIntent.resolveActivity(this.packageManager) != null) { - - this.startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) - } else { - Toast.makeText(this, "Camera not available", Toast.LENGTH_SHORT).show() - } - } - companion object { // Define request codes - private const val REQUEST_CAMERA_PERMISSION = 100 - private const val REQUEST_IMAGE_CAPTURE = 101 + const val REQUEST_CAMERA_PERMISSION = 100 + const val REQUEST_FILE_MEDIA = 101 } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/media/MediaLaunchers.kt b/app/src/main/java/net/opendasharchive/openarchive/features/media/MediaLaunchers.kt index 06bfe950..fad0f07b 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/media/MediaLaunchers.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/media/MediaLaunchers.kt @@ -1,12 +1,11 @@ package net.opendasharchive.openarchive.features.media import android.content.Intent -import android.net.Uri import androidx.activity.result.ActivityResultLauncher import com.esafirm.imagepicker.features.ImagePickerLauncher data class MediaLaunchers( val imagePickerLauncher: ImagePickerLauncher, val filePickerLauncher: ActivityResultLauncher, - val cameraLauncher: ActivityResultLauncher + val cameraLauncher: ActivityResultLauncher ) \ No newline at end of file diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/media/Picker.kt b/app/src/main/java/net/opendasharchive/openarchive/features/media/Picker.kt index 537ad9a9..57799828 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/media/Picker.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/media/Picker.kt @@ -10,8 +10,10 @@ import android.graphics.Color import android.net.Uri import android.os.Build import android.os.Environment +import android.provider.MediaStore import android.view.View import android.widget.ProgressBar +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts @@ -33,6 +35,8 @@ import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.core.logger.AppLogger import net.opendasharchive.openarchive.db.Media import net.opendasharchive.openarchive.db.Project +import net.opendasharchive.openarchive.features.main.MainActivity +import net.opendasharchive.openarchive.features.main.MainActivity.Companion.REQUEST_CAMERA_PERMISSION import net.opendasharchive.openarchive.util.Utility import net.opendasharchive.openarchive.util.extensions.makeSnackBar import org.witness.proofmode.crypto.HashUtils @@ -81,8 +85,8 @@ object Picker { } } - val cpl = activity.registerForActivityResult(ActivityResultContracts.TakePicture()) { success -> - if (success) { + val cpl = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == AppCompatActivity.RESULT_OK) { currentPhotoUri?.let { uri -> val snackbar = showProgressSnackBar(activity, root, activity.getString(R.string.importing_media)) @@ -108,9 +112,11 @@ object Picker { fun pickMedia(activity: Activity, launcher: ImagePickerLauncher) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - if (needAskForPermission(activity, arrayOf( - Manifest.permission.READ_MEDIA_IMAGES, - Manifest.permission.READ_MEDIA_VIDEO)) + if (needAskForPermission( + activity, + arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO), + MainActivity.REQUEST_FILE_MEDIA + ) ) { return } @@ -143,7 +149,7 @@ object Picker { type = "application/*" } - private fun needAskForPermission(activity: Activity, permissions: Array): Boolean { + private fun needAskForPermission(activity: Activity, permissions: Array, requestCode: Int): Boolean { var needAsk = false for (permission in permissions) { @@ -158,7 +164,7 @@ object Picker { if (!needAsk) return false - ActivityCompat.requestPermissions(activity, permissions, 2) + ActivityCompat.requestPermissions(activity, permissions, requestCode) return true } @@ -221,16 +227,34 @@ object Picker { return media } - fun takePhoto(context: Context, launcher: ActivityResultLauncher) { - val file = Utility.getOutputMediaFileByCache(context, "IMG_${System.currentTimeMillis()}.jpg") + fun takePhoto(activity: Activity, launcher: ActivityResultLauncher) { + + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + // Request permission + ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) + return + } + + val file = Utility.getOutputMediaFileByCache(activity, "IMG_${System.currentTimeMillis()}.jpg") file?.let { val uri = FileProvider.getUriForFile( - context, "${context.packageName}.provider", + activity, "${activity.packageName}.provider", it ) + currentPhotoUri = uri - launcher.launch(uri) + + val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply { + putExtra(MediaStore.EXTRA_OUTPUT, uri) + addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) // Ensure permission is granted + } + + if (takePictureIntent.resolveActivity(activity.packageManager) != null) { + launcher.launch(takePictureIntent) + } else { + Toast.makeText(activity, "Camera not available", Toast.LENGTH_SHORT).show() + } } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/media/PreviewActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/media/PreviewActivity.kt index 082d4505..8ede8e0f 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/media/PreviewActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/media/PreviewActivity.kt @@ -18,6 +18,7 @@ import net.opendasharchive.openarchive.features.core.asUiImage import net.opendasharchive.openarchive.features.core.asUiText import net.opendasharchive.openarchive.features.core.dialog.DialogType import net.opendasharchive.openarchive.features.core.dialog.showDialog +import net.opendasharchive.openarchive.features.main.MainActivity import net.opendasharchive.openarchive.util.Prefs import net.opendasharchive.openarchive.util.extensions.hide import net.opendasharchive.openarchive.util.extensions.show @@ -306,4 +307,17 @@ class PreviewActivity : BaseActivity(), View.OnClickListener, PreviewAdapter.Lis } } + + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + when (requestCode) { + MainActivity.REQUEST_FILE_MEDIA -> Picker.pickMedia(this, mediaLaunchers.imagePickerLauncher) + MainActivity.REQUEST_CAMERA_PERMISSION -> Picker.takePhoto(this, mediaLaunchers.cameraLauncher) + } + } } \ No newline at end of file diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupActivity.kt index 62257065..223bccbd 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupActivity.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.MenuItem -import androidx.activity.OnBackPressedCallback +import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import net.opendasharchive.openarchive.core.presentation.theme.SaveAppTheme import net.opendasharchive.openarchive.features.core.BaseActivity @@ -17,19 +17,9 @@ class PasscodeSetupActivity : BaseActivity() { const val EXTRA_PASSCODE_ENABLED = "passcode_enabled" } -// private val onBackPressedCallback = object : OnBackPressedCallback(enabled = true) { -// override fun handleOnBackPressed() { -// setResult(RESULT_CANCELED) -// finish() -// } -// } - - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - //onBackPressedDispatcher.addCallback(onBackPressedCallback) - setContent { SaveAppTheme { DefaultScaffold( @@ -37,11 +27,19 @@ class PasscodeSetupActivity : BaseActivity() { ComposeAppBar( title = "Lock app with passcode", onNavigationAction = { - //onBackPressedCallback.handleOnBackPressed() + setResult(RESULT_CANCELED) + finish() } ) } ) { + + // Handle back press inside Compose + BackHandler { + setResult(RESULT_CANCELED) + finish() + } + PasscodeSetupScreen( onPasscodeSet = { // Passcode successfully set diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupScreen.kt b/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupScreen.kt index 42871f47..728c651f 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupScreen.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/settings/passcode/passcode_setup/PasscodeSetupScreen.kt @@ -99,6 +99,7 @@ private fun PasscodeSetupScreenContent( verticalArrangement = Arrangement.Top, modifier = Modifier .fillMaxWidth() + .padding(top = 32.dp) .padding(horizontal = 24.dp) .padding(bottom = 24.dp) ) { @@ -116,8 +117,9 @@ private fun PasscodeSetupScreenContent( Text( text = "Make sure you remember this pin. If you forget it, you will need to reset the app, and all data will be erased.", color = MaterialTheme.colorScheme.error, + fontSize = 11.sp, textAlign = TextAlign.Center, - fontWeight = FontWeight.Light, + fontWeight = FontWeight.Medium, style = MaterialTheme.typography.labelMedium ) } diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/BaseSnowbirdFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/BaseSnowbirdFragment.kt deleted file mode 100644 index 84c1c73e..00000000 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/BaseSnowbirdFragment.kt +++ /dev/null @@ -1,34 +0,0 @@ -package net.opendasharchive.openarchive.services.snowbird - -import android.content.Context -import android.view.View -import android.view.inputmethod.InputMethodManager -import androidx.fragment.app.Fragment -import net.opendasharchive.openarchive.db.SnowbirdError -import net.opendasharchive.openarchive.extensions.androidViewModel -import net.opendasharchive.openarchive.util.Utility - -open class BaseSnowbirdFragment : Fragment() { - val snowbirdGroupViewModel: SnowbirdGroupViewModel by androidViewModel() - val snowbirdRepoViewModel: SnowbirdRepoViewModel by androidViewModel() - - open fun dismissKeyboard(view: View) { - val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(view.windowToken, 0) - } - - open fun handleError(error: SnowbirdError) { - Utility.showMaterialWarning( - requireContext(), - error.friendlyMessage - ) - } - - open fun handleLoadingStatus(isLoading: Boolean) { - if (isLoading) { - //FullScreenOverlayManager.show(this@BaseSnowbirdFragment) - } else { - //FullScreenOverlayManager.hide() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdCreateGroupFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdCreateGroupFragment.kt index ce0fa6bb..83a642da 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdCreateGroupFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdCreateGroupFragment.kt @@ -4,8 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -16,8 +14,10 @@ import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.db.SnowbirdGroup import net.opendasharchive.openarchive.db.SnowbirdRepo import net.opendasharchive.openarchive.features.core.BaseFragment +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.util.FullScreenOverlayCreateGroupManager -import net.opendasharchive.openarchive.util.Utility import timber.log.Timber class SnowbirdCreateGroupFragment: BaseFragment() { @@ -126,34 +126,25 @@ class SnowbirdCreateGroupFragment: BaseFragment() { private fun showConfirmation(repo: SnowbirdRepo?) { val group = SnowbirdGroup.get(repo!!.groupKey)!! - Utility.showMaterialPrompt( - requireContext(), - title = "Raven Group Created", - message = "Would you like to share your new group with a QR code?", - positiveButtonText = "Yes", - negativeButtonText = "No", - completion = { affirm -> - if (affirm) { - if (isJetpackNavigation) { - val action = - SnowbirdCreateGroupFragmentDirections.actionFragmentSnowbirdCreateGroupToFragmentSnowbirdShareGroup( - group.key - ) - findNavController().navigate(action) - } else { - setFragmentResult( - RESULT_REQUEST_KEY, - bundleOf( - RESULT_NAVIGATION_KEY to RESULT_NAVIGATION_VAL_SHARE_SCREEN, - RESULT_BUNDLE_GROUP_KEY to group.key - ) - ) - } - } else { + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Success + title = UiText.DynamicString("Raven Group Created") + message = UiText.DynamicString("Would you like to share your new group with a QR code?") + positiveButton { + text = UiText.DynamicString("Yes") + action = { + val action = + SnowbirdCreateGroupFragmentDirections.actionFragmentSnowbirdCreateGroupToFragmentSnowbirdShareGroup(group.key) + findNavController().navigate(action) + } + } + neutralButton { + text = UiText.DynamicString("No") + action = { parentFragmentManager.popBackStack() } } - ) + } } override fun getToolbarTitle(): String { diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFileListFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFileListFragment.kt index 3096c750..e769d0b1 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFileListFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFileListFragment.kt @@ -22,8 +22,10 @@ import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.db.SnowbirdFileItem import net.opendasharchive.openarchive.extensions.androidViewModel import net.opendasharchive.openarchive.features.core.BaseFragment +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.util.SpacingItemDecoration -import net.opendasharchive.openarchive.util.Utility import timber.log.Timber class SnowbirdFileListFragment : BaseFragment() { @@ -136,16 +138,20 @@ class SnowbirdFileListFragment : BaseFragment() { private fun onClick(item: SnowbirdFileItem) { // if (!item.isDownloaded) { - Utility.showMaterialPrompt( - requireContext(), - title = "Download Media?", - message = "Are you sure you want to download this media?", - positiveButtonText = "Yes", - negativeButtonText = "No") { affirm -> - if (affirm) { + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Warning + title = UiText.DynamicString("Download Media?") + message = UiText.DynamicString("Are you sure you want to download this media?") + positiveButton { + text = UiText.DynamicString("Yes") + action = { snowbirdFileViewModel.downloadFile(groupKey, repoKey, item.name) } } + neutralButton { + text = UiText.DynamicString("No") + } + } // } } @@ -188,10 +194,14 @@ class SnowbirdFileListFragment : BaseFragment() { private fun onFileDownloaded(uri: Uri) { handleLoadingStatus(false) Timber.d("File successfully downloaded: $uri") - Utility.showMaterialMessage( - requireContext(), - title = "Success", - message = "File successfully downloaded") + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Success + title = UiText.StringResource(R.string.label_success_title) + message = UiText.DynamicString("File successfully downloaded") + positiveButton { + text = UiText.StringResource(R.string.label_got_it) + } + } } private fun onFileUploaded(result: FileUploadResult) { diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFragment.kt index 213eba33..cc1c5cb2 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdFragment.kt @@ -13,12 +13,15 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import com.google.zxing.integration.android.IntentIntegrator import kotlinx.coroutines.launch +import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.databinding.FragmentSnowbirdBinding import net.opendasharchive.openarchive.db.SnowbirdGroup import net.opendasharchive.openarchive.extensions.getQueryParameter -import net.opendasharchive.openarchive.features.main.QRScannerActivity import net.opendasharchive.openarchive.features.core.BaseFragment -import net.opendasharchive.openarchive.util.Utility +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog +import net.opendasharchive.openarchive.features.main.QRScannerActivity import timber.log.Timber class SnowbirdFragment : BaseFragment() { @@ -127,18 +130,26 @@ class SnowbirdFragment : BaseFragment() { val name = uriString.getQueryParameter("name") if (name == null) { - Utility.showMaterialWarning( - requireContext(), - "Unable to determine group name from QR code." - ) + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Warning + title = UiText.DynamicString("Oops!") + message = UiText.DynamicString("Unable to determine group name from QR code.") + positiveButton { + text = UiText.StringResource(R.string.lbl_ok) + } + } return } if (SnowbirdGroup.exists(name)) { - Utility.showMaterialWarning( - requireContext(), - "You have already joined this group." - ) + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Warning + title = UiText.DynamicString("Oops!") + message = UiText.DynamicString("You have already joined this group.") + positiveButton { + text = UiText.StringResource(R.string.lbl_ok) + } + } return } diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdGroupListFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdGroupListFragment.kt index 1a0f1440..530bd150 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdGroupListFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdGroupListFragment.kt @@ -22,8 +22,10 @@ import net.opendasharchive.openarchive.databinding.FragmentSnowbirdGroupListBind import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.db.SnowbirdGroup import net.opendasharchive.openarchive.features.core.BaseFragment +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.util.SpacingItemDecoration -import net.opendasharchive.openarchive.util.Utility import timber.log.Timber class SnowbirdGroupListFragment : BaseFragment() { @@ -126,27 +128,19 @@ class SnowbirdGroupListFragment : BaseFragment() { private fun onLongPress(groupKey: String) { AppLogger.d("Long press!") - Utility.showMaterialPrompt( - requireContext(), - title = "Share Group", - message = "Would you like to share this group?", - positiveButtonText = "Yes", - negativeButtonText = "No" - ) { affirm -> - if (affirm) { - if (isJetpackNavigation) { + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Info + title = UiText.DynamicString("Share Group") + message = UiText.DynamicString("Would you like to share this group?") + positiveButton { + text = UiText.DynamicString("Yes") + action = { val action = SnowbirdGroupListFragmentDirections.actionFragmentSnowbirdGroupListToFragmentSnowbirdShareGroup(groupKey) findNavController().navigate(action) - } else { - setFragmentResult( - RESULT_REQUEST_KEY, - bundleOf( - RESULT_BUNDLE_NAVIGATION_KEY to RESULT_VAL_RAVEN_SHARE_SCREEN, - RESULT_BUNDLE_GROUP_KEY to groupKey - ) - ) } - //findNavController().navigate(SnowbirdGroupListFragmentDirections.navigateToSnowbirdShareScreen(groupKey)) + } + neutralButton { + text = UiText.DynamicString("No") } } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdJoinGroupFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdJoinGroupFragment.kt index 95414362..ab1d9388 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdJoinGroupFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdJoinGroupFragment.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import kotlinx.coroutines.launch +import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.databinding.FragmentSnowbirdJoinGroupBinding import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.db.SnowbirdGroup @@ -15,8 +16,10 @@ import net.opendasharchive.openarchive.db.SnowbirdRepo import net.opendasharchive.openarchive.extensions.getQueryParameter import net.opendasharchive.openarchive.extensions.showKeyboard import net.opendasharchive.openarchive.features.core.BaseFragment +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.util.FullScreenOverlayCreateGroupManager -import net.opendasharchive.openarchive.util.Utility import timber.log.Timber class SnowbirdJoinGroupFragment: BaseFragment() { @@ -115,12 +118,16 @@ class SnowbirdJoinGroupFragment: BaseFragment() { repo.save() handleCreateGroupLoadingStatus(false) snowbirdRepoViewModel.fetchRepos(groupKey, false) - Utility.showMaterialMessage( - requireContext(), - title = "Success!", - // message = "Successfully joined" - ) { - parentFragmentManager.popBackStack() + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Success + title = UiText.StringResource(R.string.label_success_title) + message = UiText.DynamicString("Successfully joined") + positiveButton { + text = UiText.StringResource(R.string.label_got_it) + action = { + parentFragmentManager.popBackStack() + } + } } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdRepoListFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdRepoListFragment.kt index e3e129b1..14a62d2b 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdRepoListFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/services/snowbird/SnowbirdRepoListFragment.kt @@ -22,8 +22,10 @@ import net.opendasharchive.openarchive.databinding.FragmentSnowbirdListReposBind import net.opendasharchive.openarchive.db.SnowbirdError import net.opendasharchive.openarchive.db.SnowbirdRepo import net.opendasharchive.openarchive.features.core.BaseFragment +import net.opendasharchive.openarchive.features.core.UiText +import net.opendasharchive.openarchive.features.core.dialog.DialogType +import net.opendasharchive.openarchive.features.core.dialog.showDialog import net.opendasharchive.openarchive.util.SpacingItemDecoration -import net.opendasharchive.openarchive.util.Utility import timber.log.Timber class SnowbirdRepoListFragment: BaseFragment() { @@ -96,11 +98,14 @@ class SnowbirdRepoListFragment: BaseFragment() { override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.action_add -> { - Utility.showMaterialWarning( - context = requireContext(), - message = "Feature not implemented yet.", - positiveButtonText = "OK" - ) + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Warning + title = UiText.DynamicString("Oops!") + message = UiText.DynamicString("Feature not implemented yet.") + positiveButton { + text = UiText.StringResource(R.string.lbl_ok) + } + } true } @@ -154,11 +159,17 @@ class SnowbirdRepoListFragment: BaseFragment() { adapter.submitList(repos) if (isRefresh && repos.isEmpty()) { - Utility.showMaterialMessage( - requireContext(), - title = "Info", - message = "No new repositories found." - ) + dialogManager.showDialog(dialogManager.requireResourceProvider()) { + type = DialogType.Info + title = UiText.StringResource(R.string.label_info_title) + message = UiText.DynamicString("No new repositories found.") + positiveButton { + text = UiText.StringResource(R.string.label_got_it) + action = { + parentFragmentManager.popBackStack() + } + } + } } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/util/CustomSwitchPreference.kt b/app/src/main/java/net/opendasharchive/openarchive/util/CustomSwitchPreference.kt index 8f28f791..d089874f 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/util/CustomSwitchPreference.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/util/CustomSwitchPreference.kt @@ -15,7 +15,7 @@ class CustomSwitchPreference @JvmOverloads constructor( super.onBindViewHolder(holder) // Find the switch inside the custom layout - val switchView = holder.findViewById(R.id.checkbox) as? MaterialSwitch + val switchView = holder.findViewById(android.R.id.checkbox) as? MaterialSwitch switchView?.apply { isChecked = this@CustomSwitchPreference.isChecked diff --git a/app/src/main/java/net/opendasharchive/openarchive/util/Utility.kt b/app/src/main/java/net/opendasharchive/openarchive/util/Utility.kt index 8182ca5d..08daa679 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/util/Utility.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/util/Utility.kt @@ -118,22 +118,6 @@ object Utility { context.startActivity(i) } - fun showMaterialWarning(context: Context, message: String? = null, positiveButtonText: String = "Ok", completion: (() -> Unit)? = null) { - showMaterialMessage(context, "Oops", message, positiveButtonText, completion) - } - - fun showMaterialMessage(context: Context, title: String = "Oops", message: String? = null, positiveButtonText: String = "Ok", completion: (() -> Unit)? = null) { - Handler(Looper.getMainLooper()).post { - MaterialAlertDialogBuilder(context) - .setTitle(title) - .setMessage(message) - .setPositiveButton(positiveButtonText) { _, _ -> - completion?.invoke() - } - .show() - } - } - fun showMaterialPrompt( context: Context, title: String, diff --git a/app/src/main/jniLibs/arm64-v8a/libsave.so b/app/src/main/jniLibs/arm64-v8a/libsave.so index 092820b3..9947fa51 100755 Binary files a/app/src/main/jniLibs/arm64-v8a/libsave.so and b/app/src/main/jniLibs/arm64-v8a/libsave.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libsave.so b/app/src/main/jniLibs/armeabi-v7a/libsave.so index 3129b936..9635dd84 100755 Binary files a/app/src/main/jniLibs/armeabi-v7a/libsave.so and b/app/src/main/jniLibs/armeabi-v7a/libsave.so differ diff --git a/app/src/main/res/drawable/onboarding_archive_png.png b/app/src/main/res/drawable/onboarding_archive_png.png index c7a99be9..776df485 100644 Binary files a/app/src/main/res/drawable/onboarding_archive_png.png and b/app/src/main/res/drawable/onboarding_archive_png.png differ diff --git a/app/src/main/res/drawable/onboarding_encrypt_png.png b/app/src/main/res/drawable/onboarding_encrypt_png.png index d7adfe3d..04360637 100644 Binary files a/app/src/main/res/drawable/onboarding_encrypt_png.png and b/app/src/main/res/drawable/onboarding_encrypt_png.png differ diff --git a/app/src/main/res/drawable/onboarding_secure_png.png b/app/src/main/res/drawable/onboarding_secure_png.png index 1927732f..7064a11f 100644 Binary files a/app/src/main/res/drawable/onboarding_secure_png.png and b/app/src/main/res/drawable/onboarding_secure_png.png differ diff --git a/app/src/main/res/drawable/onboarding_verify_png.png b/app/src/main/res/drawable/onboarding_verify_png.png index 31e3d33a..f4c8f5e0 100644 Binary files a/app/src/main/res/drawable/onboarding_verify_png.png and b/app/src/main/res/drawable/onboarding_verify_png.png differ diff --git a/app/src/test/java/net/opendasharchive/openarchive/MainMediaAdapterTest.kt b/app/src/test/java/net/opendasharchive/openarchive/MainMediaAdapterTest.kt index 9600621a..f0f1b524 100644 --- a/app/src/test/java/net/opendasharchive/openarchive/MainMediaAdapterTest.kt +++ b/app/src/test/java/net/opendasharchive/openarchive/MainMediaAdapterTest.kt @@ -91,6 +91,8 @@ class MainMediaAdapterTest { mediaList, recyclerView, checkSelecting = { }, + allowMultiProjectSelection = TODO(), + onDeleteClick = TODO(), ) } diff --git a/build.gradle.kts b/build.gradle.kts index 27898215..c0598d5a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ plugins { - id("com.android.application") version "8.3.2" apply false - id("org.jetbrains.kotlin.android") version "2.1.10" apply false - id("org.jetbrains.kotlin.plugin.compose") version "2.1.10" apply false - id("org.jetbrains.kotlin.plugin.serialization") version "2.1.10" apply false - id("com.google.devtools.ksp") version "2.1.10-1.0.30" apply false + id("com.android.application") version "8.9.0" apply false + id("org.jetbrains.kotlin.android") version "2.1.20-RC2" apply false + id("org.jetbrains.kotlin.plugin.compose") version "2.1.20-RC2" apply false + id("org.jetbrains.kotlin.plugin.serialization") version "2.1.20-RC2" apply false + id("com.google.devtools.ksp") version "2.1.20-RC2-1.0.31" apply false id("androidx.navigation.safeargs") version "2.8.8" apply false diff --git a/config/detekt-config.yml b/config/detekt-config.yml index a99959f9..a2ddec12 100644 --- a/config/detekt-config.yml +++ b/config/detekt-config.yml @@ -14,6 +14,30 @@ config: # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' excludes: '' +compose: + ReusedModifierInstance: + active: true + UnnecessaryEventHandlerParameter: + active: true + ComposableEventParameterNaming: + active: true + ComposableParametersOrdering: + active: true + ModifierHeightWithText: + active: true + MissingModifierDefaultValue: + active: true + PublicComposablePreview: + active: true + TopLevelComposableFunctions: + active: true + allowInObjects: false + ComposableFunctionName: + active: true + ConditionCouldBeLifted: + active: true + ignoreCallsWithArgumentNames: [ 'modifier', 'contentAlignment' ] + processors: active: true exclude: @@ -644,7 +668,7 @@ style: maxChainedCalls: 5 MaxLineLength: active: true - maxLineLength: 120 + maxLineLength: 180 excludePackageStatements: true excludeImportStatements: true excludeCommentStatements: false diff --git a/gradle.properties b/gradle.properties index a4059be7..aed45c8f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,3 +22,4 @@ android.enableJetifier=true android.nonTransitiveRClass=true android.nonFinalResIds=true +org.gradle.configuration-cache=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ffaeef1e..d1413b2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,14 @@ [versions] detekt="1.23.8" detekt-compose="0.4.22" +detekt-rules-compose="1.4.0" [libraries] detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } detekt-rules-libraries = { group = "io.gitlab.arturbosch.detekt", name = "detekt-rules-libraries", version.ref = "detekt" } detekt-rules-authors = { group = "io.gitlab.arturbosch.detekt", name = "detekt-rules-ruleauthors", version.ref = "detekt" } detekt-compose = { group = "io.nlopez.compose.rules", name = "detekt", version.ref = "detekt-compose" } +detekt-rules-compose = { module = "ru.kode:detekt-rules-compose", version.ref = "detekt-rules-compose" } [plugins] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5a016c74..0da8ddc3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip \ No newline at end of file