Add Fastboot/EDL reboot modes and SIM contact import/export
This commit is contained in:
@@ -25,6 +25,13 @@ enum class ViewMode {
|
||||
Edit = 3,
|
||||
PickRecent = 4,
|
||||
ConfirmDelete = 5,
|
||||
SelectSim = 6,
|
||||
};
|
||||
|
||||
enum class SimOp {
|
||||
ImportAll = 0,
|
||||
ExportAll = 1,
|
||||
ExportSingle = 2,
|
||||
};
|
||||
|
||||
enum class TextCaseMode {
|
||||
@@ -153,12 +160,19 @@ int runContactsApp(int argc, char** argv) {
|
||||
|
||||
ViewMode mode = ViewMode::List;
|
||||
ViewMode prevModeBeforeEdit = ViewMode::List;
|
||||
ViewMode prevModeBeforeSim = ViewMode::List;
|
||||
SimOp pendingSimOp = SimOp::ImportAll;
|
||||
std::string searchQuery;
|
||||
int selected = 0;
|
||||
int detailSelected = 0;
|
||||
int optionSelected = 0;
|
||||
int confirmSelected = 0;
|
||||
int recentSelected = 0;
|
||||
int simSelected = 0;
|
||||
int activeGwSlot = 1;
|
||||
std::string statusMsg;
|
||||
bool statusIsError = false;
|
||||
int64_t statusUntilMs = 0;
|
||||
|
||||
// Editor state
|
||||
bool editIsNew = true;
|
||||
@@ -360,44 +374,66 @@ int runContactsApp(int argc, char** argv) {
|
||||
|
||||
if (mode == ViewMode::Options) {
|
||||
// Popup options menu overlay
|
||||
const char* optLabels[6] = {
|
||||
const char* optLabels[8] = {
|
||||
"New Contact",
|
||||
"Add from Recent",
|
||||
"Import from SIM",
|
||||
"Export to SIM",
|
||||
"Call Contact",
|
||||
"Send SMS",
|
||||
"Edit Contact",
|
||||
"Delete Contact"
|
||||
};
|
||||
const char* optIcons[6] = {
|
||||
const char* optIcons[8] = {
|
||||
kIconPlus,
|
||||
kIconClock,
|
||||
kIconSim,
|
||||
kIconSim,
|
||||
kIconPhone,
|
||||
kIconSms,
|
||||
kIconEdit,
|
||||
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 menuH = optCount * menuRowH + 4;
|
||||
int menuH = visCount * menuRowH + 4;
|
||||
int menuY = kWinHeight - kSoftbarH - menuH;
|
||||
XSetForeground(dpy, gc, theme.spec.bgFooter);
|
||||
XFillRectangle(dpy, pix, gc, 8, menuY, kWinWidth - 16, menuH);
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
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;
|
||||
bool isSel = (i == optionSelected);
|
||||
bool isSel = (idx == optionSelected);
|
||||
if (isSel) {
|
||||
XSetForeground(dpy, gc, theme.spec.selBg);
|
||||
XFillRectangle(dpy, pix, gc, 10, ry, kWinWidth - 20, menuRowH);
|
||||
XSetForeground(dpy, gc, theme.spec.accent);
|
||||
XFillRectangle(dpy, pix, gc, 10, ry, 4, menuRowH);
|
||||
}
|
||||
XftColor* icCol = (i == 5) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||
drawPango(layoutIcon, icCol, 18, ry, menuRowH, optIcons[i]);
|
||||
XftColor* icCol = (idx == 7) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||
drawPango(layoutIcon, icCol, 18, ry, menuRowH, optIcons[idx]);
|
||||
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);
|
||||
} else {
|
||||
@@ -421,23 +457,25 @@ int runContactsApp(int argc, char** argv) {
|
||||
drawPango(layoutMain, &colWhite, 34, 4, 26, c.name, 198);
|
||||
drawPango(layoutSmall, &colYellow, 34, 30, 20, c.number, 198);
|
||||
|
||||
const char* actLabels[6] = {
|
||||
const char* actLabels[7] = {
|
||||
"Call Contact",
|
||||
"Send SMS",
|
||||
"Read Messages",
|
||||
"Open in Dialer",
|
||||
"Copy to SIM",
|
||||
"Edit Contact",
|
||||
"Delete Contact"
|
||||
};
|
||||
const char* actIcons[6] = {
|
||||
const char* actIcons[7] = {
|
||||
kIconPhone,
|
||||
kIconSms,
|
||||
kIconList,
|
||||
kIconPhoneLog,
|
||||
kIconSim,
|
||||
kIconEdit,
|
||||
kIconTrash
|
||||
};
|
||||
constexpr int kActCount = 6;
|
||||
constexpr int kActCount = 7;
|
||||
constexpr int kActVis = 5;
|
||||
int actListY = kCardH;
|
||||
int actListH = kWinHeight - kCardH - kSoftbarH;
|
||||
@@ -462,10 +500,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
XFillRectangle(dpy, pix, gc, 0, ry, 4, rowH);
|
||||
}
|
||||
XftColor* icCol =
|
||||
(idx == 5) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||
(idx == 6) ? &colRed : (isSel ? &colYellow : &colGreen);
|
||||
drawPango(layoutIcon, icCol, 10, ry, rowH, actIcons[idx]);
|
||||
drawPango(layoutMain,
|
||||
(idx == 5 && !isSel) ? &colRed : (isSel ? &colWhite : &colLight),
|
||||
(idx == 6 && !isSel) ? &colRed : (isSel ? &colWhite : &colLight),
|
||||
34, ry, rowH, actLabels[idx], 196);
|
||||
}
|
||||
|
||||
@@ -608,6 +646,57 @@ int runContactsApp(int argc, char** argv) {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -620,7 +709,7 @@ int runContactsApp(int argc, char** argv) {
|
||||
drawPango(layoutIcon, &colGreen, 0, 0, 20,
|
||||
std::string(kIconBook) + kIconUser + kIconSearch + kIconPlus +
|
||||
kIconEdit + kIconTrash + kIconSms + kIconList + kIconCheck +
|
||||
kIconPhone + kIconClock);
|
||||
kIconPhone + kIconClock + kIconSim);
|
||||
render();
|
||||
XSync(dpy, False);
|
||||
|
||||
@@ -810,6 +899,159 @@ int runContactsApp(int argc, char** argv) {
|
||||
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) {
|
||||
if (selected < 0 || selected >= static_cast<int>(filtered.size())) {
|
||||
return;
|
||||
@@ -842,11 +1084,14 @@ int runContactsApp(int argc, char** argv) {
|
||||
runAsyncArgv("xdialer", c.number.c_str());
|
||||
}
|
||||
break;
|
||||
case 4: // Edit Contact
|
||||
case 4: // Copy to SIM
|
||||
beginSimOperation(SimOp::ExportSingle);
|
||||
break;
|
||||
case 5: // Edit Contact
|
||||
beginEditContact(c);
|
||||
render();
|
||||
break;
|
||||
case 5: // Delete Contact
|
||||
case 6: // Delete Contact
|
||||
confirmSelected = 0;
|
||||
mode = ViewMode::ConfirmDelete;
|
||||
render();
|
||||
@@ -865,6 +1110,10 @@ int runContactsApp(int argc, char** argv) {
|
||||
beginNewContact("");
|
||||
mode = ViewMode::PickRecent;
|
||||
render();
|
||||
} else if (optIdx == 2) {
|
||||
beginSimOperation(SimOp::ImportAll);
|
||||
} else if (optIdx == 3) {
|
||||
beginSimOperation(SimOp::ExportAll);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -880,18 +1129,24 @@ int runContactsApp(int argc, char** argv) {
|
||||
mode = ViewMode::PickRecent;
|
||||
render();
|
||||
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);
|
||||
break;
|
||||
case 3: // Send SMS
|
||||
case 5: // Send SMS
|
||||
executeDetailAction(1);
|
||||
break;
|
||||
case 4: // Edit
|
||||
executeDetailAction(4);
|
||||
break;
|
||||
case 5: // Delete
|
||||
case 6: // Edit
|
||||
executeDetailAction(5);
|
||||
break;
|
||||
case 7: // Delete
|
||||
executeDetailAction(6);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1072,7 +1327,8 @@ int runContactsApp(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
} else if (mode == ViewMode::Options) {
|
||||
int optCount = filtered.empty() ? 2 : 6;
|
||||
int optCount =
|
||||
filtered.empty() ? (book.savedContacts().empty() ? 3 : 4) : 8;
|
||||
switch (sym) {
|
||||
case XK_Up:
|
||||
case XK_KP_Up:
|
||||
@@ -1098,7 +1354,7 @@ int runContactsApp(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
} else if (mode == ViewMode::Detail) {
|
||||
constexpr int kActCount = 6;
|
||||
constexpr int kActCount = 7;
|
||||
switch (sym) {
|
||||
case XK_Up:
|
||||
case XK_KP_Up:
|
||||
@@ -1317,10 +1573,49 @@ int runContactsApp(int argc, char** argv) {
|
||||
default:
|
||||
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) {
|
||||
int comb = softkeyPending | querySoftkeysDown(dpy);
|
||||
softkeyPending = 0;
|
||||
@@ -1366,6 +1661,8 @@ int runContactsApp(int argc, char** argv) {
|
||||
}
|
||||
mode = ViewMode::List;
|
||||
render();
|
||||
} else if (mode == ViewMode::SelectSim) {
|
||||
performSimOperation(simSelected + 1);
|
||||
}
|
||||
} else if (comb == 2) {
|
||||
// Right softkey
|
||||
@@ -1404,6 +1701,9 @@ int runContactsApp(int argc, char** argv) {
|
||||
} else if (mode == ViewMode::ConfirmDelete) {
|
||||
mode = ViewMode::Detail;
|
||||
render();
|
||||
} else if (mode == ViewMode::SelectSim) {
|
||||
mode = prevModeBeforeSim;
|
||||
render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,6 +809,78 @@ class ContactBook {
|
||||
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) {
|
||||
std::string path = getContactsFilePath();
|
||||
std::vector<std::pair<std::string, std::string>> rows;
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware/qcom/qcom_scm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
@@ -19,6 +21,7 @@
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define MSM8905_TLMM_GPIO32_PHYS 0x01020000UL
|
||||
#define MSM8905_TLMM_GPIO32_SIZE 0x1000UL
|
||||
@@ -42,16 +45,25 @@
|
||||
#define RCGR_D_TORCH_50PCT 0xff7fU
|
||||
#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_PS_HOLD_RST_CTL 0x085aU
|
||||
#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_RST_TYPE_MASK 0x0fU
|
||||
#define PON_POWER_OFF_WARM_RESET 0x01U
|
||||
#define PON_POWER_OFF_SHUTDOWN 0x04U
|
||||
#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 *gcc_camss_base;
|
||||
static struct regmap *pon_regmap;
|
||||
@@ -164,7 +176,63 @@ static void msm8905_pon_config_reset(unsigned int rst_type)
|
||||
udelay(350);
|
||||
|
||||
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)
|
||||
@@ -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)
|
||||
{
|
||||
unsigned int rst_type;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -191,7 +262,7 @@ static int msm8905_pon_reboot_notify(struct notifier_block *nb,
|
||||
if (action == SYS_POWER_OFF || action == SYS_HALT)
|
||||
rst_type = PON_POWER_OFF_SHUTDOWN;
|
||||
else
|
||||
rst_type = PON_POWER_OFF_HARD_RESET;
|
||||
rst_type = msm8905_pon_prepare_restart_mode((const char *)data);
|
||||
|
||||
msm8905_pon_config_reset(rst_type);
|
||||
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 = {
|
||||
.notifier_call = msm8905_pon_reboot_notify,
|
||||
.priority = 128,
|
||||
.priority = 64,
|
||||
};
|
||||
|
||||
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);
|
||||
snprintf(label, sizeof(label), "%s Reboot", ICON_RLD);
|
||||
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);
|
||||
add_menu_item(label, "@menu:confirm_poweroff", 1);
|
||||
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");
|
||||
add_menu_item("Yes", "sxmo_power.sh reboot", 0);
|
||||
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) {
|
||||
reset_session_defaults(1, "Confirm Poweroff");
|
||||
add_menu_item("Yes", "sxmo_power.sh poweroff", 0);
|
||||
|
||||
Reference in New Issue
Block a user