I am using visual studio app center to distribute my application the problem is I need my distribution groups to be notified when there is an update to my application and force them to update so that all of my distribution group will have the latest version. The code below I got it from the Microsoft website and I put it inside my App.xaml.cs under OnStart(). The problem is the notification for a new version of the is not displaying or notifying my distribution group.
protected override void OnStart ()
{
AppCenter.Start("android=<appsecret>;", typeof(Analytics), typeof(Crashes), typeof(Distribute));
Analytics.SetEnabledAsync(true);
Distribute.ReleaseAvailable = OnReleaseAvailable;
bool OnReleaseAvailable(ReleaseDetails releaseDetails)
{
string versionName = releaseDetails.ShortVersion;
string versionCodeOrBuildNumber = releaseDetails.Version;
string releaseNotes = releaseDetails.ReleaseNotes;
Uri releaseNotesUrl = releaseDetails.ReleaseNotesUrl;
var title = "Version " + versionName + " available!";
Task answer;
if (releaseDetails.MandatoryUpdate)
{
answer = Current.MainPage.DisplayAlert(title, releaseNotes, "Download and Install");
}
else
{
answer = Current.MainPage.DisplayAlert(title, releaseNotes, "Download and Install", "Ask Later");
}
answer.ContinueWith((task) =>
{
if (releaseDetails.MandatoryUpdate || (task as Task<bool>).Result)
{
Distribute.NotifyUpdateAction(UpdateAction.Update);
}
else
{
Distribute.NotifyUpdateAction(UpdateAction.Postpone);
}
});
return true;
}
}
If this is the document you followed, the only thing I see that it says to explicitly add to the OnStart() method is the AppCenter.Start(...) method call:
Open your App.xaml.cs (or your class that inherits from Xamarin.Forms.Application) in your shared or portable project and add the method below in the OnStart() override method.
AppCenter.Start("ios={Your Xamarin iOS App Secret};android={Your Xamarin Android App secret}", typeof(Distribute));
Then later on, in this section, it talks about customizing the update dialog, where it says:
You can customize the default update dialog's appearance by implementing the ReleaseAvailable callback. You need to register the callback before calling AppCenter.Start as shown in the following example:
Distribute.ReleaseAvailable = OnReleaseAvailable;
AppCenter.Start(...);
So, according to the above Distribute.ReleaseAvailable = OnReleaseAvailable; has to be called before AppCenter.Start. You are calling it after.
Also not sure if it is just a formatting error when you posted your question, but it looks like you are implementing the OnReleaseAvailable method inside the OnStart() method, which is not legal in C# (or not until C# 7 anyway, which I just realized... cool). So unless you're using C# 7, you will want to implement the OnReleaseAvailable method outside of the OnStart(), or any other, method. IOW it should be implemented as a direct member of the class. E.g.:
protected override void OnStart ()
{
Distribute.ReleaseAvailable = OnReleaseAvailable;
AppCenter.Start("android=<appsecret>;", typeof(Analytics), typeof(Crashes), typeof(Distribute));
Analytics.SetEnabledAsync(true);
}
bool OnReleaseAvailable(ReleaseDetails releaseDetails)
{
...
}
Related
I am working on a rather complex xamarin forms app for a while now and now I came over a problem related to the async initialization of the app. Some context before I come to the problem.
I am using the current version of Xamarin Forms, my main target is
xamarin android
I am building on top of the xamarin forms Shell
pattern
I am using a sqlite database for the main part of the apps
configuration
The app layout is mainly dependent on the configuration stored in the sqlite database (what I call a data driven app)
Therefore I am building the whole shell page layout not in xaml but in code behind of the AppShell.xaml.cs - in the constructure to be concrete (first weird point in my personal opinion).
So first remarkable point here - I need the fully loaded configuration BEFORE the AppShell is initiated.
At first a started with a mockup data service where the whole config was inside a large mocked object so it ran fully synchronized - everything perfect!
After switching over to the sqlite implementation where I am using async methods the problem started to show up because I try to load the configuration in the constructor (second weird point because such a task should never be part of a constructor in my point of view)
So my constructor of AppShellView.xaml looks like this.
public App()
{
InitializeComponent();
InitApp().SafeFireAndForget(onException: ErrorHandler);
MainPage = new AppShellView();
}
the InitApp Method where the database is queried looks like this.
private async Task InitApp()
{
_settingsService = ViewModelLocator.Resolve<ISettingsService>();
ViewModelLocator.UpdateDependencies(_settingsService.UseDemoMode);
_dataService = ViewModelLocator.Resolve<IDataService>();
ActiveConfiguration = await _dataService.GetActiveConfigurationAsync().ConfigureAwait(false);
//MainPage = new AppShellView(); this was added to reload the MainPage after the config was fully loaded
}
what obviously happens is that the MainPage = new AppShellView() in the App's constructor is called before the config is loaded - because its called in a fireandforget way. Therefore the following construction of the AppShellView does not work as intendet because the pages cannot be added because of the missing config. I hope this is clear so far.
To overcome this issue I added another MainPage = new AppShelLView() as last step of the InitApp method.
The constructor of the AppShellView looks like this:
public AppShellView()
{
InitializeComponent();
BuildMainNavigation();
BuildSettingsNavigation();
InitRoutes();
this.CurrentItem.CurrentItem = startSection;
}
This seems to work on thirst sight but triggers side effects when the app is started from AppLink Shortcuts and not to forget I ended up initializing the same object again and again which causes performance issues and simply smells...
So I hope I made my point clear so far - I know that I have a view issues in my implementation but I simply don't see the correct way of doing it.
What I would naturally would try to do is doing the configuration loading in the android init part behind the splash screen which I implemented like this: https://learn.microsoft.com/en-us/xamarin/android/user-interface/splash-screen but I also found no way to pass the data from the android Activity to the App class.
To sum up and ask a concrete question:
What is the best practice way of loading async configuration BEFORE initializing the Shell
#Jason: thanks for your fast response. So this idea seems promising, sounds like another Splash Screen but thats ok.
I tried that - for the initial setup that works fine.
App's constructor is simplified to this:
public App()
{
InitializeComponent();
MainPage = new Splashscreen();
}
The OnStart looks like this now:
protected async override void OnStart()
{
base.OnStart();
if (!IsInitiated)
{
_settingsService = ViewModelLocator.Resolve<ISettingsService>();
ViewModelLocator.UpdateDependencies(_settingsService.UseDemoMode);
_dataService = ViewModelLocator.Resolve<IDataService>();
ActiveConfiguration = await _dataService.GetActiveConfigurationAsync().ConfigureAwait(true);
MainPage = new AppShellView();
App.Current.UserAppTheme = _settingsService.OSAppTheme;
}
else
{
App.Current.UserAppTheme = _settingsService.OSAppTheme;
}
base.OnResume();
}
But I have app link functionality, where OnAppLinkRequestReceived is called.
protected override async void OnAppLinkRequestReceived(Uri uri)
{
if(MainPage is Splashscreen)
{
MainPage = new AppShellView();
}
IsInitiated = true;
var targets = uri.ToString().Replace(GlobalSetting.AppShortcutUriBase, "");
var subtargets = targets.Split('/');
App.CurrentPageId = new Guid(subtargets[0]);
if(subtargets.Length > 1 && !string.IsNullOrWhiteSpace(subtargets[1])){
await Shell.Current.GoToAsync($"//{App.CurrentPageId}?buildingPartId={new Guid(subtargets[1])}");
}
else
{
await Shell.Current.GoToAsync("//" + App.CurrentPageId);
}
}
The Problem is that its called before OnStart. So its calling the correct page first but the reinit in OnStarts resets the app to base.
I could overcome this issue with performing the init only of OnAppLinkRequestReceived is not called but then I have one last issue.
I call App.Current.UserAppTheme = _settingsService.OSAppTheme; as displayed above inside OnStart end the call inside the else black is failing with NullpointerException because App.Current seems to be null in this case. Do you know why?
Here are the final code bits that made the whole thing work. The key aspects are as follows:
Add another SplashScreen which is initiated inside App.xaml.cs constructor
Perform the long running async method in the OnStart()
Perform path based navigation which is triggered by an AppLinkRequest also inside the OnStart after storing the AppLinkRequest Uri inside a property
public App()
{
InitializeComponent();
_settingsService = ViewModelLocator.Resolve<ISettingsService>();
ViewModelLocator.UpdateDependencies(_settingsService.UseDemoMode);
_dataService = ViewModelLocator.Resolve<IDataService>();
AppLinkUri = null;
MainPage = new SplashScreen();
}
private async Task InitApp()
{
ActiveConfiguration = await _dataService.GetActiveConfigurationAsync().ConfigureAwait(true);
}
protected override async void OnStart()
{
base.OnStart();
if (ActiveConfiguration == null)
{
await InitApp().ConfigureAwait(true);
}
MainPage = new AppShellView();
if (AppLinkUri != null)
{
var targets = AppLinkUri.ToString().Replace(GlobalSetting.AppShortcutUriBase, "");
var subtargets = targets.Split('/');
App.CurrentPageId = new Guid(subtargets[0]);
if (subtargets.Length > 1 && !string.IsNullOrWhiteSpace(subtargets[1]))
{
await Shell.Current.GoToAsync($"//{App.CurrentPageId}?buildingPartId={new Guid(subtargets[1])}");
}
else
{
await Shell.Current.GoToAsync("//" + App.CurrentPageId);
}
}
base.OnResume();
}
protected override void OnAppLinkRequestReceived(Uri uri)
{
AppLinkUri = uri;
}
I'm working on a UWP project and there's something funky going on with how errors are being presented to me. I don't know if it's VS2017 or how UWP is set up.
I have a piece of code that goes online and retrieves json content, sometimes the code works and sometimes it doesn't. It works when I use Expander control from UWP Community toolkit, and fails when I want to switch to GridView. When it doesn't work, it fails on GetStringAsync method of HttpClient. The strange behavior is that the exception isn't thrown in the method where the problem occurs, the code actually redirects me back without giving an error and as soon as it gets to the property that's supposed to have a value that isn't null, I get a null exception.
This is where the problem happens:
string httpContent = "";
using (HttpClient httpClient = new HttpClient())
{
try
{
httpContent = await httpClient.GetStringAsync(uri);
}
catch (Exception e)
{
// TODO: handle errors
var x = "";
}
}
This piece of code is called from within the view model. It starts with a constructor and RefreshServerKanesWrathDataAsync is the method where json is parsed.
public CncOnlinePageViewModel()
{
cnconline = new CncOnline();
cnconline.RefreshServerKanesWrathDataAsync();
}
The second I get to GetStringAsync, the code just goes back to the constructor like nothing happened, however the method never completes, it just exits back to the constructor, and therefore fails to update observable collections with data. I then get a null exception.
I wanted to test this with VS2015, but I updated some controls that are apparently only supported withing VS2017, so I can't run the code in other versions.
I also ran into an issue with the code prior to this problem, where I tried to access files in a directory without using a token. The behavior was exactly the same, the code wasn't telling me that I didn't have access to the directory I wanted to read, it was just throwing me out of the method back into the location that made the call to read the directory. Just like with the current problem, I would then run into a null exception, which wasn't where the main problem was.
I added Template10 and UWP community toolkit to the project, if that matters.
You shouldn't call an async method from a constructor unless you're willing to provide a callback.
public CncOnlinePageViewModel()
{
cnconline = new CncOnline();
var t = cnconline.RefreshServerKanesWrathDataAsync(); // assuming returns Task<string>
t.ContinueWith(OnCompleted);
}
private void OnCompleted(Task<string> task)
{
if (task.IsFaulted)
{
// Check error
var exception = task.Exception;
}
else if (task.IsCanceled)
{
// User hit cancel?
}
else
{
// All good!
var result = task.Result;
}
}
Here's a sample where RefreshServerKanesWrathDataAsync() returns just Task (not Task<result>)
public CncOnlinePageViewModel()
{
cnconline = new CncOnline();
var t = cnconline.RefreshServerKanesWrathDataAsync(); // assuming returns Task
t.ContinueWith(OnCompleted);
}
private void OnCompleted(Task task)
{
if (task.IsFaulted)
{
// Check error
var exception = task.Exception;
}
else if (task.IsCanceled)
{
// User hit cancel?
}
else
{
// All good!
}
}
On a side note, you may also need to have Visual Studio 2017 break when any exception is thrown. In VS2017, go to Debug->Windows->Exception Settings and make sure Common Language Runtime Exceptions has a check. If it has a filled box, click the box until it turns into a checkmark.
Also..., you can tap into an event raised when any task has an unobserved exception. You can do so in the constructor of App.xaml.cs
public App()
{
TaskScheduler.UnobservedTaskException += OnUnobservedException;
}
private static void OnUnobservedException(object sender, UnobservedTaskExceptionEventArgs e)
{
// Put break point here.
var ex = e.Exception;
// This will keep your app alive, but only do it if it's safe to continue.
e.SetObserved();
}
First of all, I'm using, Xamarin with MvvmCross.
In my ViewModel, I'm using the ZXing MobileBarcodeScanner class to scan a barcode when the user clicks a button:
var scanner = new MobileBarcodeScanner();
var result = await scanner.Scan();
if (result != null)
{
CodigoProduto = result.Text;
InternalPesquisarProduto();
}
After the scan, I run the InternalPesquisarProduto void, that search for data on a remote server, based of course, on the barcode that was read. This method, also display some loading message while the data is fetched:
Ui.DisplayLoading("Searching...", "Searching data");
// Code that fetches the data
Ui.DismissLoading();
The Ui is a property on my ViewModel defined like this:
protected IUiInteractor Ui { get; set; }
I receive it by dependency injection. Here is the relevant code from the implementation being used in this scenario:
public class AndroidUiInteractor : IUiInteractor
{
private IMvxAndroidCurrentTopActivity _mvxCurrentTopActivity;
public AndroidUiInteractor(IMvxAndroidCurrentTopActivity mvxCurrentTopActivity)
{
_mvxCurrentTopActivity = mvxCurrentTopActivity;
}
public void DisplayLoading(string title, string message)
{
_mvxCurrentTopActivity.Activity.RunOnUiThread(() =>
{
_progressDlg = new ProgressDialog(_mvxCurrentTopActivity.Activity);
// Configuring the title and the message
_progressDlg.Show();
});
}
}
The problem is that when the scanner.Scan is called, my caller activity is destroyed, so when I call the Ui.DisplayLoading, the _mvxCurrentTopActivity.Activity is null.
What is most weird about this case, is that I have two Samsungs with Android 5.0 API 21 that I use in my tests, and this problem only happens in one of them, on the other, the activity is not destroyed when calling the scanner.Scan.
Note: I'm sorry for anything wrong in the code, but because of company policies, I can only access the internet by Terminal Service, and the Ctrl + V is disabled on it.
It turns out the problem was in the device. After reseting it's configurations it worked properly.
This might not be a definitive solution for everyone that faces that problem, but in my scenario it could be done.
I am creating a Windows Phone 8.1 RT application. I have installed the facebook and facebook.client sdks for setting up login and sharing purposes. For login purpose I have followed the steps as mentioned here.
My App.xaml.cs OnActivated function looks like this:
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
var protocolArgs = args as ProtocolActivatedEventArgs;
if (protocolArgs != null)
{
LifecycleHelper.FacebookAuthenticationReceived(protocolArgs);
}
Session.OnFacebookAuthenticationFinished += OnFacebookAuthenticationFinished;
}
and here is OnFacebookAuthenticationFinished method
private async void OnFacebookAuthenticationFinished(AccessTokenData session)
{
await Session.CheckAndExtendTokenIfNeeded();
if (Constant.fbSignup)
{
User fbUser = new User();
Account userAccount = new Account();
try
{
FacebookClient fbClient = new FacebookClient(session.AccessToken);
dynamic result = await fbClient.GetTaskAsync("me?fields=id,first_name,last_name,email,location");
fbUser.FirstName = result.first_name;
fbUser.LastName = result.last_name;
userAccount.UserName = result.email;
fbUser.UserAccount = userAccount;
//fbUser.City = result.location.name;
Constant.User = fbUser;
RootFrame.Navigate(typeof(SignUpPage));
}
catch (Exception ex)
{
await new MessageDialog(ex.Message).ShowAsync();
}
}
The login works fine.
Now I want to share some content using the Session.ShowFeedDialog(). I have followed the steps mentioned here for creating AppRequests within the dialog.
I am calling the ShowFeedDialog method this way from a page StoreDetailsPage.xaml.cs
All the following code rests in StorePageDetails.xaml.cs
Session.ShowFeedDialog("", link, linkDescription, linkCaption);
The posting also works fine. But I need to check whether the post was successful or not. For this purpose I tried the
Session.OnFacebookFeedFinished = Success;
where success is
public delegate void FacebookDelegate(FBResult result);
void Success(FBResult result)
{
//Code to check if post was successful
}
So my problem is after ShowFeedDialog is closed the OnActivated event is called and success delegate method is never reached or not called.
I haven't used delegates before so I don't know if there is something wrong there. Also I haven't figured out what the logic for post verification should since I was not able to step into this function. So any suggestions would be much appreciated
I have this code in my C# project:
public void startRecognition(string pName)
{
presentationName = pName;
if (WaveNative.waveInGetNumDevs() > 0)
{
string grammar = System.Environment.GetEnvironmentVariable("PUBLIC") + "\\SoundLog\\Presentations\\" + presentationName + "\\SpeechRecognition\\soundlog.cfg";
if (File.Exists(grammar))
{
File.Delete(grammar);
}
executeCommand();
/// Create an instance of SpSharedRecoContextClass which will be used
/// to interface with the incoming audio stream
recContext = new SpSharedRecoContextClass();
// Create the grammar object
recContext.CreateGrammar(1, out recGrammar);
//recContext.CreateGrammar(2, out recGrammar2);
// Set up dictation mode
//recGrammar2.SetDictationState(SpeechLib.SPRULESTATE.SPRS_ACTIVE);
//recGrammar2.SetGrammarState(SPGRAMMARSTATE.SPGS_ENABLED);
// Set appropriate grammar mode
if (File.Exists(grammar))
{
recGrammar.LoadCmdFromFile(grammar, SPLOADOPTIONS.SPLO_STATIC);
//recGrammar.SetDictationState(SpeechLib.SPRULESTATE.SPRS_INACTIVE);
recGrammar.SetGrammarState(SPGRAMMARSTATE.SPGS_ENABLED);
recGrammar.SetRuleIdState(0, SPRULESTATE.SPRS_ACTIVE);
}
/// Bind a callback to the recognition event which will be invoked
/// When a dictated phrase has been recognised.
recContext.Recognition += new _ISpeechRecoContextEvents_RecognitionEventHandler(handleRecognition);
// System.Windows.Forms.MessageBox.Show(recContext.ToString());
// gramática compilada
}
}
private static void handleRecognition(int StreamNumber,
object StreamPosition,
SpeechLib.SpeechRecognitionType RecognitionType,
SpeechLib.ISpeechRecoResult Result)
{
string temp = Result.PhraseInfo.GetText(0, -1, true);
_recognizedText = "";
// System.Windows.Forms.MessageBox.Show(temp);
// System.Windows.Forms.MessageBox.Show(recognizedWords.Count.ToString());
foreach (string word in recognizedWords)
{
if (temp.Contains(word))
{
// System.Windows.Forms.MessageBox.Show("yes");
_recognizedText = word;
}
}
}
This codes generates a dll that I use in another application.
Now, the wicked bug:
- when I run the startRecognition method in the beginning of the execution of the other application, this codes works very well. But when I run it some time after the beginning, this codes works but the handleRecognition method is never called. I see that the words are recognized because they appear on the Microsoft Speech Recognition app, but the handler method is never called.
Do you know what's the problem with this code?
NOTE: this project has some code that is allways being executed. Might that be the problem? Because the other code is running it doesn't allow it to this to run?
It could be that in the second call to startRecognition() an exception is being thrown before the handler can be added to recContext.Recognition. Put a try/catch around the everything in startRecognition(), and echo any exceptions that get thrown.
I would also output the value of WaveNative.waveInGetNumDevs() to a log or trace file. If it is not > 0 the startRecognition() method won't do anything.
I had another handler in another part of the code.
The recognition handler had to be called before the other one.
I made that way and it worked :)