pythonandroidpermissionskivy

Getting [Errno 1] Operation is not permitted error when trying to write to /storage/emulated/0 with Kivy and Python on Android


I have been around the mulberry bush for a few weeks now trying to implement the writing of a file on an android phone. My development setup is on a Windows PC with Anaconda and Pycharm. I am using kivy and buildozer to create the android app. My test app is simply trying to write a file (test.txt) to the root directory of the SD card on the phone.

The directory is: /storage/emulated/0

The error I am getting is: [Errno 1] Operation is not permitted: '/storage/emulated/0/test.txt'

I ask for permissions in the code, but the permissions are not prompted at application startup or installation.

Here is the code;

import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.utils import platform
import os

class MyGridLayout(GridLayout):
    def __init__(self, **kwargs):
        super(MyGridLayout, self).__init__(**kwargs)

        self.cols = 1

        self.dir_button = Button(text="Show Directory")
        self.dir_button.bind(on_press=self.press_dir)
        self.add_widget(self.dir_button)

        self.write_button = Button(text="Write File")
        self.write_button.bind(on_press=self.press_write)
        self.add_widget(self.write_button)

        self.text_field = TextInput(multiline=True)
        self.add_widget(self.text_field)

    def press_dir(self, instance):
        if platform == 'android':
            from android.storage import primary_external_storage_path
            self.file_dir = primary_external_storage_path()
        else:
            self.file_dir = App.get_running_app().user_data_dir

        if not os.path.exists(self.file_dir):
            os.mkdir(self.file_dir)

        self.text_field.text = self.file_dir

    def press_write(self, instance):
        self.file_name = "test.txt"
        self.full_name = os.path.join(self.file_dir, self.file_name)
        try:
            with open(self.full_name, "w") as outfile:
                outfile.write("Hello World")
        except Exception as e:
            self.text_field.text = str(e)

class TestWriteApp(App):
    def build(self):
        return MyGridLayout()

if platform == 'android':
    from android.permissions import request_permissions, Permission
    request_permissions([Permission.WRITE_EXTERNAL_STORAGE,
                         Permission.READ_EXTERNAL_STORAGE])

testwriteapp = TestWriteApp()
testwriteapp.run()

Here are the screenshots during testing; Show Directory button enter image description here

Write File button enter image description here

Thanks for your help.

I tried a number of things...adding permissions in the buildozer.spec file, changing the output location to the internal user_data_dir (which worked, I think, but I need the user to be able to use the Files app to get the generated file)


Solution

  • On Android 11+ devices you cannot write anymore to root of external storage as you try to do.

    Instead use public directories that are already there like Documents and Download.

    On Android 13 devices you do not need any permission to do so.