How to wait for methods to finish then do new action? - c#

I'm setting up my architechture to use Cef.Offscreen. In order to make it easy to work with I have divided some parts. But I run into a problem that controller loading finshes and serves a view before everything has been able to load.
Here's my structure --> Controller
public ActionResult InitBrowser()
{
ICefSharpRenderer renderer = RendererSingelton.GetInstance();
//Try to render something in default appdomain
renderer.LoginToTradingView(null, null);
ViewBag.SiteTitle = BrowserActions.RunScriptInNamedBrowser("loginbrowser", #"(function() {return document.title;} )();");
ViewBag.ImagesixtyfourUrl = BrowserActions.TakeScreenshot("loginbrowser");
//this is returned to fast, we have to wait for all
return View();
}
I have this class to get do some basic actions and initialize if needed.
public class CefSharpRenderer : MarshalByRefObject, ICefSharpRenderer
{
private ChromiumWebBrowser _browser;
private TaskCompletionSource<JavascriptResponse> _taskCompletionSource;
private string _name;
public void LoginToTradingView(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(#"https://se.tradingview.com/", "loginbrowser");
}
public void CreateBrowserAndGoToUrl(string url, string browserName)
{
CheckIfCefIsInitialized();
BrowserFactory.GetBrowserInstance(url, "browserName");
}
public void CheckIfCefIsInitialized()
{
if (!Cef.IsInitialized)
{
var settings = new CefSettings();
var assemblyPath = Path.GetDirectoryName(new Uri(GetType().Assembly.CodeBase).LocalPath);
settings.BrowserSubprocessPath = Path.Combine(assemblyPath, "CefSharp.BrowserSubprocess.exe");
settings.ResourcesDirPath = assemblyPath;
settings.LocalesDirPath = Path.Combine(assemblyPath, "locales");
var osVersion = Environment.OSVersion;
//Disable GPU for Windows 7
if (osVersion.Version.Major == 6 && osVersion.Version.Minor == 1)
{
// Disable GPU in WPF and Offscreen examples until #1634 has been resolved
settings.CefCommandLineArgs.Add("disable-gpu", "1");
}
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: false, cefApp: null);
}
}
}
I get my browserinstance here and connected the events to be fired.
public static class BrowserFactory
{
public static ChromiumWebBrowser GetBrowserInstance(string _url, string browsername)
{
if (!BrowserContainer.CheckIfBrowserExists(browsername))
{
ChromiumWebBrowser _browser = new ChromiumWebBrowser(_url);
_browser.LoadingStateChanged += BrowserEvents.OnLoadingStateChanged;
BrowserContainer.AddDataHolder(browsername, new DataBrowserHolder { BrowserName = browsername, ChromiumWebBrow = _browser });
return _browser;
}
return null;
}
}
Browserevent loads correct page.
public static class BrowserEvents
{
public static void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (args.IsLoading == false)
{
ChromiumWebBrowser cwb = (ChromiumWebBrowser)sender;
if (cwb.Address == "https://se.tradingview.com/")
{
BrowserActions.LogInToTradingView("xxxxx", "yyyyyyy", "loginbrowser");
}
}
}
}
Last my browseractions, spare med for the thread sleeps it's just under construction and it works atm.
public static class BrowserActions
{
public static void LogInToTradingView(string twusername, string twpassword, string browserName)
{
ChromiumWebBrowser _dataholder = BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow;
IFrame ifww = _dataholder.GetMainFrame();
// var lull = #"(function() { var serielength = TradingView.bottomWidgetBar._widgets.backtesting._reportWidgetsSet.reportWidget._data.filledOrders.length; return serielength; })();";
// JavascriptResponse _js = Task.Run(async () => { return await _browser.GetMainFrame().EvaluateScriptAsync(lull); }).Result;
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-header__link tv-header__link--signin js-header__signin')[0].click();})();");
// var loginusernamescript =
var loginpasswordscript = #"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value= " + twpassword + "; })();";
var clkloginbtn = #"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();";
Thread.Sleep(300);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[0].value = '" + twusername + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].click();})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { window.document.getElementsByClassName('tv-control-material-input tv-signin-dialog__input tv-control-material-input__control')[1].value = '" + twpassword + "';})();");
Thread.Sleep(50);
ifww.ExecuteJavaScriptAsync(#"(function() { document.getElementsByClassName('tv-button tv-button--no-border-radius tv-button--size_large tv-button--primary_ghost tv-button--loader')[0].click();})();");
}
public static string TakeScreenshot(string browserName)
{
try
{
Bitmap img = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.ScreenshotAsync(); }).Result;
// object mgss = img.Clone();
string baseen = ExtraFunctions.ToBase64String(img, ImageFormat.Png);
return baseen;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
public static string RunScriptInNamedBrowser(string browserName, string script)
{
try
{
string str = Task.Run(async () => { return await BrowserContainer.GetDataHolderByName(browserName).ChromiumWebBrow.GetMainFrame().EvaluateScriptAsync(script); }).Result.ToString();
// object mgss = img.Clone();
return str;
}
catch (Exception e)
{
var x = e.InnerException;
return null;
}
}
}
How can I get my browser actions to report back to my controller so that I can wait for them to finish?

For a Task asynchronous operation to report back, it's possible to use Progress<T>. How that's done is detailed in Enabling Progress and Cancellation in Async APIs. The key is:
var progressIndicator = new Progress<int>(ReportProgress);
This creates a Progress<T> object that can indicate how far a task is complete, and also call a custom method (ReportProgress) at set intervals. You can create a custom class if necessary instead of using int.
So your browser actions can report back to the controller with the progress reporting method until everything is complete.

Related

How to handle multiple file downloads in Playwright?

I have a button that when clicked will start downloading multiple files (this button will also open a chrome://downloads tab and closes it immediately.
The page.download event handler for downloads will not fire.
The page.WaitForDownloadAsync() returns only one of these files.
I do not know the file names that will be downloaded, I also do not know if more than 1 file will be downloaded, there is always the possibility that only 1 file will be downloaded, but also the possibility that multiple files will be downloaded.
How can I handle this in playwright? I would like to return a list of all the downloaded files paths.
So I resolved this with the following logic.
I created two variables:
List<string> downloadedFiles = new List<string>();
List<string> fileDownloadSession = new();
I then created a method to add as a handler to the page.Download that looks like this:
private async void downloadHandler(object sender, IDownload download)
{
fileDownloadSession.Add("Downloading...");
var waiter = await download.PathAsync();
downloadedFiles.Add(waiter);
fileDownloadSession.Remove(fileDownloadSession.First());
}
Afterwards, I created a public method to get the downloaded files that looks like this:
public List<string> GetDownloadedFiles()
{
while (fileDownloadSession.Any())
{
}
var downloadedFilesList = downloadedFiles;
downloadedFiles = new List<string>();
return downloadedFilesList;
}
All these methods and planning are in a separate class of their own so that they can monitor the downloaded files properly, and also to freeze the main thread so it can grab all of the required files.
All in all it seems just as sketchy of a solution, similarly to how you would implement it in Selenium, nothing much has changed in terms of junkyard implementations in the new frameworks.
You can find my custom class here: https://paste.mod.gg/rztmzncvtagi/0, enjoy, there is no other topic that answers this specific question for playwright on C#.
Code here, in case it gets deleted from paste.mod.gg:
using System.Net;
using System.Runtime.InteropServices.JavaScript;
using Flanium;
using FlaUI.UIA3;
using Microsoft.Playwright;
using MoreLinq;
using Polly;
namespace Fight;
public class WebBrowser
{
private IBrowser _browser;
private IBrowserContext _context;
private IPage _page;
private bool _force;
private List<string> downloadedFiles = new List<string>();
private List<string> fileDownloadSession = new();
public void EagerMode()
{
_force = true;
}
public enum BrowserType
{
None,
Chrome,
Firefox,
}
public IPage GetPage()
{
return _page;
}
public WebBrowser(BrowserType browserType = BrowserType.Chrome, bool headlessMode = false)
{
var playwright = Playwright.CreateAsync().Result;
_browser = browserType switch
{
BrowserType.Chrome => playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions {Headless = headlessMode}).Result,
BrowserType.Firefox => playwright.Firefox.LaunchAsync(new BrowserTypeLaunchOptions {Headless = headlessMode}).Result,
_ => null
};
_context = _browser.NewContextAsync().Result;
_page = _context.NewPageAsync().Result;
_page.Download += downloadHandler;
Console.WriteLine("WebBrowser was successfully started.");
}
private async void downloadHandler(object sender, IDownload download)
{
fileDownloadSession.Add("Downloading...");
var waiter = await download.PathAsync();
downloadedFiles.Add(waiter);
fileDownloadSession.Remove(fileDownloadSession.First());
}
public List<string> GetDownloadedFiles()
{
while (fileDownloadSession.Any())
{
}
var downloadedFilesList = downloadedFiles;
downloadedFiles = new List<string>();
return downloadedFilesList;
}
public void Navigate(string url)
{
_page.GotoAsync(url).Wait();
}
public void Close(string containedURL)
{
var pages = _context.Pages.Where(x => x.Url.Contains(containedURL));
if (pages.Any())
pages.ForEach(x => x.CloseAsync().Wait());
}
public IElementHandle Click(string selector, int retries = 15, int retryInterval = 1)
{
var element = Policy.HandleResult<IElementHandle>(result => result == null)
.WaitAndRetry(retries, interval => TimeSpan.FromSeconds(retryInterval))
.Execute(() =>
{
var element = FindElement(selector);
if (element != null)
{
try
{
element.ClickAsync(new ElementHandleClickOptions() {Force = _force}).Wait();
element.DisposeAsync();
return element;
}
catch (Exception e)
{
return null;
}
}
return null;
});
return element;
}
public IElementHandle FindElement(string selector)
{
IElementHandle element = null;
var Pages = _context.Pages.ToArray();
foreach (var w in Pages)
{
//============================================================
element = w.QuerySelectorAsync(selector).Result;
if (element != null)
{
return element;
}
//============================================================
var iframes = w.Frames.ToList();
var index = 0;
for (; index < iframes.Count; index++)
{
var frame = iframes[index];
element = frame.QuerySelectorAsync(selector).Result;
if (element is not null)
{
return element;
}
var children = frame.ChildFrames;
if (children.Count > 0 && iframes.Any(x => children.Any(y => y.Equals(x))) == false)
{
iframes.InsertRange(index + 1, children);
index--;
}
}
}
return element;
}
}

Xamarin unable get instance from async

I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);

