Exemplo n.º 1
0
        public void Start(Book.Book book, CollectionSettings collectionSettings, Color backColor)
        {
            if (_wifiAdvertiser != null)
            {
                Stop();
            }

            // This listens for a BloomReader to request a book.
            // It requires a firewall hole allowing Bloom to receive messages on _portToListen.
            // We initialize it before starting the Advertiser to avoid any chance of a race condition
            // where a BloomReader manages to request an advertised book before we start the listener.
            _wifiListener = new BloomReaderUDPListener();
            _wifiListener.NewMessageReceived += (sender, args) =>
            {
                var json = Encoding.UTF8.GetString(args.Data);
                try
                {
                    dynamic settings = JsonConvert.DeserializeObject(json);
                    // The property names used here must match the ones in BloomReader, doInBackground method of SendMessage,
                    // a private class of NewBookListenerService.
                    var androidIpAddress = (string)settings.deviceAddress;

                    var androidName = (string)settings.deviceName;
                    // This prevents the device (or other devices) from queuing up requests while we're busy with this one.
                    // In effect, the Android is only allowed to request a retry after we've given up this try at sending.
                    // Of course, there are async effects from network latency. But if we do get another request while
                    // handling this one, we will ignore it, since StartSendBook checks for a transfer in progress.
                    _wifiAdvertiser.Paused = true;
                    StartSendBookOverWiFi(book, androidIpAddress, androidName, backColor);
                    // Returns immediately. But we don't resume advertisements until the async send completes.
                }
                // If there's something wrong with the JSON (maybe an obsolete or newer version of reader?)
                // just ignore the request.
                catch (Exception ex) when(ex is JsonReaderException || ex is JsonSerializationException)
                {
                    _progress.Error(id: "BadBookRequest",
                                    message: "Got a book request we could not process. Possibly the device is running an incompatible version of BloomReader?");

                    //this is too technical/hard to translate
                    _progress.ErrorWithoutLocalizing($" Request contains {json}; trying to interpret as JSON we got {ex.Message}");
                }
            };

            var pathHtmlFile = book.GetPathHtmlFile();

            _wifiAdvertiser = new WiFiAdvertiser(_progress)
            {
                BookTitle     = BookStorage.SanitizeNameForFileSystem(book.Title),             // must be the exact same name as the file we will send if requested
                TitleLanguage = collectionSettings.Language1Iso639Code,
                BookVersion   = Book.Book.MakeVersionCode(File.ReadAllText(pathHtmlFile), pathHtmlFile)
            };

            AndroidView.CheckBookLayout(book, _progress);
            _wifiAdvertiser.Start();

            _progress.Message(id: "WifiInstructions1",
                              message: "On the Android, run Bloom Reader, open the menu and choose 'Receive Books from computer'.");
            _progress.Message(id: "WifiInstructions2",
                              message: "You can do this on as many devices as you like. Make sure each device is connected to the same network as this computer.");
        }
Exemplo n.º 2
0
        public static void Save(Book.Book book, BookServer bookServer, Color backColor, WebSocketProgress progress)
        {
            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)
                }
                if (DialogResult.OK == dlg.ShowDialog())
                {
                    Settings.Default.BloomDeviceFileExportFolder = Path.GetDirectoryName(dlg.FileName);
                    AndroidView.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);
                    PublishToAndroidApi.ReportAnalytics("file", book);
                }
            }
        }
Exemplo n.º 3
0
        protected override void LoadView(IShellContext shellContext)
        {
            base.LoadView(shellContext);

            var recycler = AndroidView.FindViewById <RecyclerView>(Resource.Id.flyoutcontent_recycler);

            recyclerAdapter = new AppShellFlyoutRecyclerAdapter(shellContext, OnElementSelected);
            recycler.SetAdapter(recyclerAdapter);
        }
