Hello I am having trouble writing logs to file on Android device using Xamarin.Forms (.NET Core shared project) and Serilog.
So far I have installed Serilog in Shared project. Installed Serilog, Serilog.Sinks.File, and Serilog.Sinks.Xamarin to my Android project and initialized logger in MainActivity:
Log.Logger = new LoggerConfiguration()
.WriteTo.File(Path.Combine(Environment.ExternalStorageDirectory.AbsolutePath,"XamarinLib-{Date}.txt"),
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] [{SourceContext}] {Message}{NewLine}{Exception}",
fileSizeLimitBytes: 100000000,
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true,
shared: false,
retainedFileCountLimit: 31,
encoding: Encoding.UTF8)
.WriteTo.AndroidLog()
.CreateLogger();
Afterwards I call the logger from shared project like:
Log.Information("Test writing to log file");
I can see the log command being executed in Visual Studio debug output, but the file is simply not created.
I've tried multiple locations on both emulator and actual device (no root access).
I've also tried to use RollingFile sink in similar manner with no success.
Any ideas?
First, you have to allow permissions in your AndroidManifest.xml file.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Next, The user either must, approve that on runtime in which you have to code this in your code behind. NOTE Remember to add Plugin.Permissions on your NUGET package:
InitializeComponent();
Task.Run(async () =>
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
if (status != PermissionStatus.Granted)
{
var accepted = await DisplayAlert("Storage Permission Required",
"Please enable your storage permission, it will be used to store logs and crashes",
"ACCEPT",
"CANCEL");
if(accepted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Storage);
status = results[Permission.Storage];
}
}
}
catch (Exception ex)
{
await DisplayAlert("Exception ex", "Exception ex", "OK");
}
});
OR
let them change the permissions in the settings -> app -> permissions.
Finally,
change the filename that will link to the storage/emulated/0/[your added directory].
After the closing the app, you can see it in the Android File Manager.
as pointed out by Ruben Bartelink the problem is that Android can't simply write to external storage (ie /storage/emulated/0... etc..).
I was able to log to a file on a Xamarin.Forms project in both Android and iOS.
_Tmp = System.IO.Path.GetTempPath();
_Path = System.IO.Path.Combine(_Tmp, "Serilog.txt");
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.File(_Path, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7)
.CreateLogger();
Log.Information("Started new serilogger {SERILOG} on file {FILE}", this, _Path);
Log.CloseAndFlush();
//test
foreach (string log in System.IO.Directory.GetFiles(_Tmp, "*.txt"))
{
string test = System.IO.File.ReadAllText(log);
System.Diagnostics.Debug.WriteLine($"Test[{log}] -> {test}");
}
which printed on the debug console:
[0:] Test[/data/user/0/com.******/cache/Serilog20190819.txt] -> 2019-08-19 16:00:36.997 +02:00 [INF] Started new serilogger ******.Functions.Serilogger on file /data/user/0/com.******/cache/Serilog.txt
Related
public static string SendMail(MailInfo mail)
{
string str = "";
try
{
var credentials = new ClientSecretCredential(_tenant, _clientId, _clientSecret, new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud });
GraphServiceClient graphServiceClient = new GraphServiceClient(credentials);
var message = new Message
{
Subject = mail.Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = mail.Message
},
ToRecipients = new List<Recipient>()
{
new Recipient { EmailAddress = new EmailAddress { Address = mail.ToAddress }}
}
};
graphServiceClient.Users[mail.FromAddress].SendMail(message, true).Request().PostAsync().Wait();
str = "Success";
}
catch (Exception ex)
{
str = "Exception";
}
return str;
}
This is my code .
I am getting in error on this line
var credentials = new ClientSecretCredential(_tenant, _clientId, _clientSecret, new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud });
In server inside my log i am getting the error this kinds of line
ERROR 2022-12-26 12:25:24,250 2186 Registration2 Forgototp - ameen#abi-tech.com.sg
ERROR 2022-12-26 12:25:24,297 2233 MailHelper SendForgetPassword - SendForgetPassword:ameen#abi-tech.com.sg 7472
Ameen
ERROR 2022-12-26 12:25:24,313 2249 MailHelper SendForgetPassword - The type initializer for 'Azure.Core.ClientOptions' threw an exception.
at Azure.Identity.TokenCredentialOptions..ctor()
at ABIWHIZ.MailHelper.SendMail(MailInfo mail) in C:\Users\ABI-Tech Tele-Call\Downloads\DigitalID Latest\ABIWHIZ\Helpers\MailHelper.cs:line 360
at ABIWHIZ.MailHelper.SendForgetPassword(String Email, String Password, String name) in C:\Users\ABI-Tech Tele-Call\Downloads\DigitalID Latest\ABIWHIZ\Helpers\MailHelper.cs:line 287
Please help me to complete my task
There are a few possible causes for this error:
The Azure SDK is not installed correctly: Make sure that the Azure SDK is installed correctly and all of its dependencies are available. If the SDK is not installed correctly, the ClientOptions class may not be available, and you will see this error.
One of the Azure SDK's dependencies is missing: The Azure SDK relies on several other libraries and frameworks to function. If one of these dependencies is missing or not installed correctly, you may see this error.
There is a problem with your Azure account: If you are using the Azure SDK to access an Azure service, make sure that your Azure account is set up correctly and you have the necessary permissions to access the service.
To fix this error, you will need to debug your code and figure out which of these issues is causing the problem. Once you have identified the issue, you can modify your code or install the necessary dependencies to fix the problem.
I have successfully created the .pkpass file and the api successfully returns the .pkpass file. In Xamarin forms I consume the API and try to add the .pkpass file into wallet but the wallet is not launching automatically.
The file which consumed from api via Xamarin app is working fine, there is no issue with the file. I have sent is as an email attachment and downloaded the attachment - the .pkpass file automatically opens with wallet app.
public async Task DigitalMembershipCardApple()
{
string accesstoken = _dataHelper.GetAccessTokenFromDBAsync().Result;
try
{
_oHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
HttpResponseMessage response = await _oHttpClient.GetAsync(new Uri(Constants.Urls.DigitalMembershipCardApple + _dataHelper.GetPersonID()));
byte[] filebytes = await response.Content.ReadAsByteArrayAsync();
string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), Constants.CISIMembershipCardFields.FileDownloadName);
if (File.Exists(filePath))
{
File.Delete(filePath);
File.WriteAllBytes(filePath, filebytes);
}
else
{
File.WriteAllBytes(filePath, filebytes);
}
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(filePath, Constants.CISIMembershipCardFields.MimeTypeApple)
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
throw ex;
}
}
I have also used the Xamarin essential launcher but that did not help.
Much appreciate a quick help
//Called from Xamarin Forms, now on iOS Project,
public async void AddToWallet(byte[] passByteArray)
{
NSData nsdata = NSData.FromArray(passByteArray);
NSError err = new NSError(new NSString("42"), -42);
PKPass newPass = new PKPass(nsdata, out err);
PKAddPassesViewController pkapvc = new PKAddPassesViewController(newPass);
await UIApplication.SharedApplication.KeyWindow.RootViewController.
PresentViewControllerAsync(pkapvc, true);
}
I don't use Apple wallet before while when I read document of PassKit in Xamarin.iOS, in the Adding Passes into Wallet section, it says:
Passes can be added to Wallet in the following ways:
Conduit Apps – These do not manipulate passes directly, they simply load pass files and present the user with the option of adding them to Wallet.
Companion Apps – These are written by providers to distribute passes and offer additional functionality to browse or edit them. Xamarin.iOS applications have complete access to the PassKit API to create and manipulate passes. Passes can then be added to Wallet using the PKAddPassesViewController. This process is described in more detail in the Companion Applications section of this document.
Mail is Conduit Application, it recognizes attachment as a Pass so the wallet app opens automatically .
Your xamarin app is Companion App, passes can then be added to Wallet using the PKAddPassesViewController. Read the file in your app won't open the wallet app.
My idea is you can download the file and follow the steps here to open the wallet app by using dependency-service in iOS project.
I used a different approach that show a popup to add multiple passes or review them.
Call this using a dependency service:
public async void AddAppleWalletPass(byte[] passByteArray)
{
if (PKPassLibrary.IsAvailable)
{
var library = new PKPassLibrary();
var passes = library.GetPasses();
NSData nsdata = NSData.FromArray(passByteArray);
NSError err = new NSError(new NSString("42"), -42);
PKPass newPass = new PKPass(nsdata, out err);
PKPass[] pKPasses = new PKPass[] { newPass };
await library.AddPassesAsync(pKPasses);
}
else
{
new UIAlertView("Alert", "Wallet is not available!", null, "Ok", null).Show();
}
}
I need to create a pdf file from an HTML on the server-side (dotnet core 2) and send it as an attachment of an email. I have created a node service (createPdf.js) as follows and kept it in a local directory (NodeService) within the solution -
module.exports = function (callback, html, outputFilePath) {
var pdf = require('html-pdf');
var options = { format: 'A3', orientation: 'portrait' };
pdf.create(html, options)
.toFile(outputFilePath, function (err, res) {
if (err)
return callback(null, false);
callback(null, true);
});
}
And I am triggering this function as follows -
public static async Task<bool> GeneratePdf(INodeServices nodeService, string html, string outputFilePath)
{
string pdfGenerationNodeService = Path.Combine(Directory.GetCurrentDirectory(), "NodeService", "createPdf.js");
try
{
return await nodeService.InvokeAsync<bool>(pdfGenerationNodeService, html, outputFilePath);
}
catch (Exception ex)
{
throw;
}
}
For calling this method from the controller -
public async Task<IActionResult> SendQuotationToHospitalAsync([FromServices]INodeServices nodeService, int id)
{
...
bool isAdminPdfGenerationSuccess = await PdfHelperService.GeneratePdf(nodeService, htmlContent, filePath);
...
}
I have also registered the node service in StartUp.cs -
services.AddNodeServices();
When I am triggering the function in debug mode, it's working properly and the pdf file is getting generated. However, once I deploy the application on the server, the node function is not getting triggered.
Any help regarding the issue will be very helpful. Thanks.
P.S. As it is a home project, I can not afford any premium HTML-to-PDF converter
You may have a look at this as a working example of what you are trying to achieve with NodeServices. It runs on Docker, so you can get a hint from the Dockerfile of what you need to have installed on the server to get it working there as well.
Another approach is to use PuppeteerSharp, as follows:
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new string[] { "--no-sandbox" }
});
var page = await browser.NewPageAsync();
await page.SetContentAsync(htmlContent);
await page.PdfAsync(filePath); // page.PdfStreamAsync() to generate only in-memory
I'm using angular 7 for client-side and had npm packages installed accordingly, which included the html-pdf package as well. However, I had not installed it in the directory where I have kept the node script. I was hoping that the package would be taken from the vendor.js after deployment, which was clearly not the case. I had to create another package.json file in the directory and installed it separately after deployment and everything went smooth thereafter.
That's what I was missing when deploying the application - a little manual npm install for installing the package I'm using, in the local directory of the node script.
Thanks a lot for discussing the issue, it helped me a lot in understanding what other mistakes I might have done.
I am trying to copy a preloaded SQLite db into my UWP app. On the initial installation it copies the "test.db", but the size is 0 bytes and there are no tables or data. The original db is 1300 bytes with data and tables.
Another factoid...when I create the app Using Visual Studio 2017 and compile and run/debug the app it works fine, but when I sideload the appx file or download from the Windows Store the db is empty.
Here is the code that I am using:
Task task = CopyDatabase();
private async Task CopyDatabase()
{
bool isDatabaseExisting = false;
try
{
StorageFile storageFile = await ApplicationData.Current.LocalFolder.GetFileAsync("Express.db");
isDatabaseExisting = true;
}
catch
{
isDatabaseExisting = false;
}
if (!isDatabaseExisting)
{
StorageFile databaseFile = await Package.Current.InstalledLocation.GetFileAsync("Express.db");
await databaseFile.CopyAsync(ApplicationData.Current.LocalFolder, "Express.db", NameCollisionOption.ReplaceExisting);
}
}
I'm not getting any error messages.
Does the your database file deployed correctly to the target system?
To confirm it, see your deployed - "Package" - folder. Open command prompt with administrative previleges, and see the directory
c:\Program Files\WindowsApps\your-app-id
If your database file deployed successfully, you can see it in the directory. If not, you may need to change the deploy settings.
To deploy the file to target machine, you should set the property of the one as ...
'BuildAction=Contents'
'Copy to output directory'='Always Copy'
You can set it from solution explorer and right-click the your database file.
If you succeeded the deploying file, your code will copy your database file to app local folder.
c:\Users\YOUR-USER-ID\AppData\Local\Packages\YOUR-APP-ID\LocalState
First, you would need to use await for your CopyDatabase method.
Second, I suggest you call this method in MainPage_Loaded event handler instead of MainPage's Constructor.
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
gui = this; InitializeComponent();
await CopyDatabase();
DataSetup();
CreateNewChartButton.Visibility = Visibility.Collapsed;
SignInButton_Click(null, null);
}
I have setup Firebase in an Xamarin.Android application, following this guide:
https://developer.xamarin.com/guides/android/application_fundamentals/notifications/remote-notifications-with-fcm/
It seemed to work at first, with receiving notifications without problems. Then I tried to update the code and redeploy to Device and afterwards nothing happened. Since I have been unable to receive any notifications from the Firebase Console.
I have tried
Uninstall application and deploy again
Download new google-service.json from Firebase Console
Delete and setup new Firebase Project, and updating google-service.json for this
When the application is newly deployed, and I try to send a notification, nothing happens. After an update and redeploy I get the error "Unregistered registration token" in the console.
Before setting up a new Firebase Project I would get the following error when trying to subscribe to a topic
"INVALID_PARAMETERS", but this problem has seen to disappear after creating a new project. Now then subscribing to Topic, it appears in the Console after some time. But trying to send notifications to the topic, gives no results.
Edit - Added code
MainActivity.cs
[Activity(Label = "<<App Label>>", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
private TextView msgText;
private const string Tag = "MainActivity";
private FirebaseAnalytics _analytics;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
_analytics = FirebaseAnalytics.GetInstance(this);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
msgText = FindViewById<TextView>(Resource.Id.msgText);
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
Log.Debug(Tag, "Key: {0} Value: {1}", key, value);
}
}
IsPlayServicesAvailable();
var logTokenButton = FindViewById<Button>(Resource.Id.logTokenButton);
logTokenButton.Click += delegate {
Log.Debug(Tag, "InstanceID token: " + FirebaseInstanceId.Instance.Token);
};
var subButton = FindViewById<Button>(Resource.Id.subscribeButton);
subButton.Click += SubButton_Click;
}
private void SubButton_Click(object sender, System.EventArgs e)
{
FirebaseMessaging.Instance.SubscribeToTopic("A");
Log.Debug(Tag, "Subscribed to A");
}
public bool IsPlayServicesAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
msgText.Text = GoogleApiAvailability.Instance.GetErrorString(resultCode);
else
{
msgText.Text = "This device is not supported";
Finish();
}
return false;
}
else
{
msgText.Text = "Google Play Services is available.";
return true;
}
}
}
StwFirebaseInstanceIdService.cs
[Service]
[IntentFilter(new []{ "com.google.firebase.INSTANCE_ID_EVENT" })]
public class StwFirebaseInstanceIdService : FirebaseInstanceIdService
{
private const string Tag = "StwFirebaseInstanceIdService";
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(Tag, "Refreshed Token: " + refreshedToken);
//SendRegistrationToServer(refreshedToken);
}
private void SendRegistrationToServer(string token)
{
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="<<Package Name>>" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="16" />
<application android:label="<<App Label>>">
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
I bumped into the same issue. Try to uninstall the app one more time and before you get the token (old one), call the refresh token to get the refreshed token:
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{ // Your custom implementation depends on your server }
for more information visit Here
The mentioned tutorial is quite good but misses a few points. I couldn't get it to work until I added my app's SHA1 key to the registration. You can get this by the following command:
"C:\Program Files\Java\jdk1.8.0_161\bin\keytool.exe" -list -v -keystore "%USERPROFILE%\AppData\Local\Xamarin\Mono for Android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
You might have to edit the command a bit to match your Java version.
The second thing is that that there seems to be some issues with DEBUG builds. I got this method from somewhere (don't know where now).
private void ConfigureFireBase()
{
#if DEBUG
Task.Run(() =>
{
var instanceId = FirebaseInstanceId.Instance;
instanceId.DeleteInstanceId();
Android.Util.Log.Debug("TAG", "{0} {1}", instanceId?.Token?.ToString(), instanceId.GetToken(GetString(Resource.String.gcm_defaultSenderId), Firebase.Messaging.FirebaseMessaging.InstanceIdScope));
});
// For debug mode only - will accept the HTTPS certificate of Test/Dev server, as the HTTPS certificate is invalid /not trusted
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
#endif
}
Call it from OnCreate, just after OnCreate like so:
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ConfigureFireBase();
I'm using Xamarin.Firebase.Messaging (71.1740.0) on Visual Studio for Mac (8.3.11). Testing on older (API 21) and newer (API 28) physical android devices.
First deploy works, new tokens on both devices. A custom REST API call to FCM successful:
{"multicast_id":4160565375891761047,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1576702929647400%320d8b4c320d8b4c"}]}
Changing code, and redeploying in Debug mode to the same devices works fine. Sending from Firebase console works fine, notifications to both devices in same project app in FCM.
After some hours, the custom REST API returns for the older device token (no code change):
{"multicast_id":565695677811030352,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}
Then after some more hours, the new device token also returns "NotRegistered" from the custom REST API (no code change). Also, the Firebase console no longer sends notifications to devices.
The only way I got the custom REST API and Firebase console to correctly send notifications once this happens, is to use Visual Studio to manually remove the app from the device:
Visual Studio Menu > Build > Remove from Device
Deploying again will install the app as though it were new to the device, and request a fresh token.
Note: Already unchecked "Preserve data/cache between application deploys" in the Visual Studio preferences, with no significant change. Clearing the app's data/cache doesn't work either.
Used latest Microsoft code from GitHub (Dec 2019): https://github.com/xamarin/GooglePlayServicesComponents/tree/master/samples/com.google.firebase/firebase-messaging