Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c016a1d authored by Paula's avatar Paula
Browse files

add comments on functions, remove unused one, set DOM listener in a separate file

parent f054f585
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -10,8 +10,7 @@
    <link rel="stylesheet" href="/css/styles.css" />
    <link rel="stylesheet" href="/css/loader.css" />
    <title>/e/OS Installer</title>
    <script type="module" src="/src/vue/view.manager.js"></script>
    <script type="module" src="/src/before-leave-app.js"></script>
    <script type="module" src="/src/vue/listener.manager.js"></script>
  </head>
  <body id="body">
    <div id="process-ctn">

app/src/before-leave-app.js

deleted100644 → 0
+0 −3
Original line number Diff line number Diff line
window.addEventListener("beforeunload", function (event) {
  event.preventDefault();
});
+132 −37
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ import { DeviceManager } from "./device.manager.js";
import { COMMAND } from "./enums/command.enum.js";
import { Step } from "./utils/step.class.js";
import { DebugManager } from "./debug.manager.js";

/*
 * Class to manage process
 * Check and display the steps, interact with deviceManager
@@ -21,16 +22,33 @@ export class ControllerManager {
    this.downloadChoiceEnabled = false;
  }

  /**
   * Initializes controller dependencies and binds the view.
   *
   * @param {object} view UI view manager that receives state updates.
   * @returns {Promise<void>} Resolves when dependencies are initialized.
   */
  async init(view) {
    this.deviceManager = new DeviceManager();
    await this.deviceManager.init();
    this.view = view;
  }

  /**
   * Enables or disables manual download choice.
   *
   * @param {boolean} enabled Whether user-driven download selection is enabled.
   * @returns {void}
   */
  setDownloadChoiceEnabled(enabled) {
    this.downloadChoiceEnabled = enabled;
  }

  /**
   * Advances to the next installer step, handling mode changes and connections.
   *
   * @returns {Promise<void>} Resolves when the next step is started.
   */
  async next() {
    let current = this.steps[this.currentIndex];
    let next = this.steps[this.currentIndex + 1];
@@ -82,6 +100,13 @@ export class ControllerManager {
    }
  }

  /**
   * Executes commands for the current step.
   *
   * @param {string} stepName Step identifier expected to be active.
   * @param {HTMLElement} [loader] Loader element hidden after successful connect.
   * @returns {Promise<void>} Resolves when the step has been processed.
   */
  async executeStep(stepName, loader) {
    const current = this.steps[this.currentIndex];
    let this_command;
@@ -126,19 +151,21 @@ export class ControllerManager {
  }

  /**
   * Checks whether the connected device already matches a mode.
   *
   * @param mode
   * @returns {boolean}
   * Check if device is connected to a mode
   * @param {string} mode Target device mode.
   * @returns {boolean} True when device is already in the requested mode.
   */
  inInMode(mode) {
    return this.deviceManager.isInMode(mode);
  }

  /*
    * run a command
    this throw new error if something went wwrong.
    error should contain a proposal to solve the issue.
  /**
   * Runs a parsed command by dispatching to the appropriate handler.
   *
   * @param {import("./device/command.class.js").Command} cmd Command metadata.
   * @param {HTMLElement} [loader] Optional loading element used by connect step.
   * @returns {Promise<boolean>} True when the command succeeds.
   */
  async runCommand(cmd, loader) {
    DebugManager.log("ControllerManager run command:", cmd);
@@ -173,6 +200,11 @@ export class ControllerManager {
    }
  }

  /**
   * Runs the download step, triggering file download progress callbacks.
   *
   * @returns {Promise<boolean>} True when all files are downloaded successfully.
   */
  async runDownloadCommand() {
    try {
      await this.deviceManager.downloadAll(
@@ -196,6 +228,12 @@ export class ControllerManager {
    }
  }

  /**
   * Reboots the device to the mode specified in the command.
   *
   * @param {import("./device/command.class.js").Command} cmd Reboot command.
   * @returns {Promise<boolean>} True when reboot command succeeds.
   */
  async runRebootCommand(cmd) {
    try {
      await this.deviceManager.reboot(cmd.mode);
@@ -205,6 +243,13 @@ export class ControllerManager {
    }
  }

  /**
   * Connects to the device mode requested by the command.
   *
   * @param {import("./device/command.class.js").Command} cmd Connect command.
   * @param {HTMLElement} [loader] Optional loading element hidden after connect.
   * @returns {Promise<boolean>} True when connection succeeds.
   */
  async runConnectCommand(cmd, loader) {
    const proposal =
      "Proposal: Check connection and that no other program is using the phone and retry.";
@@ -222,6 +267,12 @@ export class ControllerManager {
    }
  }

  /**
   * Flashes one file to one partition and applies a post-flash cooldown.
   *
   * @param {import("./device/command.class.js").Command} cmd Flash command.
   * @returns {Promise<boolean>} True when flashing succeeds.
   */
  async runFlashCommand(cmd) {
    const FLASH_COOLDOWN_MS = this.resources?.flash_cooldown_ms ?? 2500;
    const result = await this.deviceManager.flash(
@@ -238,6 +289,12 @@ export class ControllerManager {
    return result;
  }

  /**
   * Unlocks the bootloader if needed and can skip ahead for goto steps.
   *
   * @param {import("./device/command.class.js").Command} cmd Unlock command.
   * @returns {Promise<boolean>} True when unlock flow completes.
   */
  async runUnlockCommand(cmd) {
    let isUnlocked = false;
    let gotoStep = "";
@@ -287,6 +344,12 @@ export class ControllerManager {
    return true;
  }

  /**
   * Locks the bootloader if currently unlocked.
   *
   * @param {import("./device/command.class.js").Command} cmd Lock command.
   * @returns {Promise<boolean>} True when lock flow completes.
   */
  async runLockCommand(cmd) {
    let isLocked = false;
    if (cmd.partition) {
@@ -308,6 +371,12 @@ export class ControllerManager {
    return true;
  }

  /**
   * Connects to recovery and performs sideload.
   *
   * @param {import("./device/command.class.js").Command} cmd Sideload command.
   * @returns {Promise<boolean>} True when sideload succeeds.
   */
  async runSideloadCommand(cmd) {
    try {
      await this.deviceManager.connect("recovery");
@@ -318,6 +387,12 @@ export class ControllerManager {
    }
  }

  /**
   * Formats a partition requested by the command.
   *
   * @param {import("./device/command.class.js").Command} cmd Format command.
   * @returns {Promise<boolean>} True when format command succeeds.
   */
  async runFormatCommand(cmd) {
    try {
      return this.deviceManager.format(cmd.partition);
@@ -326,17 +401,34 @@ export class ControllerManager {
    }
  }

  /**
   * Waits for a command-defined delay.
   *
   * @param {import("./device/command.class.js").Command} cmd Delay command.
   * @returns {Promise<boolean>} True after delay completes.
   */
  async runDelayCommand(cmd) {
    await new Promise((resolve) => setTimeout(resolve, cmd.partition));
    return true;
  }

  /**
   * Forwards unknown commands to the active device implementation.
   *
   * @param {import("./device/command.class.js").Command} cmd Unknown command.
   * @returns {Promise<boolean>} True when forwarded command finishes.
   */
  async runUnknownCommand(cmd) {
    DebugManager.log(`try unknown command ${cmd.command}`);
    await this.deviceManager.runCommand(cmd.command);
    return true;
  }

  /**
   * Handles device connection: updates UI, loads resources and validates version.
   *
   * @returns {Promise<void>} Resolves when device data is loaded.
   */
  async onDeviceConnected() {
    const productName = this.deviceManager.getProductName();
    if (this.deviceManager.isFirstConnection()) {
@@ -359,6 +451,13 @@ export class ControllerManager {
      }
    }
  }

  /**
   * Validates the connected Android version against required version.
   *
   * @param {string|number} versionRequired Minimum required Android version.
   * @returns {Promise<void>} Resolves when version is valid.
   */
  async checkAndroidVersion(versionRequired) {
    const android = await this.deviceManager.getAndroidVersion();
    DebugManager.log("current android version:", android);
@@ -369,6 +468,12 @@ export class ControllerManager {
      }
    }
  }

  /**
   * Fetches the device resource JSON based on the connected device model.
   *
   * @returns {Promise<object>} Resource object or throws device-model-not-supported.
   */
  async getResources() {
    let resources = null;
    try {
@@ -417,40 +522,13 @@ export class ControllerManager {
            throw new Error("Cannot find device resource", id);
          }
        } catch {
          const id =
            "model " +
            this.deviceManager.adb.banner.model +
            " " +
            "product " +
            this.deviceManager.adb.banner.product +
            " " +
            "name " +
            this.deviceManager.adb.getProductName() +
            " " +
            "device " +
            this.deviceManager.adb.banner.device;
          const id = `model ${this.deviceManager.adb.banner.model} product ${this.deviceManager.adb.banner.product} name ${this.deviceManager.adb.getProductName()} device ${this.deviceManager.adb.banner.device}`;
          throw new Error("Error on getting device resource", id);
        }
      }

      if (model.includes("A015")) {
        try {
        this_model = "tetris";
        } catch {
          const id =
            "model " +
            this.deviceManager.adb.banner.model +
            " " +
            "product " +
            this.deviceManager.adb.banner.product +
            " " +
            "name " +
            this.deviceManager.adb.getProductName() +
            " " +
            "device " +
            this.deviceManager.adb.banner.device;
          throw new Error("Error on getting devcice resource", id);
        }
      }

      resources = await (await fetch(`resources/${this_model}.json`)).json();
@@ -485,6 +563,12 @@ export class ControllerManager {
    return resources;
  }

  /**
   * Applies device resources and extends installer steps.
   *
   * @param {object} resources Device resource payload from JSON file.
   * @returns {void}
   */
  setResources(resources) {
    this.resources = resources;
    if (this.resources.steps) {
@@ -507,10 +591,21 @@ export class ControllerManager {
    });
  }

  /**
   * Sets a local zip selected by the user.
   *
   * @param {File} file Local zip file.
   * @returns {void}
   */
  setLocalZip(file) {
    this.deviceManager.setLocalZipFile(file);
  }

  /**
   * Clears the previously set local zip file.
   *
   * @returns {void}
   */
  clearLocalZip() {
    this.deviceManager.clearLocalZipFile();
  }
+14 −0
Original line number Diff line number Diff line
/**
 * Centralized debug logger used by controller components.
 */
export class DebugManager {
  constructor() {}

  /**
   * Writes a debug message to the console.
   *
   * @param {...any} args Values to log.
   */
  static log(...args) {
    console.log("[DEBUG]", ...args);
  }

  /**
   * Writes an error message to the console.
   *
   * @param {...any} args Values to log as an error.
   */
  static error(...args) {
    console.error("[ERROR]", ...args);
  }
+145 −9
Original line number Diff line number Diff line
@@ -7,9 +7,12 @@ import { DebugManager } from "./debug.manager.js";
import { MODE } from "./enums/mode.enum.js";

/**
 * wrap device functions
 * */
 * Facade over ADB, bootloader, recovery and download operations.
 */
export class DeviceManager {
  /**
   * Creates a new DeviceManager and initializes all sub-managers.
   */
  constructor() {
    this.model = "";
    this.rom = undefined;
@@ -24,18 +27,39 @@ export class DeviceManager {
    this.wasConnected = false;
  }

  /**
   * Sets a local ROM zip file to be used instead of remote download.
   *
   * @param {File} file Local zip file selected by the user.
   * @returns {void}
   */
  setLocalZipFile(file) {
    this.downloader.setLocalZip(file);
  }

  /**
   * Clears the previously set local zip file.
   *
   * @returns {void}
   */
  clearLocalZipFile() {
    this.downloader.clearLocalZip();
  }

  /**
   * Returns whether a local zip file has been set.
   *
   * @returns {boolean} True when a local zip file is available.
   */
  hasLocalZipFile() {
    return this.downloader.hasLocalZip();
  }

  /**
   * Initializes all device sub-managers.
   *
   * @returns {Promise<void>} Resolves when all managers are ready.
   */
  async init() {
    await this.bootloader.init();
    await this.adb.init();
@@ -43,14 +67,32 @@ export class DeviceManager {
    await this.downloader.init();
  }

  /**
   * Returns whether a device has not yet been connected in this session.
   *
   * @returns {boolean} True when no connection has occurred yet.
   */
  isFirstConnection() {
    return !this.wasConnected;
  }

  /**
   * Marks the device as connected for the current session.
   *
   * @returns {void}
   */
  markAsConnected() {
    this.wasConnected = true;
  }

  /**
   * Stores resource metadata and optional behavior flags.
   *
   * @param {Array<object>} folder Resource file descriptors.
   * @param {Array<object>} steps Installer steps used to collect required files.
   * @param {object} [options] Optional controller flags.
   * @returns {void}
   */
  setResources(folder, steps, options) {
    this.folder = folder;
    this.files = steps
@@ -65,12 +107,30 @@ export class DeviceManager {
    }
  }

  /**
   * Reads bootloader lock state variable.
   *
   * @param {string} variable Fastboot variable name to query.
   * @returns {Promise<boolean>} True when variable indicates unlocked state.
   */
  async getUnlocked(variable) {
    return this.bootloader.isUnlocked(variable);
  }

  /**
   * Returns the Android version of the connected device.
   *
   * @returns {Promise<string>} Android version string.
   */
  async getAndroidVersion() {
    return await this.device.getAndroidVersion();
  }

  /**
   * Checks whether the device is still detected on the USB bus.
   *
   * @returns {Promise<boolean>} True when device appears in USB device list.
   */
  async isDetected() {
    const serial = this.serialNumber;
    if (serial) {
@@ -81,12 +141,10 @@ export class DeviceManager {
  }

  /**
   * @param mode
   * @returns {any}
   *
   * We set the device to the mode manager we went to connect to
   * And we connect the device
   * Sets the active device implementation for a given mode.
   *
   * @param {string} mode Device mode (`adb`, `bootloader`, or `recovery`).
   * @returns {Promise<void>} Resolves after active mode is set.
   */
  async setMode(mode) {
    switch (mode) {
@@ -101,6 +159,13 @@ export class DeviceManager {
        break;
    }
  }

  /**
   * Connects to the requested mode.
   *
   * @param {string} mode Target mode to connect.
   * @returns {Promise<any>} Result returned by the selected device connector.
   */
  async connect(mode) {
    await this.setMode(mode);
    try {
@@ -111,9 +176,10 @@ export class DeviceManager {
  }

  /**
   * @param mode
   * @returns {boolean}
   * Checks whether the active device is currently in a given mode.
   *
   * @param {string} mode Target mode.
   * @returns {boolean} True when active device matches mode.
   */
  isInMode(mode) {
    switch (mode) {
@@ -127,17 +193,34 @@ export class DeviceManager {
    return false;
  }

  /**
   * Erases a fastboot partition.
   *
   * @param {string} partition Partition name.
   * @returns {Promise<boolean>} True when erase command succeeds.
   */
  async erase(partition) {
    await this.bootloader.runCommand(`erase:${partition}`);
    return true;
  }

  /**
   * Formats a partition via fastboot (not universally supported).
   *
   * @returns {boolean} Always true; actual format is not implemented.
   */
  format() {
    return true;
    //        return this.bootloader.runCommand(`format ${argument}`);
    //        the fastboot format md_udc is not supported evne by the official fastboot program
  }

  /**
   * Sends a bootloader unlock command.
   *
   * @param {string} command Fastboot unlock command.
   * @returns {Promise<boolean>} True when unlock command completes.
   */
  async unlock(command) {
    // Unlock requires physical confirmation on the device (volume keys +
    // power button), so use a generous 5-minute timeout instead of the
@@ -146,12 +229,26 @@ export class DeviceManager {
    return true;
  }

  /**
   * Sends a bootloader lock command.
   *
   * @param {string} command Fastboot lock command.
   * @returns {Promise<boolean>} True when lock command completes.
   */
  async lock(command) {
    // Lock may also require physical confirmation on the device.
    await this.bootloader.runCommand(command, 300_000);
    return true;
  }

  /**
   * Flashes a downloaded file to a partition.
   *
   * @param {string} file Filename in downloader cache.
   * @param {string} partition Target partition.
   * @param {(done:number,total:number)=>void} onProgress Flash progress callback.
   * @returns {Promise<boolean>} True when flashing succeeds.
   */
  async flash(file, partition, onProgress) {
    let blob = await this.downloader.getFile(file);
    if (!blob) {
@@ -166,14 +263,30 @@ export class DeviceManager {
    return flashed;
  }

  /**
   * Returns the product name of the connected device.
   *
   * @returns {string|undefined} Product name string or undefined.
   */
  getProductName() {
    return this.device.getProductName();
  }

  /**
   * Returns the serial number of the connected device.
   *
   * @returns {string|undefined} Serial number string or undefined.
   */
  getSerialNumber() {
    return this.device.getSerialNumber();
  }

  /**
   * Reboots the connected device to a target mode.
   *
   * @param {string} mode Target reboot mode.
   * @returns {Promise<any>} Reboot result from the active device.
   */
  async reboot(mode) {
    const res = await this.device.reboot(mode);
    if (res) {
@@ -182,6 +295,12 @@ export class DeviceManager {
    return res;
  }

  /**
   * Sideloads a zip file through recovery.
   *
   * @param {string} file Filename in downloader cache.
   * @returns {Promise<any>} Sideload result from the active device.
   */
  async sideload(file) {
    let blob = await this.downloader.getFile(file);
    if (!blob) {
@@ -191,6 +310,12 @@ export class DeviceManager {
    return await this.device.sideload(blob);
  }

  /**
   * Runs a raw command against the active device implementation.
   *
   * @param {string} command Device command.
   * @returns {Promise<any>} Command result from active device.
   */
  async runCommand(command) {
    try {
      return this.device.runCommand(command);
@@ -204,6 +329,9 @@ export class DeviceManager {
   * Some USB host controllers (e.g., AMD Ryzen) are slow to re-enumerate
   * devices after a mode switch, especially Mediatek bootloader devices.
   * Resolves when a device appears or after timeout (does not reject).
   *
   * @param {number} [timeoutMs=30000] Max wait before resolving.
   * @returns {Promise<void>} Resolves when device appears or timeout expires.
   */
  waitForDeviceOnBus(timeoutMs = 30000) {
    const startTime = Date.now();
@@ -257,6 +385,14 @@ export class DeviceManager {
    });
  }

  /**
   * Downloads all required files or ingests them from a local zip.
   *
   * @param {(loaded:number,total:number,name:string)=>void} onProgress Download callback.
   * @param {(loaded:number,total:number,name:string)=>void} onUnzip Unzip callback.
   * @param {(loaded:number,total:number,name:string)=>void} onVerify Verification callback.
   * @returns {Promise<void>} Resolves when all required files are available.
   */
  async downloadAll(onProgress, onUnzip, onVerify) {
    try {
      if (this.downloader.hasLocalZip()) {
Loading