Example #1
0
        public static void Save(Book.Book book, BookServer bookServer, Color backColor, WebSocketProgress progress, AndroidPublishSettings settings = null)
        {
            var progressWithL10N = progress.WithL10NPrefix("PublishTab.Android.File.Progress.");

            var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

            if (!string.IsNullOrWhiteSpace(Settings.Default.BloomDeviceFileExportFolder) && Directory.Exists(Settings.Default.BloomDeviceFileExportFolder))
            {
                folder = Settings.Default.BloomDeviceFileExportFolder;
            }
            var initialPath = Path.Combine(folder, book.Storage.FolderName + BookCompressor.BloomPubExtensionWithDot);

            var bloomdFileDescription = LocalizationManager.GetString("PublishTab.Android.bloomdFileFormatLabel", "Bloom Book for Devices",
                                                                      "This is shown in the 'Save' dialog when you save a bloom book in the format that works with the Bloom Reader Android App");
            var filter = $"{bloomdFileDescription}|*{BookCompressor.BloomPubExtensionWithDot}";

            var destFileName = Utils.MiscUtils.GetOutputFilePathOutsideCollectionFolder(initialPath, filter);

            if (String.IsNullOrEmpty(destFileName))
            {
                return;
            }

            Settings.Default.BloomDeviceFileExportFolder = Path.GetDirectoryName(destFileName);
            PublishToAndroidApi.CheckBookLayout(book, progress);
            PublishToAndroidApi.SendBook(book, bookServer, destFileName, null,
                                         progressWithL10N,
                                         (publishedFileName, bookTitle) => progressWithL10N.GetMessageWithParams("Saving", "{0} is a file path", "Saving as {0}", destFileName),
                                         null,
                                         backColor,
                                         settings: settings);
            PublishToAndroidApi.ReportAnalytics("file", book);
        }
Example #2
0
        public static void Save(Book.Book book, BookServer bookServer, Color backColor, WebSocketProgress progress, AndroidPublishSettings settings = null)
        {
            var progressWithL10N = progress.WithL10NPrefix("PublishTab.Android.File.Progress.");

            using (var dlg = new DialogAdapters.SaveFileDialogAdapter())
            {
                dlg.DefaultExt = BookCompressor.ExtensionForDeviceBloomBook;
                var bloomdFileDescription = LocalizationManager.GetString("PublishTab.Android.bloomdFileFormatLabel", "Bloom Book for Devices", "This is shown in the 'Save' dialog when you save a bloom book in the format that works with the Bloom Reader Android App");
                dlg.Filter   = $"{bloomdFileDescription}|*{BookCompressor.ExtensionForDeviceBloomBook}";
                dlg.FileName = Path.GetFileName(book.FolderPath) + BookCompressor.ExtensionForDeviceBloomBook;
                if (!string.IsNullOrWhiteSpace(Settings.Default.BloomDeviceFileExportFolder) &&
                    Directory.Exists(Settings.Default.BloomDeviceFileExportFolder))
                {
                    dlg.InitialDirectory = Settings.Default.BloomDeviceFileExportFolder;
                    //(otherwise leave to default save location)
                }
                dlg.OverwritePrompt = true;
                if (DialogResult.OK == dlg.ShowDialog())
                {
                    Settings.Default.BloomDeviceFileExportFolder = Path.GetDirectoryName(dlg.FileName);
                    PublishToAndroidApi.CheckBookLayout(book, progress);
                    PublishToAndroidApi.SendBook(book, bookServer, dlg.FileName, null,
                                                 progressWithL10N,
                                                 (publishedFileName, bookTitle) => progressWithL10N.GetMessageWithParams("Saving", "{0} is a file path", "Saving as {0}", dlg.FileName),
                                                 null,
                                                 backColor,
                                                 settings: settings);
                    PublishToAndroidApi.ReportAnalytics("file", book);
                }
            }
        }
