Intent.ACTION_GET_CONTENT, registerForActivityResult() ve bazı hatalar

startActivityForResult() deprecated olduğundan dolayı yeni yöntem registerForResult() ile deneme amaçlı çalışırken birkaç hata ile karşılaştım, başkalarına da faydalı olması amacıyla buraya not ediyorum.

Yöntem olarak Android'in sitesindeki dokümantasyonun şu başlığındaki ikinci örneği kullandım.

class LoginFragment : Fragment() {
    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentLoginBinding.inflate(inflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.button.setOnClickListener {
            getFilePath()
        }
    }

    private fun getFilePath(){
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result->
            if(result.resultCode == Activity.RESULT_OK){
                result.data?.let {
                    it.data?.let { uri ->
                        binding.textView.text = uri.path ?: "error" //Directory of file
                    }

                }
            }
        }
        startForResult.launch(intent)

    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Ekranda bir buton ve bir de textView var. Burada basitçe yapmaya çalıştığım şey koddan da anlaşılacağı üzere kullanıcıya butona tıkladığında Intent.ACTION_GET_CONTENT ile bir dosya seçtirmek ve seçtiği dosyanın yolunu textView'da göstermek. Fakat kod bu şekilde çalıştırıldığında oluşan hata şu: java.lang.IllegalStateException: Fragment is attempting to registerForActivityResult after being created. Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).

Hatada belirtildiği şekilde registerForActivityResult() işlemini onCreate() fonksiyonuna taşıyorum. Yeni kod:

class LoginFragment : Fragment() {
    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!
    private lateinit var startForResult: ActivityResultLauncher<Intent>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result->
            if(result.resultCode == Activity.RESULT_OK){
                showDirectory(result)
            }
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentLoginBinding.inflate(inflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.button.setOnClickListener {
            getFilePath()
        }
    }

    private fun getFilePath(){
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        startForResult.launch(intent)

    }

    private fun showDirectory(result: ActivityResult){
        result.data?.let {
            it.data?.let { uri ->
                binding.textView.text = uri.path ?: "error" //Directory of file
            }

        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Bahsettiğim ilk hata bu şekilde giderilse de ikinci kod şu hatayı veriyor: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.GET_CONTENT }

Android'in sitesinde bu intent'in anlatıldığı dokümantasyonda "...ACTION_GET_INTENT aksiyonunu ve tercih ettiğiniz MIME tipini kullanın." yazıyor. Tipi belirtince ikinci hata da giderilmiş oldu:

class LoginFragment : Fragment() {
    private var _binding: FragmentLoginBinding? = null
    private val binding get() = _binding!!
    private lateinit var startForResult: ActivityResultLauncher<Intent>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result->
            if(result.resultCode == Activity.RESULT_OK){
                showDirectory(result)
            }
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentLoginBinding.inflate(inflater,container,false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.button.setOnClickListener {
            getFilePath()
        }
    }

    private fun getFilePath(){
        val intent = Intent(Intent.ACTION_GET_CONTENT).apply { type = "*/*" }
        startForResult.launch(intent)

    }

    private fun showDirectory(result: ActivityResult){
        result.data?.let {
            it.data?.let { uri ->
                binding.textView.text = uri.path ?: "error" //Directory of file
            }

        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

 

Faydalı Olabilecek Diğer Kaynaklar

https://developer.android.com/guide/components/intents-filters

https://developer.android.com/training/data-storage/use-cases

Yorumlar