diff --git a/ApkManager/AdbWindow.xaml b/ApkManager/AdbWindow.xaml
new file mode 100644
index 0000000..fb57b76
--- /dev/null
+++ b/ApkManager/AdbWindow.xaml
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApkManager/AdbWindow.xaml.cs b/ApkManager/AdbWindow.xaml.cs
new file mode 100644
index 0000000..2f58676
--- /dev/null
+++ b/ApkManager/AdbWindow.xaml.cs
@@ -0,0 +1,299 @@
+using ApkManager.Lib;
+using MahApps.Metro.Controls;
+using System.ComponentModel;
+using System.IO;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Kind = MahApps.Metro.IconPacks.PackIconMaterialKind;
+
+namespace ApkManager
+{
+ ///
+ /// Interaction logic for AdbWindow.xaml
+ ///
+ public partial class AdbWindow : MetroWindow
+ {
+ private Config cfg;
+ private Adb adb;
+ private Apk apk;
+ private bool isLoading = false;
+
+ public enum ShowMenu { Main, Install, Uninstall }
+
+ #region WINDOW
+ public AdbWindow(Apk apk)
+ {
+ InitializeComponent();
+
+ // make all flyout width same as root
+ foreach (Flyout flayout in Flyouts.Items)
+ flayout.Width = this.Width;
+
+ // set switch in settings
+ cfg = new Config();
+ swClose.IsChecked = cfg.AutoClose();
+
+ // define adb
+ adb = new Adb();
+ adb.OnProcess += (value) => ShowLoading(value);
+ adb.OutputDataReceived += (msg) => CommandOutput_Insert(msg, false);
+ adb.ErrorDataReceived += (msg) => CommandOutput_Insert(msg, true);
+
+ this.apk = apk;
+ }
+
+ private void ShowLoading(bool state = true)
+ {
+ Dispatcher.Invoke(() => {
+ isLoading = state;
+ PanelLoading.Visibility = state ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ // refresh list
+ ButtonRefresh_Click(sender, e);
+ }
+
+ private void OnClosing(object sender, CancelEventArgs e)
+ {
+ e.Cancel = isLoading;
+ }
+ #endregion
+
+ #region TOPBAR
+ private async void WindowCommand_Click(object sender, RoutedEventArgs e)
+ {
+ if (!(sender is Button btn)) return;
+ switch ((string)btn.Tag)
+ {
+ case "reconnect":
+ await adb.Reconnect();
+ ButtonRefresh_Click(null, null);
+ break;
+ case "settings":
+ menuSettings.IsOpen = !menuSettings.IsOpen;
+ break;
+ }
+ }
+
+ private void SwitchSettings_Click(object sender, RoutedEventArgs e)
+ {
+ if (!(sender is ToggleSwitch ts)) return;
+
+ var tag = ts.Tag as string;
+ if (tag == "Close")
+ cfg.AutoClose(ts.IsChecked == true);
+ }
+ #endregion
+
+ #region DEVICES
+ private async void ComboDevices_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!(sender is ComboBox combo)) return;
+ var address = combo.SelectedItem as string;
+
+ txtDevice.Text = ". . . .";
+ txtAndroid.Text = ". . . .";
+ txtArch.Text = ". . . .";
+ txtSdk.Text = ". . . .";
+ btnMenuInstall.IsEnabled = false;
+
+ if (!string.IsNullOrWhiteSpace(address))
+ {
+ var device = await adb.GetDevice(address);
+ var supported = apk.SdkVersion <= device.Sdk;
+
+ txtDevice.Text = device.Name;
+ txtAndroid.Text = device.Android;
+ txtArch.Text = device.Arch;
+ txtSdk.Text = device.Sdk.ToString();
+ txtSdk.Foreground = supported ? txtArch.Foreground : Brushes.Red;
+ btnMenuInstall.IsEnabled = supported;
+ }
+ }
+
+ private async void ButtonRefresh_Click(object sender, RoutedEventArgs e)
+ {
+ cbDevices.Items.Clear();
+
+ var devices = await adb.GetDevices();
+ foreach(var device in devices)
+ {
+ cbDevices.Items.Add(device);
+ }
+
+ cbDevices.SelectedIndex = 0;
+ }
+
+ private void ButtonWireless_Click(object sender, RoutedEventArgs e)
+ {
+ var selected = cbDevices.Text;
+ var last = cfg.LastAddress();
+ if (selected.IsValidIPAddress())
+ txtAddress.Text = selected;
+ else if (!string.IsNullOrWhiteSpace(last))
+ txtAddress.Text = last;
+
+ menuWifi.IsOpen = true;
+ }
+
+ private async void ButtonConnect_Click(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(txtAddress.Text)) return;
+
+ menuWifi.IsOpen = false;
+
+ var result = await adb.Connect(txtAddress.Text);
+ if (result)
+ {
+ if (cfg.LastAddress() != txtAddress.Text)
+ cfg.LastAddress(txtAddress.Text);
+
+ ButtonRefresh_Click(null, null);
+ }
+ else menuWifi.IsOpen = true;
+ }
+
+ private async void ButtonDisconnect_Click(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(txtAddress.Text)) return;
+
+ menuWifi.IsOpen = false;
+
+ var result = await adb.Disconnect(txtAddress.Text);
+ if (result) ButtonRefresh_Click(null, null);
+ else menuWifi.IsOpen = true;
+ }
+
+ private void TextAddress_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ if (!(sender is TextBox tb)) return;
+ if (!string.IsNullOrWhiteSpace(tb.Text))
+ {
+ var isValid = tb.Text.IsValidIPAddress();
+ IconAddress.Kind = isValid ? Kind.Wifi : Kind.WifiOff;
+ ButtonConnect.IsEnabled = isValid;
+ ButtonDisconnect.IsEnabled = isValid;
+ }
+ else
+ {
+ IconAddress.Kind = Kind.WifiOff;
+ ButtonConnect.IsEnabled = false;
+ ButtonDisconnect.IsEnabled = false;
+ }
+ }
+ #endregion
+
+ #region OUTPUT
+ private void CommandOutput_Reset()
+ {
+ Dispatcher.Invoke(() => lbOutput.Items.Clear());
+ }
+
+ private void CommandOutput_Insert(string message, bool error = false)
+ {
+ Dispatcher.Invoke(() =>
+ {
+ var color = Brushes.WhiteSmoke;
+ if (error || message.ToLower().Contains("failed"))
+ color = Brushes.IndianRed;
+ else if (message.ToLower().Contains("success"))
+ color = Brushes.LightBlue;
+
+ var item = new ListBoxItem()
+ {
+ Content = message.Trim(),
+ Background = color
+ };
+ lbOutput.Items.Add(item);
+ lbOutput.ScrollIntoView(item);
+ });
+ }
+ #endregion
+
+ #region ACTION
+ private void ShowActionMenu(ShowMenu menu)
+ {
+ if (menu == ShowMenu.Install)
+ {
+ wcReconnect.Visibility = Visibility.Collapsed;
+
+ actionMain.Visibility = Visibility.Collapsed;
+ actionInstall.Visibility = Visibility.Visible;
+ actionUninstall.Visibility = Visibility.Collapsed;
+
+ CommandOutput_Reset();
+ CommandOutput_Insert($"File APK : {Path.GetFileName(apk.FilePath)}");
+
+ gbTarget.IsEnabled = false;
+ gbAction.Header = "Action : Install";
+ gbCommand.Visibility = Visibility.Visible;
+ }
+ else if (menu == ShowMenu.Uninstall)
+ {
+ wcReconnect.Visibility = Visibility.Collapsed;
+
+ actionMain.Visibility = Visibility.Collapsed;
+ actionInstall.Visibility = Visibility.Collapsed;
+ actionUninstall.Visibility = Visibility.Visible;
+
+ CommandOutput_Reset();
+ CommandOutput_Insert($"Package : {apk.PackageName}");
+
+ gbTarget.IsEnabled = false;
+ gbAction.Header = "Action : Uninstall";
+ gbCommand.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ wcReconnect.Visibility = Visibility.Visible;
+
+ actionMain.Visibility = Visibility.Visible;
+ actionInstall.Visibility = Visibility.Collapsed;
+ actionUninstall.Visibility = Visibility.Collapsed;
+
+ gbTarget.IsEnabled = true;
+ gbAction.Header = "Select Action";
+ gbCommand.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ private void ButtonMenuInstall_Click(object sender, RoutedEventArgs e)
+ {
+ ShowActionMenu(ShowMenu.Install);
+ }
+
+ private void ButtonMenuUninstall_Click(object sender, RoutedEventArgs e)
+ {
+ ShowActionMenu(ShowMenu.Uninstall);
+ }
+
+ private void ButtonMenuBack_Click(object sender, RoutedEventArgs e)
+ {
+ ShowActionMenu(ShowMenu.Main);
+ }
+
+ private async void ButtonActionInstall_Click(object sender, RoutedEventArgs e)
+ {
+ var result = await adb.Install(cbDevices.Text, apk.FilePath);
+ if (result && cfg.AutoClose())
+ this.Close();
+ }
+
+ private async void ButtonActionUninstall_Click(object sender, RoutedEventArgs e)
+ {
+ CommandOutput_Insert("Force remove app and data....");
+ await adb.Uninstall(cbDevices.Text, apk.PackageName);
+ }
+
+ private async void ButtonActionUninstallKeep_Click(object sender, RoutedEventArgs e)
+ {
+ CommandOutput_Insert("Remove app but keep the data....");
+ await adb.Uninstall(cbDevices.Text, apk.PackageName, true);
+ }
+ #endregion
+ }
+}
diff --git a/ApkManager/ApkManager.csproj b/ApkManager/ApkManager.csproj
index 0453f4b..605ce1c 100644
--- a/ApkManager/ApkManager.csproj
+++ b/ApkManager/ApkManager.csproj
@@ -33,9 +33,28 @@
prompt
4
+
+ Resources\Playstore.ico
+
+
+ ..\packages\ControlzEx.3.0.2.4\lib\net45\ControlzEx.dll
+
+
+ ..\packages\MahApps.Metro.1.6.5\lib\net45\MahApps.Metro.dll
+
+
+ ..\packages\MahApps.Metro.IconPacks.Material.3.5.0\lib\net45\MahApps.Metro.IconPacks.Core.dll
+
+
+ ..\packages\MahApps.Metro.IconPacks.Material.3.5.0\lib\net45\MahApps.Metro.IconPacks.Material.dll
+
+
+
+ ..\packages\ControlzEx.3.0.2.4\lib\net45\System.Windows.Interactivity.dll
+
@@ -48,24 +67,48 @@
+
+ ..\packages\ZipStorer.3.6.0\lib\net20\ZipStorer.dll
+
MSBuild:Compile
Designer
+
+
+ RenamerWindow.xaml
+
+
+ Designer
+ MSBuild:Compile
+
MSBuild:Compile
Designer
+
+ AdbWindow.xaml
+
+
App.xaml
Code
+
+
+
+
+
MainWindow.xaml
Code
+
+ Designer
+ MSBuild:Compile
+
@@ -85,13 +128,33 @@
ResXFileCodeGenerator
Resources.Designer.cs
+
SettingsSingleFileGenerator
Settings.Designer.cs
-
+
+ Designer
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/ApkManager/App.config b/ApkManager/App.config
index 8e15646..e1f3697 100644
--- a/ApkManager/App.config
+++ b/ApkManager/App.config
@@ -1,6 +1,23 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ApkManager/App.xaml b/ApkManager/App.xaml
index 75e6e41..ec7bd23 100644
--- a/ApkManager/App.xaml
+++ b/ApkManager/App.xaml
@@ -2,8 +2,18 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ApkManager"
- StartupUri="MainWindow.xaml">
+ Startup="Application_Startup">
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApkManager/App.xaml.cs b/ApkManager/App.xaml.cs
index 681df86..70a05ad 100644
--- a/ApkManager/App.xaml.cs
+++ b/ApkManager/App.xaml.cs
@@ -1,10 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Configuration;
+using ApkManager.Lib;
+using System;
using System.Data;
+using System.Diagnostics;
using System.Linq;
-using System.Threading.Tasks;
+using System.Reflection;
+using System.Security.Principal;
+using System.Threading;
using System.Windows;
+using System.Windows.Media.Imaging;
namespace ApkManager
{
@@ -13,5 +16,47 @@ namespace ApkManager
///
public partial class App : Application
{
+ private void Application_Startup(object sender, StartupEventArgs e)
+ {
+ var cfg = new Config();
+ using (var mutex = new Mutex(true, "{BB8C82B6-7851-46A0-A902-48446B59CAD6}", out bool isFirstInstance))
+ {
+ if (!isFirstInstance && cfg.SingleInstance()) return;
+
+ var apks = e.Args.Where(s => s.ToLower().EndsWith(".apk")).ToArray();
+
+ if (apks.Count() <= 1)
+ new MainWindow(apks.FirstOrDefault()).ShowDialog();
+ }
+ }
+
+ public static bool IsAdministrator()
+ {
+ using (var identity = WindowsIdentity.GetCurrent())
+ {
+ var principal = new WindowsPrincipal(identity);
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+ }
+
+ public static BitmapImage GetImageFromResources(string pathResource)
+ {
+ try
+ {
+ var assembly = Assembly.GetExecutingAssembly().GetName().Name;
+ var packUri = string.Format("pack://application:,,,/{0};component/{1}", assembly, pathResource);
+ return new BitmapImage(new Uri(packUri));
+ }
+ catch (Exception e)
+ {
+ Debug.Print("App.GetImageResource: {0}", e.Message);
+ return null;
+ }
+ }
+
+ public static BitmapImage GetPlaystoreImageFromResources()
+ {
+ return GetImageFromResources("Resources/Playstore.png");
+ }
}
}
diff --git a/ApkManager/Lib/Aapt.cs b/ApkManager/Lib/Aapt.cs
new file mode 100644
index 0000000..d3967e4
--- /dev/null
+++ b/ApkManager/Lib/Aapt.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace ApkManager.Lib
+{
+ class Aapt
+ {
+ public class Result
+ {
+ public Apk Apk { get; set; }
+ public bool Success { get; set; }
+ public string Message { get; set; }
+ }
+
+ private static BitmapImage GetIconFrom(string pathApk, string pathIcon)
+ {
+ try
+ {
+ using (var stream = new MemoryStream())
+ using (var zip = ZipStorer.Open(pathApk, FileAccess.Read))
+ {
+ if (!pathIcon.EndsWith(".xml"))
+ {
+ var fileEntry = zip.ReadCentralDir().Where(f => f.FilenameInZip.Equals(pathIcon)).SingleOrDefault();
+ zip.ExtractFile(fileEntry, stream);
+ }
+ else
+ {
+ var icon = Path.GetFileNameWithoutExtension(pathIcon) + ".png";
+ var fileEntry = zip.ReadCentralDir().Where(f => f.FilenameInZip.EndsWith(icon) && f.FilenameInZip.Contains("hdpi")).LastOrDefault();
+ zip.ExtractFile(fileEntry, stream);
+ }
+
+ var bitmap = new BitmapImage();
+ bitmap.BeginInit();
+ bitmap.StreamSource = stream;
+ bitmap.CacheOption = BitmapCacheOption.OnLoad;
+ bitmap.EndInit();
+ bitmap.Freeze();
+
+ return bitmap;
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Apk.GetIcon: {0}", e.Message);
+ return App.GetPlaystoreImageFromResources();
+ }
+ }
+
+ public static async Task RunAsync(string command, params object[] args)
+ {
+ return await Task.Run(() =>
+ {
+ using (var p = new Process())
+ {
+ p.StartInfo = new ProcessStartInfo()
+ {
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ FileName = Path.Combine("Lib", "aapt2.exe"),
+ Arguments = string.Format(command, args)
+ };
+
+ p.Start();
+
+ Debug.Print("Aapt.Command: aapt2 {0}", string.Format(command, args));
+
+ var output = p.StandardOutput.ReadToEnd();
+ var error = p.StandardError.ReadToEnd();
+
+ p.WaitForExit();
+
+ if (p.ExitCode != 0) throw new Exception(error);
+ return output;
+ }
+ });
+ }
+
+ public static async Task DumbBadging(string pathApk)
+ {
+ var output = string.Empty;
+
+ try
+ {
+ output = await RunAsync("dump badging \"{0}\"", pathApk);
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Aapt.DumbBadging: {0}", e.Message);
+ return new Result()
+ {
+ Success = false,
+ Message = e.Message
+ };
+ }
+
+ var apk = new Apk() { FilePath = pathApk };
+
+ //package
+ var match = Regex.Match(output, "package: name='(.+?)'");
+ if (match.Success) apk.PackageName = match.Groups[1].Value;
+
+ //versionCode
+ match = Regex.Match(output, "package.+versionCode='(.+?)'");
+ if (match.Success)
+ {
+ double.TryParse(match.Groups[1].Value, out double versionCode);
+ apk.VersionCode = versionCode;
+ }
+
+ //versionName
+ match = Regex.Match(output, "package.+versionName='(.+?)'");
+ if (match.Success) apk.VersionName = match.Groups[1].Value;
+
+ //label
+ match = Regex.Match(output, "application.+label='(.+?)'");
+ if (match.Success) apk.Label = match.Groups[1].Value;
+
+ //icon
+ match = Regex.Match(output, "application.+icon='(.+?)'");
+ if (match.Success) apk.Icon = GetIconFrom(pathApk, match.Groups[1].Value);
+
+ //sdkVersion
+ match = Regex.Match(output, @"sdkVersion:'(\d+?)'");
+ if (match.Success)
+ {
+ int.TryParse(match.Groups[1].Value, out int sdkVersion);
+ apk.SdkVersion = sdkVersion;
+ }
+
+ //targetSdkVersion
+ match = Regex.Match(output, @"targetSdkVersion:'(\d+?)'");
+ if (match.Success)
+ {
+ int.TryParse(match.Groups[1].Value, out int targetSdkVersion);
+ apk.TargetSdkVersion = targetSdkVersion;
+ }
+
+ //permission
+ var matches = Regex.Matches(output, "uses-permission: name='(.+?)'");
+ foreach (Match m in matches) apk.Permissions.Add(m.Groups[1].Value);
+
+ //native-code
+ match = Regex.Match(output, "native-code: '(.+)'");
+ if (match.Success)
+ {
+ apk.AbiList = match.Groups[1].Value.Replace("' '",", ");
+ apk.Platforms = match.Groups[1].Value.Replace("' '", " ").Split(' ').ToList();
+ }
+ else
+ {
+ apk.AbiList = "any";
+ apk.Platforms.Add("any");
+ }
+
+ return new Result()
+ {
+ Apk = apk,
+ Success = true
+ };
+ }
+ }
+}
diff --git a/ApkManager/Lib/Adb.cs b/ApkManager/Lib/Adb.cs
new file mode 100644
index 0000000..dc4f7cf
--- /dev/null
+++ b/ApkManager/Lib/Adb.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace ApkManager
+{
+ internal class Adb
+ {
+ public class Device
+ {
+ public string Name { get; set; }
+ public string Android { get; set; }
+ public string Arch { get; set; }
+ public int Sdk { get; set; }
+
+ public override string ToString()
+ {
+ return Name.ToString();
+ }
+ }
+
+ private static bool OVERRIDE_ONPROCESSEVENT = false;
+
+ public delegate void ProcessEventHandler(bool value);
+
+ public event ProcessEventHandler OnProcess;
+
+ public delegate void OutputEventHander(string message);
+
+ public event OutputEventHander OutputDataReceived;
+
+ public delegate void ErrorEventHandler(string message);
+
+ public event ErrorEventHandler ErrorDataReceived;
+
+ public Adb()
+ {
+ }
+
+ private async Task RunAsync(string command, params object[] args)
+ {
+ return await Task.Run(() =>
+ {
+ using (var p = new Process())
+ {
+ var OutputMessage = string.Empty;
+ p.OutputDataReceived += (s, e) => {
+ if (string.IsNullOrWhiteSpace(e.Data)) return;
+ OutputMessage += e.Data + Environment.NewLine;
+ OutputDataReceived?.Invoke(e.Data);
+ };
+
+ var ErrorMessage = string.Empty;
+ p.ErrorDataReceived += (s, e) => {
+ if (string.IsNullOrWhiteSpace(e.Data)) return;
+ ErrorMessage += e.Data + Environment.NewLine;
+ ErrorDataReceived?.Invoke(e.Data);
+ };
+
+ p.StartInfo = new ProcessStartInfo()
+ {
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ FileName = Path.Combine("Lib", "adb.exe"),
+ Arguments = string.Format(command, args)
+ };
+
+ p.Start();
+
+ Debug.Print("Adb.Command: adb {0}", string.Format(command, args));
+
+ if (!OVERRIDE_ONPROCESSEVENT)
+ OnProcess?.Invoke(true);
+
+ p.BeginOutputReadLine();
+ p.BeginErrorReadLine();
+
+ p.WaitForExit();
+
+ if (!OVERRIDE_ONPROCESSEVENT)
+ OnProcess?.Invoke(false);
+
+ if (p.ExitCode != 0) throw new Exception(ErrorMessage);
+
+ Debug.Print("Adb.Output: {0}", OutputMessage);
+ return OutputMessage;
+ };
+ });
+ }
+
+ public async Task StartServer()
+ {
+ try
+ {
+ await RunAsync("start-server");
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.StartServer: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task Reconnect()
+ {
+ try
+ {
+ await RunAsync("reconnect");
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.Reconnect: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task Connect(string address)
+ {
+ try
+ {
+ if (!address.Contains(":"))
+ address += ":5555";
+
+ var result = await RunAsync("connect {0}", address);
+ return result.Contains("connected");
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.Connect: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task Disconnect(string address)
+ {
+ try
+ {
+ if (!address.Contains(":"))
+ address += ":5555";
+
+ var result = await RunAsync("disconnect {0}", address);
+ return result.Contains("disconnected") || result.Contains("no such device");
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.Disconnect: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task> GetDevices()
+ {
+ var devices = new List();
+
+ try
+ {
+ var result = await RunAsync("devices");
+ var matches = Regex.Matches(result, @"(.+?)\tdevice");
+ foreach (Match match in matches)
+ {
+ devices.Add(match.Groups[1].Value);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.GetDevices: {0}", e.Message);
+ }
+
+ return devices;
+ }
+
+ public async Task Install(string device, string pathApk)
+ {
+ try
+ {
+ var result = await RunAsync("-s {0} install -r \"{1}\"", device, pathApk);
+ return result.Contains("Success");
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.Install: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task Uninstall(string device, string package, bool keepData = false)
+ {
+ try
+ {
+ var command = keepData ? $"-s {device} shell pm uninstall -k {package}" : $"-s {device} uninstall {package}";
+ var result = await RunAsync(command);
+ return result.Contains("Success");
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Adb.Uninstall: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public async Task GetProp(string device, string prop)
+ {
+ var result = await RunAsync("-s {0} shell getprop {1}", device, prop);
+ return string.IsNullOrWhiteSpace(result) ? "Unknown" : result.Trim();
+ }
+
+ public async Task GetDevice(string address)
+ {
+ OVERRIDE_ONPROCESSEVENT = true;
+ OnProcess?.Invoke(true);
+
+ var name = await GetProp(address, "ro.product.model");
+ if (string.IsNullOrWhiteSpace(name))
+ name = await GetProp(address, "ro.product.brand");
+ if (string.IsNullOrWhiteSpace(name))
+ name = await GetProp(address, "ro.product.device");
+ if (string.IsNullOrWhiteSpace(name))
+ name = "Unknown";
+
+ var android = await GetProp(address, "ro.build.version.release");
+ if (string.IsNullOrWhiteSpace(android))
+ android = "Unknown";
+
+ var sdk = await GetProp(address, "ro.build.version.sdk");
+ int.TryParse(sdk, out int _sdk);
+
+ var arch = await GetProp(address, "ro.product.cpu.abi");
+
+ OnProcess?.Invoke(false);
+ OVERRIDE_ONPROCESSEVENT = false;
+
+ return new Device()
+ {
+ Name = name,
+ Android = android,
+ Sdk = _sdk,
+ Arch = arch
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/ApkManager/Lib/AdbWinApi.dll b/ApkManager/Lib/AdbWinApi.dll
new file mode 100644
index 0000000..7abe26c
Binary files /dev/null and b/ApkManager/Lib/AdbWinApi.dll differ
diff --git a/ApkManager/Lib/AdbWinUsbApi.dll b/ApkManager/Lib/AdbWinUsbApi.dll
new file mode 100644
index 0000000..e7a6de1
Binary files /dev/null and b/ApkManager/Lib/AdbWinUsbApi.dll differ
diff --git a/ApkManager/Lib/Apk.cs b/ApkManager/Lib/Apk.cs
new file mode 100644
index 0000000..1390b7d
--- /dev/null
+++ b/ApkManager/Lib/Apk.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Windows.Media.Imaging;
+
+namespace ApkManager.Lib
+{
+ public class Apk
+ {
+ public string PackageName { get; set; }
+ public string Label { get; set; }
+ public string VersionName { get; set; }
+ public double VersionCode { get; set; }
+ public int SdkVersion { get; set; }
+ public int TargetSdkVersion { get; set; }
+ public BitmapImage Icon { get; set; }
+ public IList Permissions { get; set; }
+ public IList Platforms { get; set; }
+ public string AbiList { get; set; }
+ public string FilePath { get; set; }
+
+ public Apk()
+ {
+ Permissions = new List();
+ Platforms = new List();
+ }
+ }
+}
diff --git a/ApkManager/Lib/Config.cs b/ApkManager/Lib/Config.cs
new file mode 100644
index 0000000..fbaae5a
--- /dev/null
+++ b/ApkManager/Lib/Config.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+
+namespace ApkManager.Lib
+{
+ class Config
+ {
+ private Configuration config;
+
+ #region KEY STRING
+ private static readonly string KEY_APPSETTINGS = "appSettings";
+ // Window
+ private static readonly string KEY_SINGLEINSTANCE = "SingleInstance";
+ private static readonly string KEY_SAVEWINDOWPOSITION = "SaveWindowPosition";
+ private static readonly string KEY_WINDOWMAIN = "WindowMain";
+ // Last used
+ private static readonly string KEY_LASTADDRESS = "LastAddress";
+ private static readonly string KEY_AUTOCLOSE = "AutoClose";
+ // Usage
+ private static readonly string KEY_PATTERN = "UsePattern";
+ private static readonly string KEY_LABEL = "UseLabel";
+ private static readonly string KEY_PACKAGE = "UsePackage";
+ private static readonly string KEY_VERSION = "UseVersion";
+ private static readonly string KEY_BUILD = "UseBuild";
+ private static readonly string KEY_SUFFIXENCLOSURE = "UseSuffixEnclosure";
+ private static readonly string KEY_SEPARATOR = "Separator";
+ #endregion
+
+ #region SET VALUE
+ private bool SetValue(string key, string value)
+ {
+ try
+ {
+ config.AppSettings.Settings[key].Value = value;
+ config.Save(ConfigurationSaveMode.Modified);
+
+ ConfigurationManager.RefreshSection(KEY_APPSETTINGS);
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Print(e.Message);
+ return false;
+ }
+ }
+
+ private bool SetValue(string key, bool value)
+ {
+ return SetValue(key, value.ToString());
+ }
+
+ private bool SetValue(string key, int value)
+ {
+ return SetValue(key, value.ToString());
+ }
+
+ private bool SetValue(string key, double value)
+ {
+ return SetValue(key, value.ToString());
+ }
+ #endregion
+
+ #region GET VALUE
+ private string GetString(string key)
+ {
+ try
+ {
+ var value = ConfigurationManager.AppSettings[key];
+ if (string.IsNullOrWhiteSpace(value))
+ throw new Exception($"Config: key \"{key}\" is not found!");
+ else
+ return value;
+ }
+ catch (Exception e)
+ {
+ Debug.Print(e.Message);
+ return string.Empty;
+ }
+ }
+
+ private bool GetBoolean(string key)
+ {
+ return GetString(key).ToLower() == "true";
+ }
+
+ private int GetInteger(string key)
+ {
+ var value = GetString(key);
+ int.TryParse(value, out int num);
+ return num;
+ }
+
+ private double GetDouble(string key)
+ {
+ var value = GetString(key);
+ double.TryParse(value, out double num);
+ return num;
+ }
+ #endregion
+
+ #region GET-SET CONFIG
+ public Config()
+ {
+ config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
+ }
+
+ public bool SingleInstance()
+ {
+ return GetBoolean(KEY_SINGLEINSTANCE);
+ }
+
+ public bool SingleInstance(bool value)
+ {
+ return SetValue(KEY_SINGLEINSTANCE, value);
+ }
+
+ public WindowPosition WindowPostition()
+ {
+ try
+ {
+ var value = GetString(KEY_WINDOWMAIN).Split(',');
+ double.TryParse(value[0], out double top);
+ double.TryParse(value[1], out double left);
+ return new WindowPosition() { Top = top, Left = left };
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Config.WindowPostition: {0}", e.Message);
+ return new WindowPosition() { Top = 0, Left = 0 };
+ }
+ }
+
+ public bool WindowPostition(WindowPosition position)
+ {
+ var window = string.Format("{0},{1}", position.Top, position.Left);
+ return SetValue(KEY_WINDOWMAIN, window);
+ }
+
+ public bool GetWindowPostition()
+ {
+ return GetBoolean(KEY_SAVEWINDOWPOSITION);
+ }
+
+ public bool SetWindowPostition(bool value)
+ {
+ return SetValue(KEY_SAVEWINDOWPOSITION, value);
+ }
+
+ public string LastAddress()
+ {
+ return GetString(KEY_LASTADDRESS);
+ }
+
+ public bool LastAddress(string address)
+ {
+ return SetValue(KEY_LASTADDRESS, address);
+ }
+
+ public bool AutoClose()
+ {
+ return GetBoolean(KEY_AUTOCLOSE);
+ }
+
+ public bool AutoClose(bool value)
+ {
+ return SetValue(KEY_AUTOCLOSE, value);
+ }
+
+ public NameFormat GetNameFormat()
+ {
+ return new NameFormat
+ {
+ UsePattern = GetString(KEY_PATTERN),
+ UseLabel = GetBoolean(KEY_LABEL),
+ UsePackage = GetBoolean(KEY_PACKAGE),
+ UseVersion = GetBoolean(KEY_VERSION),
+ UseBuild = GetBoolean(KEY_BUILD),
+ UseSuffixEnclosure = GetBoolean(KEY_SUFFIXENCLOSURE),
+ Separator = (Separator)GetInteger(KEY_SEPARATOR)
+ };
+ }
+
+ public bool SetNameFormat(NameFormat nf)
+ {
+ var pattern = SetValue(KEY_PATTERN, nf.UsePattern);
+ var label = SetValue(KEY_LABEL, nf.UseLabel);
+ var package = SetValue(KEY_PACKAGE, nf.UsePackage);
+ var version = SetValue(KEY_VERSION, nf.UseVersion);
+ var build = SetValue(KEY_BUILD, nf.UseBuild);
+ var enclosure = SetValue(KEY_SUFFIXENCLOSURE, nf.UseSuffixEnclosure);
+ var separator = SetValue(KEY_SEPARATOR, (int)nf.Separator);
+ return pattern && label && package && version && build && enclosure && separator;
+ }
+ #endregion
+ }
+}
diff --git a/ApkManager/Lib/FileAssociation.cs b/ApkManager/Lib/FileAssociation.cs
new file mode 100644
index 0000000..c5179b5
--- /dev/null
+++ b/ApkManager/Lib/FileAssociation.cs
@@ -0,0 +1,80 @@
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace ApkManager
+{
+ class FileAssociation
+ {
+ private static readonly string APK_EXTENSION = ".apk";
+ private static readonly string APK_FILE = "apkfile";
+ private static readonly string APK_DESCRIPTION = "Android Application";
+ private static readonly RegistryKey Root = Registry.ClassesRoot;
+ private static readonly string ExePath = Path.GetFullPath(System.Reflection.Assembly.GetEntryAssembly().Location);
+
+ public static bool SetAsDefault(bool value)
+ {
+ try
+ {
+ if (value)
+ {
+ Root.CreateSubKey(APK_EXTENSION).SetValue("", APK_FILE);
+ using (var key = Root.CreateSubKey(APK_FILE))
+ {
+ key.SetValue("", APK_DESCRIPTION);
+ key.CreateSubKey("DefaultIcon").SetValue("", $"\"{ExePath}\"");
+ key.CreateSubKey("Shell\\Open\\Command").SetValue("", $"\"{ExePath}\" /i \"%1\"");
+ }
+ }
+ else
+ {
+ Root.DeleteSubKeyTree(APK_EXTENSION);
+ Root.DeleteSubKeyTree(APK_FILE);
+ }
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Registry: {0}", e.Message);
+ return false;
+ }
+ }
+
+ public static bool IsDefault()
+ {
+ try
+ {
+ using (var key = Root.OpenSubKey(APK_EXTENSION, false))
+ {
+ if (key == null)
+ return false;
+
+ if (key.GetValue("").ToString() != APK_FILE)
+ return false;
+ }
+
+ using (var key = Root.OpenSubKey(APK_FILE, false))
+ {
+ if (key == null)
+ return false;
+
+ var regValue = key.OpenSubKey("Shell\\Open\\Command")?.GetValue("");
+
+ if (regValue == null)
+ return false;
+
+ if (regValue.ToString() != $"\"{ExePath}\" /i \"%1\"")
+ return false;
+ }
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Print("Registry: {0}", e.Message);
+ return false;
+ }
+ }
+ }
+}
diff --git a/ApkManager/Lib/Lib.cs b/ApkManager/Lib/Lib.cs
new file mode 100644
index 0000000..bad2d0d
--- /dev/null
+++ b/ApkManager/Lib/Lib.cs
@@ -0,0 +1,26 @@
+namespace ApkManager.Lib
+{
+ public class WindowPosition
+ {
+ public double Top { get; set; }
+ public double Left { get; set; }
+ }
+
+ public class NameFormat
+ {
+ public string UsePattern { get; set; }
+ public bool UseLabel { get; set; }
+ public bool UsePackage { get; set; }
+ public bool UseVersion { get; set; }
+ public bool UseBuild { get; set; }
+ public bool UseSuffixEnclosure { get; set; }
+ public Separator Separator { get; set; }
+ }
+
+ public enum Separator
+ {
+ Space,
+ Strip,
+ Underscore
+ }
+}
diff --git a/ApkManager/Lib/LibExtended.cs b/ApkManager/Lib/LibExtended.cs
new file mode 100644
index 0000000..ef6f105
--- /dev/null
+++ b/ApkManager/Lib/LibExtended.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace ApkManager.Lib
+{
+ public static class LibExtended
+ {
+ public static bool IsValidIPAddress(this string text)
+ {
+ return Regex.IsMatch(text, @"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{4})?$");
+ }
+
+ public static bool IsMatch(this string text, string pattern)
+ {
+ var match = Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase);
+ Debug.Print("Text: {0} -> Pattern: {1} -> {2}", text, pattern, match);
+ return match;
+ }
+
+ public static bool IsMatchInTextAndNotInLabel(this Tuple tuple, string word)
+ {
+ var pattern = string.Format("[\\W_]{0}([\\W_]|$)", word);
+ var mtext = tuple.Item1.IsMatch(pattern);
+ var mlabel = tuple.Item2.IsMatch(pattern);
+ return mtext && !mlabel;
+ }
+
+ public static string Append(this string text, string appendtext)
+ {
+ var separator = string.IsNullOrWhiteSpace(text) ? "" : " ";
+ return string.Concat(text, separator, appendtext).Trim();
+ }
+
+ public static string AppendExtApk(this string text)
+ {
+ if (!text.ToLower().EndsWith(".apk"))
+ return string.Concat(text, ".apk").Trim();
+ else
+ return text.Trim();
+ }
+
+ public static string TrimInvalidFileNameChars(this string text)
+ {
+ foreach (var f in text)
+ foreach (var i in Path.GetInvalidFileNameChars())
+ if (f == i) text = text.Replace(f.ToString(), "");
+
+ return text.Trim();
+ }
+ }
+}
diff --git a/ApkManager/Lib/aapt2.exe b/ApkManager/Lib/aapt2.exe
new file mode 100644
index 0000000..7ea8dfb
Binary files /dev/null and b/ApkManager/Lib/aapt2.exe differ
diff --git a/ApkManager/Lib/adb.exe b/ApkManager/Lib/adb.exe
new file mode 100644
index 0000000..fcbcf5c
Binary files /dev/null and b/ApkManager/Lib/adb.exe differ
diff --git a/ApkManager/MainWindow.xaml b/ApkManager/MainWindow.xaml
index 4245d47..26158dd 100644
--- a/ApkManager/MainWindow.xaml
+++ b/ApkManager/MainWindow.xaml
@@ -1,12 +1,126 @@
-
+ Title="APK Manager" Icon="Resources/Playstore.ico" ShowIconOnTitleBar="False"
+ Width="450" ResizeMode="NoResize" SizeToContent="Height" WindowStartupLocation="CenterScreen"
+ AllowDrop="True" Drop="OnFileDrop" Loaded="OnLoaded" Closing="OnClosing">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/ApkManager/MainWindow.xaml.cs b/ApkManager/MainWindow.xaml.cs
index cf5d43c..4213f4e 100644
--- a/ApkManager/MainWindow.xaml.cs
+++ b/ApkManager/MainWindow.xaml.cs
@@ -1,28 +1,199 @@
-using System;
-using System.Collections.Generic;
+using ApkManager.Lib;
+using MahApps.Metro.Controls;
+using MahApps.Metro.Controls.Dialogs;
+using Microsoft.Win32;
+using System;
+using System.IO;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
namespace ApkManager
{
///
/// Interaction logic for MainWindow.xaml
///
- public partial class MainWindow : Window
+ public partial class MainWindow : MetroWindow
{
- public MainWindow()
+ private Config cfg;
+ private Apk loadedApk;
+ private string pathApk;
+
+ #region WINDOW
+ public MainWindow(string pathApk = null)
{
InitializeComponent();
+
+ // make all flyout width same as root
+ foreach (Flyout flayout in Flyouts.Items)
+ flayout.Width = this.Width;
+
+ cfg = new Config();
+ this.pathApk = pathApk;
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ // loading position
+ if (cfg.GetWindowPostition())
+ {
+ var position = cfg.WindowPostition();
+ if (position.Top!= 0 && position.Left != 0)
+ {
+ WindowStartupLocation = WindowStartupLocation.Manual;
+ this.Top = position.Top;
+ this.Left = position.Left;
+ }
+ }
+
+ // loading switch settings
+ swHander.IsEnabled = App.IsAdministrator();
+ swHander.IsChecked = FileAssociation.IsDefault();
+ swInstance.IsChecked = cfg.SingleInstance();
+ swWindow.IsChecked = cfg.GetWindowPostition();
+
+ if (!string.IsNullOrWhiteSpace(pathApk))
+ txtPath.Text = pathApk;
+ }
+
+ private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ if (cfg.GetWindowPostition())
+ cfg.WindowPostition(new WindowPosition() { Top = this.Top, Left = this.Left });
+ }
+
+ private void OnFileDrop(object sender, DragEventArgs e)
+ {
+ if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
+
+ var files = e.Data.GetData(DataFormats.FileDrop) as string[];
+ var apk = files.Where(f => f.ToLower().EndsWith(".apk")).FirstOrDefault();
+
+ if (!string.IsNullOrWhiteSpace(apk))
+ txtPath.Text = apk;
+ }
+ #endregion
+
+ #region METHOD
+ private void ShowLoading(bool state = true)
+ {
+ Dispatcher.Invoke(() =>
+ PanelLoading.Visibility = state ? Visibility.Visible : Visibility.Collapsed
+ );
+ }
+ #endregion
+
+ private async void TxtPath_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ if(!(sender is TextBox tb)) return;
+
+ txtLabel.Text = ". . . .";
+ txtPackage.Text = ". . . .";
+ txtVersion.Text = ". . . .";
+ txtArch.Text = ". . . .";
+ txtArch.ToolTip = null;
+ txtSdk.Text = ". . . .";
+ imgIcon.Source = App.GetPlaystoreImageFromResources();
+ gbAction.IsEnabled = false;
+
+ if (string.IsNullOrWhiteSpace(tb.Text)) return;
+ if (loadedApk == null || loadedApk.FilePath != tb.Text)
+ {
+ ShowLoading();
+
+ var aapt = await Aapt.DumbBadging(tb.Text);
+ if (aapt.Success)
+ {
+ loadedApk = aapt.Apk;
+ txtLabel.Text = loadedApk.Label;
+ txtPackage.Text = loadedApk.PackageName;
+ txtVersion.Text = string.Format("{0} ( {1} )", loadedApk.VersionName, loadedApk.VersionCode);
+
+ var foundOne = loadedApk.Platforms.Count <= 1;
+ txtArch.Text = foundOne ? loadedApk.AbiList : "see list";
+ txtArch.ToolTip = foundOne ? null : loadedApk.AbiList;
+ txtArch.FontStyle = foundOne ? FontStyles.Normal : FontStyles.Italic;
+ txtArch.Foreground = foundOne ? txtSdk.Foreground : Brushes.Blue;
+
+ txtSdk.Text = loadedApk.SdkVersion.ToString();
+ imgIcon.Source = loadedApk.Icon;
+ gbAction.IsEnabled = true;
+ }
+ else
+ {
+ txtLabel.Text = "file corrupt?";
+ txtPackage.Text = "not an apk file?";
+ txtVersion.Text = "???";
+ txtArch.Text = "???";
+ txtSdk.Text = "???";
+ }
+
+ ShowLoading(false);
+ }
+ }
+
+ private void FileOpen_Click(object sender, RoutedEventArgs e)
+ {
+ var open = new OpenFileDialog()
+ {
+ Title = "Select APK",
+ Filter = "Android Package|*.apk",
+ DefaultExt = "apk",
+ RestoreDirectory = true,
+ Multiselect = false
+ };
+
+ if (open.ShowDialog() == true)
+ txtPath.Text = open.FileName;
+ }
+
+ private void MenuSettings_Click(object sender, RoutedEventArgs e)
+ {
+ menuSettings.IsOpen = !menuSettings.IsOpen;
+ }
+
+ private void SwitchSettings_Click(object sender, RoutedEventArgs e)
+ {
+ if (!(sender is ToggleSwitch ts)) return;
+ var tag = ts.Tag as string;
+ var isChecked = ts.IsChecked == true;
+
+ if (tag == "Handler")
+ ts.IsChecked = FileAssociation.SetAsDefault(isChecked);
+ if (tag == "Instance")
+ cfg.SingleInstance(isChecked);
+ if (tag == "Window")
+ cfg.SetWindowPostition(isChecked);
+ }
+
+ private void ButtonReset_Click(object sender, RoutedEventArgs e)
+ {
+ txtPath.Text = string.Empty;
+ }
+
+ private async void ButtonRenamer_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var window = new RenamerWindow(loadedApk);
+ if (window.ShowDialog().Value == false) return;
+
+ var newdest = window.GetFileDestination();
+ File.Move(loadedApk.FilePath, newdest);
+ txtPath.Text = newdest;
+ }
+ catch (Exception ex)
+ {
+ await this.ShowMessageAsync("Renamer", ex.Message);
+ }
+ }
+
+ private void ButtonInstaller_Click(object sender, RoutedEventArgs e)
+ {
+ new AdbWindow(loadedApk).ShowDialog();
}
}
-}
+}
\ No newline at end of file
diff --git a/ApkManager/Properties/AssemblyInfo.cs b/ApkManager/Properties/AssemblyInfo.cs
index 82c462c..44174c4 100644
--- a/ApkManager/Properties/AssemblyInfo.cs
+++ b/ApkManager/Properties/AssemblyInfo.cs
@@ -51,5 +51,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("2020.4.1.1333")]
+[assembly: AssemblyFileVersion("2020.4.1.1333")]
diff --git a/ApkManager/RenamerWindow.xaml b/ApkManager/RenamerWindow.xaml
new file mode 100644
index 0000000..c26074d
--- /dev/null
+++ b/ApkManager/RenamerWindow.xaml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApkManager/RenamerWindow.xaml.cs b/ApkManager/RenamerWindow.xaml.cs
new file mode 100644
index 0000000..513d5c5
--- /dev/null
+++ b/ApkManager/RenamerWindow.xaml.cs
@@ -0,0 +1,252 @@
+using ApkManager.Lib;
+using MahApps.Metro.Controls;
+using System;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using Separator = ApkManager.Lib.Separator;
+
+namespace ApkManager
+{
+ ///
+ /// Interaction logic for RenamerWindow.xaml
+ ///
+ public partial class RenamerWindow : MetroWindow
+ {
+ private Apk apk;
+ private Config config;
+ private string destination;
+
+ public RenamerWindow(Apk apk)
+ {
+ InitializeComponent();
+
+ this.apk = apk;
+ this.config = new Config();
+ }
+
+ public string GetFileDestination()
+ {
+ return destination;
+ }
+
+ private void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ var name = Path.GetFileName(apk.FilePath);
+ tbSource.Text = name;
+ tbDestination.Text = name;
+
+ // get config
+ var nf = config.GetNameFormat();
+
+ // set pattern
+ tbPattern.Text = nf.UsePattern;
+
+ // set base
+ cbBaseLabel.IsChecked = nf.UseLabel;
+ cbBasePackage.IsChecked = nf.UsePackage;
+ cbBaseVersion.IsChecked = nf.UseVersion;
+ cbBaseBuild.IsChecked = nf.UseBuild;
+ // make sure name base not empty
+ if (!nf.UseLabel && !nf.UsePackage)
+ cbBaseLabel.IsChecked = true;
+
+ // set suffix
+ var tuple = new Tuple(name, apk.Label);
+ cbSuffixPro.IsChecked = tuple.IsMatchInTextAndNotInLabel("pro");
+ cbSuffixPremium.IsChecked = tuple.IsMatchInTextAndNotInLabel("premium");
+ cbSuffixPaid.IsChecked = tuple.IsMatchInTextAndNotInLabel("paid");
+ cbSuffixDonate.IsChecked = tuple.IsMatchInTextAndNotInLabel("donate");
+ cbSuffixVip.IsChecked = tuple.IsMatchInTextAndNotInLabel("vip");
+ cbSuffixFull.IsChecked = tuple.IsMatchInTextAndNotInLabel("full");
+ cbSuffixPatched.IsChecked = tuple.IsMatchInTextAndNotInLabel("patched");
+ cbSuffixUnlocked.IsChecked = tuple.IsMatchInTextAndNotInLabel("unlock(ed)?");
+ cbSuffixMod.IsChecked = tuple.IsMatchInTextAndNotInLabel("mod(ded)?");
+ cbSuffixAdFree.IsChecked = tuple.IsMatchInTextAndNotInLabel("ad[-| ]?free");
+ cbSuffixLite.IsChecked = tuple.IsMatchInTextAndNotInLabel("lite");
+ cbSuffixFinal.IsChecked = tuple.IsMatchInTextAndNotInLabel("final");
+ cbSuffixBeta.IsChecked = tuple.IsMatchInTextAndNotInLabel("beta");
+
+ // set separator
+ cbSuffixEnclosure.IsChecked = nf.UseSuffixEnclosure;
+ switch (nf.Separator)
+ {
+ case Separator.Strip:
+ rbSeparatorStrip.IsChecked = true;
+ break;
+ case Separator.Underscore:
+ rbSeparatorUnderscore.IsChecked = true;
+ break;
+ default:
+ rbSeparatorSpace.IsChecked = true;
+ break;
+ }
+
+ // preview changed
+ CheckChanged_Click(sender, e);
+ }
+
+ private void CheckBox_Click(object sender, RoutedEventArgs e)
+ {
+ var cb = sender as CheckBox;
+
+ // check if label & package is not used
+ if (cb.Content == cbBaseLabel.Content && cb.IsChecked == false && cbBasePackage.IsChecked == false)
+ cbBasePackage.IsChecked = true;
+ if (cb.Content == cbBasePackage.Content && cb.IsChecked == false && cbBaseLabel.IsChecked == false)
+ cbBaseLabel.IsChecked = true;
+
+ CheckChanged_Click(sender, e);
+ }
+
+ private void CheckChanged_Click(object sender, RoutedEventArgs e)
+ {
+ // base
+ var namebase = string.Empty;
+ if (cbBaseLabel.IsChecked == true)
+ namebase = namebase.Append(apk.Label);
+ if (cbBasePackage.IsChecked == true)
+ namebase = namebase.Append(apk.PackageName);
+ if (cbBaseVersion.IsChecked == true)
+ namebase = namebase.Append((apk.VersionName.ToLower().StartsWith("v") ? "" : "v") + apk.VersionName);
+ if (cbBaseBuild.IsChecked == true)
+ namebase = namebase.Append("b" + apk.VersionCode);
+
+ // base final
+ if (rbSeparatorStrip.IsChecked == true)
+ namebase = namebase.Replace(" ", "-");
+ if (rbSeparatorUnderscore.IsChecked == true)
+ namebase = namebase.Replace(" ", "_");
+
+ // suffix
+ var namesuffix = string.Empty;
+ if (cbSuffixPro.IsChecked == true)
+ namesuffix = namesuffix.Append("Pro");
+ if (cbSuffixPremium.IsChecked == true)
+ namesuffix = namesuffix.Append("Premium");
+ if (cbSuffixPaid.IsChecked == true)
+ namesuffix = namesuffix.Append("Paid");
+ if (cbSuffixDonate.IsChecked == true)
+ namesuffix = namesuffix.Append("Donate");
+ if (cbSuffixVip.IsChecked == true)
+ namesuffix = namesuffix.Append("VIP");
+ if (cbSuffixFull.IsChecked == true)
+ namesuffix = namesuffix.Append("Full");
+ if (cbSuffixPatched.IsChecked == true)
+ namesuffix = namesuffix.Append("Patched");
+ if (cbSuffixUnlocked.IsChecked == true)
+ namesuffix = namesuffix.Append("Unlocked");
+ if (cbSuffixMod.IsChecked == true)
+ namesuffix = namesuffix.Append("Mod");
+ if (cbSuffixAdFree.IsChecked == true)
+ namesuffix = namesuffix.Append("AdFree");
+ if (cbSuffixLite.IsChecked == true)
+ namesuffix = namesuffix.Append("Lite");
+ if (cbSuffixFinal.IsChecked == true)
+ namesuffix = namesuffix.Append("Final");
+ if (cbSuffixBeta.IsChecked == true)
+ namesuffix = namesuffix.Append("Beta");
+
+ // suffix final
+ if (cbSuffixEnclosure.IsChecked == true)
+ {
+ namesuffix = Regex.Replace(namesuffix, "(\\w+)", delegate (Match match) {
+ return string.Format("[{0}]", match.Groups[1].Value);
+ }).Replace(" ","");
+ }
+ else
+ {
+ if (rbSeparatorStrip.IsChecked == true)
+ namesuffix = namesuffix.Replace(" ", "-");
+ if (rbSeparatorUnderscore.IsChecked == true)
+ namesuffix = namesuffix.Replace(" ", "_");
+ }
+
+ // define final name
+ var name = string.Empty;
+
+ // pattern override
+ if (!string.IsNullOrWhiteSpace(tbPattern.Text))
+ {
+ name = tbPattern.Text
+ .Replace("%label%", apk.Label)
+ .Replace("%package%", apk.PackageName)
+ .Replace("%version%", apk.VersionName)
+ .Replace("%build%", apk.VersionCode.ToString())
+ .Replace("%base%", namebase)
+ .Replace("%suffix%", namesuffix);
+ }
+ else
+ {
+ var separator = "";
+ if (rbSeparatorSpace.IsChecked == true)
+ separator = " ";
+ if (rbSeparatorStrip.IsChecked == true && cbSuffixEnclosure.IsChecked == false)
+ separator = "-";
+ if (rbSeparatorUnderscore.IsChecked == true && cbSuffixEnclosure.IsChecked == false)
+ separator = "_";
+
+ name = string.Format("{0}{1}{2}", namebase, separator, namesuffix).Trim();
+ }
+
+ // set new name
+ tbDestination.Text = name.AppendExtApk().TrimInvalidFileNameChars();
+ }
+
+ private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ var invalidchars = Path.GetInvalidFileNameChars().Where(c => e.Text.Where(t => t == c).Count() > 0);
+ e.Handled = invalidchars.Count() > 0;
+ base.OnPreviewTextInput(e);
+ }
+
+ private void TbPattern_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ CheckChanged_Click(sender, e);
+ }
+
+ private void TbDestination_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ var tb = sender as TextBox;
+ btnRenamer.IsEnabled = false;
+ if (!string.IsNullOrWhiteSpace(tb.Text))
+ {
+ try
+ {
+ var filename = tb.Text.AppendExtApk();
+ var folder = Path.GetDirectoryName(apk.FilePath);
+ destination = Path.Combine(folder, filename);
+
+ if (!File.Exists(destination))
+ btnRenamer.IsEnabled = true;
+ }
+ catch (Exception) { }
+ }
+ }
+
+ private void ButtonRename_Click(object sender, RoutedEventArgs e)
+ {
+ var nf = new NameFormat()
+ {
+ UsePattern = tbPattern.Text,
+ UseLabel = cbBaseLabel.IsChecked == true,
+ UsePackage = cbBasePackage.IsChecked == true,
+ UseVersion = cbBaseVersion.IsChecked == true,
+ UseBuild = cbBaseBuild.IsChecked == true,
+ UseSuffixEnclosure = cbSuffixEnclosure.IsChecked == true
+ };
+ if (rbSeparatorSpace.IsChecked == true)
+ nf.Separator = Separator.Space;
+ if (rbSeparatorStrip.IsChecked == true)
+ nf.Separator = Separator.Strip;
+ if (rbSeparatorUnderscore.IsChecked == true)
+ nf.Separator = Separator.Underscore;
+ config.SetNameFormat(nf);
+
+ this.DialogResult = true;
+ }
+ }
+}
diff --git a/ApkManager/Resources/Android.ico b/ApkManager/Resources/Android.ico
new file mode 100644
index 0000000..a751858
Binary files /dev/null and b/ApkManager/Resources/Android.ico differ
diff --git a/ApkManager/Resources/Playstore.ico b/ApkManager/Resources/Playstore.ico
new file mode 100644
index 0000000..0ff00db
Binary files /dev/null and b/ApkManager/Resources/Playstore.ico differ
diff --git a/ApkManager/Resources/Playstore.png b/ApkManager/Resources/Playstore.png
new file mode 100644
index 0000000..98fe3da
Binary files /dev/null and b/ApkManager/Resources/Playstore.png differ
diff --git a/ApkManager/packages.config b/ApkManager/packages.config
new file mode 100644
index 0000000..082fd6a
--- /dev/null
+++ b/ApkManager/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file