Move Piggy wallet to its own repo

This commit is contained in:
Thomas Farstrike
2025-06-05 22:08:53 +02:00
parent bced8bb8d6
commit f1f6314df8
5 changed files with 0 additions and 1164 deletions
@@ -1,23 +0,0 @@
{
"name": "Lightning Piggy",
"publisher": "LightningPiggy Foundation",
"short_description": "Display wallet that shows balance, transactions, receive QR code etc.",
"long_description": "See https://www.LightningPiggy.com",
"icon_url": "https://apps.micropythonos.com/apps/com.lightningpiggy.displaywallet/icons/com.lightningpiggy.displaywallet_0.0.1_64x64.png",
"download_url": "https://apps.micropythonos.com/apps/com.lightningpiggy.displaywallet/mpks/com.lightningpiggy.displaywallet_0.0.1.mpk",
"fullname": "com.lightningpiggy.displaywallet",
"version": "0.0.1",
"category": "finance",
"activities": [
{
"entrypoint": "assets/displaywallet.py",
"classname": "DisplayWallet",
"intent_filters": [
{
"action": "main",
"category": "launcher"
}
]
}
]
}
@@ -1,277 +0,0 @@
# This code grabs images from the camera in RGB565 format (2 bytes per pixel)
# and sends that to the QR decoder if QR decoding is enabled.
# The QR decoder then converts the RGB565 to grayscale, as that's what quirc operates on.
# It would be slightly more efficient to capture the images from the camera in L8/grayscale format,
# or in YUV format and discarding the U and V planes, but then the image will be gray (not great UX)
# and the performance impact of converting RGB565 to grayscale is probably minimal anyway.
import lvgl as lv
try:
import webcam
except Exception as e:
print(f"Info: could not import webcam module: {e}")
from mpos.apps import Activity
class Camera(Activity):
width = 240
height = 240
status_label_text = "No camera found."
status_label_text_searching = "Searching QR codes...\n\nHold still and make them big!\n10cm for simple QR codes,\n20cm for complex."
status_label_text_found = "Decoding QR..."
cam = None
current_cam_buffer = None # Holds the current memoryview to prevent garbage collection
image = None
image_dsc = None
scanqr_mode = None
use_webcam = False
keepliveqrdecoding = False
capture_timer = None
# Widgets:
qr_label = None
qr_button = None
snap_button = None
status_label = None
status_label_cont = None
def onCreate(self):
self.scanqr_mode = self.getIntent().extras.get("scanqr_mode")
main_screen = lv.obj()
main_screen.set_style_pad_all(0, 0)
main_screen.set_style_border_width(0, 0)
main_screen.set_size(lv.pct(100), lv.pct(100))
main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
close_button = lv.button(main_screen)
close_button.set_size(60,60)
close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0)
close_label = lv.label(close_button)
close_label.set_text(lv.SYMBOL.CLOSE)
close_label.center()
close_button.add_event_cb(lambda e: self.finish(),lv.EVENT.CLICKED,None)
self.snap_button = lv.button(main_screen)
self.snap_button.set_size(60, 60)
self.snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0)
self.snap_button.add_flag(lv.obj.FLAG.HIDDEN)
self.snap_button.add_event_cb(self.snap_button_click,lv.EVENT.CLICKED,None)
snap_label = lv.label(self.snap_button)
snap_label.set_text(lv.SYMBOL.OK)
snap_label.center()
self.qr_button = lv.button(main_screen)
self.qr_button.set_size(60, 60)
self.qr_button.add_flag(lv.obj.FLAG.HIDDEN)
self.qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
self.qr_button.add_event_cb(self.qr_button_click,lv.EVENT.CLICKED,None)
self.qr_label = lv.label(self.qr_button)
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
self.qr_label.center()
# Initialize LVGL image widget
self.image = lv.image(main_screen)
self.image.align(lv.ALIGN.LEFT_MID, 0, 0)
# Create image descriptor once
self.image_dsc = lv.image_dsc_t({
"header": {
"magic": lv.IMAGE_HEADER_MAGIC,
"w": self.width,
"h": self.height,
"stride": self.width * 2,
"cf": lv.COLOR_FORMAT.RGB565
#"cf": lv.COLOR_FORMAT.L8
},
'data_size': self.width * self.height * 2,
'data': None # Will be updated per frame
})
self.image.set_src(self.image_dsc)
self.status_label_cont = lv.obj(main_screen)
self.status_label_cont.set_size(lv.pct(66),lv.pct(60))
self.status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0)
self.status_label_cont.set_style_bg_color(lv.color_white(), 0)
self.status_label_cont.set_style_bg_opa(66, 0)
self.status_label_cont.set_style_border_width(0, 0)
self.status_label = lv.label(self.status_label_cont)
self.status_label.set_text("No camera found.")
self.status_label.set_long_mode(lv.label.LONG.WRAP)
self.status_label.set_style_text_color(lv.color_white(), 0)
self.status_label.set_width(lv.pct(100))
self.status_label.center()
self.setContentView(main_screen)
def onResume(self, screen):
self.cam = init_internal_cam()
if self.cam:
self.image.set_rotation(900) # internal camera is rotated 90 degrees
else:
print("camtest.py: no internal camera found, trying webcam on /dev/video0")
try:
self.cam = webcam.init("/dev/video0")
self.use_webcam = True
except Exception as e:
print(f"camtest.py: webcam exception: {e}")
if self.cam:
print("Camera initialized, continuing...")
self.capture_timer = lv.timer_create(self.try_capture, 100, None)
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
if self.scanqr_mode:
self.start_qr_decoding()
else:
self.qr_button.remove_flag(lv.obj.FLAG.HIDDEN)
self.snap_button.remove_flag(lv.obj.FLAG.HIDDEN)
else:
print("No camera found, stopping camtest.py")
if self.scanqr_mode:
self.finish()
def onStop(self, screen):
print("camtest.py backgrounded, cleaning up...")
if self.capture_timer:
self.capture_timer.delete()
if self.use_webcam:
webcam.deinit(self.cam)
elif self.cam:
self.cam.deinit()
print("camtest.py cleanup done.")
def qrdecode_one(self):
try:
import qrdecode
result = qrdecode.qrdecode_rgb565(self.current_cam_buffer, self.width, self.height)
#result = bytearray("INSERT_QR_HERE", "utf-8")
if not result:
self.status_label.set_text(self.status_label_text_searching)
else:
self.stop_qr_decoding()
result = remove_bom(result)
result = print_qr_buffer(result)
print(f"QR decoding found: {result}")
if self.scanqr_mode:
self.setResult(True, result)
self.finish()
else:
self.status_label.set_text(result) # in the future, the status_label text should be copy-paste-able
except ValueError as e:
print("QR ValueError: ", e)
self.status_label.set_text(self.status_label_text_searching)
except TypeError as e:
print("QR TypeError: ", e)
self.status_label.set_text(self.status_label_text_found)
except Exception as e:
print("QR got other error: ", e)
def snap_button_click(self, e):
print("Picture taken!")
import os
try:
os.mkdir("data")
except OSError:
pass
try:
os.mkdir("data/com.example.camtest")
except OSError:
pass
if self.current_cam_buffer is not None:
filename="data/com.example.camtest/capture.raw"
try:
with open(filename, 'wb') as f:
f.write(self.current_cam_buffer)
print(f"Successfully wrote current_cam_buffer to {filename}")
except OSError as e:
print(f"Error writing to file: {e}")
def start_qr_decoding(self):
print("Activating live QR decoding...")
self.keepliveqrdecoding = True
self.qr_label.set_text(lv.SYMBOL.EYE_CLOSE)
self.status_label_cont.remove_flag(lv.obj.FLAG.HIDDEN)
self.status_label.set_text(self.status_label_text_searching)
def stop_qr_decoding(self):
print("Deactivating live QR decoding...")
self.keepliveqrdecoding = False
self.qr_label.set_text(lv.SYMBOL.EYE_OPEN)
self.status_label_text = self.status_label.get_text()
if self.status_label_text in (self.status_label_text_searching or self.status_label_text_found): # if it found a QR code, leave it
self.status_label_cont.add_flag(lv.obj.FLAG.HIDDEN)
def qr_button_click(self, e):
if not self.keepliveqrdecoding:
self.start_qr_decoding()
else:
self.stop_qr_decoding()
def try_capture(self, event):
#print("capturing camera frame")
try:
if self.use_webcam:
self.current_cam_buffer = webcam.capture_frame(self.cam, "rgb565")
elif self.cam.frame_available():
self.current_cam_buffer = self.cam.capture()
if self.current_cam_buffer and len(self.current_cam_buffer):
self.image_dsc.data = self.current_cam_buffer
#image.invalidate() # does not work so do this:
self.image.set_src(self.image_dsc)
if not self.use_webcam:
self.cam.free_buffer() # Free the old buffer
if self.keepliveqrdecoding:
self.qrdecode_one()
except Exception as e:
print(f"Camera capture exception: {e}")
# Non-class functions:
def init_internal_cam():
try:
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
cam = Camera(
data_pins=[12,13,15,11,14,10,7,2],
vsync_pin=6,
href_pin=4,
sda_pin=21,
scl_pin=16,
pclk_pin=9,
xclk_pin=8,
xclk_freq=20000000,
powerdown_pin=-1,
reset_pin=-1,
pixel_format=PixelFormat.RGB565,
#pixel_format=PixelFormat.GRAYSCALE,
frame_size=FrameSize.R240X240,
grab_mode=GrabMode.LATEST
)
#cam.init() automatically done when creating the Camera()
#cam.reconfigure(frame_size=FrameSize.HVGA)
#frame_size=FrameSize.HVGA, # 480x320
#frame_size=FrameSize.QVGA, # 320x240
#frame_size=FrameSize.QQVGA # 160x120
cam.set_vflip(True)
return cam
except Exception as e:
print(f"init_cam exception: {e}")
return None
def print_qr_buffer(buffer):
try:
# Try to decode buffer as a UTF-8 string
result = buffer.decode('utf-8')
# Check if the string is printable (ASCII printable characters)
if all(32 <= ord(c) <= 126 for c in result):
return result
except Exception as e:
pass
# If not a valid string or not printable, convert to hex
hex_str = ' '.join([f'{b:02x}' for b in buffer])
return hex_str.lower()
# Byte-Order-Mark is added sometimes
def remove_bom(buffer):
bom = b'\xEF\xBB\xBF'
if buffer.startswith(bom):
return buffer[3:]
return buffer
@@ -1,363 +0,0 @@
from mpos.apps import Activity, Intent
import mpos.config
from wallet import LNBitsWallet, NWCWallet
from captureqr import Camera
class DisplayWallet(Activity):
wallet = None
receive_qr_data = None
destination = None
# widgets
balance_label = None
receive_qr = None
payments_label = None
def onCreate(self):
main_screen = lv.obj()
main_screen.set_style_pad_all(10, 0)
self.balance_label = lv.label(main_screen)
self.balance_label.set_text("")
self.balance_label.align(lv.ALIGN.TOP_LEFT, 0, 0)
self.balance_label.set_style_text_font(lv.font_montserrat_22, 0)
self.receive_qr = lv.qrcode(main_screen)
self.receive_qr.set_size(50)
self.receive_qr.set_dark_color(lv.color_black())
self.receive_qr.set_light_color(lv.color_white())
self.receive_qr.align(lv.ALIGN.TOP_RIGHT,0,0)
self.receive_qr.set_style_border_color(lv.color_white(), 0)
self.receive_qr.set_style_border_width(3, 0);
self.receive_qr.add_flag(lv.obj.FLAG.CLICKABLE)
self.receive_qr.add_event_cb(self.qr_clicked_cb,lv.EVENT.CLICKED,None)
balance_line = lv.line(main_screen)
balance_line.set_points([{'x':0,'y':35},{'x':200,'y':35}],2)
self.payments_label = lv.label(main_screen)
self.payments_label.set_text("")
self.payments_label.align_to(balance_line,lv.ALIGN.OUT_BOTTOM_LEFT,0,10)
self.payments_label.set_style_text_font(lv.font_montserrat_16, 0)
settings_button = lv.button(main_screen)
settings_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0)
snap_label = lv.label(settings_button)
snap_label.set_text(lv.SYMBOL.SETTINGS)
snap_label.center()
settings_button.add_event_cb(self.settings_button_tap,lv.EVENT.CLICKED,None)
self.setContentView(main_screen)
def onStart(self, main_screen):
self.main_ui_set_defaults()
def onResume(self, main_screen):
if not self.wallet or not self.wallet.is_running(): # just started the app or just returned from settings_screen
config = mpos.config.SharedPreferences("com.lightningpiggy.displaywallet")
wallet_type = config.get_string("wallet_type")
if wallet_type == "lnbits":
try:
self.receive_qr_data = config.get_string("lnbits_static_receive_code")
self.wallet = LNBitsWallet(config.get_string("lnbits_url"), config.get_string("lnbits_readkey"))
except Exception as e:
self.payments_label.set_text(f"Couldn't initialize LNBitsWallet\nbecause: {e}")
elif wallet_type == "nwc":
try:
self.wallet = NWCWallet(config.get_string("nwc_url"))
self.receive_qr_data = wallet.lud16
except Exception as e:
self.payments_label.set_text(f"Couldn't initialize NWCWallet\nbecause: {e}")
else:
self.payments_label.set_text(f"No or unsupported wallet\ntype configured: '{wallet_type}'")
if self.receive_qr_data:
print(f"Setting static_receive_code: {self.receive_qr_data}")
self.receive_qr.update(self.receive_qr_data, len(self.receive_qr_data))
can_check_network = True
try:
import network
except Exception as e:
can_check_network = False
if can_check_network and not network.WLAN(network.STA_IF).isconnected():
self.payments_label.set_text(f"WiFi is not connected, can't\ntalk to {wallet_type} backend.")
else:
if self.wallet:
self.payments_label.set_text(f"Connecting to {wallet_type} backend...")
self.wallet.start(self.redraw_balance_cb, self.redraw_payments_cb)
else:
self.payments_label.set_text(f"Could not start {wallet_type} backend.")
def onStop(self, main_screen):
if self.wallet and self.destination != FullscreenQR:
self.wallet.stop()
self.destination = None
def redraw_balance_cb(self):
# this gets called from another thread (the wallet) so make sure it happens in the LVGL thread using lv.async_call():
lv.async_call(lambda l: self.balance_label.set_text(str(self.wallet.last_known_balance)), None)
def redraw_payments_cb(self):
# this gets called from another thread (the wallet) so make sure it happens in the LVGL thread using lv.async_call():
lv.async_call(lambda l: self.payments_label.set_text(str(self.wallet.payment_list)), None)
def settings_button_tap(self, event):
self.startActivity(Intent(activity_class=SettingsActivity))
def main_ui_set_defaults(self):
self.balance_label.set_text(lv.SYMBOL.REFRESH)
self.payments_label.set_text(lv.SYMBOL.REFRESH)
self.receive_qr.update("EMPTY PLACEHOLDER", len("EMPTY PLACEHOLDER"))
def qr_clicked_cb(self, event):
print("QR clicked")
if not self.receive_qr_data:
return
self.destination = FullscreenQR
self.startActivity(Intent(activity_class=FullscreenQR).putExtra("receive_qr_data", self.receive_qr_data))
# Used to list and edit all settings:
class SettingsActivity(Activity):
def __init__(self):
super().__init__()
self.prefs = mpos.config.SharedPreferences("com.lightningpiggy.displaywallet")
self.settings = [
{"title": "Wallet Type", "key": "wallet_type", "value_label": None, "cont": None},
{"title": "LNBits URL", "key": "lnbits_url", "value_label": None, "cont": None},
{"title": "LNBits Read Key", "key": "lnbits_readkey", "value_label": None, "cont": None},
{"title": "Static receive code", "key": "lnbits_static_receive_code", "value_label": None, "cont": None},
{"title": "NWC URL", "key": "nwc_url", "value_label": None, "cont": None},
]
self.keyboard = None
self.textarea = None
self.radio_container = None
self.active_radio_index = 0 # Track active radio button index
def onCreate(self):
screen = lv.obj()
print("creating SettingsActivity ui...")
screen.set_size(lv.pct(100), lv.pct(100))
screen.set_style_pad_all(10, 0)
screen.set_flex_flow(lv.FLEX_FLOW.COLUMN)
screen.set_style_border_width(0, 0)
# Create settings entries
for setting in self.settings:
# Container for each setting
setting_cont = lv.obj(screen)
setting_cont.set_width(lv.pct(100))
setting_cont.set_height(lv.SIZE_CONTENT)
setting_cont.set_style_border_width(1, 0)
setting_cont.set_style_border_side(lv.BORDER_SIDE.BOTTOM, 0)
setting_cont.set_style_pad_all(8, 0)
setting_cont.add_flag(lv.obj.FLAG.CLICKABLE)
setting["cont"] = setting_cont # Store container reference for visibility control
# Title label (bold, larger)
title = lv.label(setting_cont)
title.set_text(setting["title"])
title.set_style_text_font(lv.font_montserrat_16, 0)
title.set_pos(0, 0)
# Value label (smaller, below title)
value = lv.label(setting_cont)
value.set_text(self.prefs.get_string(setting["key"], "Not set"))
value.set_style_text_font(lv.font_montserrat_12, 0)
value.set_style_text_color(lv.color_hex(0x666666), 0)
value.set_pos(0, 20)
setting["value_label"] = value # Store reference for updating
setting_cont.add_event_cb(
lambda e, s=setting: self.startSettingActivity(s), lv.EVENT.CLICKED, None
)
self.setContentView(screen)
def onResume(self, screen):
wallet_type = self.prefs.get_string("wallet_type", "lnbits")
# update setting visibility based on wallet_type:
for setting in self.settings:
if setting["key"].startswith("lnbits_"):
if wallet_type != "lnbits":
setting["cont"].add_flag(lv.obj.FLAG.HIDDEN)
else:
setting["cont"].remove_flag(lv.obj.FLAG.HIDDEN)
elif setting["key"].startswith("nwc_"):
if wallet_type != "nwc":
setting["cont"].add_flag(lv.obj.FLAG.HIDDEN)
else:
setting["cont"].remove_flag(lv.obj.FLAG.HIDDEN)
def startSettingActivity(self, setting):
intent = Intent(activity_class=SettingActivity)
intent.putExtra("setting", setting)
self.startActivity(intent)
# Used to edit one setting:
class SettingActivity(Activity):
def __init__(self):
super().__init__()
self.prefs = mpos.config.SharedPreferences("com.lightningpiggy.displaywallet")
self.setting = None
def onCreate(self):
setting = self.getIntent().extras.get("setting")
settings_screen_detail = lv.obj()
settings_screen_detail.set_style_pad_all(10, 0)
settings_screen_detail.set_flex_flow(lv.FLEX_FLOW.COLUMN)
top_cont = lv.obj(settings_screen_detail)
top_cont.set_width(lv.pct(100))
top_cont.set_height(lv.SIZE_CONTENT)
top_cont.set_style_pad_all(0, 0)
top_cont.set_flex_flow(lv.FLEX_FLOW.ROW)
top_cont.set_style_flex_main_place(lv.FLEX_ALIGN.SPACE_BETWEEN, 0)
setting_label = lv.label(top_cont)
setting_label.set_text(setting["title"])
setting_label.align(lv.ALIGN.TOP_LEFT,0,0)
setting_label.set_style_text_font(lv.font_montserrat_22, 0)
# Camera for text
cambutton = lv.button(top_cont)
cambutton.align(lv.ALIGN.TOP_RIGHT,0,0)
cambuttonlabel = lv.label(cambutton)
cambuttonlabel.set_text("SCAN QR")
cambuttonlabel.center()
cambutton.add_event_cb(self.cambutton_cb, lv.EVENT.CLICKED, None)
if setting["key"] == "wallet_type":
cambutton.add_flag(lv.obj.FLAG.HIDDEN)
# Create container for radio buttons
self.radio_container = lv.obj(settings_screen_detail)
self.radio_container.set_width(lv.pct(100))
self.radio_container.set_height(lv.SIZE_CONTENT)
self.radio_container.set_flex_flow(lv.FLEX_FLOW.COLUMN)
self.radio_container.add_event_cb(self.radio_event_handler, lv.EVENT.CLICKED, None)
# Create radio buttons
options = [("LNBits", "lnbits"), ("Nostr Wallet Connect", "nwc")]
current_wallet = self.prefs.get_string("wallet_type", "lnbits")
self.active_radio_index = 0 if current_wallet == "lnbits" else 1
for i, (text, _) in enumerate(options):
cb = self.create_radio_button(self.radio_container, text, i)
if i == self.active_radio_index:
cb.add_state(lv.STATE.CHECKED)
else:
# Textarea for other settings
self.textarea = lv.textarea(settings_screen_detail)
self.textarea.set_width(lv.pct(100))
self.textarea.set_height(lv.SIZE_CONTENT)
self.textarea.set_text(self.prefs.get_string(setting["key"], ""))
self.textarea.add_event_cb(self.show_keyboard, lv.EVENT.CLICKED, None)
self.textarea.add_event_cb(self.show_keyboard, lv.EVENT.FOCUSED, None)
self.textarea.add_event_cb(self.hide_keyboard, lv.EVENT.DEFOCUSED, None)
# Initialize keyboard (hidden initially)
self.keyboard = lv.keyboard(lv.layer_sys())
self.keyboard.set_size(lv.pct(100), lv.pct(40))
self.keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
self.keyboard.add_flag(lv.obj.FLAG.HIDDEN)
self.keyboard.add_event_cb(self.keyboard_cb, lv.EVENT.READY, None)
self.keyboard.add_event_cb(self.keyboard_cb, lv.EVENT.CANCEL, None)
self.keyboard.set_textarea(self.textarea)
# Button container
btn_cont = lv.obj(settings_screen_detail)
btn_cont.set_width(lv.pct(100))
btn_cont.set_height(lv.SIZE_CONTENT)
btn_cont.set_style_pad_all(5, 0)
btn_cont.set_flex_flow(lv.FLEX_FLOW.ROW)
btn_cont.set_style_flex_main_place(lv.FLEX_ALIGN.SPACE_BETWEEN, 0)
# Save button
save_btn = lv.button(btn_cont)
save_btn.set_size(lv.pct(45), lv.SIZE_CONTENT)
save_label = lv.label(save_btn)
save_label.set_text("Save")
save_label.center()
save_btn.add_event_cb(lambda e, s=setting: self.save_setting(s), lv.EVENT.CLICKED, None)
# Cancel button
cancel_btn = lv.button(btn_cont)
cancel_btn.set_size(lv.pct(45), lv.SIZE_CONTENT)
cancel_label = lv.label(cancel_btn)
cancel_label.set_text("Cancel")
cancel_label.center()
cancel_btn.add_event_cb(lambda e: self.finish(), lv.EVENT.CLICKED, None)
self.setContentView(settings_screen_detail)
def hide_keyboard(self, event=None):
print("hide_keyboard: hiding keyboard")
self.keyboard.add_flag(lv.obj.FLAG.HIDDEN)
def show_keyboard(self, event):
print("showing keyboard")
self.keyboard.remove_flag(lv.obj.FLAG.HIDDEN)
def keyboard_cb(self, event=None):
print("keyboard_cb: Keyboard event triggered")
code = event.get_code()
if code == lv.EVENT.READY or code == lv.EVENT.CANCEL:
print("keyboard_cb: READY or CANCEL or RETURN clicked, hiding keyboard")
self.hide_keyboard()
def radio_event_handler(self, event):
old_cb = self.radio_container.get_child(self.active_radio_index)
old_cb.remove_state(lv.STATE.CHECKED)
self.active_radio_index = -1
for childnr in range(self.radio_container.get_child_count()):
child = self.radio_container.get_child(childnr)
state = child.get_state()
print(f"radio_container child's state: {state}")
if state != lv.STATE.DEFAULT: # State can be something like 19 = lv.STATE.HOVERED & lv.STATE.CHECKED & lv.STATE.FOCUSED
self.active_radio_index = childnr
break
print(f"active_radio_index is now {self.active_radio_index}")
def create_radio_button(self, parent, text, index):
cb = lv.checkbox(parent)
cb.set_text(text)
cb.add_flag(lv.obj.FLAG.EVENT_BUBBLE)
# Add circular style to indicator for radio button appearance
style_radio = lv.style_t()
style_radio.init()
style_radio.set_radius(lv.RADIUS_CIRCLE)
cb.add_style(style_radio, lv.PART.INDICATOR)
style_radio_chk = lv.style_t()
style_radio_chk.init()
style_radio_chk.set_bg_image_src(None)
cb.add_style(style_radio_chk, lv.PART.INDICATOR | lv.STATE.CHECKED)
return cb
def gotqr_result_callback(self, result):
print(f"QR capture finished, result: {result}")
if result.get("result_code"):
data = result.get("data")
print(f"Setting textarea data: {data}")
self.textarea.set_text(data)
def cambutton_cb(self, event):
print("cambutton clicked!")
self.startActivityForResult(Intent(activity_class=Camera).putExtra("scanqr_mode", True), self.gotqr_result_callback)
def save_setting(self, setting):
if setting["key"] == "wallet_type" and self.radio_container:
selected_idx = self.active_radio_index
new_value = "lnbits" if selected_idx == 0 else "nwc"
elif self.textarea:
new_value = self.textarea.get_text()
else:
new_value = ""
editor = self.prefs.edit()
editor.put_string(setting["key"], new_value)
editor.commit()
setting["value_label"].set_text(new_value if new_value else "Not set")
self.finish()
class FullscreenQR(Activity):
# No __init__() so super.__init__() will be called automatically
def onCreate(self):
receive_qr_data = self.getIntent().extras.get("receive_qr_data")
qr_screen = lv.obj()
big_receive_qr = lv.qrcode(qr_screen)
big_receive_qr.set_size(240) # TODO: make this dynamic
big_receive_qr.set_dark_color(lv.color_black())
big_receive_qr.set_light_color(lv.color_white())
big_receive_qr.center()
big_receive_qr.set_style_border_color(lv.color_white(), 0)
big_receive_qr.set_style_border_width(3, 0);
big_receive_qr.update(receive_qr_data, len(receive_qr_data))
self.setContentView(qr_screen)
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB