The request payload is invalid. Ensure to provide a valid request payload - botframework

After 10 to 15 times of return await sc.BeginDialogAsync(nameof(MainDialog));
the error was:
Errors":["The request payload is invalid. Ensure to provide a valid
request payload."]
Scenario:
Use: Enterprise Bot Template
In Main Dialog after Dispatch => QnA Answer Result is asking for feedback using suggestion card [Thumbs Up/Down]
If the user is selecting the Thumbs Up/Down, they are able to end the dialog.
If they do not select any option, the user needs to enter another query that needs to be processed through Maindialog again (DISPATCH => LUIS/QnA).
In my scenario, the user without giving feedback is entering new queries after 10
to 15 times is getting a server error.
var answers = await qnaService.GetAnswersAsync(dc.Context);
if (answers != null && answers.Count() > 0 && answers[0].Score >= _bestQnAScore)
{
string personalizedAnswer = DialogUtilities.PersonalizeAnswer(answers[0].Answer, _authResult, greetingInQNAAnswerSetting);
if (!string.IsNullOrEmpty(personalizedAnswer))
{
personalizedAnswer = StringUtils.URLBlankSpaceHelper(personalizedAnswer);
await dc.Context.SendActivityAsync(personalizedAnswer, personalizedAnswer, "acceptingInput");
string[] arrQnAMetadata = _configuration["QnAMetadata"].ToString().Split(',');
string stringToCheck = (answers[0].Metadata.Length > 0) ? answers[0].Metadata.FirstOrDefault().Value.ToLower() : string.Empty;
bool isChitChat = arrQnAMetadata.Any(stringToCheck.Contains);
if (!isChitChat)
{
await dc.BeginDialogAsync(nameof(FeedbackDialog), new VM_Luis_AuthResult(null, _authResult, _EnumDISPATCHIntent.ToString()));
}
else
{
#region OPEN/CLOSE TICKET
FeedbackLogger.LogUserFeedback(_authResult.UserEmail, dc.Context.Activity.Conversation.Id, dc.Context.Activity.Text, _authResult.Country, _authResult.City, _authResult.OfficeLocation, TicketState.Open, TicketStatus.None, ref this._topScoringIntent, ref this._ticketNumber);
FeedbackLogger.LogUserFeedback(_authResult.UserEmail, dc.Context.Activity.Conversation.Id, dc.Context.Activity.Text, _authResult.Country, _authResult.City, _authResult.OfficeLocation, TicketState.Closed, TicketStatus.Successful, ref this._topScoringIntent, ref this._ticketNumber);
#endregion
await dc.EndDialogAsync();
}
}
}
else
{
await _responder.ReplyWith(dc.Context, MainResponses.ResponseIds.Confused);
await dc.EndDialogAsync();
}
FeedbackDialog.cs
private async Task<DialogTurnResult> OnFeedbackResult(WaterfallStepContext sc, CancellationToken cancellationToken)
{
var res = Convert.ToString(sc.Result);
if (InterruptionsConstants.NegativeFeedback.Any(f => res.ToLower() == f))
{
await sc.Context.SendActivityAsync("Thank You!");
return await sc.EndDialogAsync();
}
else if (InterruptionsConstants.PositiveFeedback.Any(f => res.ToLower() == f))
{
await sc.Context.SendActivityAsync("Thank you very much");
return await sc.EndDialogAsync();
}
else
{
return await sc.BeginDialogAsync(nameof(MainDialog));
}
}

Related

I am trying to use flutter Isolate with database data. But it's throwing an error. I don't know why is it happening?