Example #3
0
        /// <summary>
        /// Send the book to a client over local network, typically WiFi (at least on Android end).
        /// This is currently called on the UDPListener thread.
        /// Enhance: if we spin off another thread to do the transfer, especially if we create the file
        /// and read it into memory once and share the content, we can probably serve multiple
        /// requesting devices much faster. Currently, we are only handling one request at a time,
        /// since we pause advertising while sending and ignore requests that come in during sending.
        /// If the user switches away from the Android tab while a transfer
        /// is in progress, the thread will continue and complete the request. Quitting Bloom
        /// is likely to leave the transfer incomplete.
        /// </summary>
        /// <param name="book"></param>
        /// <param name="androidIpAddress"></param>
        /// <param name="androidName"></param>
        private void StartSendBookToClientOnLocalSubNet(Book.Book book, string androidIpAddress, string androidName, Color backColor)
        {
            // Locked in case more than one thread at a time can handle incoming packets, though I don't think
            // this is true. Also, Stop() on the main thread cares whether _wifiSender is null.
            lock (this)
            {
                // We only support one send at a time. If we somehow get more than one request, we ignore the other.
                // The device will retry soon if still listening and we are still advertising.
                if (_wifiSender != null)                 // indicates transfer in progress
                {
                    return;
                }
                // now THIS transfer is 'in progress' as far as any thread checking this is concerned.
                _wifiSender = new WebClient();
            }
            _wifiSender.UploadDataCompleted += (sender, args) =>
            {
                // Runs on the async transfer thread AFTER the transfer initiated below.
                if (args.Error != null)
                {
                    ReportException(args.Error);
                }
                // Should we report if canceled? Thinking not, we typically only cancel while shutting down,
                // it's probably too late for a useful report.

                // To avoid contention with Stop(), which may try to cancel the send if it finds
                // an existing wifiSender, and may destroy the advertiser we are trying to restart.
                lock (this)
                {
                    Debug.WriteLine($"upload completed, sender is {_wifiSender}, cancelled is {args.Cancelled}");
                    if (_wifiSender != null)                     // should be null only in a desperate abort-the-thread situation.
                    {
                        _wifiSender.Dispose();
                        _wifiSender = null;
                    }

                    if (_wifiAdvertiser != null)
                    {
                        _wifiAdvertiser.Paused = false;
                    }
                }
            };
            // Now we actually start the send...but using an async API, so there's no long delay here.
            PublishToAndroidApi.SendBook(book, _bookServer,
                                         null, (publishedFileName, bloomDPath) =>
            {
                var androidHttpAddress = "http://" + androidIpAddress + ":5914";                         // must match BloomReader SyncServer._serverPort.
                _wifiSender.UploadDataAsync(new Uri(androidHttpAddress + "/putfile?path=" + Uri.EscapeDataString(publishedFileName)), File.ReadAllBytes(bloomDPath));
            },
                                         _progress,
                                         (publishedFileName, bookTitle) => _progress.GetMessageWithParams(id: "Sending",
                                                                                                          comment: "{0} is the name of the book, {1} is the name of the device",
                                                                                                          message: "Sending \"{0}\" to device {1}",
                                                                                                          parameters: new object[] { bookTitle, androidName }),
                                         null,
                                         backColor);
            PublishToAndroidApi.ReportAnalytics("wifi", book);
        }
Example #4
0
 // internal virtual for testing only
 protected virtual void SendBookDoWork(Book.Book book, Color backColor)
 {
     PublishToAndroidApi.SendBook(book, _bookServer,
                                  null, (publishedFileName, path) =>
     {
         _lastPublishedBloomdSize = GetSizeOfBloomdFile(path);
         _androidDeviceUsbConnection.SendBook(path);
     },
                                  _progress,
                                  (publishedFileName, bookTitle) =>
                                  _androidDeviceUsbConnection.BookExists(publishedFileName) ?
                                  _progress.GetTitleMessage("ReplacingBook", "Replacing existing \"{0}\"...", bookTitle) :
                                  _progress.GetTitleMessage("SendingBook", "Sending \"{0}\" to your Android device...", bookTitle),
                                  publishedFileName => _androidDeviceUsbConnection.BookExists(publishedFileName),
                                  backColor);
     PublishToAndroidApi.ReportAnalytics("usb", book);
 }
