mirror of
https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot.git
synced 2026-07-23 04:22:09 +08:00
248 lines
8.1 KiB
Python
248 lines
8.1 KiB
Python
from __future__ import annotations
|
|
|
|
import pyray as rl
|
|
|
|
from openpilot.system.hardware.base import Profile
|
|
from openpilot.system.hardware.tici.esim_manager import EsimManager, EsimOperationState, EsimUiState, get_esim_manager
|
|
from openpilot.system.ui.lib.application import gui_app
|
|
from openpilot.system.ui.lib.multilang import tr
|
|
from openpilot.system.ui.widgets import DialogResult, Widget
|
|
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
|
from openpilot.system.ui.widgets.keyboard import Keyboard
|
|
from openpilot.system.ui.widgets.list_view import ListItem, button_item, text_item
|
|
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
|
|
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
|
from openpilot.system.ui.widgets.esim_scanner import EsimQrScannerDialog
|
|
|
|
|
|
class EsimPanel(Widget):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._manager: EsimManager = get_esim_manager()
|
|
self._state = EsimUiState()
|
|
self._keyboard = Keyboard(max_text_size=256, min_text_size=0)
|
|
self._callback_registered = False
|
|
self._scroller = Scroller([])
|
|
self._rebuild_scroller()
|
|
|
|
def show_event(self):
|
|
if not self._callback_registered:
|
|
self._manager.add_callback(self._on_state_update)
|
|
self._callback_registered = True
|
|
self._manager.refresh_profiles()
|
|
|
|
def hide_event(self):
|
|
if self._callback_registered:
|
|
self._manager.remove_callback(self._on_state_update)
|
|
self._callback_registered = False
|
|
|
|
def _is_busy(self) -> bool:
|
|
return self._state.busy
|
|
|
|
def is_supported(self) -> bool:
|
|
return self._manager.is_supported()
|
|
|
|
def _on_state_update(self, state: EsimUiState):
|
|
self._state = state
|
|
self._rebuild_scroller()
|
|
|
|
def _state_text(self) -> str:
|
|
if self._state.message:
|
|
return self._state.message
|
|
return self._state.state.value
|
|
|
|
def _rebuild_scroller(self):
|
|
items: list[Widget] = [
|
|
text_item(lambda: tr("Status"), lambda: self._state_text()),
|
|
button_item(
|
|
lambda: tr("Refresh Profiles"),
|
|
lambda: tr("REFRESH"),
|
|
callback=self._on_refresh,
|
|
enabled=lambda: not self._is_busy(),
|
|
),
|
|
button_item(
|
|
lambda: tr("Add Profile"),
|
|
lambda: tr("ADD"),
|
|
description=lambda: tr("Scan QR or enter activation code"),
|
|
callback=self._on_add_profile,
|
|
enabled=lambda: not self._is_busy(),
|
|
),
|
|
]
|
|
|
|
profiles = self._state.profiles or []
|
|
for profile in profiles:
|
|
items.append(self._make_profile_item(profile))
|
|
|
|
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
|
|
|
def _make_profile_item(self, profile: Profile) -> ListItem:
|
|
label = profile.nickname if profile.nickname else profile.iccid
|
|
provider = profile.provider if profile.provider else tr("Unknown provider")
|
|
description = f"{provider}\n{profile.iccid}"
|
|
action = tr("ACTIVE") if profile.enabled else tr("USE")
|
|
return button_item(
|
|
lambda label=label: label,
|
|
lambda action=action: action,
|
|
description=lambda description=description: description,
|
|
callback=lambda profile=profile: self._on_profile_selected(profile),
|
|
enabled=lambda: not self._is_busy(),
|
|
)
|
|
|
|
def _on_refresh(self):
|
|
self._manager.refresh_profiles()
|
|
|
|
def _on_add_profile(self):
|
|
options = [tr("Scan QR"), tr("Enter Code")]
|
|
dialog = MultiOptionDialog(tr("Add eSIM Profile"), options, current="")
|
|
dialog.selection = options[0]
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
if dialog.selection == options[0]:
|
|
self._scan_qr()
|
|
else:
|
|
self._manual_code_entry()
|
|
|
|
gui_app.set_modal_overlay(dialog, _done)
|
|
|
|
def _scan_qr(self):
|
|
scanner = EsimQrScannerDialog()
|
|
self._manager.set_scanning_state(True)
|
|
|
|
def _done(result: int):
|
|
self._manager.set_scanning_state(False)
|
|
if result != DialogResult.CONFIRM or not scanner.code:
|
|
return
|
|
self._confirm_add_code(scanner.code)
|
|
|
|
gui_app.set_modal_overlay(scanner, _done)
|
|
|
|
def _manual_code_entry(self):
|
|
self._keyboard.reset(min_text_size=1)
|
|
self._keyboard.set_title(tr("Enter Activation Code"), tr("format: LPA:1$...$..."))
|
|
self._keyboard.clear()
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
code = self._keyboard.text.strip()
|
|
if code:
|
|
self._confirm_add_code(code)
|
|
|
|
gui_app.set_modal_overlay(self._keyboard, _done)
|
|
|
|
def _confirm_add_code(self, code: str):
|
|
confirm = ConfirmDialog("", tr("Continue"), tr("Cancel"))
|
|
code_preview = code if len(code) < 64 else (code[:61] + "...")
|
|
confirm.set_text(tr("Use activation code:\n{}").format(code_preview))
|
|
confirm.reset()
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
self._prompt_nickname_and_add(code)
|
|
|
|
gui_app.set_modal_overlay(confirm, _done)
|
|
|
|
def _prompt_nickname_and_add(self, code: str):
|
|
self._keyboard.reset(min_text_size=0)
|
|
self._keyboard.set_title(tr("Optional Nickname"), tr("leave blank to skip"))
|
|
self._keyboard.clear()
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
nickname = self._keyboard.text.strip()
|
|
self._manager.add_profile(code, nickname if nickname else None)
|
|
|
|
gui_app.set_modal_overlay(self._keyboard, _done)
|
|
|
|
def _on_profile_selected(self, profile: Profile):
|
|
options = []
|
|
if not profile.enabled:
|
|
options.append(tr("Activate"))
|
|
options.append(tr("Rename"))
|
|
if self._manager.is_comma_profile(profile.iccid):
|
|
options.append(tr("Remove Comma pSIM"))
|
|
elif not profile.enabled:
|
|
options.append(tr("Delete"))
|
|
|
|
if len(options) == 0:
|
|
return
|
|
|
|
dialog = MultiOptionDialog(tr("Profile Actions"), options, current="")
|
|
dialog.selection = options[0]
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
if dialog.selection == tr("Activate"):
|
|
self._manager.switch_profile(profile.iccid)
|
|
elif dialog.selection == tr("Rename"):
|
|
self._rename_profile(profile)
|
|
elif dialog.selection == tr("Remove Comma pSIM"):
|
|
self._remove_comma_profile()
|
|
elif dialog.selection == tr("Delete"):
|
|
self._delete_profile(profile)
|
|
|
|
gui_app.set_modal_overlay(dialog, _done)
|
|
|
|
def _rename_profile(self, profile: Profile):
|
|
self._keyboard.reset(min_text_size=1)
|
|
self._keyboard.set_title(tr("Rename Profile"), "")
|
|
self._keyboard.set_text(profile.nickname or "")
|
|
|
|
def _done(result: int):
|
|
if result != DialogResult.CONFIRM:
|
|
return
|
|
name = self._keyboard.text.strip()
|
|
if name:
|
|
self._manager.rename_profile(profile.iccid, name)
|
|
|
|
gui_app.set_modal_overlay(self._keyboard, _done)
|
|
|
|
def _delete_profile(self, profile: Profile):
|
|
confirm = ConfirmDialog("", tr("Delete"), tr("Cancel"))
|
|
confirm.set_text(tr("Delete disabled profile \"{}\"?").format(profile.nickname or profile.iccid))
|
|
confirm.reset()
|
|
|
|
def _done(result: int):
|
|
if result == DialogResult.CONFIRM:
|
|
self._manager.delete_profile(profile.iccid)
|
|
|
|
gui_app.set_modal_overlay(confirm, _done)
|
|
|
|
def _remove_comma_profile(self):
|
|
confirm = ConfirmDialog("", tr("Continue"), tr("Cancel"))
|
|
confirm.set_text(
|
|
tr("This will permanently wipe the Comma pSIM profile from the SIM.")
|
|
)
|
|
confirm.reset()
|
|
|
|
def _done(result: int):
|
|
if result == DialogResult.CONFIRM:
|
|
self._remove_comma_profile_final_warning()
|
|
|
|
gui_app.set_modal_overlay(confirm, _done)
|
|
|
|
def _remove_comma_profile_final_warning(self):
|
|
confirm = ConfirmDialog("", tr("Remove"), tr("Cancel"))
|
|
confirm.set_text(
|
|
tr("After this, you must use your own eSIM profile.\n\nYou cannot use Comma Prime again unless you buy a new SIM from comma.")
|
|
)
|
|
confirm.reset()
|
|
|
|
def _done(result: int):
|
|
if result == DialogResult.CONFIRM:
|
|
self._manager.bootstrap()
|
|
|
|
gui_app.set_modal_overlay(confirm, _done)
|
|
|
|
def _render(self, _):
|
|
if not self.is_supported():
|
|
from openpilot.system.ui.widgets.label import gui_label
|
|
gui_label(self._rect, tr("Insert the original comma SIM card that came with the device to use eSIM"), 64, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
|
|
return
|
|
self._scroller.render(self._rect)
|