I'm using the hive database. My code is:
HiveStocktaking? stocktaking =
_database.getStocktakingById(_stocktakingId);
StocktakingStats stocktakingStats =
_database.getStocktakingStats(_stocktakingId, true);
List<HiveStocktakingItem> stocktakingItemShortage = _database
.getStocktakingAllItem(_stocktakingId, 'shortage', '',
getAll: withProducts)
.values
.first;
List<HiveStocktakingItem> stocktakingItemSurplus = _database
.getStocktakingAllItem(_stocktakingId, 'surplus', '',
getAll: withProducts)
.values
.first;
int shortageLength = withProducts ? stocktakingItemShortage.length : 0;
int surplusLength = withProducts ? stocktakingItemSurplus.length : 0;
if (type == 'excel') {
createIsolate(
stocktaking,
stocktakingStats,
stocktakingItemShortage,
stocktakingItemSurplus,
shortageLength,
surplusLength,
);
}
Future createIsolate(
HiveStocktaking? stocktaking,
StocktakingStats stocktakingStats,
List<HiveStocktakingItem> stocktakingItemShortage,
List<HiveStocktakingItem> stocktakingItemSurplus,
int shortageLength,
int surplusLength) async {
ReceivePort receivePort = ReceivePort();
Isolate.spawn(isolateFunction, receivePort.sendPort);
SendPort childSendPort = await receivePort.first;
ReceivePort responsePort = ReceivePort();
childSendPort.send([
stocktaking,
stocktakingStats,
stocktakingItemShortage,
stocktakingItemSurplus,
shortageLength,
surplusLength,
responsePort.sendPort
]);
var sum = await responsePort.first;
print('sum: $sum');
}
void isolateFunction(SendPort mainSendPort) async {
ReceivePort childReceivePort = ReceivePort();
mainSendPort.send(childReceivePort.sendPort);
await for (var message in childReceivePort) {
HiveStocktaking? stocktaking = message[0];
StocktakingStats stocktakingStats = message[1];
List<HiveStocktakingItem> stocktakingItemShortage = message[2];
List<HiveStocktakingItem> stocktakingItemSurplus = message[3];
int shortageLength = message[4];
int surplusLength = message[5];
SendPort replyPort = message[6];
//heavy task
sync.Workbook workbook = sync.Workbook();
var sheet = workbook.worksheets[0];
sheet.getRangeByIndex(1, 1)
..setText('Отчет по инвентаризации')
..columnWidth = 40
..cellStyle.bold = true
..rowHeight = 30
..cellStyle.fontSize = 20;
sheet.getRangeByIndex(2, 1).setText('Магазин: ${stocktaking?.shopName}');
sheet
.getRangeByIndex(3, 1)
.setText('Дата начала: ${stocktaking?.createdAt}');
sheet
.getRangeByIndex(4, 1)
.setText('Дата завершения: ${stocktaking?.finishedAt}');
sheet.getRangeByIndex(2, 3)
..setText(
'Отсканировано товаров: ${BaseFunctions.numberRound(stocktakingStats.totalScannedMeasurementValue)}')
..columnWidth = 30;
sheet.getRangeByIndex(3, 3).setText(
'Недостач: ${BaseFunctions.numberRound(stocktakingStats.totalMeasurementValue)}');
sheet.getRangeByIndex(4, 3).setText(
'Излишков: ${BaseFunctions.numberRound(stocktakingStats.surplus)}');
// etc generating codes
List<int> bytes = workbook.saveAsStream();
workbook.dispose();
var uint8list = Uint8List.fromList(bytes);
if (Platform.isMacOS) {
String fileName = 'stocktaking_report_' +
DateFormat('dd-MM-yyyy_HH-mm-ss').format(DateTime.now()) +
'.pdf';
String? path =
await PathProviderPlatform.instance.getApplicationSupportPath();
final File file =
File(Platform.isWindows ? '$path\\$fileName' : '$path/$fileName');
await file.writeAsBytes(bytes, flush: true);
await Process.run('open', <String>['$path/$fileName'], runInShell: true);
} else {
await FileSaver.instance.saveFile(
'stocktaking_report_' +
DateFormat('dd-MM-yyyy_HH-mm-ss').format(DateTime.now()),
uint8list,
'xlsx',
mimeType: MimeType.MICROSOFTEXCEL,
);
}
replyPort.send(1);
}
}
But it is throwing this error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:io' Class: _RandomAccessFileOpsImpl#13069316)
if I give the createIsolate fake data it works perfectly.
if (type == 'excel') {
ProjectFiles.createIsolate(
HiveStocktaking(),
StocktakingStats(),
[],
[],
0,
0,
);
}
I found a way to solve this issue. If you want to use the hive database in an isolate function note that you must init your database in the isolate function and close the boxes (which you want to use in the isolate) in the main thread. Register the boxes' adapter in the isolate. After your doing close the boxes which opened in isolate. To initiate the database in isolate, you have to set the database path to Hive.init(path). You can send this path through isolate's port. If you want to use closed boxes in the main thread you have to reopen it. Here some codes for example:
static Future<bool> myFunc() async {
ReceivePort receivePort = ReceivePort();
Isolate.spawn(isolateFunc, receivePort.sendPort);
SendPort childSendPort = await receivePort.first;
await Hive.box<YourBox>('boxName').close();
// database path
Directory appDocumentDir = await getApplicationDocumentsDirectory();
ReceivePort responsePort = ReceivePort();
childSendPort.send([appDocumentDir, responsePort.sendPort]);
var sum = await responsePort.first;
var box = await Hive.openBox<YourBox>('boxName');
HiveDatabase.setInstanceYourBox(box);
return sum == 1;
}
static void isolateDownloadStocktaking(SendPort mainSendPort) async {
ReceivePort childReceivePort = ReceivePort();
mainSendPort.send(childReceivePort.sendPort);
await for (var message in childReceivePort) {
Directory appDocumentDir = message[0];
Hive.init(appDocumentDir.path);
Hive.registerAdapter(YourBoxAdapter());
await HiveDatabase.getInstanceYourBox();
// you can use your box
SendPort replyPort = message[1];
await Hive.box<YourBox>('boxName').close();
replyPort.send(1);
}
}
HiveDatabase class is here:
class HiveDatabase {
static Box<YourBox>? _yourBox;
static HiveDatabase instance = HiveDatabase._();
HiveDatabase._();
static Future<HiveDatabase> getInstanceYourBox() async {
_yourBox ??= await Hive.openBox<YourBox>('boxName');
return instance;
}
static Future<HiveDatabase> setInstanceYourBox(Box<YourBox> box) async {
_yourBox = box;
return instance;
}
List<YourBox> elements() {
return (_yourBox?.values ?? []).toList();
}
Future<void> updateElement(YourBox value) async {
await _yourBox?.put(value.id, value);
}
Future<void> addElement(YourBox value) async {
await _yourBox?.put(value.id, value);
}
Future<void> deleteElement(int index) async {
await _yourBox?.deleteAt(index);
}
Future<void> clearYourBox() async {
await _yourBox?.clear();
}
}

Bot Framework v4 - IndexOutOfRangeException when 2 tabs are open

I made a bot with bot framework v4, using C#, and it's on a webpage, https://websitebotv2.azurewebsites.net/, if there's only 1 user it works fine but the moment I open it on a new tab it gives a IndexOutOfRangeException when I start the conversation.
What do I need to do to make it work with multiple tabs open?
When my bot stars it creates a waterfall dialog asking the name and greeting the user:
public dialogBotBot(dialogBotAccessors accessors, LuisRecognizer luis, QnAMaker qna)
{
// Set the _accessors
_accessors = accessors ?? throw new ArgumentNullException(nameof(accessors));
// The DialogSet needs a DialogState accessor, it will call it when it has a turn context.
_dialogs = new DialogSet(accessors.ConversationDialogState);
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[] {
NameStepAsync,
NameConfirmStepAsync,
};
// The incoming luis variable is the LUIS Recognizer we added above.
this.Recognizer = luis ?? throw new System.ArgumentNullException(nameof(luis));
// The incoming QnA variable is the QnAMaker we added above.
this.QnA = qna ?? throw new System.ArgumentNullException(nameof(qna));
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
_dialogs.Add(new WaterfallDialog("details", waterfallSteps));
_dialogs.Add(new TextPrompt("name"));
}
Then I will save his name on UserProfile class, which contains the field Name and Context, the Context has the purpose of saving the conversation.
This works the first time, but if I open a new tab or refresh the current tab for a new conversation the bot will fetch the first conversation data.
The Exception is thrown in Startup.cs in:
services.AddBot<dialogBotBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
// Catches any errors that occur during a conversation turn and logs them to currently
// configured ILogger.
ILogger logger = _loggerFactory.CreateLogger<dialogBotBot>();
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync(exception + "\nSorry, it looks like something went wrong.\n" + exception.Message);
};
// Create and add conversation state.
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
// Create and add user state.
var userState = new UserState(dataStore);
options.State.Add(userState);
});
My onTurnAsync method is:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
// Handle Message activity type, which is the main activity type for shown within a conversational interface
// Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
// see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
if (turnContext.Activity.Type == ActivityTypes.Message)
{
//Get the current user profile
userProfile = await _accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(), cancellationToken);
userProfile.Contexto.Add(turnContext.Activity.Text);
foreach (string s in userProfile.Contexto)
await turnContext.SendActivityAsync(s);
// Get the conversation state from the turn context.
var state = await _accessors.CounterState.GetAsync(turnContext, () => new CounterState());
// Bump the turn count for this conversation.
state.TurnCount++;
// Check LUIS model
var recognizerResult = await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);
var topIntent = recognizerResult?.GetTopScoringIntent();
// Get the Intent as a string
string strIntent = (topIntent != null) ? topIntent.Value.intent : "";
// Get the IntentScore as a double
double dblIntentScore = (topIntent != null) ? topIntent.Value.score : 0.0;
// Only proceed with LUIS if there is an Intent
// and the score for the Intent is greater than 95
if (strIntent != "" && (dblIntentScore > 2))
{
switch (strIntent)
{
case "None":
//add the bot response to contexto
await turnContext.SendActivityAsync("Desculpa, não percebi.");
break;
case "Utilities_Help":
//add the bot response to contexto
await turnContext.SendActivityAsync("Quero-te ajudar!\nO que precisas?");
break;
default:
// Received an intent we didn't expect, so send its name and score.
//add the bot response to contexto
await turnContext.SendActivityAsync($"Intent: {topIntent.Value.intent} ({topIntent.Value.score}).");
break;
}
}
else
{
if (userProfile.Name == null)
{
// Run the DialogSet - let the framework identify the current state of the dialog from the dialog stack and figure out what (if any) is the active dialog.
var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
// If the DialogTurnStatus is Empty we should start a new dialog.
if (results.Status == DialogTurnStatus.Empty)
{
await dialogContext.BeginDialogAsync("details", null, cancellationToken);
}
}
else
{
var answers = await this.QnA.GetAnswersAsync(turnContext);
if (answers.Any() && answers[0].Score > 0.7)
{
// If the service produced one or more answers, send the first one.
await turnContext.SendActivityAsync(answers[0].Answer + "\n" + state.TurnCount);
}
else
{
var responseMessage = $"Ainda não sei a resposta mas vou averiguar\nPosso-te ajudar com mais alguma coisa?";
String connectionString = "Data Source=botdataserverv1.database.windows.net;" +
"Initial Catalog=botDataBase;" +
"User id=AzureAdmin#botdataserverv1.database.windows.net;" +
"Password=admin_123;";
SqlConnection connection = new SqlConnection(connectionString);
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommand command;
String sms = turnContext.Activity.Text;
float result = answers[0].Score;
String insertMessage = "insert into Mensagem(texto,contexto,grauCerteza)" +
"values('" + sms + "', 'Falta apurar o contexto' ," + result + ")";
connection.Open();
command = new SqlCommand(insertMessage, connection);
adapter.InsertCommand = new SqlCommand(insertMessage, connection);
adapter.InsertCommand.ExecuteNonQuery();
command.Dispose();
connection.Close();
await turnContext.SendActivityAsync(responseMessage);
}
}
// Save the user profile updates into the user state.
await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
// Set the property using the accessor.
await _accessors.CounterState.SetAsync(turnContext, state);
// Save the new turn count into the conversation state.
await _accessors.ConversationState.SaveChangesAsync(turnContext);
}
}
}
Your problem is here:
float result = answers[0].Score;
You have:
// Check to see if we have any answers
if (answers.Any() && answers[0].Score > 0.7)
{
[...]
// This is fine
}
else // else, WE HAVE NO ANSWERS
{
[...]
// At this point, answers is an empty array, so answers[0] throws an IndexOutOfRangeException
float result = answers[0].Score;
The reason this happens on refresh is because the new tab's user uses the same User Id. The bot already knows their name, so doesn't show the dialog, and when it calls await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);, the user hasn't entered anything in the new turnContext, so it returns an empty array.
Sidenote: You can set the userID in WebChat with this:
window.WebChat.renderWebChat(
{
directLine: directLine,
userID: "USER_ID" // Make is use Math.random() or something if you want it to be random for each refresh
},
this.botWindowElement.nativeElement
);

