diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml index 67c74cf..7832dc5 100644 --- a/.github/workflows/updater.yml +++ b/.github/workflows/updater.yml @@ -4,28 +4,24 @@ on: workflow_dispatch: inputs: user_input_version: - description: 'Version /tag to target' + description: "Version /tag to target" release: - types: [created] + types: [released, published] jobs: generate_updater_json: runs-on: ubuntu-latest steps: - - run: | - # https://dev.to/mrmike/github-action-handling-input-default-value-5f2g - USER_INPUT_VERSION=${{ github.event.inputs.user_input_version }} - echo "user_input_version=${USER_INPUT_VERSION:-"latest"}" >> "$GITHUB_OUTPUT" - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - cache: 'npm' - cache-dependency-path: 'package-lock.json' + cache: "npm" + cache-dependency-path: "package-lock.json" - env: GH_TOKEN: ${{ github.token }} - RELEASE_EVENT: ${{github.event.release}} - RELEASE_TAG: ${{github.event.inputs.user_input_version}} + RELEASE_EVENT: ${{ github.event.release }} + RELEASE_TAG: ${{ github.event.inputs.user_input_version }} run: | cd ./scripts npm ci diff --git a/README.md b/README.md index 36b31e3..8eaee44 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This software is in beta 🚧 -Simple Android Debloater is a free and open source project to disable unwanted system apps that careers / OEMs force install in our mobile phones. +Simple Android Debloater is a free and open source project to disable unwanted system apps that carriers / OEMs force install in our mobile phones. This is an attempt like [Universal Android Debloater](https://github.com/0x192/universal-android-debloater/) built with [Tauri](https://tauri.app/) and [Sveltekit](https://kit.svelte.dev/). @@ -116,4 +116,4 @@ Run rust and node at once: - [x] Import CSV and Bulk Enable / Disable -- [ ] SDK Compatability checks \ No newline at end of file +- [ ] SDK Compatability checks diff --git a/scripts/updater_template.js b/scripts/updater_template.js index edef041..d8ea97f 100644 --- a/scripts/updater_template.js +++ b/scripts/updater_template.js @@ -1,30 +1,7 @@ import { Octokit } from '@octokit/core'; import fs from 'fs'; import fetch from 'node-fetch'; - -let template_json = { - version: '0.5.0', - notes: 'Test version', - pub_date: '2023-06-22T19:25:57Z', - platforms: { - // 'darwin-x86_64': { - // signature: 'Content of app.tar.gz.sig', - // url: 'https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz' - // }, - // 'darwin-aarch64': { - // signature: 'Content of app.tar.gz.sig', - // url: 'https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz' - // }, - // 'linux-x86_64': { - // signature: 'Content of app.AppImage.tar.gz.sig', - // url: 'https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz' - // }, - // 'windows-x86_64': { - // signature: 'Content of app-setup.nsis.sig or app.msi.sig, depending on the chosen format', - // url: 'https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip' - // } - } -}; +import { release } from 'os'; const octokit = new Octokit({ auth: process.env.GH_TOKEN @@ -52,11 +29,28 @@ async function getReleaseForTag(tag) { return data; } -async function getSignatureAssetContent(sig_url) { - const response = await fetch(sig_url); - let sig_content = await response.text(); +async function downloadLatestJSON(release) { + let latestJsonURL = ''; + + for (let a of release.assets) { + if (a.name == 'latest.json') { + latestJsonURL = a.browser_download_url; + } + } + + if (!latestJsonURL) { + throw new Error('latest.json is not present'); + } + + const response = await fetch(latestJsonURL); + let json = await response.json(); + + if (!json) { + console.log(response); + throw new Error('Error in response json'); + } - return sig_content; + return json; } async function getReleaseFromEventOrTag() { @@ -107,52 +101,10 @@ async function getReleaseFromEventOrTag() { }; } - let version = release.tag; - if (version.charAt(0) === 'v') { - version = version.substring(1); // remove first letter - } - - let asset_url_map = release.assets.reduce((map, a) => { - map[a.name] = a.browser_download_url; - return map; - }, {}); - - let extensions = { - linux: 'amd64.AppImage.tar.gz', - darwin: '.app.tar.gz', - windows: 'x64-setup.nsis.zip' - }; - - let platform_sig_template = {}; - - for (let [platform, ext] of Object.entries(extensions)) { - for (let [name, url] of Object.entries(asset_url_map)) { - if (name.endsWith(ext)) { - let sig_url = asset_url_map[`${name}.sig`]; - let sig_content = await getSignatureAssetContent(sig_url); - - platform_sig_template[`${platform}-x86_64`] = { - signature: sig_content, - url: url - }; - - if (platform == 'darwin') { - // apple silicon chips - platform_sig_template[`${platform}-aarch64`] = { - signature: sig_content, - url: url - }; - } - } - } - } - - template_json.version = version; - template_json.notes = release.body; - template_json.pub_date = release.published_at; - template_json.platforms = platform_sig_template; + let content = await downloadLatestJSON(release); + content.notes = release.body; - fs.writeFile('sad_updater.json', JSON.stringify(template_json), 'utf8', function (err) { + fs.writeFile('sad_updater.json', JSON.stringify(content), 'utf8', function (err) { if (err) throw err; console.log('Dumped sad_updater.json'); }); diff --git a/src-tauri/src/adb_cmd.rs b/src-tauri/src/adb_cmd.rs index 2d813ab..99e3c22 100644 --- a/src-tauri/src/adb_cmd.rs +++ b/src-tauri/src/adb_cmd.rs @@ -5,8 +5,33 @@ use std::os::windows::process::CommandExt; use log::info; -pub trait ADBCommand { +pub trait ADBCommand: Sized { fn execute(&self) -> Result; + fn arg>(self, arg: S) -> Self; + fn args(self, args: I) -> Self + where + I: IntoIterator, + S: AsRef, + { + let mut s1 = self; + for arg in args { + s1 = s1.arg(arg); + } + s1 + } + + fn arg_prepend>(self, arg: S) -> Self; + fn args_prepend(self, args: I) -> Self + where + I: IntoIterator, + S: AsRef, + { + let mut s1 = self; + for arg in args { + s1 = s1.arg_prepend(arg); + } + s1 + } } #[derive(Debug, thiserror::Error)] @@ -15,65 +40,50 @@ pub enum ADBError { Unknown(String), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct ADBRaw { - adb_path: String, - sub_commands: Vec, + cmd_str: String, + argsv: Vec, } impl ADBRaw { - pub fn new(adb_path: String, value: Vec) -> Self { + pub fn new(adb_path: String) -> Self { + let mut cmd_str = "adb"; + if !adb_path.is_empty() { + cmd_str = adb_path.as_str(); + } Self { - adb_path: adb_path, - sub_commands: value, + cmd_str: cmd_str.to_string(), + argsv: vec![], } } } -#[derive(Debug, Clone)] -pub struct ADBShell { - adb_path: String, - sub_commands: Vec, - device_id: String, -} - -impl ADBShell { - pub fn new(adb_path: String) -> Self { - Self { - adb_path: adb_path, - sub_commands: vec![], - device_id: String::from(""), - } - } +impl ADBCommand for ADBRaw { + fn arg>(self, arg: S) -> Self { + // https://users.rust-lang.org/t/best-way-to-clone-and-append-a-single-element/68675/2 - pub fn for_device(mut self, device_id: String) -> Self { - self.device_id = device_id; - return self; + let mut s1 = self; + s1.argsv.push(arg.as_ref().to_owned()); + return s1; } - pub fn with_commands(mut self, sub_commands: &[&str]) -> Self { - self.sub_commands = sub_commands.iter().map(|s| String::from(*s)).collect(); - return self; + fn arg_prepend>(self, arg: S) -> Self { + let mut s1 = self; + s1.argsv.insert(0, arg.as_ref().to_owned()); + return s1; } -} -impl ADBCommand for ADBRaw { fn execute(&self) -> Result { + let mut command = Command::new(self.cmd_str.to_owned()); + command.args(self.argsv.to_vec()); - let mut cmd_str = "adb"; - if !self.adb_path.is_empty() { - cmd_str = self.adb_path.as_str(); - } - - let mut command = Command::new(cmd_str); - - // https://stackoverflow.com/a/38186733/6323666 - let args = self - .sub_commands - .iter() - .map(|s| s.as_str()) - .collect::>(); - command.args(args); + // let args = self + // .sub_commands + // .iter() + // .map(|s| s.as_str()) + // .collect::>(); + // command.args(args); info!("command {:?}", command); @@ -107,17 +117,39 @@ impl ADBCommand for ADBRaw { } } +#[derive(Debug, Clone)] +pub struct ADBShell { + adb_raw: ADBRaw, +} + +impl ADBShell { + pub fn new(adb_path: String) -> Self { + let adbr = ADBRaw::new(adb_path).arg("shell"); + Self { adb_raw: adbr } + } +} + impl ADBCommand for ADBShell { - fn execute(&self) -> Result { - let mut sub_commands_with_shell: Vec = vec![String::from("shell")]; + fn arg>(self, arg: S) -> Self { + let mut s1 = self; + s1.adb_raw = s1.adb_raw.arg(arg.as_ref()); + return s1; + } - if !String::is_empty(&self.device_id.to_owned()) { - sub_commands_with_shell.insert(0, String::from("-s")); - sub_commands_with_shell.insert(1, self.device_id.to_owned()); - } + fn arg_prepend>(self, arg: S) -> Self { + let mut s1 = self; + s1.adb_raw = s1.adb_raw.arg_prepend(arg.as_ref()); + return s1; + } - sub_commands_with_shell.extend(self.sub_commands.to_owned()); - let adb_raw = ADBRaw::new(self.adb_path.to_owned(), sub_commands_with_shell); - return adb_raw.execute(); + fn execute(&self) -> Result { + return self.adb_raw.execute(); } } + +pub fn for_device<'a, T: ADBCommand + Clone>(abdc: &'a T, device_id: String) -> T { + // ideally its -s but we send in reverse so prepend works properly + return abdc + .clone() + .args_prepend(vec!["-s", &device_id].into_iter().rev()); +} diff --git a/src-tauri/src/devices.rs b/src-tauri/src/devices.rs index 8949e13..99726cf 100644 --- a/src-tauri/src/devices.rs +++ b/src-tauri/src/devices.rs @@ -1,4 +1,4 @@ -use crate::adb_cmd::{ADBCommand, ADBRaw, ADBShell}; +use crate::adb_cmd::{self, ADBCommand, ADBRaw, ADBShell}; use anyhow::{anyhow, Error, Result}; use core::result::Result::Ok; use serde::{Deserialize, Serialize}; @@ -61,7 +61,9 @@ pub struct ADBTerminalImpl { impl ADBTerminalImpl { pub fn list_devices(&self) -> Result> { - let res = ADBRaw::new(self.adb_path.to_owned(), vec![String::from("devices")]).execute(); + let res = ADBRaw::new(self.adb_path.to_owned()) + .arg("devices") + .execute(); match res { Err(e) => { return Err(e.into()); @@ -101,8 +103,9 @@ impl ADBTerminalImpl { return Err(e); } Ok(d) => { - let shell_cmd: ADBShell = ADBShell::new(self.adb_path.to_owned()).for_device(d.id.to_owned()); - let res = shell_cmd.with_commands(&["getprop"]).execute(); + let shell_cmd: ADBShell = + adb_cmd::for_device(&ADBShell::new(self.adb_path.to_owned()), d.id.to_owned()); + let res = shell_cmd.arg("getprop").execute(); match res { Err(e) => { return Err(e.into()); diff --git a/src-tauri/src/packages.rs b/src-tauri/src/packages.rs index 24361be..7dc19a2 100644 --- a/src-tauri/src/packages.rs +++ b/src-tauri/src/packages.rs @@ -1,10 +1,10 @@ -use crate::adb_cmd::{ADBCommand, ADBShell}; +use crate::adb_cmd::{self, ADBCommand, ADBShell}; use anyhow::{anyhow, Error, Result}; use lazy_static::lazy_static; +use regex::Regex; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::{fmt::Display, str::FromStr}; -use regex::Regex; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum PackageState { @@ -110,14 +110,17 @@ const LIST_ENABLED_PACKAGES: &str = "pm list packages -e"; const LIST_DISABLED_PACKAGES: &str = "pm list packages -d"; const LIST_UNINSTALLED_DISABLED_PACKAGES: &str = "pm list packages -u -d"; -pub struct ADBTerminalImpl { - adb_path: String, +pub struct ADBTerminalImpl +where + F: ADBCommand + ?Sized, // TODO: Factory with closures? +{ + adb_command: F, } -impl ADBTerminalImpl { +impl ADBTerminalImpl { pub fn new(adb_path: String) -> Self { Self { - adb_path: String::from(adb_path), + adb_command: ADBShell::new(adb_path), } } } @@ -137,9 +140,9 @@ lazy_static! { static ref PACKAGE_PARSE_REGEX: Regex = Regex::new(r"(.*)\.apk\=(.*)").unwrap(); } -impl ADBTerminalImpl { +impl ADBTerminalImpl { pub fn list_packages(&self, device_id: String, user_id: String) -> Result> { - let shell_cmd = ADBShell::new(self.adb_path.to_owned()).for_device(device_id.to_owned()); + let shell_cmd = adb_cmd::for_device(&self.adb_command.clone(), device_id.to_owned()); let ( mut all_pkg, @@ -165,7 +168,6 @@ impl ADBTerminalImpl { ); let ( - cmd_all_pkg, cmd_enabled_pkg, cmd_disabled_pkg, cmd_system_pkg, @@ -174,45 +176,21 @@ impl ADBTerminalImpl { ) = ( shell_cmd .clone() - .with_commands(&[LIST_ALL_PACKAGES_INCLUDING_UNINSTALLED]), + .args(&[LIST_ENABLED_PACKAGES, "--user", &user_id]), shell_cmd .clone() - .with_commands(&[LIST_ENABLED_PACKAGES, "--user", &user_id]), + .args(&[LIST_DISABLED_PACKAGES, "--user", &user_id]), shell_cmd .clone() - .with_commands(&[LIST_DISABLED_PACKAGES, "--user", &user_id]), + .args(&[LIST_SYSTEM_PACKAGES, "--user", &user_id]), shell_cmd .clone() - .with_commands(&[LIST_SYSTEM_PACKAGES, "--user", &user_id]), + .args(&[LIST_THIRD_PARTY_PACKAGES, "--user", &user_id]), shell_cmd .clone() - .with_commands(&[LIST_THIRD_PARTY_PACKAGES, "--user", &user_id]), - shell_cmd.clone().with_commands(&[ - LIST_UNINSTALLED_DISABLED_PACKAGES, - "--user", - &user_id, - ]), + .args(&[LIST_UNINSTALLED_DISABLED_PACKAGES, "--user", &user_id]), ); - fn callback_all_pkg( - container: &mut HashMap, - ) -> impl FnMut(String) -> Result<()> + '_ { - let parser = |s: String| -> Result<()> { - let ot = s.replace("package:", ""); - for l in ot.lines() { - let caps = PACKAGE_PARSE_REGEX.captures(l).unwrap(); - container.insert( - caps.get(2).unwrap().as_str().to_string(), - PackageAttribs { - package_path: caps.get(1).unwrap().as_str().to_string(), - }, - ); - } - return Ok(()); - }; - return parser; - } - fn callback(container: &mut HashSet) -> impl FnMut(String) -> Result<()> + '_ { let parser = |s: String| -> Result<()> { let ot = s.replace("package:", ""); @@ -224,17 +202,22 @@ impl ADBTerminalImpl { return parser; } - let res = Self::_execute_and_parse(cmd_all_pkg, callback_all_pkg(&mut all_pkg)) - .and_then(|_| Self::_execute_and_parse(cmd_enabled_pkg, callback(&mut enabled_pkg))) - .and_then(|_| Self::_execute_and_parse(cmd_disabled_pkg, callback(&mut disabled_pkg))) - .and_then(|_| Self::_execute_and_parse(cmd_system_pkg, callback(&mut sys_pkg))) - .and_then(|_| Self::_execute_and_parse(cmd_tpp_pkg, callback(&mut tpp_pkg))) - .and_then(|_| { - Self::_execute_and_parse( - cmd_uninstalled_disabled_pkg, - callback(&mut uninstalled_disabled_pkg), - ) - }); + let res = + Self::execute_list_all_with_fallback(&shell_cmd, user_id.to_owned(), &mut all_pkg) + .and_then(|_| { + Self::_execute_and_parse(&cmd_enabled_pkg, callback(&mut enabled_pkg)) + }) + .and_then(|_| { + Self::_execute_and_parse(&cmd_disabled_pkg, callback(&mut disabled_pkg)) + }) + .and_then(|_| Self::_execute_and_parse(&cmd_system_pkg, callback(&mut sys_pkg))) + .and_then(|_| Self::_execute_and_parse(&cmd_tpp_pkg, callback(&mut tpp_pkg))) + .and_then(|_| { + Self::_execute_and_parse( + &cmd_uninstalled_disabled_pkg, + callback(&mut uninstalled_disabled_pkg), + ) + }); match res { Err(e) => { @@ -283,28 +266,21 @@ impl ADBTerminalImpl { pkg: String, clear_pkg: bool, ) -> Result<()> { - let shell_cmd: ADBShell = - ADBShell::new(self.adb_path.to_owned()).for_device(device_id.to_owned()); + let shell_cmd = adb_cmd::for_device(&self.adb_command.clone(), device_id.to_owned()); let (cmd_disable_pkg, cmd_fstop_pkg, cmd_clear_pkg) = ( - shell_cmd.clone().with_commands(&[ - "pm disable-user", - "--user", - &user_id, - &pkg.to_owned(), - ]), - shell_cmd.clone().with_commands(&[ - "am force-stop", - "--user", - &user_id, - &pkg.to_owned(), - ]), shell_cmd .clone() - .with_commands(&["pm clear", "--user", &user_id, &pkg.to_owned()]), + .args(["pm disable-user", "--user", &user_id, &pkg.to_owned()]), + shell_cmd + .clone() + .args(["am force-stop", "--user", &user_id, &pkg.to_owned()]), + shell_cmd + .clone() + .args(&["pm clear", "--user", &user_id, &pkg.to_owned()]), ); - Self::_execute_and_parse(cmd_disable_pkg, |s| { + Self::_execute_and_parse(&cmd_disable_pkg, |s| { if s.contains(&format!( "Package {} new state: disabled-user", pkg.to_owned() @@ -314,7 +290,7 @@ impl ADBTerminalImpl { return Err(anyhow!(s)); }) .and_then(|_| { - Self::_execute_and_parse(cmd_fstop_pkg, |s| { + Self::_execute_and_parse(&cmd_fstop_pkg, |s| { if s.is_empty() { return Ok(()); } @@ -323,7 +299,7 @@ impl ADBTerminalImpl { })?; if clear_pkg { - Self::_execute_and_parse(cmd_clear_pkg, |s| { + Self::_execute_and_parse(&cmd_clear_pkg, |s| { if s.eq("Success") { return Ok(()); } @@ -335,13 +311,11 @@ impl ADBTerminalImpl { } pub fn enable_package(&self, device_id: String, user_id: String, pkg: String) -> Result<()> { - let shell_cmd: ADBShell = - ADBShell::new(self.adb_path.to_owned()).for_device(device_id.to_owned()); + let shell_cmd = adb_cmd::for_device(&self.adb_command.clone(), device_id.to_owned()); - let cmd_enable_pkg = - shell_cmd.with_commands(&["pm enable", "--user", &user_id, &pkg.to_owned()]); + let cmd_enable_pkg = shell_cmd.args(["pm enable", "--user", &user_id, &pkg.to_owned()]); - Self::_execute_and_parse(cmd_enable_pkg, |s| { + Self::_execute_and_parse(&cmd_enable_pkg, |s| { if s.contains(&format!("Package {} new state: enabled", pkg.to_owned())) { return Ok(()); } @@ -352,17 +326,16 @@ impl ADBTerminalImpl { } pub fn install_package(&self, device_id: String, user_id: String, pkg: String) -> Result<()> { - let shell_cmd: ADBShell = - ADBShell::new(self.adb_path.to_owned()).for_device(device_id.to_owned()); + let shell_cmd = adb_cmd::for_device(&self.adb_command.clone(), device_id.to_owned()); - let cmd_enable_pkg = shell_cmd.with_commands(&[ + let cmd_enable_pkg = shell_cmd.args([ "cmd package install-existing", "--user", &user_id, &pkg.to_owned(), ]); - Self::_execute_and_parse(cmd_enable_pkg, |s| { + Self::_execute_and_parse(&cmd_enable_pkg, |s| { if s.contains(&format!("Package {} new state: enabled", pkg.to_owned())) { return Ok(()); } @@ -372,8 +345,61 @@ impl ADBTerminalImpl { return Ok(()); } - fn _execute_and_parse( - cmd: ADBShell, + fn execute_list_all_with_fallback<'a, T: ADBCommand + Clone>( + cmd: &'a T, + user_id: String, + mut container: &mut HashMap, + ) -> Result<()> { + let (cmd_all_pkg, cmd_all_package_user0_fallback, cmd_all_package_user_fallback) = ( + cmd.clone().args([LIST_ALL_PACKAGES_INCLUDING_UNINSTALLED]), + cmd.clone() + .args([LIST_ALL_PACKAGES_INCLUDING_UNINSTALLED, "--user", "0"]), + cmd.clone() + .args([LIST_ALL_PACKAGES_INCLUDING_UNINSTALLED, "--user", &user_id]), + ); + + fn callback( + container: &mut HashMap, + ) -> impl FnMut(String) -> Result<()> + '_ { + let parser = |s: String| -> Result<()> { + let ot = s.replace("package:", ""); + for l in ot.lines() { + let caps = PACKAGE_PARSE_REGEX.captures(l).unwrap(); + container.insert( + caps.get(2).unwrap().as_str().to_string(), + PackageAttribs { + package_path: caps.get(1).unwrap().as_str().to_string(), + }, + ); + } + return Ok(()); + }; + return parser; + } + + return Self::_execute_and_parse(&cmd_all_pkg, callback(&mut container)) + .or_else(|e| { + if !e + .to_string() + .contains("Shell does not have permission to access user") + { + return Err(e); + } + Self::_execute_and_parse(&cmd_all_package_user0_fallback, callback(&mut container)) + }) + .or_else(|e| { + if !e + .to_string() + .contains("Shell does not have permission to access user") + { + return Err(e); + } + Self::_execute_and_parse(&cmd_all_package_user_fallback, callback(&mut container)) + }); + } + + fn _execute_and_parse<'a, T: ADBCommand>( + cmd: &'a T, mut parser: impl FnMut(String) -> Result<()>, ) -> Result<()> { let res = cmd.execute(); diff --git a/src-tauri/src/users.rs b/src-tauri/src/users.rs index d5f56f7..46f8e84 100644 --- a/src-tauri/src/users.rs +++ b/src-tauri/src/users.rs @@ -1,4 +1,4 @@ -use crate::adb_cmd::{ADBCommand, ADBShell}; +use crate::adb_cmd::{self, ADBCommand, ADBShell}; use anyhow::{anyhow, Result}; use lazy_static::lazy_static; use regex::Regex; @@ -31,10 +31,12 @@ lazy_static! { impl ADBTerminalImpl { pub fn list_users(&self, device_id: String) -> Result> { - let shell_cmd: ADBShell = - ADBShell::new(self.adb_path.to_owned()).for_device(device_id.to_owned()); + let shell_cmd: ADBShell = adb_cmd::for_device( + &ADBShell::new(self.adb_path.to_owned()), + device_id.to_owned(), + ); - let res = shell_cmd.with_commands(&["pm list users "]).execute(); + let res = shell_cmd.args(&["pm list users "]).execute(); match res { Err(e) => { return Err(e.into()); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 752fa89..4aba047 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "Simple Android Debloater", - "version": "0.5.0" + "version": "0.6.0" }, "tauri": { "allowlist": { @@ -106,7 +106,7 @@ }, "windows": [ { - "fullscreen": true, + "fullscreen": false, "height": 600, "resizable": true, "title": "Simple Android Debloater",