Example #5
0
 /// <summary>
 /// Send the book to a client over local network, typically WiFi (at least on Android end).
 /// This is currently called on the UDPListener thread.
 /// Enhance: if we spin off another thread to do the transfer, especially if we create the file
 /// and read it into memory once and share the content, we can probably serve multiple
 /// requesting devices much faster. Currently, we are only handling one request at a time,
 /// since we pause advertising while sending and ignore requests that come in during sending.
 /// If the user switches away from the Android tab while a transfer
 /// is in progress, the thread will continue and complete the request. Quitting Bloom
 /// is likely to leave the transfer incomplete.
 /// </summary>
 private void StartSendBookToClientOnLocalSubNet(Book.Book book, string androidIpAddress, string androidName, Color backColor, AndroidPublishSettings settings = null)
 {
     // Locked in case more than one thread at a time can handle incoming packets, though I don't think
     // this is true. Also, Stop() on the main thread cares whether _wifiSender is null.
     lock (this)
     {
         // We only support one send at a time. If we somehow get more than one request, we ignore the other.
         // The device will retry soon if still listening and we are still advertising.
         if (_wifiSender != null)                 // indicates transfer in progress
         {
             return;
         }
         // now THIS transfer is 'in progress' as far as any thread checking this is concerned.
         _wifiSender = new WebClient();
     }
     _wifiSender.UploadDataCompleted += WifiSenderUploadCompleted;
     // Now we actually start the send...but using an async API, so there's no long delay here.
     PublishToAndroidApi.SendBook(book, _bookServer,
                                  null, (publishedFileName, bloomDPath) =>
     {
         var androidHttpAddress = "http://" + androidIpAddress + ":5914";                         // must match BloomReader SyncServer._serverPort.
         _wifiSender.UploadDataAsync(new Uri(androidHttpAddress + "/putfile?path=" + Uri.EscapeDataString(publishedFileName)), File.ReadAllBytes(bloomDPath));
         Debug.WriteLine($"upload started to http://{androidIpAddress}:5914 ({androidName}) for {publishedFileName}");
     },
                                  _progress,
                                  (publishedFileName, bookTitle) => _progress.GetMessageWithParams(idSuffix: "Sending",
                                                                                                   comment: "{0} is the name of the book, {1} is the name of the device",
                                                                                                   message: "Sending \"{0}\" to device {1}",
                                                                                                   parameters: new object[] { bookTitle, androidName }),
                                  null,
                                  backColor,
                                  settings: settings);
     // Occasionally preparing a book for sending will, despite our best efforts, result in a different sha.
     // For example, it might change missing or out-of-date mp3 files. In case the sha we just computed
     // is different from the one we're advertising, update the advertisement, so at least subsequent
     // advertisements will conform to the version the device just got.
     _wifiAdvertiser.BookVersion = BookCompressor.LastVersionCode;
     lock (this)
     {
         // The UploadDataCompleted event handler quit working at Bloom 4.6.1238 Alpha (Windows test build).
         // The data upload still works, but the event handler is *NEVER* called.  Trying to revise the upload
         // by using UploadDataTaskAsync with async/await  did not work any better: the await never happened.
         // To get around this bug, we introduce a timer that periodically checks the IsBusy flag of the
         // _wifiSender object.  It's a hack, but I haven't come up with anything better in two days of
         // looking at this problem.
         // See https://issues.bloomlibrary.org/youtrack/issue/BL-7227 for details.
         if (_uploadTimer == null)
         {
             _uploadTimer = new System.Timers.Timer
             {
                 Interval = 500.0,
                 Enabled  = false
             };
             _uploadTimer.Elapsed += (sender, args) =>
             {
                 if (_wifiSender != null && _wifiSender.IsBusy)
                 {
                     return;
                 }
                 _uploadTimer.Stop();
                 Debug.WriteLine("upload timed out, appears to be finished");
                 WifiSenderUploadCompleted(_uploadTimer, null);
             };
         }
         _uploadTimer.Start();
     }
     PublishToAndroidApi.ReportAnalytics("wifi", book);
 }