Create Web API in .net Core to process the Large no of records Parallel

I am developing API for to process around 25000 records. For each record I have to call another API which will return additional details for each product .These details needs to be updated in my database
The problem is since , I am processing large no of records & different API is being called inside my API, processing time is very High & data may be processed incorrectly .
[Route("GetSymbolDetailsParallel/{Exchange?}/{MarketGuid?}/{Symbol?}")]
public async Task<IActionResult> GetSymbolDetailsParallel(string Exchange = "", string MarketGuid = "", string Symbol = "")
{
GlobalFunctions objGolobal = new GlobalFunctions();
MongoClient client = new MongoClient(_ConFigSettings.MongoConnectionString);
var db = client.GetDatabase(_ConFigSettings.DatabaseName);
if (!string.IsNullOrEmpty(Symbol) && !string.IsNullOrEmpty(Exchange))
{
SymbolsBE objSymbols = db.GetCollection<SymbolsBE>("Symbols").Find(x => x.Symbol == Symbol + '.' + Exchange).FirstOrDefault();
await objGolobal.getSymbolsDetails(objSymbols, _ConFigSettings);
}
else if (!string.IsNullOrEmpty(MarketGuid))
{
try
{
// Get the List from the MongoDb Database & Pass the list to the function
List<SymbolsBE> lstSymbols = db.GetCollection<SymbolsBE>("Symbols").Find(x => x.MarketGuid == MarketGuid
&& x.isActive == true
&& (x.Fundamental == null || x.Fundamental.Code == "")).ToList();
GetMultipleFundamentalAsync(lstSymbols, _ConFigSettings);
}
catch (Exception ex)
{
}
}
return Ok("Sucess");
}
public async Task GetMultipleFundamentalAsync(List<SymbolsBE> lst, ConfigSettings _ConFigSettings)
{
DateTime StartDate = DateTime.Now;
int cnt = lst.Count;
for (int i = 0; i < cnt; i++)
{
await Task.Factory.StartNew(async () =>
{
await getSymbolsDetails(lst[i], _ConFigSettings);
});
}
}
public async Task getSymbolsDetails(SymbolsBE objSymbol, ConfigSettings _ConFigSettings)
{
// Code Download Details from API using HttpResponseMessage Res = await client.GetAsync(URL)
// This response will be saved in the Database
}

