Konubinix' opinionated web of thoughts

Simple Camera With Kivy

Fleeting

The simple pwa offline camera works well, but has one issue: it reloads when scrolling down.

Let’s try building a simple camera with kivy, using the camera example as basis.

from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from jnius import autoclass
import time
import os
from android.runnable import run_on_ui_thread
from kivy.animation import Animation
from kivy import platform
import plyer

Builder.load_string('''
<CameraClick>:
    Camera:
        id: camera
        resolution: (640, 480)
        # resolution: (-1, -1) # best
        play: True
        allow_stretch: True
    Button:
        id: button
        pos: ((self.parent.width - self.width), (self.parent.height - self.height) / 2)
        size_hint: (None, None)
        size: (200, 200)
        on_press: root.capture()
        background_color: (1, 1, 1, 0) # transparent
        Image:
            id: img
            allow_stretch: True
            keep_ratio: True
            # center in the parent button
            y: self.parent.y + self.parent.height / 2 - self.height / 2
            x: self.parent.x + self.parent.width / 2 - self.width / 2
            # fit to the parent button (keep_ratio will prevent it from being distorded)
            size: (self.parent.width, self.parent.height)

''')


class CameraClick(FloatLayout):
    def __init__(self, *args, **kwargs):
        super(CameraClick, self).__init__(*args, **kwargs)
        self.doing = False
        print("camera click")
        Environment = autoclass('android.os.Environment')
        self.download_folder = os.path.join(Environment.getExternalStorageDirectory().absolutePath, "Download")
        if not os.path.exists(self.download_folder):
            os.mkdir(self.download_folder)
        self.ids.img.source = os.path.join(__file__, "..", "icon.png")

    def done(self, *args):
        self.doing = False

    def shoot(self, *args):
        timestr = time.strftime("%Y%m%d_%H%M%S")
        camera = self.ids['camera']
        camera.export_to_png(os.path.join(self.download_folder, "IMG_{}.png".format(timestr)))
        button = self.ids['button']
        self.anim2 = Animation(background_color=(0, 0, 0, 1), duration=0.5)
        self.anim2 &= Animation(size=(200, 200), duration=0.3)
        self.anim2 &= Animation(pos=((self.width - 200), (self.height - 200) / 2), duration=0.5)
        self.anim2.bind(on_complete=self.done)
        self.anim2.start(button)

    def capture(self):
        if self.doing:
            return

        self.doing = True
        button = self.ids['button']
        self.anim = Animation(background_color=(1, 1, 1, 1), duration=0.2)
        self.anim &= Animation(size=(self.height, self.height), duration=0.2)
        self.anim &= Animation(pos=((self.width - self.height) / 2, 0), duration=0.2)
        self.anim.bind(on_complete=self.shoot)
        self.anim.start(button)


class TestCamera(App):

    def build(self):
        Window.bind(on_keyboard=self.hook_keyboard)
        #plyer.orientation.set_landscape()
        self.enter_fullscreen()
        self.keep_screen_awake()
        return CameraClick()

    def enter_fullscreen(self):
        nil

    def keep_screen_awake(self):
        nil

    def hook_keyboard(self, window, key, *args):
        if key == 27:
            exit(0)
            return False

def run():
    TestCamera().run()

Let’s create a full application, based on from the python android runtime to a custom app.

apktool d app.apk
ipfs get https://konubinix.eu/ipfs/bafkreib4imx2wyoiy5i6xik47kddxqho7iqescorb3zcijxmsyecyxtmfm -o app/assets/icon.png
Saving file(s) to app/assets/icon.png

I changed the main.py to call TestCamera().run() out of run()

pushd "app/assets/" > /dev/null
{
    rm private.tar
    tar -c --gzip -f private.tar .
    mv private.tar /tmp/private.tar && rm * && mv /tmp/private.tar ./
}
popd > /dev/null
magick https://konubinix.eu/ipfs/bafkreib4imx2wyoiy5i6xik47kddxqho7iqescorb3zcijxmsyecyxtmfm ./app/res/drawable/presplash.jpg
magick https://konubinix.eu/ipfs/bafkreib4imx2wyoiy5i6xik47kddxqho7iqescorb3zcijxmsyecyxtmfm -resize 128x128 -background white -gravity center -extent 128x128 ./app/res/mipmap/icon.png
grep -rl '\bproofofconcept\b' | while read line
do
    echo "Editing ${line}"
    sed -i -r 's/proofofconcept/simplecamera/' "${line}"
done
find -path "*eu/konix/poc"
./app/smali_classes3/eu/konix/poc
./app/smali/eu/konix/poc

Renamed into eu/konix/simplecamera

Also globally renamed eu/konix/poc into eu/konix/simplecamera and afterwards eu.konix.poc into eu.konix.simplecamera and “poc” into “simplecamera” and “Poc” into “Simplecamera”

apktool b app && apksigner sign --ks your-keystore.jks --ks-key-alias your-alias --ks-pass pass:000000 ./app/dist/app.apk
I: Using Apktool 2.10.0 with 4 thread(s).
I: Checking whether sources has changed...
I: Checking whether sources has changed...
I: Checking whether sources has changed...
I: Checking whether sources has changed...
I: Checking whether sources has changed...
I: Checking whether sources has changed...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...
I: Built apk into: app/dist/app.apk

Once installed, I needed to provide the authorizations manually (photo and sdcard), as the application did not ask for them.

clk android adb uninstall eu.konix.simplecamera
clk android adb install ./app/dist/app.apk
ipfa ./app/dist/app.apk

Notes linking here