Android 10 проблема открытой камеры с компонентом навигации - PullRequest
0 голосов
/ 05 февраля 2020

У меня есть приложение 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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...