How do I Attach an AdaptiveCardFromJson in a LUIS Bot C#?

I asked a similar question recently but wasn't specific enough. I see that there is some code with the AdaptiveCards NuGet Package to attach an AdaptiveCardFromJson and AdaptiveCardFromSDK, which under a the normal Microsoft Bot Model is available.
However, under the Microsoft LUIS Bot Model isn't an option, here's the code I have which returns an employee lookup result from a SQL DB Search:
[LuisIntent("Who_is_Employee")]
public async Task Who_is_EmployeeIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation recommendation;
if (result.TryFindEntity("Communication.ContactName", out recommendation))
{
List<Employee> results = EmployeeService.FindEmployees(recommendation.Entity);
if (results.Count > 0)
{
string response = "";
foreach (Employee e in results)
{
string name = e.FullName;
string title = e.JobTitle;
response += " " + name + " " + title + "\n";
}
await context.PostAsync(response);
}
}
else
{
await context.PostAsync(" Sorry, I couldn't find who you were looking for.");
}
}
I would like that information to be returned as an AdaptiveCard, how do I achieve this?
Mark,
you need to craft your adaptive card either as json or using the SDK to create an instance of AdaptiveCard. Here is a great place to learn more about this.
Once you've crafted your card and have an instance of the AdaptiveCard class, you need to create a new message and attach the card to that message. The new message is what you'll post back to the user.
The code will look something like this
var card = AdaptiveCard.FromJson(<your json here>);
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
var myRespsonse = context.MakeMessage();
myRespsonse.Attachments.Add(attachment);
await context.PostAsync(myRespsonse, CancellationToken.None);
This was the code I ended up having to use to make this successful:
[LuisIntent("Who_is_Employee")]
public async Task Who_is_EmployeeIntent(IDialogContext context, LuisResult result)
{
EntityRecommendation recommendation;
if (result.TryFindEntity("Communication.ContactName", out recommendation))
{
List<Employee> results = EmployeeService.FindEmployees(recommendation.Entity);
if (results.Count > 0)
{
/* Single line per result */
/*
string response = "";
foreach (Employee e in results)
{
string name = e.FullName;
string title = e.JobTitle;
response += " " + name + " " + title + "\n";
}
await context.PostAsync(response);
*/
/* Adaptive card per result */
// Load json template
string physicalPath = System.Web.HttpContext.Current.Server.MapPath("../AdaptiveCards/EmployeeLookup.json");
string jsonTemplate = "";
using (StreamReader r = new StreamReader(physicalPath))
{
jsonTemplate = r.ReadToEnd();
}
var respsonse = context.MakeMessage();
foreach (Employee e in results)
{
string employeeJson = jsonTemplate;
employeeJson = employeeJson.Replace("{{FullName}}", e.FullName);
employeeJson = employeeJson.Replace("{{JobTitle}}", e.JobTitle);
employeeJson = employeeJson.Replace("{{Reference}}", e.Reference);
employeeJson = employeeJson.Replace("{{Phone}}", e.Phone);
employeeJson = employeeJson.Replace("{{Email}}", e.Email);
employeeJson = employeeJson.Replace("{{Mobile}}", e.Mobile);
AdaptiveCard card = AdaptiveCard.FromJson(employeeJson).Card;
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
respsonse.Attachments.Add(attachment);
}
await context.PostAsync(respsonse);
}
}
else
{
await context.PostAsync(" Sorry, I couldn't find who you were looking for.");
}
}

