diff --git a/app/src/main/java/com/dergoogler/mmrl/MainActivity.java b/app/src/main/java/com/dergoogler/mmrl/MainActivity.java index 794bef87..a59bb8f1 100644 --- a/app/src/main/java/com/dergoogler/mmrl/MainActivity.java +++ b/app/src/main/java/com/dergoogler/mmrl/MainActivity.java @@ -127,8 +127,8 @@ public void onGlobalLayout() { @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { switch (consoleMessage.messageLevel()) { - case TIP -> Log.i("MMRLWebViewClient", consoleMessage.message()); - case LOG -> Log.d("MMRLWebViewClient", consoleMessage.message()); + case LOG -> Log.i("MMRLWebViewClient", consoleMessage.message()); + case DEBUG -> Log.d("MMRLWebViewClient", consoleMessage.message()); case WARNING -> Log.w("MMRLWebViewClient", consoleMessage.message()); case ERROR -> Log.e("MMRLWebViewClient", consoleMessage.message()); default -> Log.v("MMRLWebViewClient", consoleMessage.message()); diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx index 20a1eab0..5ceeb4b1 100644 --- a/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx +++ b/src/activitys/InstallTerminalV2Activity/hooks/useExploreInstall.tsx @@ -8,28 +8,33 @@ import { useSettings } from "@Hooks/useSettings"; import { Terminal } from "@Native/Terminal"; import { BuildConfig } from "@Native/BuildConfig"; import { Shell } from "@Native/Shell"; -import { Add, Remove, CodeRounded, ArrowBackIosRounded, RestartAlt } from "@mui/icons-material"; +import { RestartAlt } from "@mui/icons-material"; +import { SuFile } from "@Native/SuFile"; -const useExploreInstall = (): [() => void, number] => { - const TMPDIR = "/data/local/tmp"; +type ExploreInstall = { + url: string; + printExit?: boolean; +}; +const useExploreInstall = (): [(options: ExploreInstall) => Promise, number] => { + const TMPDIR = "/data/local/tmp"; const { extra } = useActivity(); const [downloadProgress, setDownloadProgress] = React.useState(0); const [printTerminalError] = useSettings("print_terminal_error"); - const { addText, addButton, setLastLine, rebootDevice, getInstallCLI } = useLines(); + const { source, issues } = extra; - const { modSource, source, issues } = extra; - - return [ - () => { - const url = modSource[0]; - // const urls = modSource; - + const exploreInstaller = async (options: ExploreInstall): Promise => { + return new Promise((resolve, reject) => { + const url = options.url; + const printExit = options.printExit ?? true; const modPath = `${TMPDIR}/${uuidv1()}.zip`; + console.debug(modPath); + const dl = new Download(url, modPath); + // Handle download progress dl.onChange = (obj) => { switch (obj.type) { case "downloading": @@ -52,6 +57,7 @@ const useExploreInstall = (): [() => void, number] => { ROOTMANAGER: Shell.getRootManager(), }; + // Add terminal line output explore_install.onLine = (line) => { addText(line); }; @@ -63,67 +69,79 @@ const useExploreInstall = (): [() => void, number] => { } explore_install.onExit = (code) => { - switch (code) { - case Shell.M_INS_SUCCESS: - addText(" "); - addText( - "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m" - ); - addButton("Reboot", { - startIcon: , - onClick: rebootDevice, - }); - addText( - "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" - ); - if (issues) { - addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`); - } - if (source) { - addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`); - } - break; - case Shell.M_INS_FAILURE: - addText(" "); - addText( - "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" - ); - if (issues) { - addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`); - } - if (source) { - addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`); - } - break; - case Shell.TERM_INTR_ERR: - addText("! \x1b[31mInternal error!\x1b[0m"); - break; - default: - addText("? Unknown code returned"); - break; + SuFile.deleteRecursive(modPath); + + if (printExit) { + switch (code) { + case Shell.M_INS_SUCCESS: + addText(" "); + addText( + "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m" + ); + addButton("Reboot", { + startIcon: , + onClick: rebootDevice, + }); + addText( + "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m" + ); + if (issues) { + addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`); + } + if (source) { + addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`); + } + break; + case Shell.M_INS_FAILURE: + addText(" "); + addText( + "\x1b[2mModules that cause issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m" + ); + if (issues) { + addText(`> \x1b[32mIssues: \x1b[33m${issues}\x1b[0m`); + } + if (source) { + addText(`> \x1b[32mSource: \x1b[33m${source}\x1b[0m`); + } + break; + case Shell.TERM_INTR_ERR: + addText("! \x1b[31mInternal error!\x1b[0m"); + break; + default: + addText("? Unknown code returned"); + break; + } } + + resolve(); // Resolve the promise once the terminal exits }; - explore_install.exec( - getInstallCLI({ - ZIPFILE: modPath, - }) - ); + try { + // Execute the command but don't expect a return value + explore_install.exec(getInstallCLI({ ZIPFILE: modPath })); + } catch (err) { + addText(`! \x1b[31mExecution error: ${err}\x1b[0m`); + reject(err); // Reject the promise on execution error + } break; } }; + // Handle download errors dl.onError = (err) => { setDownloadProgress(0); addText("! \x1b[31mUnable to download the module\x1b[0m"); addText("! \x1b[31mERR: " + err + "\x1b[0m"); + reject(err); // Reject the promise on download error }; + // Start the download dl.start(); - }, - downloadProgress, - ]; + }); + }; + + return [exploreInstaller, downloadProgress]; }; export { useExploreInstall }; diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx index 6b5ed5f5..7b1e67d6 100644 --- a/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx +++ b/src/activitys/InstallTerminalV2Activity/hooks/useLines.tsx @@ -15,6 +15,7 @@ interface LinesContext { setLastLine: (text: string, props?: object) => void; rebootDevice: (reason?: string) => void; getInstallCLI: (adds?: Record) => string; + clearTerminal: () => void; } const LinesContext = React.createContext({ @@ -30,6 +31,7 @@ const LinesContext = React.createContext({ getInstallCLI(adds) { return "exit " + Shell.TERM_INTR_ERR; }, + clearTerminal() {}, }); type IntrCommand = (args: string[], options: Record, add: any) => void; @@ -212,6 +214,10 @@ const LinesProvider = (props: LinesProviderProps) => { } }, []); + const clearTerminal = () => { + setLines([]); + }; + const value = React.useMemo( () => ({ processCommand: processCommand, @@ -222,8 +228,9 @@ const LinesProvider = (props: LinesProviderProps) => { setLastLine: setLastLine, rebootDevice, getInstallCLI, + clearTerminal, }), - [processCommand, lines, setLines, useInt, setUseInt, addButton, addText, setLastLine, rebootDevice, getInstallCLI] + [processCommand, lines, setLines, useInt, setUseInt, addButton, addText, setLastLine, rebootDevice, getInstallCLI, clearTerminal] ); return ; diff --git a/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx b/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx index 517e2beb..ff0cf5ff 100644 --- a/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx +++ b/src/activitys/InstallTerminalV2Activity/hooks/useLocalInstall.tsx @@ -10,20 +10,108 @@ import { BuildConfig } from "@Native/BuildConfig"; import { Shell } from "@Native/Shell"; import { Add, Remove, CodeRounded, ArrowBackIosRounded, RestartAlt } from "@mui/icons-material"; -const useLocalInstall = (): [() => void] => { - const TMPDIR = "/data/local/tmp"; +// const useLocalInstall = (): [() => void] => { +// const TMPDIR = "/data/local/tmp"; + +// const { extra } = useActivity(); +// const [printTerminalError] = useSettings("print_terminal_error"); + +// const { addText, addButton, rebootDevice, getInstallCLI } = useLines(); + +// const { modSource } = extra; + +// return [ +// () => { +// const zipfile = modSource[0]; +// // const zipfiles = modSource; + +// const local_install = new Terminal({ +// cwd: TMPDIR, +// printError: printTerminalError, +// }); + +// local_install.env = { +// ASH_STANDALONE: "1", +// MMRL: "true", +// MMRL_INTR: "true", +// MMRL_VER: BuildConfig.VERSION_CODE.toString(), +// ROOTMANAGER: Shell.getRootManager(), +// }; + +// local_install.onLine = (line) => { +// addText(line); +// }; + +// if (printTerminalError) { +// local_install.onError = (err) => { +// addText(`\x1b[38;5;130mⓘ${err}`); +// }; +// } + +// local_install.onExit = (code) => { +// switch (code) { +// case Shell.M_INS_SUCCESS: +// addText(" "); + +// addText( +// "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m" +// ); + +// addButton("Reboot", { +// startIcon: , +// onClick: rebootDevice, +// }); + +// addText( +// "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" +// ); +// break; + +// case Shell.M_INS_FAILURE: +// addText(" "); + +// addText( +// "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" +// ); +// break; + +// case Shell.TERM_INTR_ERR: +// addText("! \x1b[31mInternal error!\x1b[0m"); +// break; + +// default: +// addText("- Unknown code returned"); +// break; +// } +// }; + +// local_install.exec( +// getInstallCLI({ +// ZIPFILE: zipfile, +// }) +// ); +// }, +// ]; +// }; + +// export { useLocalInstall }; + +type LocalInstall = { + file: string; + printExit?: boolean; +}; +const useLocalInstall = (): [(options: LocalInstall) => Promise] => { + const TMPDIR = "/data/local/tmp"; const { extra } = useActivity(); const [printTerminalError] = useSettings("print_terminal_error"); - const { addText, addButton, rebootDevice, getInstallCLI } = useLines(); - const { modSource } = extra; - return [ - () => { - const zipfile = modSource[0]; - // const zipfiles = modSource; + const localInstaller = async (options: LocalInstall): Promise => { + return new Promise((resolve, reject) => { + const zipfile = options.file; + const printExit = options.printExit ?? true; const local_install = new Terminal({ cwd: TMPDIR, @@ -38,60 +126,70 @@ const useLocalInstall = (): [() => void] => { ROOTMANAGER: Shell.getRootManager(), }; + // Handle terminal output lines local_install.onLine = (line) => { addText(line); }; + // Handle terminal errors if (printTerminalError) { local_install.onError = (err) => { addText(`\x1b[38;5;130mⓘ${err}`); }; } + // Handle terminal exit with a promise resolution local_install.onExit = (code) => { - switch (code) { - case Shell.M_INS_SUCCESS: - addText(" "); - - addText( - "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m" - ); - - addButton("Reboot", { - startIcon: , - onClick: rebootDevice, - }); - - addText( - "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" - ); - break; - - case Shell.M_INS_FAILURE: - addText(" "); - - addText( - "\x1b[2mModules that causes issues after installing belog not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to thier support page\x1b[2m" - ); - break; - - case Shell.TERM_INTR_ERR: - addText("! \x1b[31mInternal error!\x1b[0m"); - break; - - default: - addText("- Unknown code returned"); - break; + if (printExit) { + switch (code) { + case Shell.M_INS_SUCCESS: + addText(" "); + addText( + "\x1b[93mYou can press the \x1b[33;4mbutton\x1b[93;0m\x1b[93m below to \x1b[33;4mreboot\x1b[93;0m\x1b[93m your device\x1b[0m" + ); + addButton("Reboot", { + startIcon: , + onClick: rebootDevice, + }); + addText( + "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m" + ); + break; + + case Shell.M_INS_FAILURE: + addText(" "); + addText( + "\x1b[2mModules that causes issues after installing belong not to \x1b[35;4mMMRL\x1b[0;2m!\nPlease report these issues to their support page\x1b[2m" + ); + break; + + case Shell.TERM_INTR_ERR: + addText("! \x1b[31mInternal error!\x1b[0m"); + break; + + default: + addText("- Unknown code returned"); + break; + } } + resolve(); // Resolve on unknown code, as execution has finished }; - local_install.exec( - getInstallCLI({ - ZIPFILE: zipfile, - }) - ); - }, - ]; + // Execute the installation command + try { + local_install.exec( + getInstallCLI({ + ZIPFILE: zipfile, + }) + ); + } catch (err) { + addText(`! \x1b[31mExecution error: ${err}\x1b[0m`); + reject(err); // Reject on execution error + } + }); + }; + + return [localInstaller]; }; export { useLocalInstall }; diff --git a/src/activitys/InstallTerminalV2Activity/index.tsx b/src/activitys/InstallTerminalV2Activity/index.tsx index 676b2670..bbc4b3e3 100644 --- a/src/activitys/InstallTerminalV2Activity/index.tsx +++ b/src/activitys/InstallTerminalV2Activity/index.tsx @@ -14,7 +14,6 @@ import { useLocalInstall } from "./hooks/useLocalInstall"; export interface TerminalActivityExtra { exploreInstall: boolean; modSource: string[]; - id: string; source?: string; issues?: string; } @@ -53,7 +52,7 @@ const InstallerComponent = () => { const termEndRef = React.useRef(null); - const { lines } = useLines(); + const { clearTerminal, lines } = useLines(); // ensure that it is always the same function const nativeVolumeEventPrevent = React.useCallback((e: Event) => { @@ -87,13 +86,45 @@ const InstallerComponent = () => { const [exploreInstaller, downloadProgress] = useExploreInstall(); const [localInstaller] = useLocalInstall(); + const handleExploreInstall = React.useCallback(async () => { + const { modSource } = extra; + + for (let idx = 0; idx < modSource.length; idx++) { + const mod = modSource[idx]; + const isLastItem = idx === modSource.length - 1; + const shouldPrintExit = modSource.length === 1 || isLastItem; + + try { + await exploreInstaller({ url: mod, printExit: shouldPrintExit }); + } catch (error) { + console.error("An error occurred during installation:", error); + } + } + }, []); + + const handleLocalInstall = React.useCallback(async () => { + const { modSource } = extra; + + for (let idx = 0; idx < modSource.length; idx++) { + const mod = modSource[idx]; + const isLastItem = idx === modSource.length - 1; + const shouldPrintExit = modSource.length === 1 || isLastItem; + + try { + await localInstaller({ file: mod, printExit: shouldPrintExit }); + } catch (error) { + console.error("An error occurred during installation:", error); + } + } + }, []); + const install = () => { const { exploreInstall } = extra; if (exploreInstall) { - exploreInstaller(); + handleExploreInstall(); } else { - localInstaller(); + handleLocalInstall(); } }; diff --git a/src/activitys/MainActivity.tsx b/src/activitys/MainActivity.tsx index 1b8536d1..f7dcbb43 100644 --- a/src/activitys/MainActivity.tsx +++ b/src/activitys/MainActivity.tsx @@ -29,6 +29,7 @@ import pkg from "@Package"; import { LogcatActivity } from "./LogcatActivity"; import UnverifiedHostActivity from "./UnverifiedHostActivity"; import { LandingActivity } from "./LandingActivity"; +import { ModulesQueue } from "@Components/ModulesQueue"; const getLocation = () => { if (window.location !== window.parent.location) { @@ -310,39 +311,41 @@ const MainActivity = (): JSX.Element => { const matches = useMediaQuery("(max-width: 767px)"); return ( - { - const mmrlFolder = new SuFile(modFS("MMRLFOL")); + + { + const mmrlFolder = new SuFile(modFS("MMRLFOL")); - if (Shell.isSuAvailable() && !mmrlFolder.exist()) { - mmrlFolder.create(SuFile.NEW_FOLDERS); - } - }} - > - - - - - - popPage(options)} - routeConfig={routeConfig} - renderPage={renderPage} - onPostPush={() => onPostPush()} - onPostPop={() => onPostPop()} - /> - - - + if (Shell.isSuAvailable() && !mmrlFolder.exist()) { + mmrlFolder.create(SuFile.NEW_FOLDERS); + } + }} + > + + + + + + popPage(options)} + routeConfig={routeConfig} + renderPage={renderPage} + onPostPush={() => onPostPush()} + onPostPop={() => onPostPop()} + /> + + + + ); }; diff --git a/src/activitys/MainApplication.tsx b/src/activitys/MainApplication.tsx index 60c41407..c42bb99d 100644 --- a/src/activitys/MainApplication.tsx +++ b/src/activitys/MainApplication.tsx @@ -166,7 +166,7 @@ const MainApplication = () => { }} onClick={() => { const chooseModule = new Chooser("application/zip"); - + chooseModule.allowMultiChoose = true; chooseModule.onChose = (files) => { if (Chooser.isSuccess(files)) { context.pushPage({ diff --git a/src/activitys/ModuleViewActivity/index.tsx b/src/activitys/ModuleViewActivity/index.tsx index abe885f6..9d97b4e5 100644 --- a/src/activitys/ModuleViewActivity/index.tsx +++ b/src/activitys/ModuleViewActivity/index.tsx @@ -381,7 +381,7 @@ const ModuleViewActivity = () => { confirm({ title: strings("install_module", { name: name }), description: strings("install_module_dialog_desc", { name: {name} }), - confirmationText: "Yes", + confirmationText: strings("yes"), }).then(() => { context.pushPage({ component: InstallTerminalV2Activity, @@ -389,7 +389,6 @@ const ModuleViewActivity = () => { extra: { issues: support, source: track.source, - id: id, exploreInstall: true, modSource: [latestVersion.zipUrl], }, diff --git a/src/components/ModulesQueue.tsx b/src/components/ModulesQueue.tsx index 583215a7..b71a76db 100644 --- a/src/components/ModulesQueue.tsx +++ b/src/components/ModulesQueue.tsx @@ -5,6 +5,10 @@ import CloseIcon from "@mui/icons-material/Close"; import { useStrings } from "@Hooks/useStrings"; import { os } from "@Native/Os"; import { useFormatBytes } from "@Hooks/useFormatBytes"; +import { Shell } from "@Native/Shell"; +import { useConfirm } from "material-ui-confirm"; +import { ActivityContext } from "@Hooks/useActivity"; +import InstallTerminalV2Activity, { TerminalActivityExtra } from "@Activitys/InstallTerminalV2Activity"; interface ModulesQueueContext { addModule: (queue: Queue) => void; @@ -25,7 +29,9 @@ interface Queue { size?: number; } -interface ModulesQueueProps extends React.PropsWithChildren {} +interface ModulesQueueProps extends React.PropsWithChildren { + context: ActivityContext; +} const QueueItem = ({ module, onClick }: any) => { const [moduleFileSize, moduleFileSizeByteText] = useFormatBytes(module.size); @@ -45,6 +51,8 @@ export const ModulesQueue = (props: ModulesQueueProps) => { const [queue, setQueue] = React.useState([]); const [open, setOpen] = React.useState(false); const { strings } = useStrings(); + const confirm = useConfirm(); + const { context } = props; const addModule = (queue: Queue) => { setQueue((qu) => { @@ -76,6 +84,8 @@ export const ModulesQueue = (props: ModulesQueueProps) => { [addModule, removeModule, toggleDrawer, open] ); + const isQueueNotEmpty = React.useMemo(() => queue.length !== 0, [queue]); + return ( {props.children} @@ -83,7 +93,7 @@ export const ModulesQueue = (props: ModulesQueueProps) => { { )} + + diff --git a/src/index.tsx b/src/index.tsx index 7033a6e9..e44e3e1b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,10 @@ import React from "react"; import ons from "onsenui"; -import { Button, Card, CardContent, CssBaseline, Divider } from "@mui/material"; +import { Button, CssBaseline, Divider } from "@mui/material"; import { LightTheme } from "@Styles/light_theme"; import { ConfirmProvider } from "material-ui-confirm"; import { ThemeProvider, useTheme } from "@Hooks/useTheme"; -import { StringsProvider, useStrings } from "@Hooks/useStrings"; +import { StringsProvider } from "@Hooks/useStrings"; import { Preventer, render } from "react-render-tools"; import { MainActivity } from "@Activitys/MainActivity"; import { RepoProvider } from "@Hooks/useRepos"; @@ -23,7 +23,6 @@ import { Toolbar } from "@Components/onsenui/Toolbar"; import { Pre } from "@Components/dapi/Pre"; import { Code } from "@Components/dapi/Code"; import { Anchor } from "@Components/dapi/Anchor"; -import { ModulesQueue } from "@Components/ModulesQueue"; ons.platform.select("android"); @@ -104,9 +103,7 @@ ons.ready(() => { - - - + diff --git a/src/locales/en.ts b/src/locales/en.ts index 198ecb1b..2f1641aa 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -167,4 +167,6 @@ export const en = { install_queue_empty: "Your queue is empty. Add some modules!", alr_add_queue: "This module has been already added to your queue", add_t_queue: "Module has been added to your queue", + start_mod_ini_queue: "Start install queue?", + start_mod_ini_queue_desc: "Are you sure that you want to start this queue?", }; diff --git a/src/native/Terminal.ts b/src/native/Terminal.ts index 5d8335f8..0df6ecfc 100644 --- a/src/native/Terminal.ts +++ b/src/native/Terminal.ts @@ -81,8 +81,6 @@ class Terminal extends Native { this._onLine(stream.stdout); } else if (stream.stdout && this._onError) { this._onError(stream.stdout); - } else { - Log.e("Terminal:exec", "Unable to find proper terminal stream"); } }, onExit: this._onExit,