Add Fastboot/EDL reboot modes and SIM contact import/export
This commit is contained in:
@@ -52,6 +52,7 @@ sudo ./flash-edl.sh
|
|||||||
The script will:
|
The script will:
|
||||||
1. Temporarily pause host `ModemManager` so it does not interfere with `05c6:9008`.
|
1. Temporarily pause host `ModemManager` so it does not interfere with `05c6:9008`.
|
||||||
2. Wait for the phone to appear in Qualcomm EDL mode (`05c6:9008`):
|
2. Wait for the phone to appear in Qualcomm EDL mode (`05c6:9008`):
|
||||||
|
- **From Bananarchy Linux**: open the **Power** menu (`End / Power` key or `System menu -> Power`) and select **Reboot to EDL** (or run `sxmo_power.sh edl`).
|
||||||
- **Button combo**: power off the phone, hold **D-Pad up + D-Pad down**, and plug in the USB cable.
|
- **Button combo**: power off the phone, hold **D-Pad up + D-Pad down**, and plug in the USB cable.
|
||||||
- **EDL cable**: hold the EDL cable button while plugging in USB.
|
- **EDL cable**: hold the EDL cable button while plugging in USB.
|
||||||
- **From ADB / Fastboot**: the script automatically sends `adb reboot edl` or `fastboot oem edl` if detected.
|
- **From ADB / Fastboot**: the script automatically sends `adb reboot edl` or `fastboot oem edl` if detected.
|
||||||
@@ -63,7 +64,7 @@ The script will:
|
|||||||
|
|
||||||
### Method B: Fastboot flashing (once `lk2nd` is installed)
|
### Method B: Fastboot flashing (once `lk2nd` is installed)
|
||||||
|
|
||||||
1. Hold **D-Pad up** while powering on the phone to enter `lk2nd` Fastboot mode.
|
1. Enter `lk2nd` Fastboot mode either by selecting **Reboot to Fastboot** from the **Power** menu (`sxmo_power.sh fastboot`) or by holding **D-Pad up** while powering on the phone.
|
||||||
2. Flash the system image to `userdata`:
|
2. Flash the system image to `userdata`:
|
||||||
```bash
|
```bash
|
||||||
fastboot flash userdata ./images/bananarchy-nokia-argon.img
|
fastboot flash userdata ./images/bananarchy-nokia-argon.img
|
||||||
@@ -128,7 +129,7 @@ All fourteen core phone overlays are compiled as shared-object plugins (`/usr/li
|
|||||||
|
|
||||||
1. **`DMenu` (`dmenu.so` / `dmenu` / `xdmenu`) — Zero-Lag System & Context Menus**:
|
1. **`DMenu` (`dmenu.so` / `dmenu` / `xdmenu`) — Zero-Lag System & Context Menus**:
|
||||||
- **Open / Close**: Press **Left softkey (`Menu`)** for the **System menu** (`sys`) or **Right softkey (`Back`)** inside an application for its **Context menu** (`ctx`); press the same softkey or `Escape` to close.
|
- **Open / Close**: Press **Left softkey (`Menu`)** for the **System menu** (`sys`) or **Right softkey (`Back`)** inside an application for its **Context menu** (`ctx`); press the same softkey or `Escape` to close.
|
||||||
- **Built-in Native Menus & `< 0.5 ms` In-Place Transitions**: Renders `sys`, `ctx`, `applications` (including **Nano**, **Bantam**, **Files**, **Camera**, **Dialer**, **Contacts**), `scripts`, `modem`, `config`, `power` (with Polkit + `elogind` multi-session **Reboot** and **Poweroff** via `sxmo_power.sh`), `audio`, `notifications`, `wm`, `timezone`, `games`, `st`, `nano`, `sthotkeys`, and confirmation prompts directly inside the resident `240x296` plugin window without spawning shell scripts or new X11 connections. Submenu transitions (`@menu:...`) and hardware toggles (`@toggle:flashlight`, `@toggle:brightness`, `@toggle:autosuspend`, `@toggle:autocallscreenoff`, `@toggle:bluetooth`, `@toggle:dnd`) update in place in `< 0.5 ms`.
|
- **Built-in Native Menus & `< 0.5 ms` In-Place Transitions**: Renders `sys`, `ctx`, `applications` (including **Nano**, **Bantam**, **Files**, **Camera**, **Dialer**, **Contacts**), `scripts`, `modem`, `config`, `power` (with **Reboot**, **Reboot to Fastboot**, **Reboot to EDL**, and **Poweroff** via `sxmo_power.sh`), `audio`, `notifications`, `wm`, `timezone`, `games`, `st`, `nano`, `sthotkeys`, and confirmation prompts directly inside the resident `240x296` plugin window without spawning shell scripts or new X11 connections. Submenu transitions (`@menu:...`) and hardware toggles (`@toggle:flashlight`, `@toggle:brightness`, `@toggle:autosuspend`, `@toggle:autocallscreenoff`, `@toggle:bluetooth`, `@toggle:dnd`) update in place in `< 0.5 ms`.
|
||||||
- **Client Socket Mode (`/tmp/dwm_plugin_dmenu.sock`)**: Any script piping items into `/usr/bin/dmenu` or `sxmo_dmenu.sh` streams its item list over the Unix domain socket to the resident `dmenu.so` daemon and receives the user's selection instantaneously, while `/tmp/dwm_plugin_dmenu.active` provides zero-overhead `sxmo_dmenu.sh isopen` checks.
|
- **Client Socket Mode (`/tmp/dwm_plugin_dmenu.sock`)**: Any script piping items into `/usr/bin/dmenu` or `sxmo_dmenu.sh` streams its item list over the Unix domain socket to the resident `dmenu.so` daemon and receives the user's selection instantaneously, while `/tmp/dwm_plugin_dmenu.active` provides zero-overhead `sxmo_dmenu.sh isopen` checks.
|
||||||
|
|
||||||
2. **`XDrawer` (`xdrawer.so` / `xdrawer`) — Global Phone Controls**:
|
2. **`XDrawer` (`xdrawer.so` / `xdrawer`) — Global Phone Controls**:
|
||||||
@@ -160,8 +161,9 @@ All fourteen core phone overlays are compiled as shared-object plugins (`/usr/li
|
|||||||
|
|
||||||
6. **`Contacts` (`contacts.so` / `xcontacts` / `sxmo_contactmenu.sh`) — Full-Featured Phonebook**:
|
6. **`Contacts` (`contacts.so` / `xcontacts` / `sxmo_contactmenu.sh`) — Full-Featured Phonebook**:
|
||||||
- **Open**: On the empty desktop, press the **Right softkey (`Names`)**, or select **Contacts** from the Sxmo menu.
|
- **Open**: On the empty desktop, press the **Right softkey (`Names`)**, or select **Contacts** from the Sxmo menu.
|
||||||
- **Phonebook List & Live Search**: Typing `0`–`9` filters contacts in real time by T9 name or phone number; `Up`/`Down` scrolls; **Call (green)** immediately calls the selected contact; **Center `OK` (`Return`)** opens the contact's details & actions card; **Left softkey (`Options`)** opens the options menu (**New Contact**, **Add from Recent**, **Call Contact**, **Send SMS**, **Edit Contact**, **Delete Contact**).
|
- **Phonebook List & Live Search**: Typing `0`–`9` filters contacts in real time by T9 name or phone number; `Up`/`Down` scrolls; **Call (green)** immediately calls the selected contact; **Center `OK` (`Return`)** opens the contact's details & actions card; **Left softkey (`Options`)** opens the options menu (**New Contact**, **Add from Recent**, **Import from SIM**, **Export to SIM**, **Call Contact**, **Send SMS**, **Edit Contact**, **Delete Contact**).
|
||||||
- **Contact Details View**: Provides one-click actions for **Call Contact**, **Send SMS**, **Read Messages**, **Open in Dialer**, **Edit Contact**, and **Delete Contact**.
|
- **SIM Card Import & Export (Dual-SIM Aware)**: Select **Import from SIM** or **Export to SIM** in the **Options** menu (or **Copy to SIM** inside a contact's detail view) to transfer contacts between `~/.config/sxmo/contacts.tsv` and the SIM card phonebook (`EF_ADN` / `SM`). If both **SIM 1** and **SIM 2** are installed, Contacts prompts you to choose between **SIM 1** and **SIM 2**; if only one SIM is installed, it uses that slot automatically.
|
||||||
|
- **Contact Details View**: Provides one-click actions for **Call Contact**, **Send SMS**, **Read Messages**, **Open in Dialer**, **Copy to SIM**, **Edit Contact**, and **Delete Contact**.
|
||||||
- **Built-in Contact Editor**: Supports adding new contacts or editing existing contacts directly in the `240x320` UI. In the **Name** field, keypad keys `0`–`9` provide multi-tap character entry (`#` cycles case mode `[Abc]` -> `[ABC]` -> `[abc]` -> `[123]`, `*` inserts space). In the **Number** field, hold `0` or press `Right` to insert `+`, or select **Use Recent Number** to pick from recent unknown callers in `modemlog.tsv`. Press **Left softkey (`Save`)** or select **Save Contact** to write directly to `~/.config/sxmo/contacts.tsv`.
|
- **Built-in Contact Editor**: Supports adding new contacts or editing existing contacts directly in the `240x320` UI. In the **Name** field, keypad keys `0`–`9` provide multi-tap character entry (`#` cycles case mode `[Abc]` -> `[ABC]` -> `[abc]` -> `[123]`, `*` inserts space). In the **Number** field, hold `0` or press `Right` to insert `+`, or select **Use Recent Number** to pick from recent unknown callers in `modemlog.tsv`. Press **Left softkey (`Save`)** or select **Save Contact** to write directly to `~/.config/sxmo/contacts.tsv`.
|
||||||
|
|
||||||
7. **`SMS Manager / Editor` (`sms.so` / `xsms` / `sxmo_modemtext.sh`) — Conversations & Composer**:
|
7. **`SMS Manager / Editor` (`sms.so` / `xsms` / `sxmo_modemtext.sh`) — Conversations & Composer**:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Instead of spawning slow shell scripts for everyday phone tasks, Bananarchy runs
|
|||||||
- **System & context menus (`dmenu`)**: Instant full-screen 10-line vertical menus with live T9 filtering and built-in submenus.
|
- **System & context menus (`dmenu`)**: Instant full-screen 10-line vertical menus with live T9 filtering and built-in submenus.
|
||||||
- **Global controls drawer (`xdrawer`)**: Press **both softkeys together** from anywhere to adjust speaker/earpiece volume, ringer mode, flashlight, mic/sound mute, screen brightness, and keypad backlight.
|
- **Global controls drawer (`xdrawer`)**: Press **both softkeys together** from anywhere to adjust speaker/earpiece volume, ringer mode, flashlight, mic/sound mute, screen brightness, and keypad backlight.
|
||||||
- **Phone dialer (`xdialer`), call log (`xcalllog`) & call screen (`xcallscreen`)**: Press any digit on the home screen to start dialing with live T9 contact matching, run carrier USSD codes (`*100#`, etc.) interactively, or type `*#06#` to inspect both IMEIs. Incoming and active calls open a dedicated call screen with earpiece/speaker routing, microphone mute, DTMF keypad, and one-key call recording (`~/Recordings/`).
|
- **Phone dialer (`xdialer`), call log (`xcalllog`) & call screen (`xcallscreen`)**: Press any digit on the home screen to start dialing with live T9 contact matching, run carrier USSD codes (`*100#`, etc.) interactively, or type `*#06#` to inspect both IMEIs. Incoming and active calls open a dedicated call screen with earpiece/speaker routing, microphone mute, DTMF keypad, and one-key call recording (`~/Recordings/`).
|
||||||
- **Contacts (`xcontacts`) & SMS manager (`xsms`)**: Full phonebook editor and threaded conversation/draft SMS manager with multi-tap keypad text entry.
|
- **Contacts (`xcontacts`) & SMS manager (`xsms`)**: Full phonebook editor with SIM card contact import/export (supporting SIM 1 / SIM 2 selection on dual-SIM setups) and threaded conversation/draft SMS manager with multi-tap keypad text entry.
|
||||||
- **Modem & Dual-SIM manager (`xmodem`)**: 4-tab cellular control center (`1:SIMs`, `2:Radio`, `3:AT`, `4:Log`) supporting physical SIM 1 / SIM 2 active slot switching, SIM PIN unlock and PUK unblock wizards, 2G/3G/4G mode selection, interactive USSD sessions, and a live AT command console.
|
- **Modem & Dual-SIM manager (`xmodem`)**: 4-tab cellular control center (`1:SIMs`, `2:Radio`, `3:AT`, `4:Log`) supporting physical SIM 1 / SIM 2 active slot switching, SIM PIN unlock and PUK unblock wizards, 2G/3G/4G mode selection, interactive USSD sessions, and a live AT command console.
|
||||||
- **Networks & Bluetooth manager (`xnetworks`)**: 3-tab connectivity manager (`1:Wi-Fi`, `2:Cell/APN`, `3:Bluetooth`) for Wi-Fi networks, WPA2 password entry, hotspot/USB tethering, mobile data & APN editing, and Bluetooth pairing.
|
- **Networks & Bluetooth manager (`xnetworks`)**: 3-tab connectivity manager (`1:Wi-Fi`, `2:Cell/APN`, `3:Bluetooth`) for Wi-Fi networks, WPA2 password entry, hotspot/USB tethering, mobile data & APN editing, and Bluetooth pairing.
|
||||||
- **File manager (`xfiles`) & camera (`xcamera`)**: Graphical directory browser with built-in text and image viewers, plus a live V4L2 camera viewfinder with digital zoom, flash/torch control, self-timer, and photo gallery.
|
- **File manager (`xfiles`) & camera (`xcamera`)**: Graphical directory browser with built-in text and image viewers, plus a live V4L2 camera viewfinder with digital zoom, flash/torch control, self-timer, and photo gallery.
|
||||||
|
|||||||
@@ -30,5 +30,8 @@ permit nopass :wheel as root cmd rtcwake
|
|||||||
permit nopass :wheel as root cmd tee args -a /sys/power/wake_lock
|
permit nopass :wheel as root cmd tee args -a /sys/power/wake_lock
|
||||||
permit nopass :wheel as root cmd tee args -a /sys/power/wake_unlock
|
permit nopass :wheel as root cmd tee args -a /sys/power/wake_unlock
|
||||||
permit nopass :wheel as root cmd tee args /sys/power/wakeup_count
|
permit nopass :wheel as root cmd tee args /sys/power/wakeup_count
|
||||||
|
permit nopass :wheel as root cmd tee args /sys/module/leds_msm8905_torch/parameters/next_reboot_mode
|
||||||
|
permit nopass :wheel as root cmd /usr/bin/sxmo_power.sh
|
||||||
|
permit nopass :wheel as root cmd sxmo_power.sh
|
||||||
permit nopass :wheel as root cmd rc-service args hexagonrpcd-adsp-sensorspd stop
|
permit nopass :wheel as root cmd rc-service args hexagonrpcd-adsp-sensorspd stop
|
||||||
permit nopass :wheel as root cmd systemctl args stop hexagonrpcd-adsp-sensorspd
|
permit nopass :wheel as root cmd systemctl args stop hexagonrpcd-adsp-sensorspd
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ getprogchoices() {
|
|||||||
$icon_lck Lock (Screen off)^0^sxmo_state.sh set screenoff
|
$icon_lck Lock (Screen off)^0^sxmo_state.sh set screenoff
|
||||||
$icon_out Logout^0^confirm Logout && sxmo_power.sh logout
|
$icon_out Logout^0^confirm Logout && sxmo_power.sh logout
|
||||||
$icon_rld Reboot^0^confirm Reboot && sxmo_power.sh reboot
|
$icon_rld Reboot^0^confirm Reboot && sxmo_power.sh reboot
|
||||||
|
$icon_rld Reboot to Fastboot^0^confirm Fastboot && sxmo_power.sh fastboot
|
||||||
|
$icon_rld Reboot to EDL^0^confirm EDL && sxmo_power.sh edl
|
||||||
$icon_pwr Poweroff^0^confirm Poweroff && sxmo_power.sh poweroff"
|
$icon_pwr Poweroff^0^confirm Poweroff && sxmo_power.sh poweroff"
|
||||||
;;
|
;;
|
||||||
modem)
|
modem)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export PATH="/sbin:/usr/sbin:$PATH"
|
|||||||
. sxmo_common.sh
|
. sxmo_common.sh
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
printf "usage: %s [reboot|poweroff|logout|togglewm]\n" "$(basename "$0")"
|
printf "usage: %s [reboot|fastboot|bootloader|edl|poweroff|logout|togglewm]\n" "$(basename "$0")"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_polkit_login1() {
|
run_polkit_login1() {
|
||||||
@@ -89,9 +89,29 @@ run_priv_power() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_kernel_reboot_mode() {
|
||||||
|
rmode="$1"
|
||||||
|
param_path="/sys/module/leds_msm8905_torch/parameters/next_reboot_mode"
|
||||||
|
if [ -e "$param_path" ]; then
|
||||||
|
printf "%s\n" "$rmode" | /usr/bin/doas tee "$param_path" >/dev/null 2>&1 || \
|
||||||
|
printf "%s\n" "$rmode" > "$param_path" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
force_reboot_cmd() {
|
||||||
|
rmode="$1"
|
||||||
|
/usr/bin/doas python3 -c "
|
||||||
|
import ctypes, os
|
||||||
|
os.sync()
|
||||||
|
libc = ctypes.CDLL(None)
|
||||||
|
libc.syscall(88, 0xfee1dead, 672274793, 0xa1b2c3d4, b'$rmode')
|
||||||
|
" >/dev/null 2>&1 || /usr/bin/doas /sbin/reboot -f >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
reboot)
|
reboot)
|
||||||
sync
|
sync
|
||||||
|
set_kernel_reboot_mode ""
|
||||||
sxmo_hook_power.sh reboot || true
|
sxmo_hook_power.sh reboot || true
|
||||||
(sleep 4; [ -u /usr/bin/doas ] && /usr/bin/doas /sbin/reboot -f >/dev/null 2>&1 || [ -u /usr/bin/sudo ] && /usr/bin/sudo -n /sbin/reboot -f >/dev/null 2>&1) &
|
(sleep 4; [ -u /usr/bin/doas ] && /usr/bin/doas /sbin/reboot -f >/dev/null 2>&1 || [ -u /usr/bin/sudo ] && /usr/bin/sudo -n /sbin/reboot -f >/dev/null 2>&1) &
|
||||||
if ! run_priv_power reboot; then
|
if ! run_priv_power reboot; then
|
||||||
@@ -102,6 +122,26 @@ case "$1" in
|
|||||||
fi
|
fi
|
||||||
sxmo_jobs.sh stop all >/dev/null 2>&1 || true
|
sxmo_jobs.sh stop all >/dev/null 2>&1 || true
|
||||||
;;
|
;;
|
||||||
|
fastboot|bootloader)
|
||||||
|
sync
|
||||||
|
set_kernel_reboot_mode "bootloader"
|
||||||
|
sxmo_hook_power.sh reboot || true
|
||||||
|
(sleep 4; force_reboot_cmd "bootloader") &
|
||||||
|
if ! run_priv_power reboot; then
|
||||||
|
force_reboot_cmd "bootloader"
|
||||||
|
fi
|
||||||
|
sxmo_jobs.sh stop all >/dev/null 2>&1 || true
|
||||||
|
;;
|
||||||
|
edl)
|
||||||
|
sync
|
||||||
|
set_kernel_reboot_mode "edl"
|
||||||
|
sxmo_hook_power.sh reboot || true
|
||||||
|
(sleep 4; force_reboot_cmd "edl") &
|
||||||
|
if ! run_priv_power reboot; then
|
||||||
|
force_reboot_cmd "edl"
|
||||||
|
fi
|
||||||
|
sxmo_jobs.sh stop all >/dev/null 2>&1 || true
|
||||||
|
;;
|
||||||
poweroff)
|
poweroff)
|
||||||
sync
|
sync
|
||||||
sxmo_hook_power.sh poweroff || true
|
sxmo_hook_power.sh poweroff || true
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -558,8 +558,331 @@ case "$cmd" in
|
|||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
sim-contacts-import)
|
||||||
|
req_slot="${2:-auto}"
|
||||||
|
if ! wait_for_qmicli_modem 3; then
|
||||||
|
echo "ERROR: No modem available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
|
||||||
|
if [ -z "$QMI_CARDS" ]; then
|
||||||
|
echo "ERROR: Cannot read SIM card status"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
eval "$(parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT")"
|
||||||
|
|
||||||
|
target_slot=""
|
||||||
|
target_aid=""
|
||||||
|
if [ "$req_slot" = "1" ]; then
|
||||||
|
if [ "$SLOT1_STATE" != "present" ]; then
|
||||||
|
echo "ERROR: SIM 1 is not inserted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$req_slot" = "2" ]; then
|
||||||
|
if [ "$SLOT2_STATE" != "present" ]; then
|
||||||
|
echo "ERROR: SIM 2 is not inserted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
else
|
||||||
|
if [ "$PRIMARY_GW_SLOT" = "1" ] && [ "$SLOT1_STATE" = "present" ]; then
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$PRIMARY_GW_SLOT" = "2" ] && [ "$SLOT2_STATE" = "present" ]; then
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
elif [ "$SLOT1_STATE" = "present" ]; then
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$SLOT2_STATE" = "present" ]; then
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
else
|
||||||
|
echo "ERROR: No SIM card detected"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
orig_gw_slot="$PRIMARY_GW_SLOT"
|
||||||
|
orig_gw_aid=""
|
||||||
|
if [ "$orig_gw_slot" = "1" ]; then
|
||||||
|
orig_gw_aid="$SLOT1_AID"
|
||||||
|
elif [ "$orig_gw_slot" = "2" ]; then
|
||||||
|
orig_gw_aid="$SLOT2_AID"
|
||||||
|
fi
|
||||||
|
switched_gw=0
|
||||||
|
restore_gw_slot() {
|
||||||
|
if [ "$switched_gw" = "1" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true
|
||||||
|
if [ -n "$orig_gw_slot" ] && [ "$orig_gw_slot" != "0" ] && [ -n "$orig_gw_aid" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session="slot=$orig_gw_slot,activate=yes,session-type=primary-gw-provisioning,aid=$orig_gw_aid" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
switched_gw=0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap restore_gw_slot EXIT INT TERM
|
||||||
|
|
||||||
|
if [ "$PRIMARY_GW_SLOT" != "$target_slot" ] && [ -n "$target_aid" ]; then
|
||||||
|
if [ "$PRIMARY_GW_SLOT" != "0" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
if $QMICLI_MODEM --uim-change-provisioning-session="slot=$target_slot,activate=yes,session-type=primary-gw-provisioning,aid=$target_aid" >/dev/null 2>&1; then
|
||||||
|
switched_gw=1
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp_contacts="/tmp/sim_import_$$.tsv"
|
||||||
|
rm -f "$tmp_contacts"
|
||||||
|
: > "$tmp_contacts"
|
||||||
|
|
||||||
|
# Method 1: Standard 3GPP AT+CPBS="SM" / AT+CPBR
|
||||||
|
at_info="$("$0" at auto 'AT+CMEE=2;+CSCS="GSM";+CPBS="SM";+CPBR=?' 2>/dev/null || true)"
|
||||||
|
max_idx="$(printf "%s\n" "$at_info" | sed -n 's/.*+CPBR:[[:space:]]*([0-9]*-\([0-9]*\)).*/\1/p' | head -n1)"
|
||||||
|
if [ -n "$max_idx" ] && [ "$max_idx" -gt 0 ] 2>/dev/null; then
|
||||||
|
if [ "$max_idx" -gt 500 ]; then
|
||||||
|
max_idx=500
|
||||||
|
fi
|
||||||
|
start_idx=1
|
||||||
|
while [ "$start_idx" -le "$max_idx" ]; do
|
||||||
|
end_idx=$((start_idx + 49))
|
||||||
|
if [ "$end_idx" -gt "$max_idx" ]; then
|
||||||
|
end_idx="$max_idx"
|
||||||
|
fi
|
||||||
|
chunk="$("$0" at auto "AT+CPBR=${start_idx},${end_idx}" 2>/dev/null || true)"
|
||||||
|
if [ -n "$chunk" ]; then
|
||||||
|
printf "%s\n" "$chunk" | awk '
|
||||||
|
/^\+CPBR:[[:space:]]*[0-9]+,/ {
|
||||||
|
line = $0
|
||||||
|
sub(/^\+CPBR:[[:space:]]*[0-9]+,[[:space:]]*"/, "", line)
|
||||||
|
q1 = index(line, "\"")
|
||||||
|
if (q1 <= 0) next
|
||||||
|
num = substr(line, 1, q1 - 1)
|
||||||
|
rest = substr(line, q1 + 1)
|
||||||
|
sub(/^[[:space:]]*,[[:space:]]*/, "", rest)
|
||||||
|
c2 = index(rest, ",")
|
||||||
|
if (c2 <= 0) next
|
||||||
|
ton = substr(rest, 1, c2 - 1) + 0
|
||||||
|
name = substr(rest, c2 + 1)
|
||||||
|
sub(/^[[:space:]]*"/, "", name)
|
||||||
|
sub(/"[[:space:]]*$/, "", name)
|
||||||
|
gsub(/[\t\r\n]/, " ", name)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", name)
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", num)
|
||||||
|
if (num == "") next
|
||||||
|
if (ton == 145 && substr(num, 1, 1) != "+") {
|
||||||
|
num = "+" num
|
||||||
|
}
|
||||||
|
if (name == "") name = num
|
||||||
|
printf "%s\t%s\n", num, name
|
||||||
|
}
|
||||||
|
' >> "$tmp_contacts"
|
||||||
|
fi
|
||||||
|
start_idx=$((end_idx + 1))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Method 2: Direct QMI UIM EF_ADN (3F00:7F10:6F3A) record fallback
|
||||||
|
if [ ! -s "$tmp_contacts" ]; then
|
||||||
|
adn_attr="$($QMICLI_MODEM --uim-get-file-attributes=0x3F00,0x7F10,0x6F3A 2>/dev/null || true)"
|
||||||
|
reclen="$(printf "%s\n" "$adn_attr" | awk -F"'" '/Record length:/ {print $2 + 0; exit}')"
|
||||||
|
reccount="$(printf "%s\n" "$adn_attr" | awk -F"'" '/Record count:/ {print $2 + 0; exit}')"
|
||||||
|
if [ -n "$reclen" ] && [ -n "$reccount" ] && [ "$reclen" -ge 14 ] 2>/dev/null && [ "$reccount" -ge 1 ] 2>/dev/null; then
|
||||||
|
if [ "$reccount" -gt 250 ]; then
|
||||||
|
reccount=250
|
||||||
|
fi
|
||||||
|
rec=1
|
||||||
|
while [ "$rec" -le "$reccount" ]; do
|
||||||
|
raw_rec="$($QMICLI_MODEM --uim-read-record="file=0x3F00,0x7F10,0x6F3A,record-number=${rec},record-length=${reclen}" 2>/dev/null || true)"
|
||||||
|
if [ -n "$raw_rec" ]; then
|
||||||
|
printf "%s\n" "$raw_rec" | awk -v rlen="$reclen" '
|
||||||
|
function hex2val(h, i, c, v, res) {
|
||||||
|
res = 0
|
||||||
|
h = toupper(h)
|
||||||
|
for (i = 1; i <= length(h); i++) {
|
||||||
|
c = substr(h, i, 1)
|
||||||
|
v = index("0123456789ABCDEF", c) - 1
|
||||||
|
if (v < 0) return -1
|
||||||
|
res = res * 16 + v
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
function bcd_digit(n) {
|
||||||
|
if (n >= 0 && n <= 9) return "" n
|
||||||
|
if (n == 10) return "*"
|
||||||
|
if (n == 11) return "#"
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
/^[[:space:]]*([0-9A-Fa-f]{2}:)+[0-9A-Fa-f]{2}[[:space:]]*$/ {
|
||||||
|
line = $0
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
|
||||||
|
n = split(line, hex, ":")
|
||||||
|
if (n < 14) next
|
||||||
|
alpha_len = n - 14
|
||||||
|
bcd_len = hex2val(hex[alpha_len + 1])
|
||||||
|
ton_npi = hex2val(hex[alpha_len + 2])
|
||||||
|
if (bcd_len <= 0 || bcd_len > 11 || bcd_len == 255) next
|
||||||
|
num = ""
|
||||||
|
if (int(ton_npi / 16) % 8 == 1) {
|
||||||
|
num = "+"
|
||||||
|
}
|
||||||
|
bcd_bytes = bcd_len - 1
|
||||||
|
if (bcd_bytes > 10) bcd_bytes = 10
|
||||||
|
for (i = 1; i <= bcd_bytes; i++) {
|
||||||
|
b = hex2val(hex[alpha_len + 2 + i])
|
||||||
|
if (b < 0 || b == 255) break
|
||||||
|
lo = b % 16
|
||||||
|
hi = int(b / 16)
|
||||||
|
d1 = bcd_digit(lo)
|
||||||
|
if (d1 == "") break
|
||||||
|
num = num d1
|
||||||
|
d2 = bcd_digit(hi)
|
||||||
|
if (d2 == "") break
|
||||||
|
num = num d2
|
||||||
|
}
|
||||||
|
if (num == "" || num == "+") next
|
||||||
|
name = ""
|
||||||
|
for (i = 1; i <= alpha_len; i++) {
|
||||||
|
b = hex2val(hex[i])
|
||||||
|
if (b < 0 || b == 255) break
|
||||||
|
if (b >= 32 && b < 127 && b != 9) {
|
||||||
|
name = name sprintf("%c", b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", name)
|
||||||
|
if (name == "") name = num
|
||||||
|
printf "%s\t%s\n", num, name
|
||||||
|
}
|
||||||
|
' >> "$tmp_contacts"
|
||||||
|
fi
|
||||||
|
rec=$((rec + 1))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$tmp_contacts"
|
||||||
|
rm -f "$tmp_contacts"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
sim-contacts-export)
|
||||||
|
req_slot="${2:-auto}"
|
||||||
|
src_tsv="${3:-/home/user/.config/sxmo/contacts.tsv}"
|
||||||
|
if [ ! -r "$src_tsv" ]; then
|
||||||
|
echo "ERROR: Contacts file not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! wait_for_qmicli_modem 3; then
|
||||||
|
echo "ERROR: No modem available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
|
||||||
|
if [ -z "$QMI_CARDS" ]; then
|
||||||
|
echo "ERROR: Cannot read SIM card status"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
eval "$(parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT")"
|
||||||
|
|
||||||
|
target_slot=""
|
||||||
|
target_aid=""
|
||||||
|
if [ "$req_slot" = "1" ]; then
|
||||||
|
if [ "$SLOT1_STATE" != "present" ]; then
|
||||||
|
echo "ERROR: SIM 1 is not inserted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$req_slot" = "2" ]; then
|
||||||
|
if [ "$SLOT2_STATE" != "present" ]; then
|
||||||
|
echo "ERROR: SIM 2 is not inserted"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
else
|
||||||
|
if [ "$PRIMARY_GW_SLOT" = "1" ] && [ "$SLOT1_STATE" = "present" ]; then
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$PRIMARY_GW_SLOT" = "2" ] && [ "$SLOT2_STATE" = "present" ]; then
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
elif [ "$SLOT1_STATE" = "present" ]; then
|
||||||
|
target_slot="1"
|
||||||
|
target_aid="$SLOT1_AID"
|
||||||
|
elif [ "$SLOT2_STATE" = "present" ]; then
|
||||||
|
target_slot="2"
|
||||||
|
target_aid="$SLOT2_AID"
|
||||||
|
else
|
||||||
|
echo "ERROR: No SIM card detected"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
orig_gw_slot="$PRIMARY_GW_SLOT"
|
||||||
|
orig_gw_aid=""
|
||||||
|
if [ "$orig_gw_slot" = "1" ]; then
|
||||||
|
orig_gw_aid="$SLOT1_AID"
|
||||||
|
elif [ "$orig_gw_slot" = "2" ]; then
|
||||||
|
orig_gw_aid="$SLOT2_AID"
|
||||||
|
fi
|
||||||
|
switched_gw=0
|
||||||
|
restore_gw_slot_exp() {
|
||||||
|
if [ "$switched_gw" = "1" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true
|
||||||
|
if [ -n "$orig_gw_slot" ] && [ "$orig_gw_slot" != "0" ] && [ -n "$orig_gw_aid" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session="slot=$orig_gw_slot,activate=yes,session-type=primary-gw-provisioning,aid=$orig_gw_aid" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
switched_gw=0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap restore_gw_slot_exp EXIT INT TERM
|
||||||
|
|
||||||
|
if [ "$PRIMARY_GW_SLOT" != "$target_slot" ] && [ -n "$target_aid" ]; then
|
||||||
|
if [ "$PRIMARY_GW_SLOT" != "0" ]; then
|
||||||
|
$QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
if $QMICLI_MODEM --uim-change-provisioning-session="slot=$target_slot,activate=yes,session-type=primary-gw-provisioning,aid=$target_aid" >/dev/null 2>&1; then
|
||||||
|
switched_gw=1
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
at_info="$("$0" at auto 'AT+CMEE=2;+CSCS="GSM";+CPBS="SM";+CPBR=?' 2>/dev/null || true)"
|
||||||
|
if printf "%s\n" "$at_info" | grep -qE "ERROR"; then
|
||||||
|
echo "ERROR: SIM $target_slot phonebook not ready"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
tlen="$(printf "%s\n" "$at_info" | sed -n 's/.*+CPBR:[[:space:]]*([0-9]*-[0-9]*),[0-9]*,\([0-9]*\).*/\1/p' | head -n1)"
|
||||||
|
if [ -z "$tlen" ] || [ "$tlen" -le 0 ] 2>/dev/null; then
|
||||||
|
tlen=14
|
||||||
|
fi
|
||||||
|
|
||||||
|
exported=0
|
||||||
|
while IFS="$(printf '\t')" read -r raw_num raw_name; do
|
||||||
|
num="$(printf "%s" "$raw_num" | tr -cd '+0-9*#')"
|
||||||
|
name="$(printf "%s" "$raw_name" | tr -d '"\r\n\t\\' | cut -c1-"$tlen")"
|
||||||
|
[ -n "$num" ] || continue
|
||||||
|
[ -n "$name" ] || name="$(printf "%s" "$num" | cut -c1-"$tlen")"
|
||||||
|
ton=129
|
||||||
|
case "$num" in
|
||||||
|
+*) ton=145 ;;
|
||||||
|
esac
|
||||||
|
res="$("$0" at auto "AT+CPBW=,\"${num}\",${ton},\"${name}\"" 2>/dev/null || true)"
|
||||||
|
if printf "%s\n" "$res" | grep -q "OK"; then
|
||||||
|
exported=$((exported + 1))
|
||||||
|
fi
|
||||||
|
done < "$src_tsv"
|
||||||
|
|
||||||
|
echo "EXPORTED=${exported}"
|
||||||
|
echo "SLOT=${target_slot}"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 [boot|status|switch <1|2>|verify-pin <slot> <pin>|unblock-puk <slot> <puk> <newpin>|set-pin-lock <slot> <enable|disable> <pin>|change-pin <slot> <oldpin> <newpin>|imei|usb-tether <on|off|status>|at <port> <cmd>]" >&2
|
echo "Usage: $0 [boot|status|switch <1|2>|verify-pin <slot> <pin>|unblock-puk <slot> <puk> <newpin>|set-pin-lock <slot> <enable|disable> <pin>|change-pin <slot> <oldpin> <newpin>|imei|usb-tether <on|off|status>|at <port> <cmd>|sim-contacts-import [1|2|auto]|sim-contacts-export [1|2|auto] [tsv_file]]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ case "$WMCLASS" in
|
|||||||
$icon_lck Lock (Screen off) ^ 0 ^ sxmo_state.sh set screenoff
|
$icon_lck Lock (Screen off) ^ 0 ^ sxmo_state.sh set screenoff
|
||||||
$icon_out Logout ^ 0 ^ confirm Logout && sxmo_power.sh logout
|
$icon_out Logout ^ 0 ^ confirm Logout && sxmo_power.sh logout
|
||||||
$icon_rld Reboot ^ 0 ^ confirm Reboot && sxmo_power.sh reboot
|
$icon_rld Reboot ^ 0 ^ confirm Reboot && sxmo_power.sh reboot
|
||||||
|
$icon_rld Reboot to Fastboot ^ 0 ^ confirm Fastboot && sxmo_power.sh fastboot
|
||||||
|
$icon_rld Reboot to EDL ^ 0 ^ confirm EDL && sxmo_power.sh edl
|
||||||
$icon_pwr Poweroff ^ 0 ^ confirm Poweroff && sxmo_power.sh poweroff
|
$icon_pwr Poweroff ^ 0 ^ confirm Poweroff && sxmo_power.sh poweroff
|
||||||
"
|
"
|
||||||
WINNAME=Power
|
WINNAME=Power
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -25,6 +25,13 @@ enum class ViewMode {
|
|||||||
Edit = 3,
|
Edit = 3,
|
||||||
PickRecent = 4,
|
PickRecent = 4,
|
||||||
ConfirmDelete = 5,
|
ConfirmDelete = 5,
|
||||||
|
SelectSim = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SimOp {
|
||||||
|
ImportAll = 0,
|
||||||
|
ExportAll = 1,
|
||||||
|
ExportSingle = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TextCaseMode {
|
enum class TextCaseMode {
|
||||||
@@ -153,12 +160,19 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
|
|
||||||
ViewMode mode = ViewMode::List;
|
ViewMode mode = ViewMode::List;
|
||||||
ViewMode prevModeBeforeEdit = ViewMode::List;
|
ViewMode prevModeBeforeEdit = ViewMode::List;
|
||||||
|
ViewMode prevModeBeforeSim = ViewMode::List;
|
||||||
|
SimOp pendingSimOp = SimOp::ImportAll;
|
||||||
std::string searchQuery;
|
std::string searchQuery;
|
||||||
int selected = 0;
|
int selected = 0;
|
||||||
int detailSelected = 0;
|
int detailSelected = 0;
|
||||||
int optionSelected = 0;
|
int optionSelected = 0;
|
||||||
int confirmSelected = 0;
|
int confirmSelected = 0;
|
||||||
int recentSelected = 0;
|
int recentSelected = 0;
|
||||||
|
int simSelected = 0;
|
||||||
|
int activeGwSlot = 1;
|
||||||
|
std::string statusMsg;
|
||||||
|
bool statusIsError = false;
|
||||||
|
int64_t statusUntilMs = 0;
|
||||||
|
|
||||||
// Editor state
|
// Editor state
|
||||||
bool editIsNew = true;
|
bool editIsNew = true;
|
||||||
@@ -360,44 +374,66 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
|
|
||||||
if (mode == ViewMode::Options) {
|
if (mode == ViewMode::Options) {
|
||||||
// Popup options menu overlay
|
// Popup options menu overlay
|
||||||
const char* optLabels[6] = {
|
const char* optLabels[8] = {
|
||||||
"New Contact",
|
"New Contact",
|
||||||
"Add from Recent",
|
"Add from Recent",
|
||||||
|
"Import from SIM",
|
||||||
|
"Export to SIM",
|
||||||
"Call Contact",
|
"Call Contact",
|
||||||
"Send SMS",
|
"Send SMS",
|
||||||
"Edit Contact",
|
"Edit Contact",
|
||||||
"Delete Contact"
|
"Delete Contact"
|
||||||
};
|
};
|
||||||
const char* optIcons[6] = {
|
const char* optIcons[8] = {
|
||||||
kIconPlus,
|
kIconPlus,
|
||||||
kIconClock,
|
kIconClock,
|
||||||
|
kIconSim,
|
||||||
|
kIconSim,
|
||||||
kIconPhone,
|
kIconPhone,
|
||||||
kIconSms,
|
kIconSms,
|
||||||
kIconEdit,
|
kIconEdit,
|
||||||
kIconTrash
|
kIconTrash
|
||||||
};
|
};
|
||||||
int optCount = filtered.empty() ? 2 : 6;
|
int optCount = filtered.empty() ? (book.savedContacts().empty() ? 3 : 4) : 8;
|
||||||
|
constexpr int kMaxOptVis = 6;
|
||||||
|
int visCount = std::min(optCount, kMaxOptVis);
|
||||||
|
int optScroll = 0;
|
||||||
|
if (optionSelected >= visCount) {
|
||||||
|
optScroll = optionSelected - visCount + 1;
|
||||||
|
}
|
||||||
|
if (optScroll > std::max(0, optCount - visCount)) {
|
||||||
|
optScroll = std::max(0, optCount - visCount);
|
||||||
|
}
|
||||||
int menuRowH = 34;
|
int menuRowH = 34;
|
||||||
int menuH = optCount * menuRowH + 4;
|
int menuH = visCount * menuRowH + 4;
|
||||||
int menuY = kWinHeight - kSoftbarH - menuH;
|
int menuY = kWinHeight - kSoftbarH - menuH;
|
||||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||||
XFillRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 16, menuH);
|
XFillRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 16, menuH);
|
||||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||||
XDrawRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 17, menuH - 1);
|
XDrawRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 17, menuH - 1);
|
||||||
|
|
||||||
for (int i = 0; i < optCount; ++i) {
|
for (int i = 0; i < visCount; ++i) {
|
||||||
|
int idx = optScroll + i;
|
||||||
|
if (idx >= optCount) break;
|
||||||
int ry = menuY + 2 + i * menuRowH;
|
int ry = menuY + 2 + i * menuRowH;
|
||||||
bool isSel = (i == optionSelected);
|
bool isSel = (idx == optionSelected);
|
||||||
if (isSel) {
|
if (isSel) {
|
||||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||||
XFillRectangle(dpy, pix, gc, 10, ry, kWinWidth - 20, menuRowH);
|
XFillRectangle(dpy, pix, gc, 10, ry, kWinWidth - 20, menuRowH);
|
||||||
XSetForeground(dpy, gc, theme.spec.accent);
|
XSetForeground(dpy, gc, theme.spec.accent);
|
||||||
XFillRectangle(dpy, pix, gc, 10, ry, 4, menuRowH);
|
XFillRectangle(dpy, pix, gc, 10, ry, 4, menuRowH);
|
||||||
}
|
}
|
||||||
XftColor* icCol = (i == 5) ? &colRed : (isSel ? &colYellow : &colGreen);
|
XftColor* icCol = (idx == 7) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||||
drawPango(layoutIcon, icCol, 18, ry, menuRowH, optIcons[i]);
|
drawPango(layoutIcon, icCol, 18, ry, menuRowH, optIcons[idx]);
|
||||||
drawPango(layoutMain, isSel ? &colWhite : &colLight, 42, ry, menuRowH,
|
drawPango(layoutMain, isSel ? &colWhite : &colLight, 42, ry, menuRowH,
|
||||||
optLabels[i], 180);
|
optLabels[idx], 180);
|
||||||
|
}
|
||||||
|
if (optCount > visCount) {
|
||||||
|
int sbH = std::max(14, ((menuH - 4) * visCount) / optCount);
|
||||||
|
int sbY = menuY + 2 +
|
||||||
|
(((menuH - 4) - sbH) * optScroll) / (optCount - visCount);
|
||||||
|
XSetForeground(dpy, gc, theme.spec.divider);
|
||||||
|
XFillRectangle(dpy, pix, gc, kWinWidth - 12, sbY, 2, sbH);
|
||||||
}
|
}
|
||||||
drawSoftbar("Select", "OK", "Back", &colYellow);
|
drawSoftbar("Select", "OK", "Back", &colYellow);
|
||||||
} else {
|
} else {
|
||||||
@@ -421,23 +457,25 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
drawPango(layoutMain, &colWhite, 34, 4, 26, c.name, 198);
|
drawPango(layoutMain, &colWhite, 34, 4, 26, c.name, 198);
|
||||||
drawPango(layoutSmall, &colYellow, 34, 30, 20, c.number, 198);
|
drawPango(layoutSmall, &colYellow, 34, 30, 20, c.number, 198);
|
||||||
|
|
||||||
const char* actLabels[6] = {
|
const char* actLabels[7] = {
|
||||||
"Call Contact",
|
"Call Contact",
|
||||||
"Send SMS",
|
"Send SMS",
|
||||||
"Read Messages",
|
"Read Messages",
|
||||||
"Open in Dialer",
|
"Open in Dialer",
|
||||||
|
"Copy to SIM",
|
||||||
"Edit Contact",
|
"Edit Contact",
|
||||||
"Delete Contact"
|
"Delete Contact"
|
||||||
};
|
};
|
||||||
const char* actIcons[6] = {
|
const char* actIcons[7] = {
|
||||||
kIconPhone,
|
kIconPhone,
|
||||||
kIconSms,
|
kIconSms,
|
||||||
kIconList,
|
kIconList,
|
||||||
kIconPhoneLog,
|
kIconPhoneLog,
|
||||||
|
kIconSim,
|
||||||
kIconEdit,
|
kIconEdit,
|
||||||
kIconTrash
|
kIconTrash
|
||||||
};
|
};
|
||||||
constexpr int kActCount = 6;
|
constexpr int kActCount = 7;
|
||||||
constexpr int kActVis = 5;
|
constexpr int kActVis = 5;
|
||||||
int actListY = kCardH;
|
int actListY = kCardH;
|
||||||
int actListH = kWinHeight - kCardH - kSoftbarH;
|
int actListH = kWinHeight - kCardH - kSoftbarH;
|
||||||
@@ -462,10 +500,10 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||||
}
|
}
|
||||||
XftColor* icCol =
|
XftColor* icCol =
|
||||||
(idx == 5) ? &colRed : (isSel ? &colYellow : &colGreen);
|
(idx == 6) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||||
drawPango(layoutIcon, icCol, 10, ry, rowH, actIcons[idx]);
|
drawPango(layoutIcon, icCol, 10, ry, rowH, actIcons[idx]);
|
||||||
drawPango(layoutMain,
|
drawPango(layoutMain,
|
||||||
(idx == 5 && !isSel) ? &colRed : (isSel ? &colWhite : &colLight),
|
(idx == 6 && !isSel) ? &colRed : (isSel ? &colWhite : &colLight),
|
||||||
34, ry, rowH, actLabels[idx], 196);
|
34, ry, rowH, actLabels[idx], 196);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +646,57 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawSoftbar("Delete", "Select", "Cancel", &colRed);
|
drawSoftbar("Delete", "Select", "Cancel", &colRed);
|
||||||
|
} else if (mode == ViewMode::SelectSim) {
|
||||||
|
XSetForeground(dpy, gc, theme.spec.bgHeader);
|
||||||
|
XFillRectangle(dpy, pix, gc, 0, 0, kWinWidth, kHeaderH);
|
||||||
|
XSetForeground(dpy, gc, theme.spec.border);
|
||||||
|
XFillRectangle(dpy, pix, gc, 0, kHeaderH - 1, kWinWidth, 1);
|
||||||
|
drawPango(layoutIcon, &colYellow, 8, 0, kHeaderH, kIconSim);
|
||||||
|
const char* simTitle =
|
||||||
|
(pendingSimOp == SimOp::ImportAll)
|
||||||
|
? "Import from SIM"
|
||||||
|
: ((pendingSimOp == SimOp::ExportSingle) ? "Copy to SIM"
|
||||||
|
: "Export to SIM");
|
||||||
|
drawPango(layoutMain, &colWhite, 32, 0, kHeaderH, simTitle, 200);
|
||||||
|
|
||||||
|
drawPango(layoutSmall, &colMuted, 12, 48, 22, "Select SIM card slot:",
|
||||||
|
216);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
int slotNum = i + 1;
|
||||||
|
int ry = 78 + i * 54;
|
||||||
|
bool isSel = (i == simSelected);
|
||||||
|
XSetForeground(dpy, gc, isSel ? theme.spec.selBg : theme.spec.bgButton);
|
||||||
|
XFillRectangle(dpy, pix, gc, 8, ry, kWinWidth - 16, 46);
|
||||||
|
if (isSel) {
|
||||||
|
XSetForeground(dpy, gc, theme.spec.accent);
|
||||||
|
XFillRectangle(dpy, pix, gc, 8, ry, 4, 46);
|
||||||
|
}
|
||||||
|
drawPango(layoutIcon, isSel ? &colYellow : &colGreen, 16, ry, 46,
|
||||||
|
kIconSim);
|
||||||
|
std::string label =
|
||||||
|
std::string("SIM ") + std::to_string(slotNum) +
|
||||||
|
((activeGwSlot == slotNum) ? " [Active]" : "");
|
||||||
|
drawPango(layoutMain, isSel ? &colWhite : &colLight, 42, ry, 46, label,
|
||||||
|
182);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawSoftbar("Select", "OK", "Cancel", &colGreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statusMsg.empty() &&
|
||||||
|
(mode == ViewMode::List || mode == ViewMode::Detail)) {
|
||||||
|
int bannerH = 26;
|
||||||
|
int bannerY = kWinHeight - kSoftbarH - bannerH;
|
||||||
|
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||||
|
XFillRectangle(dpy, pix, gc, 0, bannerY, kWinWidth, bannerH);
|
||||||
|
XSetForeground(dpy, gc,
|
||||||
|
statusIsError ? colRed.pixel : theme.spec.accent);
|
||||||
|
XFillRectangle(dpy, pix, gc, 0, bannerY, kWinWidth, 1);
|
||||||
|
drawPango(layoutIcon, statusIsError ? &colRed : &colYellow, 8, bannerY,
|
||||||
|
bannerH, kIconSim);
|
||||||
|
drawPango(layoutSmall, statusIsError ? &colRed : &colWhite, 30, bannerY,
|
||||||
|
bannerH, statusMsg, 204);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -620,7 +709,7 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
drawPango(layoutIcon, &colGreen, 0, 0, 20,
|
drawPango(layoutIcon, &colGreen, 0, 0, 20,
|
||||||
std::string(kIconBook) + kIconUser + kIconSearch + kIconPlus +
|
std::string(kIconBook) + kIconUser + kIconSearch + kIconPlus +
|
||||||
kIconEdit + kIconTrash + kIconSms + kIconList + kIconCheck +
|
kIconEdit + kIconTrash + kIconSms + kIconList + kIconCheck +
|
||||||
kIconPhone + kIconClock);
|
kIconPhone + kIconClock + kIconSim);
|
||||||
render();
|
render();
|
||||||
XSync(dpy, False);
|
XSync(dpy, False);
|
||||||
|
|
||||||
@@ -810,6 +899,159 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
lastTapMs = now;
|
lastTapMs = now;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto setStatus = [&](const std::string& msg, bool isErr, int durationMs = 3500) {
|
||||||
|
statusMsg = msg;
|
||||||
|
statusIsError = isErr;
|
||||||
|
statusUntilMs = (durationMs > 0) ? (nowMs() + durationMs) : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto performSimOperation = [&](int slotNum) {
|
||||||
|
mode = (pendingSimOp == SimOp::ExportSingle) ? ViewMode::Detail
|
||||||
|
: ViewMode::List;
|
||||||
|
if (pendingSimOp == SimOp::ImportAll) {
|
||||||
|
setStatus("Reading SIM " + std::to_string(slotNum) + " contacts...",
|
||||||
|
false, 0);
|
||||||
|
render();
|
||||||
|
int rc = 0;
|
||||||
|
std::string out = runCaptureCmd(
|
||||||
|
"doas /usr/libexec/msm-modem-uim-selection sim-contacts-import " +
|
||||||
|
std::to_string(slotNum) + " 2>&1",
|
||||||
|
&rc);
|
||||||
|
if (out.rfind("ERROR:", 0) == 0) {
|
||||||
|
std::string err = trimStr(out.substr(6));
|
||||||
|
setStatus(err.empty() ? "SIM import failed" : err, true);
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<std::pair<std::string, std::string>> rows;
|
||||||
|
std::stringstream ss(out);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
size_t tab = line.find('\t');
|
||||||
|
if (tab == std::string::npos) continue;
|
||||||
|
std::string num = trimStr(line.substr(0, tab));
|
||||||
|
std::string name = trimStr(line.substr(tab + 1));
|
||||||
|
if (!num.empty()) {
|
||||||
|
rows.push_back({num, name.empty() ? num : name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rows.empty()) {
|
||||||
|
setStatus("No contacts on SIM " + std::to_string(slotNum), false);
|
||||||
|
} else {
|
||||||
|
int imported = book.importContactsBatch(rows);
|
||||||
|
contactsStamp.checkAndUpdate(getContactsFilePath());
|
||||||
|
updateFilter();
|
||||||
|
if (imported < 0) {
|
||||||
|
setStatus("Failed to save imported contacts", true);
|
||||||
|
} else {
|
||||||
|
setStatus("Imported " + std::to_string(imported) + " from SIM " +
|
||||||
|
std::to_string(slotNum),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
} else {
|
||||||
|
std::string srcFile = getContactsFilePath();
|
||||||
|
std::string tmpSingle;
|
||||||
|
if (pendingSimOp == SimOp::ExportSingle) {
|
||||||
|
if (selected < 0 || selected >= static_cast<int>(filtered.size())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tmpSingle = "/tmp/sim_single_" + std::to_string(::getpid()) + ".tsv";
|
||||||
|
std::ofstream tf(tmpSingle, std::ios::trunc);
|
||||||
|
tf << filtered[selected].number << "\t" << filtered[selected].name
|
||||||
|
<< "\n";
|
||||||
|
tf.close();
|
||||||
|
srcFile = tmpSingle;
|
||||||
|
} else if (book.savedContacts().empty()) {
|
||||||
|
setStatus("Phonebook is empty", true);
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setStatus("Writing to SIM " + std::to_string(slotNum) + "...", false, 0);
|
||||||
|
render();
|
||||||
|
int rc = 0;
|
||||||
|
std::string out = runCaptureCmd(
|
||||||
|
"doas /usr/libexec/msm-modem-uim-selection sim-contacts-export " +
|
||||||
|
std::to_string(slotNum) + " " + shellQuote(srcFile) + " 2>&1",
|
||||||
|
&rc);
|
||||||
|
if (!tmpSingle.empty()) {
|
||||||
|
::unlink(tmpSingle.c_str());
|
||||||
|
}
|
||||||
|
if (out.rfind("ERROR:", 0) == 0) {
|
||||||
|
std::string err = trimStr(out.substr(6));
|
||||||
|
setStatus(err.empty() ? "SIM export failed" : err, true);
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int exported = 0;
|
||||||
|
std::stringstream ss(out);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (line.rfind("EXPORTED=", 0) == 0) {
|
||||||
|
exported = std::atoi(line.c_str() + 9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exported > 0) {
|
||||||
|
if (pendingSimOp == SimOp::ExportSingle) {
|
||||||
|
setStatus("Copied contact to SIM " + std::to_string(slotNum), false);
|
||||||
|
} else {
|
||||||
|
setStatus("Exported " + std::to_string(exported) + " to SIM " +
|
||||||
|
std::to_string(slotNum),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setStatus("SIM " + std::to_string(slotNum) + " write failed", true);
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto beginSimOperation = [&](SimOp op) {
|
||||||
|
pendingSimOp = op;
|
||||||
|
prevModeBeforeSim =
|
||||||
|
(op == SimOp::ExportSingle) ? ViewMode::Detail : ViewMode::List;
|
||||||
|
std::string st = runCaptureCmd(
|
||||||
|
"doas /usr/libexec/msm-modem-uim-selection status 2>/dev/null");
|
||||||
|
bool s1Present = false;
|
||||||
|
bool s2Present = false;
|
||||||
|
int gwSlot = 1;
|
||||||
|
std::stringstream ss(st);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.pop_back();
|
||||||
|
}
|
||||||
|
if (line == "SLOT1_STATE=present") {
|
||||||
|
s1Present = true;
|
||||||
|
} else if (line == "SLOT2_STATE=present") {
|
||||||
|
s2Present = true;
|
||||||
|
} else if (line.rfind("PRIMARY_GW_SLOT=", 0) == 0) {
|
||||||
|
int v = std::atoi(line.c_str() + 16);
|
||||||
|
if (v == 1 || v == 2) {
|
||||||
|
gwSlot = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeGwSlot = gwSlot;
|
||||||
|
if (s1Present && s2Present) {
|
||||||
|
simSelected = (gwSlot == 2) ? 1 : 0;
|
||||||
|
mode = ViewMode::SelectSim;
|
||||||
|
render();
|
||||||
|
} else if (s1Present) {
|
||||||
|
performSimOperation(1);
|
||||||
|
} else if (s2Present) {
|
||||||
|
performSimOperation(2);
|
||||||
|
} else {
|
||||||
|
mode = prevModeBeforeSim;
|
||||||
|
setStatus("No SIM card detected", true);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto executeDetailAction = [&](int actIdx) {
|
auto executeDetailAction = [&](int actIdx) {
|
||||||
if (selected < 0 || selected >= static_cast<int>(filtered.size())) {
|
if (selected < 0 || selected >= static_cast<int>(filtered.size())) {
|
||||||
return;
|
return;
|
||||||
@@ -842,11 +1084,14 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
runAsyncArgv("xdialer", c.number.c_str());
|
runAsyncArgv("xdialer", c.number.c_str());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4: // Edit Contact
|
case 4: // Copy to SIM
|
||||||
|
beginSimOperation(SimOp::ExportSingle);
|
||||||
|
break;
|
||||||
|
case 5: // Edit Contact
|
||||||
beginEditContact(c);
|
beginEditContact(c);
|
||||||
render();
|
render();
|
||||||
break;
|
break;
|
||||||
case 5: // Delete Contact
|
case 6: // Delete Contact
|
||||||
confirmSelected = 0;
|
confirmSelected = 0;
|
||||||
mode = ViewMode::ConfirmDelete;
|
mode = ViewMode::ConfirmDelete;
|
||||||
render();
|
render();
|
||||||
@@ -865,6 +1110,10 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
beginNewContact("");
|
beginNewContact("");
|
||||||
mode = ViewMode::PickRecent;
|
mode = ViewMode::PickRecent;
|
||||||
render();
|
render();
|
||||||
|
} else if (optIdx == 2) {
|
||||||
|
beginSimOperation(SimOp::ImportAll);
|
||||||
|
} else if (optIdx == 3) {
|
||||||
|
beginSimOperation(SimOp::ExportAll);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -880,18 +1129,24 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
mode = ViewMode::PickRecent;
|
mode = ViewMode::PickRecent;
|
||||||
render();
|
render();
|
||||||
break;
|
break;
|
||||||
case 2: // Call
|
case 2: // Import from SIM
|
||||||
|
beginSimOperation(SimOp::ImportAll);
|
||||||
|
break;
|
||||||
|
case 3: // Export to SIM
|
||||||
|
beginSimOperation(SimOp::ExportAll);
|
||||||
|
break;
|
||||||
|
case 4: // Call
|
||||||
executeDetailAction(0);
|
executeDetailAction(0);
|
||||||
break;
|
break;
|
||||||
case 3: // Send SMS
|
case 5: // Send SMS
|
||||||
executeDetailAction(1);
|
executeDetailAction(1);
|
||||||
break;
|
break;
|
||||||
case 4: // Edit
|
case 6: // Edit
|
||||||
executeDetailAction(4);
|
|
||||||
break;
|
|
||||||
case 5: // Delete
|
|
||||||
executeDetailAction(5);
|
executeDetailAction(5);
|
||||||
break;
|
break;
|
||||||
|
case 7: // Delete
|
||||||
|
executeDetailAction(6);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1072,7 +1327,8 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (mode == ViewMode::Options) {
|
} else if (mode == ViewMode::Options) {
|
||||||
int optCount = filtered.empty() ? 2 : 6;
|
int optCount =
|
||||||
|
filtered.empty() ? (book.savedContacts().empty() ? 3 : 4) : 8;
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XK_Up:
|
case XK_Up:
|
||||||
case XK_KP_Up:
|
case XK_KP_Up:
|
||||||
@@ -1098,7 +1354,7 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (mode == ViewMode::Detail) {
|
} else if (mode == ViewMode::Detail) {
|
||||||
constexpr int kActCount = 6;
|
constexpr int kActCount = 7;
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XK_Up:
|
case XK_Up:
|
||||||
case XK_KP_Up:
|
case XK_KP_Up:
|
||||||
@@ -1317,9 +1573,48 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (mode == ViewMode::SelectSim) {
|
||||||
|
switch (sym) {
|
||||||
|
case XK_Up:
|
||||||
|
case XK_KP_Up:
|
||||||
|
case XK_Down:
|
||||||
|
case XK_KP_Down:
|
||||||
|
simSelected = 1 - simSelected;
|
||||||
|
render();
|
||||||
|
break;
|
||||||
|
case XK_1:
|
||||||
|
case XK_KP_1:
|
||||||
|
simSelected = 0;
|
||||||
|
performSimOperation(1);
|
||||||
|
break;
|
||||||
|
case XK_2:
|
||||||
|
case XK_KP_2:
|
||||||
|
simSelected = 1;
|
||||||
|
performSimOperation(2);
|
||||||
|
break;
|
||||||
|
case XK_Return:
|
||||||
|
case XK_KP_Enter:
|
||||||
|
performSimOperation(simSelected + 1);
|
||||||
|
break;
|
||||||
|
case XK_BackSpace:
|
||||||
|
case XK_Left:
|
||||||
|
case XK_KP_Left:
|
||||||
|
mode = prevModeBeforeSim;
|
||||||
|
render();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible && !statusMsg.empty() && statusUntilMs > 0 &&
|
||||||
|
nowMs() >= statusUntilMs) {
|
||||||
|
statusMsg.clear();
|
||||||
|
statusUntilMs = 0;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
if (visible && softkeyPending != 0 && nowMs() >= softkeyDeadlineMs) {
|
if (visible && softkeyPending != 0 && nowMs() >= softkeyDeadlineMs) {
|
||||||
int comb = softkeyPending | querySoftkeysDown(dpy);
|
int comb = softkeyPending | querySoftkeysDown(dpy);
|
||||||
@@ -1366,6 +1661,8 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
mode = ViewMode::List;
|
mode = ViewMode::List;
|
||||||
render();
|
render();
|
||||||
|
} else if (mode == ViewMode::SelectSim) {
|
||||||
|
performSimOperation(simSelected + 1);
|
||||||
}
|
}
|
||||||
} else if (comb == 2) {
|
} else if (comb == 2) {
|
||||||
// Right softkey
|
// Right softkey
|
||||||
@@ -1404,6 +1701,9 @@ int runContactsApp(int argc, char** argv) {
|
|||||||
} else if (mode == ViewMode::ConfirmDelete) {
|
} else if (mode == ViewMode::ConfirmDelete) {
|
||||||
mode = ViewMode::Detail;
|
mode = ViewMode::Detail;
|
||||||
render();
|
render();
|
||||||
|
} else if (mode == ViewMode::SelectSim) {
|
||||||
|
mode = prevModeBeforeSim;
|
||||||
|
render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -809,6 +809,78 @@ class ContactBook {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int importContactsBatch(
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& items) {
|
||||||
|
if (items.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::string path = getContactsFilePath();
|
||||||
|
ensureParentDir(path);
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>> rows;
|
||||||
|
{
|
||||||
|
std::ifstream in(path);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
if (line.empty()) continue;
|
||||||
|
size_t tab = line.find('\t');
|
||||||
|
if (tab == std::string::npos) continue;
|
||||||
|
std::string num = trimStr(line.substr(0, tab));
|
||||||
|
std::string name = trimStr(line.substr(tab + 1));
|
||||||
|
if (!num.empty() && !name.empty()) {
|
||||||
|
rows.push_back({num, name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (const auto& item : items) {
|
||||||
|
std::string newNumber = trimStr(item.first);
|
||||||
|
std::string newName = trimStr(item.second);
|
||||||
|
for (char& ch : newName) {
|
||||||
|
if (ch == '\t' || ch == '\r' || ch == '\n') {
|
||||||
|
ch = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string newNorm = normalizePhone(newNumber);
|
||||||
|
if (newNorm.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (newName.empty()) {
|
||||||
|
newName = newNumber;
|
||||||
|
}
|
||||||
|
bool replaced = false;
|
||||||
|
for (auto& r : rows) {
|
||||||
|
if (r.first == newNumber ||
|
||||||
|
(!newNorm.empty() && normalizePhone(r.first) == newNorm)) {
|
||||||
|
r.first = newNumber;
|
||||||
|
r.second = newName;
|
||||||
|
replaced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!replaced) {
|
||||||
|
rows.push_back({newNumber, newName});
|
||||||
|
}
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream out(path, std::ios::trunc);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (const auto& r : rows) {
|
||||||
|
out << r.first << "\t" << r.second << "\n";
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
load();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
bool deleteContact(const std::string& number, const std::string& name) {
|
bool deleteContact(const std::string& number, const std::string& name) {
|
||||||
std::string path = getContactsFilePath();
|
std::string path = getContactsFilePath();
|
||||||
std::vector<std::pair<std::string, std::string>> rows;
|
std::vector<std::pair<std::string, std::string>> rows;
|
||||||
|
|||||||
@@ -8,10 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/firmware/qcom/qcom_scm.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
@@ -19,6 +21,7 @@
|
|||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
#define MSM8905_TLMM_GPIO32_PHYS 0x01020000UL
|
#define MSM8905_TLMM_GPIO32_PHYS 0x01020000UL
|
||||||
#define MSM8905_TLMM_GPIO32_SIZE 0x1000UL
|
#define MSM8905_TLMM_GPIO32_SIZE 0x1000UL
|
||||||
@@ -42,16 +45,25 @@
|
|||||||
#define RCGR_D_TORCH_50PCT 0xff7fU
|
#define RCGR_D_TORCH_50PCT 0xff7fU
|
||||||
#define RCGR_CMD_ENABLE_UPDATE 0x0003U /* ROOT_EN | CMD_UPDATE */
|
#define RCGR_CMD_ENABLE_UPDATE 0x0003U /* ROOT_EN | CMD_UPDATE */
|
||||||
|
|
||||||
/* PM8909 PON registers for PS_HOLD shutdown vs hard reset configuration */
|
/* PM8909 PON registers for PS_HOLD shutdown vs hard/warm reset configuration */
|
||||||
#define QPNP_PON_REVISION2 0x0801U
|
#define QPNP_PON_REVISION2 0x0801U
|
||||||
#define QPNP_PON_PS_HOLD_RST_CTL 0x085aU
|
#define QPNP_PON_PS_HOLD_RST_CTL 0x085aU
|
||||||
#define QPNP_PON_PS_HOLD_RST_CTL2 0x085bU
|
#define QPNP_PON_PS_HOLD_RST_CTL2 0x085bU
|
||||||
|
#define QPNP_PON_SOFT_RB_SPARE 0x088fU
|
||||||
#define QPNP_PON_S2_CNTL_EN 0x80U
|
#define QPNP_PON_S2_CNTL_EN 0x80U
|
||||||
#define QPNP_PON_RST_TYPE_MASK 0x0fU
|
#define QPNP_PON_RST_TYPE_MASK 0x0fU
|
||||||
#define PON_POWER_OFF_WARM_RESET 0x01U
|
#define PON_POWER_OFF_WARM_RESET 0x01U
|
||||||
#define PON_POWER_OFF_SHUTDOWN 0x04U
|
#define PON_POWER_OFF_SHUTDOWN 0x04U
|
||||||
#define PON_POWER_OFF_HARD_RESET 0x07U
|
#define PON_POWER_OFF_HARD_RESET 0x07U
|
||||||
|
|
||||||
|
/* MSM8905/MSM8909 TCSR Boot Misc Detect register for EDL (05c6:9008) */
|
||||||
|
#define MSM8905_TCSR_BOOT_MISC_DETECT 0x0193d100U
|
||||||
|
#define MSM8905_DLOAD_EDL 0x00000001U
|
||||||
|
|
||||||
|
static char next_reboot_mode[32];
|
||||||
|
module_param_string(next_reboot_mode, next_reboot_mode, sizeof(next_reboot_mode), 0644);
|
||||||
|
MODULE_PARM_DESC(next_reboot_mode, "Next reboot target mode (bootloader, fastboot, edl)");
|
||||||
|
|
||||||
static void __iomem *tlmm_gpio32_base;
|
static void __iomem *tlmm_gpio32_base;
|
||||||
static void __iomem *gcc_camss_base;
|
static void __iomem *gcc_camss_base;
|
||||||
static struct regmap *pon_regmap;
|
static struct regmap *pon_regmap;
|
||||||
@@ -164,7 +176,63 @@ static void msm8905_pon_config_reset(unsigned int rst_type)
|
|||||||
udelay(350);
|
udelay(350);
|
||||||
|
|
||||||
pr_info("leds-msm8905-torch: configured PM8909 PON PS_HOLD_RST_CTL=0x%02x (%s)\n",
|
pr_info("leds-msm8905-torch: configured PM8909 PON PS_HOLD_RST_CTL=0x%02x (%s)\n",
|
||||||
rst_type, (rst_type == PON_POWER_OFF_SHUTDOWN) ? "shutdown" : "hard-reset");
|
rst_type,
|
||||||
|
(rst_type == PON_POWER_OFF_SHUTDOWN) ? "shutdown" :
|
||||||
|
(rst_type == PON_POWER_OFF_WARM_RESET) ? "warm-reset" : "hard-reset");
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int msm8905_pon_prepare_restart_mode(const char *cmd)
|
||||||
|
{
|
||||||
|
const char *mode = next_reboot_mode;
|
||||||
|
char trimmed[32];
|
||||||
|
size_t len;
|
||||||
|
int ret;
|
||||||
|
void __iomem *tcsr_dload;
|
||||||
|
|
||||||
|
if ((!mode || !mode[0]) && cmd && cmd[0])
|
||||||
|
mode = cmd;
|
||||||
|
|
||||||
|
if (!mode || !mode[0])
|
||||||
|
return PON_POWER_OFF_HARD_RESET;
|
||||||
|
|
||||||
|
strscpy(trimmed, mode, sizeof(trimmed));
|
||||||
|
len = strlen(trimmed);
|
||||||
|
while (len > 0 && (trimmed[len - 1] == '\n' || trimmed[len - 1] == '\r' ||
|
||||||
|
trimmed[len - 1] == ' ' || trimmed[len - 1] == '\t')) {
|
||||||
|
trimmed[--len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(trimmed, "bootloader") == 0 || strcmp(trimmed, "fastboot") == 0) {
|
||||||
|
if (pon_regmap) {
|
||||||
|
/*
|
||||||
|
* Write 0x04 (1 << 2) into PON_SOFT_RB_SPARE (0x088f) matching
|
||||||
|
* mode-bootloader = <0x02> so lk2nd enters Fastboot mode on reboot.
|
||||||
|
*/
|
||||||
|
regmap_update_bits(pon_regmap, QPNP_PON_SOFT_RB_SPARE, 0xfcU, 0x04U);
|
||||||
|
}
|
||||||
|
pr_info("leds-msm8905-torch: armed PM8909 PON_SOFT_RB_SPARE for lk2nd Fastboot\n");
|
||||||
|
return PON_POWER_OFF_HARD_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(trimmed, "edl") == 0) {
|
||||||
|
if (pon_regmap)
|
||||||
|
regmap_update_bits(pon_regmap, QPNP_PON_SOFT_RB_SPARE, 0xfcU, 0x00U);
|
||||||
|
|
||||||
|
ret = qcom_scm_io_writel(MSM8905_TCSR_BOOT_MISC_DETECT, MSM8905_DLOAD_EDL);
|
||||||
|
if (ret) {
|
||||||
|
tcsr_dload = ioremap(MSM8905_TCSR_BOOT_MISC_DETECT, 4);
|
||||||
|
if (tcsr_dload) {
|
||||||
|
writel_relaxed(MSM8905_DLOAD_EDL, tcsr_dload);
|
||||||
|
wmb();
|
||||||
|
iounmap(tcsr_dload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pr_info("leds-msm8905-torch: armed TCSR_BOOT_MISC_DETECT (0x0193d100) for EDL (scm_ret=%d)\n",
|
||||||
|
ret);
|
||||||
|
return PON_POWER_OFF_WARM_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PON_POWER_OFF_HARD_RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int msm8905_pon_sys_poweroff(struct sys_off_data *data)
|
static int msm8905_pon_sys_poweroff(struct sys_off_data *data)
|
||||||
@@ -176,8 +244,11 @@ static int msm8905_pon_sys_poweroff(struct sys_off_data *data)
|
|||||||
|
|
||||||
static int msm8905_pon_sys_restart(struct sys_off_data *data)
|
static int msm8905_pon_sys_restart(struct sys_off_data *data)
|
||||||
{
|
{
|
||||||
|
unsigned int rst_type;
|
||||||
|
|
||||||
msm8905_torch_hw_disable();
|
msm8905_torch_hw_disable();
|
||||||
msm8905_pon_config_reset(PON_POWER_OFF_HARD_RESET);
|
rst_type = msm8905_pon_prepare_restart_mode(data ? data->cmd : NULL);
|
||||||
|
msm8905_pon_config_reset(rst_type);
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +262,7 @@ static int msm8905_pon_reboot_notify(struct notifier_block *nb,
|
|||||||
if (action == SYS_POWER_OFF || action == SYS_HALT)
|
if (action == SYS_POWER_OFF || action == SYS_HALT)
|
||||||
rst_type = PON_POWER_OFF_SHUTDOWN;
|
rst_type = PON_POWER_OFF_SHUTDOWN;
|
||||||
else
|
else
|
||||||
rst_type = PON_POWER_OFF_HARD_RESET;
|
rst_type = msm8905_pon_prepare_restart_mode((const char *)data);
|
||||||
|
|
||||||
msm8905_pon_config_reset(rst_type);
|
msm8905_pon_config_reset(rst_type);
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
@@ -199,7 +270,7 @@ static int msm8905_pon_reboot_notify(struct notifier_block *nb,
|
|||||||
|
|
||||||
static struct notifier_block msm8905_pon_reboot_nb = {
|
static struct notifier_block msm8905_pon_reboot_nb = {
|
||||||
.notifier_call = msm8905_pon_reboot_notify,
|
.notifier_call = msm8905_pon_reboot_notify,
|
||||||
.priority = 128,
|
.priority = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void msm8905_pon_lookup_regmap(void)
|
static void msm8905_pon_lookup_regmap(void)
|
||||||
|
|||||||
@@ -2190,6 +2190,10 @@ open_builtin_menu(const char *menu_name, int keep_idx)
|
|||||||
add_menu_item(label, "@menu:confirm_logout", 1);
|
add_menu_item(label, "@menu:confirm_logout", 1);
|
||||||
snprintf(label, sizeof(label), "%s Reboot", ICON_RLD);
|
snprintf(label, sizeof(label), "%s Reboot", ICON_RLD);
|
||||||
add_menu_item(label, "@menu:confirm_reboot", 1);
|
add_menu_item(label, "@menu:confirm_reboot", 1);
|
||||||
|
snprintf(label, sizeof(label), "%s Reboot to Fastboot", ICON_RLD);
|
||||||
|
add_menu_item(label, "@menu:confirm_fastboot", 1);
|
||||||
|
snprintf(label, sizeof(label), "%s Reboot to EDL", ICON_RLD);
|
||||||
|
add_menu_item(label, "@menu:confirm_edl", 1);
|
||||||
snprintf(label, sizeof(label), "%s Poweroff", ICON_PWR);
|
snprintf(label, sizeof(label), "%s Poweroff", ICON_PWR);
|
||||||
add_menu_item(label, "@menu:confirm_poweroff", 1);
|
add_menu_item(label, "@menu:confirm_poweroff", 1);
|
||||||
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
|
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
|
||||||
@@ -2204,6 +2208,14 @@ open_builtin_menu(const char *menu_name, int keep_idx)
|
|||||||
reset_session_defaults(1, "Confirm Reboot");
|
reset_session_defaults(1, "Confirm Reboot");
|
||||||
add_menu_item("Yes", "sxmo_power.sh reboot", 0);
|
add_menu_item("Yes", "sxmo_power.sh reboot", 0);
|
||||||
add_menu_item("No", "@menu:power", 1);
|
add_menu_item("No", "@menu:power", 1);
|
||||||
|
} else if (strcmp(menu_name, "confirm_fastboot") == 0) {
|
||||||
|
reset_session_defaults(1, "Confirm Fastboot");
|
||||||
|
add_menu_item("Yes", "sxmo_power.sh fastboot", 0);
|
||||||
|
add_menu_item("No", "@menu:power", 1);
|
||||||
|
} else if (strcmp(menu_name, "confirm_edl") == 0) {
|
||||||
|
reset_session_defaults(1, "Confirm EDL Mode");
|
||||||
|
add_menu_item("Yes", "sxmo_power.sh edl", 0);
|
||||||
|
add_menu_item("No", "@menu:power", 1);
|
||||||
} else if (strcmp(menu_name, "confirm_poweroff") == 0) {
|
} else if (strcmp(menu_name, "confirm_poweroff") == 0) {
|
||||||
reset_session_defaults(1, "Confirm Poweroff");
|
reset_session_defaults(1, "Confirm Poweroff");
|
||||||
add_menu_item("Yes", "sxmo_power.sh poweroff", 0);
|
add_menu_item("Yes", "sxmo_power.sh poweroff", 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user