// TODO: Document
 public static void LoadPackageIntoStorage(ApplicationPackage package, string pathStorage)
 {
     File.Copy(package.PackagePath,
               Path.Combine(GetAppDirPathInStorage(package.PackageName, pathStorage),
                            NamingHelper.GetPackageNameInStorage(package)),
               true);
 }
 // TODO: Document
 public static string GetPackageNameInStorage(ApplicationPackage appPackage)
 {
     if (appPackage.GetType() == typeof(AndroidApplicationPackage))
     {
         return String.Format("{0}-{1}.apk", appPackage.PackageName, appPackage.CodeVersion);
     }
     else
     {
         return String.Format("{0}-{1}", appPackage.PackageName, appPackage.CodeVersion);
     }
 }
 public PatchManifest(string patchName, ApplicationPackage referencePackage, ApplicationPackage targetePackage,
                      IEnumerable<string> filesNEW, IEnumerable<string> filesDELETE,
                      IEnumerable<string> filesUPDATED)
 {
     this.PatchName = patchName;
     this.ReferencePackage = referencePackage;
     this.TargetPackage = targetePackage;
     FilesNEW = new SerializableHashSet<string>(filesNEW);
     FilesDELETE = new SerializableHashSet<string>(filesDELETE);
     FilesUPDATED = new SerializableHashSet<string>(filesUPDATED);
 }
 public bool ValidatePackage(ApplicationPackage appPackage)
 {
     return true;
 }
        /// <summary>
        /// Add the specified package to the storage and update the meta information
        /// </summary>
        /// <param name="appPackage"></param>
        public void LoadPackageIntoStorage(ApplicationPackage appPackage)
        {
            // If application is new and is not presented in storage yet
            if (!StorageOperations.IsAppInStorage(appPackage.PackageName, PathStorage))
            {
                StorageOperations.CreateAppDirInStorage(appPackage.PackageName, PathStorage);
                StorageOperations.CreateEmptyStoredAppInfo(appPackage.PackageName, appPackage.ApplicationName, PathStorage);
            }

            StoredAppInfo appInfo = StorageOperations.GetStoredAppInfo(appPackage.PackageName, PathStorage);

            // Add new version if it is not already in storage
            if (!appInfo.ContainsVersion(appPackage.CodeVersion))
            {
                string apkNameInStorage = NamingHelper.GetPackageNameInStorage(appPackage);

                // Physically copy package into storage
                StorageOperations.LoadPackageIntoStorage(appPackage, PathStorage);

                appInfo.AddVersion(appPackage.CodeVersion, apkNameInStorage);

                // Update meta info for the application and for the storage itself
                StorageOperations.UpdateStoredAppInfo(appInfo, PathStorage);
                StorageOperations.AddAppPackageToStorageInfo(appPackage.PackageName, PathStorage);
            }
        }
 public static string GetPatchName(ApplicationPackage reference, ApplicationPackage target)
 {
     return (String.Format("{0}-{1}-{2}.deltapatch", reference.PackageName, reference.CodeVersion, target.CodeVersion));
 }
 //public PatchManifest()
 //{
 //    PatchName = String.Empty;
 //    OldApk = target packageInfo();
 //    NewApk = target packageInfo();
 //    FilesNEW = new SerializableHashSet<string>();
 //    FilesDELETE = new SerializableHashSet<string>();
 //    FilesUPDATED = new SerializableHashSet<string>();
 //}
 //public PatchManifest(string patchName)
 //    : this()
 //{
 //    PatchName = patchName;
 //}
 public PatchManifest(string patchName, ApplicationPackage referencePackage, ApplicationPackage targetePackage)
 {
     this.PatchName = patchName;
     this.ReferencePackage = referencePackage;
     this.TargetPackage = targetePackage;
 }
 public void ComputePatch(ApplicationPackage reference, ApplicationPackage target, string pathOutputPatch)
 {
     IDeltaEncodingAlgorithm deltaEncodingAlgorithm = new BSDIFFDeltaEncodingAlgortihm();
     deltaEncodingAlgorithm.ComputeDelta(reference.PackagePath, target.PackagePath, pathOutputPatch);
 }
 /// <summary>
 /// Checks that the specified file exists and has .apk extension.
 /// </summary>
 /// <param name="pathApk">
 /// Path to the checked file.
 /// </param>
 /// <returns>
 /// TRUE - file is OK
 /// FALSE - file is NOT OK
 /// </returns>
 public static bool ValidatePackage(ApplicationPackage appPackage)
 {
     // Apk is considered bad only if the file does not exist or if it does not have .apk extension.
     if (!File.Exists(appPackage.PackagePath) || !Path.GetExtension(appPackage.PackagePath).Equals(".apk")) return false;
     return true;
 }
        /// <summary>
        /// Computes patch file for two versions of application presented as two APK files.
        /// Step 1 - Validate both APK files
        /// Step 2 - Change extensions from .apk to .zip and save new files' paths
        /// Step 3 - Create patch directory
        /// Step 4 - Decompress both APKs into the patch directory
        /// Step 5 - Load manifests for both APKs
        /// Step 6 - Construct sets of marked files (files are marked as DELETE, NEW, SAME, UPDATED)
        /// Step 7 - Copy files that are marked NEW into the constructed patch main directory
        /// Step 8 - Delta encode files that are marked UPDATED nad copy .diff files into the patch
        /// Step 9 - Create PatchManifest.xml
        /// Step 10 - Compress patch
        /// Clean up
        /// </summary>
        /// <param name="reference"></param>
        /// <param name="target"></param>
        /// <param name="pathOutputPatch"></param>
        public void ComputePatch(ApplicationPackage reference, ApplicationPackage target, string pathOutputPatch)
        {
            // Step 1 - Validate both APK files
            if (!AndroidPackageManager.ValidatePackage(reference) || !AndroidPackageManager.ValidatePackage(target))
            {
                LogManager.Log(LogMessageType.Error, "Bad APK input files", "Patcher");
                return;
            }

            // Step 2 - Change extensions from .apk to .zip and save new files' paths
            //pathOldApk = FileManager.ChangeExtension(pathOldApk, ".zip");
            //pathNewApk = FileManager.ChangeExtension(pathNewApk, ".zip");

            // Step 3 - Create patch directory
            string pathPatchingWorkDir = Patcher.GetPatchingWorkDir();
            ConstructPathingWorkDir(pathPatchingWorkDir);

            // Step 4 - Decompress both APKs into the temporary directory
            string pathReferenceDir = Path.Combine(pathPatchingWorkDir, NAME_REFERENCE_DIR);
            string pathTargetDir = Path.Combine(pathPatchingWorkDir, NAME_TARGET_DIR);
            CompressionManager.DecompressZIP(reference.PackagePath, pathReferenceDir);
            CompressionManager.DecompressZIP(target.PackagePath, pathTargetDir);

            // Step 5 - Load manifests for both APKs
            AndroidApplicationManifest referenceManifest = AndroidPackageManager.GetApplicationManifest(pathReferenceDir);
            AndroidApplicationManifest targetManifest = AndroidPackageManager.GetApplicationManifest(pathTargetDir);

            // Step 6 - Construct sets of marked files (files are marked as DELETE, NEW, SAME, UPDATED)
            // Mark DELETE files
            HashSet<AndroidApplicationManifestRecord> filesMarkedDELETE = MarkFilesDELETE(referenceManifest, targetManifest);
            // Mark NEW files
            HashSet<AndroidApplicationManifestRecord> filesMarkedNEW = MarkFilesNEW(referenceManifest, targetManifest);
            // Mark UPDATED files
            HashSet<AndroidApplicationManifestRecord> filesMarkedUPDATED = MarkFilesUPDATED(referenceManifest, targetManifest);

            // Step 7 - Copy files that are marked NEW into the constructed patch main directory
            foreach (AndroidApplicationManifestRecord record in filesMarkedNEW)
            {
                CopyFileIntoPatch(record.FileName, Path.Combine(pathPatchingWorkDir, NAME_PATCH_DIR), pathTargetDir);
            }

            // Step 8 - Delta encode files that are marked UPDATED nad copy .diff files into the patch
            var deltaEncodingAlgorithm = new BSDIFFDeltaEncodingAlgortihm();
            foreach (AndroidApplicationManifestRecord record in filesMarkedUPDATED)
            {
                string pathDiffFile = Path.Combine(pathPatchingWorkDir, NAME_PATCH_DIR, record.FileName.Replace('\\', '+')) + ".diff";
                deltaEncodingAlgorithm.ComputeDelta(Path.Combine(pathReferenceDir, record.FileName),
                                                    Path.Combine(pathTargetDir, record.FileName),
                                                    pathDiffFile);

                // We don't want to include .diff file into the patch if its size is greater than the new file size.
                // Instead, we include the new version of file itself.
                if (FileManager.GetFileSize(pathDiffFile) > FileManager.GetFileSize(Path.Combine(pathTargetDir, record.FileName)))
                {
                    File.Delete(pathDiffFile);
                    CopyFileIntoPatch(record.FileName, Path.Combine(pathPatchingWorkDir, NAME_PATCH_DIR), pathTargetDir);

                    // Add files to filesMarkedNEW and filesMarkedDELETE
                    // During deployment we will delete old file and copy the new one instead of patching the old file
                    filesMarkedDELETE.Add(record);
                    filesMarkedNEW.Add(record);
                }
            }
            // Remove all files that are in filesMarkedNEW from filesMarkedUPDATED
            filesMarkedUPDATED.ExceptWith(filesMarkedNEW);

            // Step 9 - Create PatchManifest.xml
            string patchName = NamingHelper.GetPatchName(reference, target);
            PatchManifest manifest = new PatchManifest(patchName, reference, target);
            // We need only files names in the manifest
            foreach (AndroidApplicationManifestRecord record in filesMarkedNEW)
            {
                manifest.AddFileNEW(record.FileName);
            }
            foreach (AndroidApplicationManifestRecord record in filesMarkedDELETE)
            {
                manifest.AddFileDELETE(record.FileName);
            }
            foreach (AndroidApplicationManifestRecord record in filesMarkedUPDATED)
            {
                manifest.AddFileUPDATED(record.FileName);
            }
            FileManager.SerializeObjectToXML(manifest, Path.Combine(pathPatchingWorkDir, NAME_PATCH_DIR, NAME_PATCH_MANIFEST));

            // Step 10 - Compress patch
            CompressionManager.CompressDirToZIP(Path.Combine(pathPatchingWorkDir, NAME_PATCH_DIR),
                                                Path.Combine(pathPatchingWorkDir, patchName));

            // Clean up
            // Delete temp directory
            FileManager.DeleteDirectory(pathPatchingWorkDir);

            Console.WriteLine(String.Format("Finished patch creation: {0} -> {1}", reference.PackagePath, target.PackagePath));
        }