Xamarin-CrossDownloadManager - waiting for download file

I use Xamarin-CrossDownloadManager (https://github.com/SimonSimCity/Xamarin-CrossDownloadManager) and I need waiting for download a file. I have this code:
private static async Task<bool> FileDownloadedTest(string LinkToFile, string PathFile)
{
var downloadManager = CrossDownloadManager.Current;
CrossDownloadManager.Current.PathNameForDownloadedFile = new System.Func<IDownloadFile, string>(file => {
return PathFile;
});
{
await DeleteFile(PathFile);
var file = downloadManager.CreateDownloadFile(LinkToFile);
await Task.Run(() => downloadManager.Start(file, true)); //why this not wait???
}
bool FileExist = await IsFileExist(PathFile);
return FileExist;
}
Why it not wait for finish download action? How to do it?
On library site they wrote, that I can watch the IDownloadManager.Queue to get information when the file is downloaded. But, I don't know how to use this in my method... Can you help me?
PS: Sorry for my english, I'm still learning it ;)
With that library, there is no callback or event published for when a file is finished downloading, but you can do a simple check and wait some more loop.
await Task.Run(async () =>
{
var downloadManager = CrossDownloadManager.Current;
var file = downloadManager.CreateDownloadFile(someFileBasedUrl);
downloadManager.Start(file);
bool isDownloading = true;
while (isDownloading)
{
await Task.Delay(10 * 1000);
isDownloading = IsDownloading(file);
}
});
The IsDownloading method:
bool IsDownloading(IDownloadFile file)
{
if (file == null) return false;
switch (file.Status)
{
case DownloadFileStatus.INITIALIZED:
case DownloadFileStatus.PAUSED:
case DownloadFileStatus.PENDING:
case DownloadFileStatus.RUNNING:
return true;
case DownloadFileStatus.COMPLETED:
case DownloadFileStatus.CANCELED:
case DownloadFileStatus.FAILED:
return false;
default:
throw new ArgumentOutOfRangeException();
}
}
Re: https://github.com/SimonSimCity/Xamarin-CrossDownloadManager/blob/develop/Sample/DownloadExample/Downloader.cs#L46
I don't know why IDownloadFile.Status = COMPLETED not working, but i found solve problem:
await Task.Run(() =>
{
var downloadManager = CrossDownloadManager.Current;
var file = downloadManager.CreateDownloadFile(LinkToFile);
downloadManager.Start(file);
while (file.Status == DownloadFileStatus.INITIALIZED)
{
while (file.TotalBytesExpected > file.TotalBytesWritten)
{
Debug.WriteLine(file.Status);
}
}
});
Somebody know why DownloadFileStatus.INITIALIZED working, but DownloadFileStatus.COMPLETED not?
SushiHangover's answer above worked great, and I combined it with ACR User Dialog's package (https://github.com/aritchie/userdialogs) to show a nice loading progress screen to the user while waiting for the download to complete. This works nicely on Android (I couldn't test iOS yet).
...
var downloadManager = CrossDownloadManager.Current;
var fileToDownload = downloadManager.CreateDownloadFile(args.Url);
downloadManager.Start(fileToDownload, true);
args.Cancel = true;
UserDialogs.Instance.Progress("Downloading").Show();
bool isDownloading = true;
while (isDownloading)
{
await Task.Delay(100);
if (fileToDownload.TotalBytesExpected > 0)
{
UserDialogs.Instance.Progress().PercentComplete = (int)(fileToDownload.TotalBytesWritten / fileToDownload.TotalBytesExpected * 100);
Console.WriteLine(("DOWNLOAD PROGRESS: " + fileToDownload.TotalBytesWritten / fileToDownload.TotalBytesExpected * 100).ToString() + "%");
}
isDownloading = IsDownloading(fileToDownload);
}
UserDialogs.Instance.Progress().Hide();
//Below code opens the download location after download has completed.
Intent intent = new Intent(DownloadManager.ActionViewDownloads);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
return;
}
}
}
bool IsDownloading(IDownloadFile file)
{
if (file == null) return false;
switch (file.Status)
{
case DownloadFileStatus.INITIALIZED:
case DownloadFileStatus.PAUSED:
case DownloadFileStatus.PENDING:
case DownloadFileStatus.RUNNING:
return true;
case DownloadFileStatus.COMPLETED:
case DownloadFileStatus.CANCELED:
case DownloadFileStatus.FAILED:
return false;
default:
throw new ArgumentOutOfRangeException();
}
}

Resources