diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/ApkInfoPanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/ApkInfoPanel.xaml.cs
index 484e5bd..46b4dc5 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/ApkInfoPanel.xaml.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/ApkInfoPanel.xaml.cs
@@ -20,6 +20,7 @@ using QuickLook.Common.Helpers;
using QuickLook.Common.Plugin;
using QuickLook.Plugin.AppViewer.PackageParsers.Apk;
using System;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
@@ -63,32 +64,45 @@ public partial class ApkInfoPanel : UserControl, IAppInfoPanel
if (File.Exists(path))
{
var size = new FileInfo(path).Length;
- ApkInfo apkInfo = ApkParser.Parse(path);
+ IApkInfo info = Path.GetExtension(path).ToLower() switch
+ {
+ ".apk" => ApkParser.Parse(path),
+ ".aab" => AabParser.Parse(path),
+ _ => throw new NotSupportedException("Extension is not supported."),
+ };
var last = File.GetLastWriteTime(path);
Dispatcher.Invoke(() =>
{
- applicationName.Text = apkInfo.Label;
- versionName.Text = apkInfo.VersionName;
- versionCode.Text = apkInfo.VersionCode;
- abis.Text = string.Join(", ", apkInfo.ABIs ?? []);
- packageName.Text = apkInfo.PackageName;
- minSdkVersion.Text = apkInfo.MinSdkVersion;
- targetSdkVersion.Text = apkInfo.TargetSdkVersion;
+ applicationName.Text = info.Label;
+ versionName.Text = info.VersionName;
+ versionCode.Text = info.VersionCode;
+ abis.Text = string.Join(", ", info.ABIs ?? []);
+ packageName.Text = info.PackageName;
+ minSdkVersion.Text = info.MinSdkVersion;
+ targetSdkVersion.Text = info.TargetSdkVersion;
totalSize.Text = size.ToPrettySize(2);
modDate.Text = last.ToString(CultureInfo.CurrentCulture);
- permissions.ItemsSource = apkInfo.Permissions;
+ permissions.ItemsSource = info.Permissions;
- if (apkInfo.HasIcon)
+ if (info.HasIcon)
{
- using var stream = new MemoryStream(apkInfo.Logo);
- var icon = new BitmapImage();
- icon.BeginInit();
- icon.CacheOption = BitmapCacheOption.OnLoad;
- icon.StreamSource = stream;
- icon.EndInit();
- icon.Freeze();
- image.Source = icon;
+ try
+ {
+ using var stream = new MemoryStream(info.Logo);
+ var icon = new BitmapImage();
+ icon.BeginInit();
+ icon.CacheOption = BitmapCacheOption.OnLoad;
+ icon.StreamSource = stream;
+ icon.EndInit();
+ icon.Freeze();
+ image.Source = icon;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ image.Source = new BitmapImage(new Uri("pack://application:,,,/QuickLook.Plugin.AppViewer;component/Resources/android.png"));
+ }
}
else
{
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabInfo.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabInfo.cs
new file mode 100644
index 0000000..64ca1e6
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabInfo.cs
@@ -0,0 +1,20 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Apk;
+
+public class AabInfo : ApkInfo;
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabParser.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabParser.cs
new file mode 100644
index 0000000..abb5e93
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/AabParser.cs
@@ -0,0 +1,72 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using ICSharpCode.SharpZipLib.Zip;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Apk;
+
+public static class AabParser
+{
+ public static AabInfo Parse(string path)
+ {
+ try
+ {
+ using var zip = new ZipFile(path);
+
+ var aabReader = new ApkReader.AabReader();
+ ApkReader.AabInfo baseInfo = aabReader.Read(path);
+ AabInfo info = new()
+ {
+ VersionName = baseInfo.VersionName,
+ VersionCode = baseInfo.VersionCode,
+ TargetSdkVersion = ApiLevel.Create(baseInfo.TargetSdkVersion).ToString(),
+ Permissions = baseInfo.Permissions,
+ PackageName = baseInfo.PackageName,
+ MinSdkVersion = ApiLevel.Create(baseInfo.MinSdkVersion).ToString(),
+ Icon = baseInfo.Icon,
+ Icons = baseInfo.Icons,
+ Label = baseInfo.Label,
+ Labels = baseInfo.Labels,
+ Locales = baseInfo.Locales,
+ Densities = baseInfo.Densities,
+ ABIs = [.. baseInfo.Abis],
+ LaunchableActivity = baseInfo.LaunchableActivity,
+ };
+
+ if (baseInfo.HasIcon)
+ {
+ ZipEntry entry = zip.GetEntry(baseInfo.Icons.Values
+ .Where(icon => icon.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
+ .LastOrDefault());
+ using var s = new BinaryReader(zip.GetInputStream(entry));
+ info.Logo = s.ReadBytes((int)entry.Size);
+ }
+
+ return info;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
+ return new AabInfo();
+ }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/ApkInfo.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/ApkInfo.cs
index 807437f..c1afc50 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/ApkInfo.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/ApkInfo.cs
@@ -19,7 +19,7 @@ using System.Collections.Generic;
namespace QuickLook.Plugin.AppViewer.PackageParsers.Apk;
-public class ApkInfo
+public class ApkInfo : IApkInfo
{
public string VersionName { get; set; }
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/IApkInfo.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/IApkInfo.cs
new file mode 100644
index 0000000..c20de2f
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Apk/IApkInfo.cs
@@ -0,0 +1,55 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System.Collections.Generic;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Apk;
+
+public interface IApkInfo
+{
+ public string VersionName { get; set; }
+
+ public string VersionCode { get; set; }
+
+ public string TargetSdkVersion { get; set; }
+
+ public List Permissions { get; set; }
+
+ public string PackageName { get; set; }
+
+ public string MinSdkVersion { get; set; }
+
+ public string Icon { get; set; }
+
+ public Dictionary Icons { get; set; }
+
+ public byte[] Logo { get; set; }
+
+ public string Label { get; set; }
+
+ public Dictionary Labels { get; set; }
+
+ public bool HasIcon { get; }
+
+ public List Locales { get; set; }
+
+ public List Densities { get; set; }
+
+ public string LaunchableActivity { get; set; }
+
+ public string[] ABIs { get; set; }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/Plugin.cs
index b3eb1ba..ff16758 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/Plugin.cs
@@ -30,8 +30,8 @@ public class Plugin : IViewer
[
// Android
".apk", ".apk.1", // Android Package
+ ".aab", // Android App Bundle
//".aar", // Android Archive
- //".aab", // Android App Bundle
// Windows
".appx", ".appxbundle", // Windows APPX installer
@@ -51,7 +51,7 @@ public class Plugin : IViewer
// Ubuntu
".deb", // Debian Package
".appimage", // AppImage Format
- ".rpm", // Red Hat Package Manager
+ ".rpm", // Red Hat Package Manager
// Others
".wgt", ".wgtu", // UniApp Widget
@@ -75,7 +75,7 @@ public class Plugin : IViewer
{
context.PreferredSize = Path.GetExtension(ConfirmPath(path)).ToLower() switch
{
- ".apk" => new Size { Width = 600, Height = 510 },
+ ".apk" or ".aab" => new Size { Width = 600, Height = 510 },
".ipa" => new Size { Width = 560, Height = 510 },
".hap" => new Size { Width = 560, Height = 500 },
".msi" => new Size { Width = 560, Height = 230 },
@@ -99,7 +99,7 @@ public class Plugin : IViewer
_path = path;
_ip = Path.GetExtension(ConfirmPath(path)).ToLower() switch
{
- ".apk" => new ApkInfoPanel(context),
+ ".apk" or ".aab" => new ApkInfoPanel(context),
".ipa" => new IpaInfoPanel(context),
".hap" => new HapInfoPanel(context),
".msi" => new MsiInfoPanel(context),