Search code examples
pythonandroidkivykivy-languagekivymd

Import 'android.permissions' not working in Kivy app


So I want to use plyer's filechooser, and while it works fine on my pc, it doesn't actually allow any selection of files on android. It opens the file chooser and doesnt get the filepath even when the user selects a file. I've been all around the web and cant seem to find a solution, because all I see is that android.permissions SHOULD come with a kivy installation.

Can anybody help me out?

For clarification I've added a simple code that basically shows what I'm going for. This works on pc too but the textinput is empty on android, meaning it didn't 'get' any file.

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from plyer import filechooser
import os


    


class MyApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.file_paths = []
        self.file_names = []

    def build(self):
        layout = BoxLayout(orientation='vertical')
        button = Button(text='Open File Chooser', on_release=self.show_file_chooser)
        self.text_input = TextInput(readonly=True)
        layout.add_widget(button)
        layout.add_widget(self.text_input)
        return layout

    def show_file_chooser(self, *args):
        filechooser.open_file(on_selection=self.handle_selection, multiple=True)

    def handle_selection(self, selection):
        self.file_paths = []
        self.file_names = []

        for file_path in selection:
            self.file_paths.append(file_path)
            file_name = os.path.basename(file_path)
            self.file_names.append(file_name)

        # Print the list of file paths
        print(self.file_paths)
        print(self.file_names)

        # Update the UI with the selected file names
        self.text_input.text = '\n'.join(self.file_names)


if __name__ == '__main__':
    MyApp().run()

Plyer filechooser seems not to be working because of permission issues, but android.permissions isnt working, even when i downgrade kivy.


Solution

  • Indeed, to use a file chooser on Android, you have to ask for the permission first. This is done in two steps:

    1. Add the permissions you need in the buildozer spec file that allows you to compile your code for android. In your case, edit the android.permissions line in your file to ask for READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE:
    # (list) Permissions
    android.permissions = READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE
    
    1. Request for the permissions when your code starts. To do this, you have to import the android.permissions module. No need to install it, this is done by Buildozer automatically when compiling the code for android. Then use the command request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE]) to request the permission. A popup will appear on android, allowing the user to choose.

    Here is a code modification proposal:

    import os
    from kivy.app import App
    from kivy.uix.button import Button
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.textinput import TextInput
    from kivy import platform
    from plyer import filechooser
    
    if platform == "android":
        from android.permissions import request_permissions, Permission, check_permission  # pylint: disable=import-error # type: ignore
        request_permissions([Permission.READ_EXTERNAL_STORAGE,
                            Permission.WRITE_EXTERNAL_STORAGE])
    
    
    class MyApp(App):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.file_paths = []
            self.file_names = []
    
        def build(self):
            layout = BoxLayout(orientation='vertical')
            button = Button(text='Open File Chooser',
                            on_release=self.show_file_chooser)
            self.text_input = TextInput(readonly=True)
            layout.add_widget(button)
            layout.add_widget(self.text_input)
            return layout
    
        def show_file_chooser(self, *args):
            filechooser.open_file(
                on_selection=self.handle_selection, multiple=True)
    
        def handle_selection(self, selection):
            self.file_paths = []
            self.file_names = []
    
            for file_path in selection:
                self.file_paths.append(file_path)
                file_name = os.path.basename(file_path)
                self.file_names.append(file_name)
    
            # Print the list of file paths
            print(self.file_paths)
            print(self.file_names)
    
            # Update the UI with the selected file names
            self.text_input.text = '\n'.join(self.file_names)
    
    
    if __name__ == '__main__':
        MyApp().run()
    

    To go further I suggest you to take a look at this repository it really helped me to integrate a nice file chooser on my own applications and has a relatively simple API.