C# call function from async false result [duplicate]

I've written a Xamarin.Forms Application that connects to an ESP32 via Bluetooth. Now I'd like to get a value from a CustomControl.JoystickControl from the MainPage.xaml Page.
I've tried it like that:
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
public static Bluetooth.IBth bth = new Bluetooth.Bth();
public MainPage()
{
InitializeComponent();
Task.Run(async () => bth.Start("mecanumWheelRobot", 200, false));
}
public CustomControls.JoystickControl JoystickControlElement
{
get { return JoystickControl; }
}
public CustomControls.JoystickControl JoystickControlElement1
{
get { return JoystickControl1; }
}
}
Now I'd like to get the JoystickControlElement from the following Async Function:
var mainpage = new Views.MainPage();
string test = mainpage.test;
(these two lines are the problem)
class Bth : IBth
{
private CancellationTokenSource _ct { get; set; }
const int RequestResolveError = 1000;
public Bth()
{
}
public void Start(string name, int sleepTime = 200, bool readAsCharArray = false)
{
Task.Run(async () => loop(name, sleepTime, readAsCharArray));
}
private async Task loop(string name, int sleepTime, bool readAsCharArray)
{
BluetoothDevice device = null;
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
BluetoothSocket BthSocket = null;
_ct = new CancellationTokenSource();
while (_ct.IsCancellationRequested == false)
{
try
{
Thread.Sleep(sleepTime);
adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null)
System.Diagnostics.Debug.WriteLine("No Bluetooth adapter found.");
else
System.Diagnostics.Debug.WriteLine("Adapter found!!");
if (!adapter.IsEnabled)
System.Diagnostics.Debug.WriteLine("Bluetooth adapter is not enabled.");
else
System.Diagnostics.Debug.WriteLine("Adapter enabled!");
System.Diagnostics.Debug.WriteLine("Try to connect to " + name);
foreach (var bd in adapter.BondedDevices)
{
System.Diagnostics.Debug.WriteLine("Paired devices found: " + bd.Name.ToUpper());
if (bd.Name.ToUpper().IndexOf(name.ToUpper()) >= 0)
{
System.Diagnostics.Debug.WriteLine("Found " + bd.Name + ". Try to connect with it!");
device = bd;
break;
}
}
if (device == null)
{
System.Diagnostics.Debug.WriteLine("Named device not found.");
MainThread.BeginInvokeOnMainThread(() =>
{
// Code to run on the main thread
});
}
else
{
UUID uuid = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
BthSocket = device.CreateInsecureRfcommSocketToServiceRecord(uuid);
else
BthSocket = device.CreateRfcommSocketToServiceRecord(uuid);
if (BthSocket != null)
{
//Task.Run ((Func<Task>)loop); /*) => {
await BthSocket.ConnectAsync();
if (BthSocket.IsConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Connected!");
// Code to run on the main thread
//pages.bluetoothBarcodeScan.connectedScanner();
});
System.Diagnostics.Debug.WriteLine("Connected!");
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastSuccess("Writing!");
// Code to run on the main thread
//
});
while (_ct.IsCancellationRequested == false)
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
var bytes = Encoding.ASCII.GetBytes("{test}-");
BthSocket.OutputStream.Write(bytes, 0, bytes.Length);
Thread.Sleep(100);
}
System.Diagnostics.Debug.WriteLine("Exit the inner loop");
}
}
else
System.Diagnostics.Debug.WriteLine("BthSocket = null");
}
}
catch(Exception ex)
{
service.toast.toastSuccess(ex.Message);
}
finally
{
if (BthSocket != null)
BthSocket.Close();
device = null;
adapter = null;
}
}
System.Diagnostics.Debug.WriteLine("Exit the external loop");
}
public void Cancel()
{
if (_ct != null)
{
System.Diagnostics.Debug.WriteLine("Send a cancel to task!");
_ct.Cancel();
MainThread.BeginInvokeOnMainThread(() =>
{
service.toast.toastError("Disconnected");
// Code to run on the main thread
});
}
}
public async Task<string> GetData()
{
var mainpage = new Views.MainPage();
string test = mainpage.test;
return "1";
}
public ObservableCollection<string> PairedDevices()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
ObservableCollection<string> devices = new ObservableCollection<string>();
foreach (var bd in adapter.BondedDevices)
devices.Add(bd.Name);
return devices;
}
#endregion
}
The async function always starts new when it comes to this line.
How should I get the variable?
You can change your constructor method like:
public Bth(string txt)
{}
And then pass the mainpage.test like:
public static Bluetooth.IBth bth = new Bluetooth.Bth(txt);

RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method

I am tring to Scan document from my Application but excutution time my code stuck somewhere and Code did not worked. some time throw same error which I mention in Header. I given here my all code and also which I tried.
This is part of my code
private async void HomePageScanner()
{
try
{
string pdfFileName = string.Empty;
pdfFileName = Guid.NewGuid().ToString() + ".pdf";
//scanPdfFile = pdfFileName;
StorageFolder scanTempFolder = await KnownFolders.PicturesLibrary.CreateFolderAsync(Constants.PATH_TEMP_SCAN, CreationCollisionOption.OpenIfExists);
DeviceInformationDisplay selectedScanner = CmbScannerList.SelectedItem as DeviceInformationDisplay;
string ScannerID = selectedScanner.Id;
StorageFolder pdfFolder_DataFTP = await KnownFolders.PicturesLibrary.CreateFolderAsync(Constants.PATH_SCAN, CreationCollisionOption.OpenIfExists);
//var task = Task.Run(async () => { await ScanToFolder(ScannerID, scanTempFolder, pdfFileName, pdfFolder_DataFTP, null); });
//Task.Run(task);
ScanToFolder(ScannerID, scanTempFolder, pdfFileName, pdfFolder_DataFTP, null).RunSynchronously();
// await ScanToFolder(ScannerID, scanTempFolder, pdfFileName, pdfFolder_DataFTP, null);
}
catch (Exception ex) { new ExceptionHelper().AddException(ex.Message, this.ToString(), "HomePageScanner"); }
}
public async Task ScanToFolder(string deviceId, StorageFolder scanTempFolder, string pdfFileName, StorageFolder pdfFolder_dataFTP, StorageFolder pdfFolder_DataServer)
{
try
{
scanningDialog.ShowAsync();
_scanerTimer.Tick += _scanerTimer_Tick;
_scanerTimer.Interval = new TimeSpan(0, 0, 1);
_scanerTimer.Start();
// ImageScanner myScanner = await ImageScanner.FromIdAsync(deviceId);
ImageScanner myScanner = null;//= ImageScanner.FromIdAsync(deviceId);
await Task.Run(async () =>
{
myScanner = await ImageScanner.FromIdAsync(deviceId);
// var task = Task.Run(async () => { myScanner = await ImageScanner.FromIdAsync(deviceId);
_isScanContinue = true;
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
if (myScanner.IsScanSourceSupported(ImageScannerScanSource.AutoConfigured))
{
myScanner.FlatbedConfiguration.Format = ImageScannerFormat.Jpeg; //Code Stuck here some time
for (int i = 0; i < _totalPageToScan; i++)
{
_pageCounter++;
if (i > 0)
{
scanningDialog.IsEnabled = false;
//scanningDialog.Hide();
ContentDialog scanConfirmationDialog = new ContentDialog()
{
Title = "Click OK button to scan the next page of your document",
CloseButtonText = "OK"
};
scanConfirmationDialog.Closed += ScanConfirmationDialog_Closed;
await scanConfirmationDialog.ShowAsync();
}
}
}
});
}
}
private void _scanerTimer_Tick(object sender, object e)
{
_scanCounter++;
if (_scanCounter == 30)
{
if (_isScanContinue == false)
{
CommonCls.ShowToastMessage("Something went wrong. please check your scanner");
this.Hide();
scanningDialog.DataContext = "Something went wrong!!";
scanningDialog.Hide();
}
}
}
I updated Complete Please View and Help me to solve this.
I hope a better solution which I needed most.
Thank You
You should think object oriented: one object is the scanner, one object is the dialog. They have to be completely independent each other and communicate via a clear interface. You can start from the code below where I skipped all the real scanning logic.
class ScannerInfoEventArgs : EventArgs
{
public string Message { get; set; }
public string ButtonText { get; set; }
public bool DialogEanbled { get; set; }
public bool Close { get; set; }
}
class Scanner
{
private int _totalPageToScan;
private int _pageCounter;
public event EventHandler<ScannerInfoEventArgs>? OnScannerInfo;
public delegate void ScannerInfoEventHandler(object sender, ScannerInfoEventArgs e);
public async Task HomePageScanner()
{
try
{
string pdfFileName = string.Empty;
pdfFileName = Guid.NewGuid().ToString() + ".pdf";
_totalPageToScan = 10; // or your calculation here
_pageCounter = 0;
await ScanToFolder("c:\\tempfolder", pdfFileName, "c:\\pdf_data", "c:\\data_server");
}
catch (Exception ex)
{
}
}
public bool IsScanSourceSupported(string source)
{
// yuor logic here
return true;
}
public enum ImageType
{
png,
jpeg
}
public ImageType imageType
{
get; set;
}
public async Task ScanToFolder(string scanTempFolder, string pdfFileName, string pdfFolder_dataFTP, string pdfFolder_DataServer)
{
try
{
await Task.Run(() =>
{
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
if (this.IsScanSourceSupported("your parameter here"))
{
this.imageType = ImageType.jpeg; //Code Stuck here some time
for (int i = 0; i < _totalPageToScan; i++)
{
_pageCounter++;
if (i > 0)
{
ScannerInfoEventArgs ev = new ScannerInfoEventArgs() { Message = "Click OK button to scan the next page of your document", ButtonText = "OK", DialogEanbled = false, Close = false };
OnScannerInfo?.Invoke(this, ev);
if (ev.Close)
{
break;
}
}
if (i == 30) // or something more meaningful condition
{
ScannerInfoEventArgs ev = new ScannerInfoEventArgs() { Message = "Something went wrong.please check your scanner", ButtonText = "OK", DialogEanbled = true, Close = true };
OnScannerInfo?.Invoke(this, ev);
break;
}
}
}
});
}
catch (Exception)
{ }
}
}
class FakeDialog
{
public void ShowDialog(object sender, ScannerInfoEventArgs e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.ButtonText);
string asw = Console.ReadLine();
if (asw == "stop")
{
e.Close = true;
}
}
}
static class Program
{
static async Task Main()
{
var scanner = new Scanner();
var dialog = new FakeDialog();
scanner.OnScannerInfo += dialog.ShowDialog;
await scanner.HomePageScanner();
}
}
You should keep just the idea of object separation and asyn/await patter. Hope this can be a starting point.