Exemplo n.º 4
0
        protected override TView CreateItem(AndroidContext context, AndroidView <TView> element)
        {
            var view = (TView)Activator.CreateInstance(typeof(TView), context.Context);

            if (context.LayoutParamsFactory != null)
            {
                // hang on, we need to get the dimensions from the attributes
                var dimensions = element.Attributes.GetAttributeOrDefault(View.DimensionsAttribute);

                view.LayoutParameters = context.LayoutParamsFactory(dimensions ?? Dimensions.WrapWrap);
            }

            return(view);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Attempt to connect to a device
        /// </summary>
        /// <param name="book"></param>
        public void Connect(Book.Book book, Color backColor)
        {
            try
            {
                // Calls to this come from JavaScript, not sure they will always be on the UI thread.
                // Before I added this, I definitely saw race conditions with more than one thread trying
                // to figure out what was connected.
                lock (this)
                {
                    AndroidView.CheckBookLayout(book, _progress);
                    if (_connectionHandler != null)
                    {
                        // we're in an odd state...should only be able to click the button that calls this
                        // while stopped.
                        // Try to really get into the right state in case the user tries again.
                        _androidDeviceUsbConnection.StopFindingDevice();
                        return;
                    }
                    // Create this while locked...once we have it, can't enter the main logic of this method
                    // on another thread.
                    _connectionHandler = new BackgroundWorker();
                }
                _progress.Message(id: "LookingForDevice",
                                  message: "Looking for an Android device connected by USB cable and set up for MTP...",
                                  comment: "This is a progress message; MTP is an acronym for the system that allows computers to access files on devices.");

                _androidDeviceUsbConnection.OneReadyDeviceFound    = HandleFoundAReadyDevice;
                _androidDeviceUsbConnection.OneReadyDeviceNotFound = HandleFoundOneNonReadyDevice;
                // Don't suppress the first message after (re)starting.
                _previousDeviceNotFoundReportType = DeviceNotFoundReportType.Unknown;


                _connectionHandler.DoWork             += (sender, args) => _androidDeviceUsbConnection.ConnectAndSendToOneDevice(book, backColor);
                _connectionHandler.RunWorkerCompleted += (sender, args) =>
                {
                    if (args.Error != null)
                    {
                        UsbFailConnect(args.Error);
                    }
                    _connectionHandler = null;                     // now OK to try to connect again.
                };
                _connectionHandler.RunWorkerAsync();
            }
            catch (Exception e)
            {
                UsbFailConnect(e);
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Create a <see cref="AndroidView{TView}"/> instance without optional dimensions and id
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dimensions"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static AndroidView <T> View <T>(Dimensions dimensions = default, int?id = default) where T : Android.Views.View
        {
            var control = new AndroidView <T>();

            if (dimensions != default)
            {
                control.Dimensions(dimensions);
            }

            if (id.HasValue)
            {
                control.Id(id.Value);
            }

            return(control);
        }
Exemplo n.º 7
0
        public void SetDisplayMode(PublishModel.DisplayModes displayMode)
        {
            // This is only supposed to be active in one mode of PublishView.
            Browser.SuppressJavaScriptErrors = false;

            if (displayMode != PublishModel.DisplayModes.Upload && _publishControl != null)
            {
                Controls.Remove(_publishControl);
                _publishControl = null;
            }
            if (displayMode != PublishModel.DisplayModes.EPUB && _epubPreviewControl != null && Controls.Contains(_epubPreviewControl))
            {
                Controls.Remove(_epubPreviewControl);
            }
            if (displayMode != PublishModel.DisplayModes.Android && _androidControl != null && Controls.Contains(_androidControl))
            {
                Controls.Remove(_androidControl);

                // disposal of the browser is good but it hides a multitude of sins that we'd rather catch and fix during development. E.g. BL-4901
                if (!ApplicationUpdateSupport.IsDevOrAlpha)
                {
                    _androidControl.Dispose();
                    _androidControl = null;
                }
            }
            if (displayMode != PublishModel.DisplayModes.Upload && displayMode != PublishModel.DisplayModes.EPUB && displayMode != PublishModel.DisplayModes.Android)
            {
                _pdfViewer.Visible = true;
            }
            switch (displayMode)
            {
            case PublishModel.DisplayModes.WaitForUserToChooseSomething:
                _printButton.Enabled = _saveButton.Enabled = false;
                Cursor = Cursors.Default;
                _workingIndicator.Visible = false;
                _pdfViewer.Visible        = false;
                break;

            case PublishModel.DisplayModes.Working:
                _printButton.Enabled     = _saveButton.Enabled = false;
                _workingIndicator.Cursor = Cursors.WaitCursor;
                Cursor = Cursors.WaitCursor;
                _workingIndicator.Visible = true;
                _pdfViewer.Visible        = false;
                break;

            case PublishModel.DisplayModes.ShowPdf:
                Logger.WriteEvent("Entering Publish PDF Screen");
                if (RobustFile.Exists(_model.PdfFilePath))
                {
                    _pdfViewer.Visible        = true;
                    _workingIndicator.Visible = false;
                    Cursor = Cursors.Default;
                    _saveButton.Enabled  = true;
                    _printButton.Enabled = _pdfViewer.ShowPdf(_model.PdfFilePath);
                }
                break;

            case PublishModel.DisplayModes.Printing:
                _simpleAllPagesRadio.Enabled = false;
                _bookletCoverRadio.Enabled   = false;
                _bookletBodyRadio.Enabled    = false;
                _printButton.Enabled         = _saveButton.Enabled = false;
                _workingIndicator.Cursor     = Cursors.WaitCursor;
                Cursor = Cursors.WaitCursor;
                _workingIndicator.Visible = true;
                break;

            case PublishModel.DisplayModes.ResumeAfterPrint:
                _simpleAllPagesRadio.Enabled = true;
                _pdfViewer.Visible           = true;
                _workingIndicator.Visible    = false;
                Cursor = Cursors.Default;
                _saveButton.Enabled  = true;
                _printButton.Enabled = true;
                break;

            case PublishModel.DisplayModes.Upload:
            {
                Logger.WriteEvent("Entering Publish Upload Screen");
                _workingIndicator.Visible = false;                         // If we haven't finished creating the PDF, we will indicate that in the progress window.
                _saveButton.Enabled       = _printButton.Enabled = false;  // Can't print or save in this mode...wouldn't be obvious what would be saved.
                _pdfViewer.Visible        = false;
                Cursor = Cursors.Default;

                if (_publishControl == null)
                {
                    SetupPublishControl();
                }

                break;
            }

            case PublishModel.DisplayModes.EPUB:
            {
                Logger.WriteEvent("Entering Publish Epub Screen");
                // We may reuse this for the process of generating the ePUB staging files. For now, skip it.
                _workingIndicator.Visible = false;
                _printButton.Enabled      = false;                    // don't know how to print an ePUB
                _pdfViewer.Visible        = false;
                Cursor = Cursors.WaitCursor;
                _epubPreviewControl = ElectronicPublishView.SetupEpubControl(_epubPreviewControl, _isolator, () => _saveButton.Enabled = _model.EpubMaker.ReadyToSave());
                _epubPreviewControl.SetBounds(_pdfViewer.Left, _pdfViewer.Top,
                                              _pdfViewer.Width, _pdfViewer.Height);
                _epubPreviewControl.Dock   = _pdfViewer.Dock;
                _epubPreviewControl.Anchor = _pdfViewer.Anchor;
                var saveBackGround = _epubPreviewControl.BackColor;                     // changed to match parent during next statement
                Controls.Add(_epubPreviewControl);
                _epubPreviewControl.BackColor = saveBackGround;                         // keep own color.
                // Typically this control is dock.fill. It has to be in front of tableLayoutPanel1 (which is Left) for Fill to work.
                _epubPreviewControl.BringToFront();
                Cursor = Cursors.Default;

                // We rather mangled the Readium code in the process of cutting away its own navigation
                // and other controls. It produces all kinds of JavaScript errors, but it seems to do
                // what we want. So just suppress the toasts for all of them.
                Browser.SuppressJavaScriptErrors = true;
                break;
            }

            case PublishModel.DisplayModes.Android:
            {
                Logger.WriteEvent("Entering Publish Android Screen");
                _workingIndicator.Visible = false;
                _printButton.Enabled      = false;
                _pdfViewer.Visible        = false;
                Cursor          = Cursors.WaitCursor;
                _androidControl = new AndroidView(_isolator);
                _androidControl.SetBounds(_pdfViewer.Left, _pdfViewer.Top,
                                          _pdfViewer.Width, _pdfViewer.Height);
                _androidControl.Dock   = _pdfViewer.Dock;
                _androidControl.Anchor = _pdfViewer.Anchor;
                var saveBackGround = _androidControl.BackColor;                     // changed to match parent during next statement
                Controls.Add(_androidControl);
                _androidControl.BackColor = saveBackGround;                         // keep own color.
                // Typically this control is dock.fill. It has to be in front of tableLayoutPanel1 (which is Left) for Fill to work.
                _androidControl.BringToFront();
                Cursor = Cursors.Default;

                break;
            }
            }
            UpdateSaveButton();
        }