Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Three issues on Android #29

Open
RobertFlatt opened this issue Jul 28, 2020 · 7 comments
Open

Three issues on Android #29

RobertFlatt opened this issue Jul 28, 2020 · 7 comments

Comments

@RobertFlatt
Copy link

  1. App crashes if index=1 , I was expecting the selfie camera; index = 0 works as expected.
  2. How to release and attach the camera during on_pause() and on_resume() events ?
  3. on_camera_ready() is not called because on_index() is not called by default.

Despite the issues, the code is a joy to read. :)

Example code (not as pretty as I would like because no on_camera_ready) , and log for the first issue follow:

#!/usr/bin/env python
from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform
from kivy.clock import Clock
from os.path import isdir
from os import mkdir
try:
    from android.permissions import request_permissions, check_permission, \
        Permission
    from android.storage import primary_external_storage_path
except:
    pass

kv = """
#:import XCamera kivy_garden.xcamera.XCamera
FloatLayout:
    orientation: 'vertical'
    XCamera:
        id: xcamera
        on_picture_taken: app.picture_taken(*args)
        on_camera_ready: app.camera_ready()
    BoxLayout:
        orientation: 'horizontal'
        size_hint: 1, None
        height: sp(50)
        Button:
            text: 'Set landscape'
            on_release: xcamera.force_landscape()
        Button:
            text: 'Restore orientation'
            on_release: xcamera.restore_orientation()
"""

class CameraApp(App):
    def build(self):
        return Builder.load_string(kv)

    def on_start(self):
        if platform == 'android':
            # ISSUE 1 :
            # Crash when index =1 , index = 0 is OK
            self.root.ids.xcamera.index = 0
            request_permissions([Permission.WRITE_EXTERNAL_STORAGE,
                                 Permission.CAMERA],
                                self.setup_storage)
            self.setup_storage([],[])

    # ISSUE 2 : how to open and close camera ?
    def on_pause(self):
        return True

    def on_resume(self):
        pass

    def camera_ready(self):
        # In kv above     on_camera_ready: app.camera_ready()
        # ISSUE 3 :
        # This is never called because on_index is not called by default
        pass
        
    def setup_storage(self,permissions,grants):
        if platform == 'android':
            if check_permission(Permission.WRITE_EXTERNAL_STORAGE):
                path = join(primary_external_storage_path(),'Pictures')
                if not isdir(path):
                    mkdir(path)
                path = join(path,'Xcamera')
                if not isdir(path):
                    mkdir(path)
                self.root.ids.xcamera.directory = path
            if check_permission(Permission.CAMERA):
                # One sec hack because on_camera_ready() isnt called
                Clock.schedule_once(self.force_landscape,1)

    def force_landscape(self,dt):
        # Force long edge of image to be parallel to long edge of screen.
        # This is independent of the orientation of the screen in the world.
        # At least that is what happens on my Pixel 3
        self.root.ids.xcamera.force_landscape()

    def picture_taken(self, obj, filename):
        print('Picture taken and saved to {}'.format(filename))

def main():
    CameraApp().run()


if __name__ == '__main__':
    main()

Crash log

07-27 13:43:50.324 31415 31444 I python  : [INFO   ] [Base        ] Leaving application in progress...
07-27 13:43:50.324 31415 31444 I python  :  Traceback (most recent call last):
07-27 13:43:50.324 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/app/main.py", line 91, in <module>
07-27 13:43:50.325 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/app/main.py", line 87, in main
07-27 13:43:50.325 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/app.py", line 855, in run
07-27 13:43:50.325 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/base.py", line 504, in runTouchApp
07-27 13:43:50.325 26747 26747 W ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@79e1678
07-27 13:43:50.325 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/core/window/window_sdl2.py", line 747, in mainloop
07-27 13:43:50.326 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/core/window/window_sdl2.py", line 479, in _mainloop
07-27 13:43:50.326 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/base.py", line 339, in idle
07-27 13:43:50.326 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/clock.py", line 591, in tick
07-27 13:43:50.326 31415 31444 I python  :    File "kivy/_clock.pyx", line 384, in kivy._clock.CyClockBase._process_events
07-27 13:43:50.326 31415 31444 I python  :    File "kivy/_clock.pyx", line 414, in kivy._clock.CyClockBase._process_events
07-27 13:43:50.326 31415 31444 I python  :    File "kivy/_clock.pyx", line 412, in kivy._clock.CyClockBase._process_events
07-27 13:43:50.327 31415 31444 I python  :    File "kivy/_clock.pyx", line 167, in kivy._clock.ClockEvent.tick
07-27 13:43:50.327 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/core/camera/camera_android.py", line 155, in _update
07-27 13:43:50.327 31415 31444 I python  :    File "kivy/_event.pyx", line 703, in kivy._event.EventDispatcher.dispatch
07-27 13:43:50.327 31415 31444 I python  :    File "kivy/_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
07-27 13:43:50.328 31415 31444 I python  :    File "kivy/_event.pyx", line 1138, in kivy._event.EventObservers._dispatch
07-27 13:43:50.328 31415 31415 V SDL     : onWindowFocusChanged(): false
07-27 13:43:50.328 31415 31444 I python  :    File "/home/bobf/ex/xcam/.buildozer/android/platform/build-arm64-v8a/build/python-installs/xcam/kivy/uix/camera.py", line 111, in _camera_loaded
07-27 13:43:50.328 31415 31444 I python  :  AttributeError: 'NoneType' object has no attribute 'size'
07-27 13:43:50.328 31415 31444 I python  : Python for android ended.




@RobertFlatt
Copy link
Author

RobertFlatt commented Jul 29, 2020

I was able to address issue 3 (I think) but I still can't pause/resume or access a camera other than 0.
Even removing and replacing the xcamera widget doesn't address pause/resume.