Content Observer doesn't monitor the DownloadManager's download progress on Android

I implemented the download functionality in my android app using the download manager.
The download manager dows its job well, and once download is completed, the broadcast receiver I set is called successfully.
But, I want to monitor the download progress and display it inside my app, and not rely only on the download manager's notification.
So, I implemented a "ContentProvider and a ContentObserver" to query frequently the download manager for download progress.
Here is my content provider:
[ContentProvider(new string[] { DownloadsContentProvider.Authority })]
public class DownloadsContentProvider : ContentProvider
{
public const string Authority = "com.myapp.Myapp.DownloadProvider";
public DownloadsContentProvider()
{
}
public static Android.Net.Uri ProviderUri(long downloadId)
{
Android.Net.Uri uri = Android.Net.Uri.Parse($"http://content//downloads/my_downloads/{downloadId}");
var builder = new Android.Net.Uri.Builder()
.Authority(Authority)
.Scheme(ContentResolver.SchemeFile)
.Path(uri.Path)
.Query(uri.Query)
.Fragment(uri.Fragment);
return builder.Build();
}
public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
{
return 0;
}
public override string GetType(Android.Net.Uri uri)
{
return null;
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
return null;
}
public override bool OnCreate()
{
return true;
}
public override ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
{
throw new NotImplementedException();
}
public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
{
return 0;
}
}
Then, I created a content observer to observe what happens and trigger the query of downloads progress.
public DownloadProgressContentObserver(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference,
transfer)
{
}
public DownloadProgressContentObserver(Handler? handler) : base(handler)
{
}
public DownloadProgressContentObserver() : base(null)
{
}
public override void OnChange(bool selfChange, Uri? uri)
{
base.OnChange(selfChange, uri);
var downloadId = uri.ToString().Substring(uri.ToString().LastIndexOf(Path.PathSeparator) + 1);
if (!string.IsNullOrEmpty(downloadId))
{
ComputeDownloadStatus(Convert.ToInt64(downloadId));
//TODO: dispatch this download percentage to the whole app, and the database
}
}
public void ComputeDownloadStatus(long downloadId)
{
long downloadedBytes = 0;
long totalSize = 0;
int status = 0;
DownloadManager.Query query = new DownloadManager.Query().SetFilterById(downloadId);
var downloadManager = DownloadManager.FromContext(Android.App.Application.Context);
var cursor = downloadManager.InvokeQuery(query);
String downloadFilePath = (cursor.GetString(cursor.GetColumnIndex(DownloadManager.ColumnLocalUri))).Replace("file://", "");
try
{
if (cursor != null && cursor.MoveToFirst())
{
downloadedBytes =
cursor.GetLong(cursor.GetColumnIndexOrThrow(DownloadManager.ColumnBytesDownloadedSoFar));
totalSize =
cursor.GetInt(cursor.GetColumnIndexOrThrow(DownloadManager.ColumnTotalSizeBytes));
}
}
finally
{
if (cursor != null)
{
cursor.Close();
}
}
var percentage = (downloadedBytes / totalSize) * 100;
}
}
This is how I use both, and register them in the download manager to monitor the download progress.
var manager = DownloadManager.FromContext(Android.App.Application.Context);
var request = new DownloadManager.Request(Android.Net.Uri.Parse(downloadUrl));
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
request.SetDestinationInExternalPublicDir(downloadsPath, fileName);
request.SetTitle(productTitle);
request.SetDescription(downloadDescription);
long downloadId = manager.Enqueue(request);
//I provide a valid URI with my content provicer
var uri = DownloadsContentProvider.ProviderUri(downloadId);
var contentResolver = Android.App.Application.Context.ContentResolver;
var observer = new DownloadProgressContentObserver();
contentResolver.RegisterContentObserver(uri, true, observer);
ProductContentObservers.Add(downloadId, observer);
I have read a lot of doc, and my implementation seems to be ok. But the content observer's "OnCHange" method is never called.
Can someone please point out what I might be doing wrong ?

Categories