У меня есть приложение android с компонентом навигации, и при тестировании на Android 10 происходит сбой при попытке открыть камеру телефона. Он отлично работает на Android версиях ниже 10.
Код и ошибка приведены ниже.
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/main_layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorBackgroundApp" >
<include layout="@layout/toolbar_main"
android:id="@+id/app_bar_main"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/app_bar_main"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"/>
<include layout="@layout/layout_user_image"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_below="@id/app_bar_main"
android:layout_marginTop="-30dp"
/>
<FrameLayout
android:id="@+id/layout_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible">
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
</RelativeLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
app:menu="@menu/menu_nav_drawer"
/>
</androidx.drawerlayout.widget.DrawerLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(com.ionBIZMobile.R.layout.activity_main)
searchToolbar = findViewById(com.ionBIZMobile.R.id.search_toolbar)
setupNavigation()
}
private fun setupNavigation() {
setSupportActionBar(toolbar)
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
}
drawerToggle = ActionBarDrawerToggle(this, drawer_layout, R.string.open_drawer, R.string.close_drawer)
drawerToggle.isDrawerIndicatorEnabled = true
drawer_layout.addDrawerListener(drawerToggle)
drawerToggle.syncState()
//navController = findNavController(R.id.my_nav_host_fragment)
navController = (supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment).navController
appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
nav_view.setupWithNavController(navController)
// setupActionBarWithNavController(navController, drawer_layout)
setupActionBarWithNavController(navController, appBarConfiguration)
nav_view.setNavigationItemSelectedListener {
if (searchToolbar.isVisible) searchToolbar.collapse()
when (it.itemId) {
R.id.relations_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_relations_fragment) }
R.id.planning_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_planning_fragment) }
R.id.timesheets_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_timesheets_fragment) }
R.id.costs_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_costs_fragment) }
R.id.leaves_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_leaves_fragment) }
R.id.notes_fragment -> drawer_layout.consume { navController.navigate(R.id.action_global_notes_fragment) }
R.id.logout -> drawer_layout.consume { viewModel.logout() }
}
false
}
navController.addOnDestinationChangedListener(NavController.OnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
com.ionBIZMobile.R.id.main_fragment -> viewModel.userImageVisible.value = true
else
-> viewModel.userImageVisible.value = false
}
})
}
private val quickPermissionsOptionStorage = QuickPermissionsOptions(
handleRationale = false,
rationaleMessage = "Custom rational message",
permanentlyDeniedMessage = "Custom permanently denied message",
rationaleMethod = { req -> rationaleCallbackStorage(req) },
permanentDeniedMethod = { req -> permissionsPermanentlyDeniedStorage(req) }
)
private fun rationaleCallbackStorage(req: QuickPermissionsRequest) {
AlertDialog.Builder(this)
.setTitle(R.string.Permission_Denied)
.setMessage(R.string.Permission_Denied_Storage_Message)
.setPositiveButton(R.string.yes) { _, _ -> req.proceed() }
.setNegativeButton(R.string.cancel) { _, _ -> req.cancel() }
.setCancelable(false)
.show()
}
private fun permissionsPermanentlyDeniedStorage(req: QuickPermissionsRequest) {
AlertDialog.Builder(this)
.setTitle(R.string.Permission_Permanently_Denied)
.setMessage(R.string.Permission_Permanently_Denied_Storage_Message)
.setPositiveButton(R.string.AppSettings) { _, _ -> req.openAppSettings() }
.setNegativeButton(R.string.cancel) { _, _ -> req.cancel() }
.setCancelable(false)
.show()
}
private fun createImageFolder() {
externalImagesFolder = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), ION_BIZ_FOLDER)
if (!externalImagesFolder.exists()) {
externalImagesFolder.mkdirs()
}
}
private fun downloadAttachment() = runWithPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
options = quickPermissionsOptionStorage) {
startDownload()
}
private fun startDownload() {
if (viewModel.attachment.value != null) {
viewModel.downloadAttachment()
}
}
private fun saveAttachment(attachmentFile: AttachmentFile) {
val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), attachmentFile.FileName)
try {
if (!file.exists()) {
file.createNewFile()
}
val bytes = Base64.decode(attachmentFile.Data, Base64.DEFAULT)
val fos = FileOutputStream(file)
fos.write(bytes)
fos.close()
SingleMediaScanner(this, file, this)
Toast.makeText(this, R.string.Attachment_Downloaded, Toast.LENGTH_LONG).show()
} catch (e: Exception) {
Timber.e(e)
}
}
private val quickPermissionsOption = QuickPermissionsOptions(
handleRationale = false,
rationaleMessage = "Custom rational message",
permanentlyDeniedMessage = "Custom permanently denied message",
rationaleMethod = { req -> rationaleCallbackCameraAndStorage(req) },
permanentDeniedMethod = { req -> permissionsPermanentlyDeniedCameraAndStorage(req) }
)
private fun rationaleCallbackCameraAndStorage(req: QuickPermissionsRequest) {
AlertDialog.Builder(this)
.setTitle(R.string.Permissions_Denied)
.setMessage(R.string.Permissions_Denied_Camera_Storage_Message)
.setPositiveButton(R.string.yes) { _, _ -> req.proceed() }
.setNegativeButton(R.string.cancel) { _, _ -> req.cancel() }
.setCancelable(false)
.show()
}
private fun permissionsPermanentlyDeniedCameraAndStorage(req: QuickPermissionsRequest) {
AlertDialog.Builder(this)
.setTitle(R.string.Permissions_Permanently_Denied)
.setMessage(R.string.Permissions_Permanently_Denied_Camera_Storage_Message)
.setPositiveButton(R.string.AppSettings) { _, _ -> req.openAppSettings() }
.setNegativeButton(R.string.cancel) { _, _ -> req.cancel() }
.setCancelable(false)
.show()
}
private fun openCameraApp() = runWithPermissions(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
options = quickPermissionsOption) {
startImageCapture()
}
private fun startImageCapture() {
createImageFolder()
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
imageFile = createImageFile()
if (!imageFile!!.exists()) {
imageFile!!.createNewFile()
}
val photoURI = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", imageFile!!)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, RC_CAPTURE_IMAGE)
}
private fun createImageFile(): File {
val timestamp = timestampFormat.format(Date())
val fileName = PICTURE_NAME_FORMAT.format(PICTURE_NAME_PREFIX, timestamp)
return File(externalImagesFolder, fileName)
}
private fun openFilePicker() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
val title = getString(R.string.Intent_attach_file_from)
val chooser = Intent.createChooser(intent, title)
if (intent.resolveActivity(packageManager) != null) {
startActivityForResult(chooser, RC_FILE_PICKER)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
RC_CAPTURE_IMAGE -> onImageCaptured()
RC_FILE_PICKER -> onFileSelected(data)
}
} else if (resultCode == Activity.RESULT_CANCELED) {
if (requestCode == RC_CAPTURE_IMAGE) {
if (imageFile != null) {
imageFile!!.delete()
imageFile = null
}
}
}
}
private fun onImageCaptured() {
SingleMediaScanner(this, imageFile!!)
val photoURI = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", imageFile!!)
setAttachmentFile(photoURI)
}
override fun onScanCompleted(file: File) {
downloadedFile = file
Snackbar.make(cost_details_root, file.name, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.Open, openFileListener)
.show()
}
private val openFileListener = View.OnClickListener {
if (downloadedFile == null) return@OnClickListener
val mimeTypeMap = MimeTypeMap.getSingleton()
val intent = Intent(Intent.ACTION_VIEW)
val fileUri = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", downloadedFile!!)
val mimeType = mimeTypeMap.getMimeTypeFromExtension(downloadedFile!!.extension)
intent.setDataAndType(fileUri, mimeType)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val title = getString(R.string.Intent_open_file_using)
val chooser = Intent.createChooser(intent, title)
if (intent.resolveActivity(packageManager) != null) {
startActivity(chooser)
} else {
Toast.makeText(this, R.string.Message_no_suitable_application_for_opening_this_type_of_file, Toast.LENGTH_LONG).show()
}
}
private fun onFileSelected(data: Intent?) {
setAttachmentFile(data?.data)
}
private fun setAttachmentFile(uri: Uri?) {
val fileDetail = getFileDetailFromUri(this, uri)
val byteArray = getBytesFromUri(this, uri)
if (fileDetail?.fileName != null && byteArray != null) {
val base64string = Base64.encodeToString(byteArray, Base64.DEFAULT)
viewModel.selectedAttachmentFile.value = AttachmentFile(base64string, fileDetail.fileName!!)
viewModel.attachment.value = fileDetail.fileName
} else {
Timber.w("Received invalid file")
}
}
java .lang.RuntimeException: Невозможно запустить Activity ComponentInfo {... ui.main.MainActivity}: java .lang.IllegalStateException: Вы должны вызвать setGraph () перед вызовом getGraph () в android .app.ActivityThread.performLaunchActivity (ActivityThread. java: 3270 ) в android .app.ActivityThread.handleLaunchActivity (ActivityThread. java: 3409) в android .app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem. java: 83) в android .app.servertransaction. TransactionExecutor.executeCallbacks (TransactionExecutor. java: 135) в android .app.servertransaction.TransactionExecutor.execute (TransactionExecutor. java: 95) в android .app.ActivityThread $ H.handleMessage (ActivityThread. java: 2016) в android .os.Handler.dispatchMessage (Обработчик. java: 107) в android .os.Looper.l oop (Looper. java: 214) в android. app.ActivityThread.main (ActivityThread. java: 7356) в java .lang.reflect.M ethod.invoke (собственный метод) в com. android .internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit. java: 492) в com. android .internal.os.ZygoteInit.main (ZygoteInit. java: 930) Причина: java .lang.IllegalStateException: Вы должны вызвать setGraph () перед вызовом getGraph () в androidx.navigation.NavController.getGraph (NavController. java: 677) в MainActivity.setupNavigation (MainActivity) .kt: 85) в MainActivity.onCreate (MainActivity.kt: 57) в android .app.Activity.performCreate (Activity. java: 7802) в android .app.Activity.performCreate (Activity. java: 7791) в android .app.Instrumentation.callActivityOnCreate (Instrumentation. java: 1299) в android .app.ActivityThread.performLaunchActivity (ActivityThread. java: 3245) в android .app.ActivityThread .handleLaunchActivity (ActivityThread. java: 3409) в android .app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem. java: 83) в android .app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor. * 1054. : 135) на android. app.servertransaction.TransactionExecutor.execute (TransactionExecutor. java: 95) в android .app.ActivityThread $ H.handleMessage (ActivityThread. java: 2016) в android .os.Handler.dispatchMessage (обработчик. java: 107) в android .os.Looper.l oop (Looper. java: 214) в android .app.ActivityThread.main (ActivityThread. java: 7356) в java .lang.reflect.Method.invoke (собственный метод) в com. android .internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit. java: 492) в com. android .internal.os.ZygoteInit .main (ZygoteInit. java: 930)