Forcing an exit on_pause is a sort of workaround for issue 2

Resume, and index=1 are (I think) basic functionality, so I must be missing something basic.
Anybody have any suggestions?

#!/usr/bin/env python
from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy_garden.xcamera import XCamera
from os.path import isdir
from os import mkdir
try:
    from android.permissions import request_permission, check_permission
    from android.permissions import Permission
    from android.storage import primary_external_storage_path
    from jnius import autoclass
    HardwareCamera = autoclass('android.hardware.Camera')
except:
    pass

class CameraApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.platform_camera = None
        self.platform_camera_paused = False

    def build(self):
        self.xcamera = XCamera()
        self.xcamera.on_picture_taken = self.picture_taken
        self.xcamera._camera.bind(on_load=self.camera_loaded)
        self.layout = FloatLayout()
        self.layout.add_widget(self.xcamera)
        return self.layout

    def on_start(self):
        if platform == 'android':
            if HardwareCamera.getNumberOfCameras() > 1:
                # ISSUE 1 : how to select the other camera ?
                pass
            request_permission(Permission.WRITE_EXTERNAL_STORAGE)

    # ISSUE 2 : how to open and close camera ?
    def on_pause(self):
        if platform == 'android':
            if self.platform_camera:
                # Ignore any on_pause before the camera is loaded
                # and ignore the pair'd on_resume
                '''
                # Try: Remove widget 
                self.layout.remove_widget(self.xcamera)
                self.xcamera = None
                Builder.unload_file('xcamera.kv') 
                '''
                ''' 
                # Try: Disconnect camera
                self.platform_camera.stop()
                self.platform_camera._release_camera()
                '''
                self.platform_camera_paused = True
                # This is a work around, requires multiple taps to 'resume'
                return False
        return True

    def on_resume(self):
        if platform == 'android':
            if self.platform_camera_paused:
                '''
                # Try: Recreate widget - black screen
                self.xcamera = XCamera()
                self.xcamera.on_picture_taken = self.picture_taken
                self.xcamera._camera.bind(on_load=self.camera_loaded)
                self.xcamera.play = True
                self.xcamera.index = 0
                self.layout.add_widget(self.xcamera)
                '''
                '''
                # Try: Reconnect camera - image freezes
                self.platform_camera.init_camera()
                self.platform_camera.start()
                '''
                self.platform_camera_paused = False
        
    def camera_loaded(self,platform_camera):
        # Depending on the platform, 'platform_camera' is in one of:
        # https://github.com/kivy/kivy/tree/master/kivy/core/camera
        if platform == 'android':
            # Save the CameraAndroid instance for on_pause/on_resume
            # Because well behaved apps disconnect and connect the camera
            # Assumes only one camera visible at one time.
            self.platform_camera = platform_camera
            # The goal is for the horizon in the image on the screen to
            # be parallel to the horizon in the physical world, and
            # mostly fill the screen.
            # So force long edge of the image to be parallel to long edge
            # of the screen. Thus the image is independent of the
            # orientation of the screen in the physical world.
            # The goal is met on my Pixel 3 by force_landscape()
            # The goal may not be met on all other devices.
            #
            #In camera portrait mode, a little extra delay is needed !
            Clock.schedule_once(self.force_landscape,0)

            # We need to check we can write to the shared directory at
            # each potential use, because we do not own the shared directory
            # and permission can be revoked.
            self.xcamera.directory = None # app install directory
            if check_permission(Permission.WRITE_EXTERNAL_STORAGE):
                path = join(primary_external_storage_path(),'Pictures')
                if not isdir(path):
                    mkdir(path)
                path = join(path,'XCamera')
                if not isdir(path):
                    mkdir(path)
                self.xcamera.directory = path

    def force_landscape(self,dt):
        self.xcamera.force_landscape()
        
    def picture_taken(self, filename):
        print('Picture taken and saved to {}'.format(filename))

def main():
    CameraApp().run()


if __name__ == '__main__':
    main()

@will702
Copy link

will702 commented Jun 14, 2021

can u do that on kivy camera ? i @RobertFlatt

@RobertFlatt
Copy link
Author

You don't say what "that" is, but Xcamera is a wrapper around Kivy camera so whatever "that" is, probably.

A quick look at the code would have shown you this https://github.com/kivy-garden/xcamera/blob/develop/src/kivy_garden/xcamera/xcamera.py#L9

If you want to know what Kivy camera can do, look at the code https://github.com/kivy/kivy/blob/master/kivy/uix/camera.py#L36

@will702
Copy link

will702 commented Jun 15, 2021

yes i already try to change the index when the app is running and I got a pyjnius error @RobertFlatt

@0000Blaze
Copy link

I did somewhat fix the ISSUE 1 in my own project . I just added a index: 1 into the xcamera.kv file in example from https://github.com/kivy-garden/xcamera/tree/develop/src/kivy_garden/xcamera and also added a index: 1 into my own custom style.kv file calling the xcamera widget

@RobertFlatt
Copy link
Author

I gave up with Xcamera and wrote a new camera https://github.com/Android-for-Python/Camera4Kivy

@wanaxe
Copy link

wanaxe commented Jun 8, 2022

I had a similar situation. My laptop has an integrated camera (which actually includes 2: a RGB camera plus a IR camera), and a Logitech C170 webcam. OK for index = 0. Crash if index = 1 (i.e., error can't open camera by index).

Googled serveral hours for a solution. Then I tried larger index values, and finally get all cameras working:

  • index = 0: Integrated RGB camera
  • index = 2: Integrated IR camera
  • index = 4: Logitech C170 webcam

Yet I have no idea how those indices are assigned. So, the solution / workaround is to try different index values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants