What directory is the appsettings.json file copied to on Android - android

I am trying to get my Xamarin Forms App to read the appsettings.json config file for setting up RestSharp. I am targeting Android. The appsettings.json file is set to Copy Always in the root of my Android forms project in Visual Studio. I am trying to read in a Factory pattern.
internal class ConfigFactory
{
public ConfigFactory()
{
try
{
SetupSimpleConfiguration();
ReadSimpleConfiguration();
}
catch (Exception e)
{
Log.Logger.Error(e, "Exception Constructing Config Factory");
throw;
}
}
public RestClient QueryClient { get; private set; }
public RestClient CommandClient { get; private set; }
private IConfigurationRoot Config { get; set; }
private void ReadSimpleConfiguration()
{
string val1 = Config["QueryURL"];
string val2 = Config["CommandURL"];
QueryClient = new RestClient(val1);
CommandClient = new RestClient(val2);
}
private void SetupSimpleConfiguration()
{
var currentDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
Log.Logger.Debug("Enter Simple {#Directory}", currentDirectory);
Config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("appsettings.json")
.Build();
Log.Logger.Debug("Leave Simple {#Config}", Config);
}
}
The Exception I keep running into is:
The configuration file 'appsettings.json' was not found and is not
optional. The physical path is
'/data/user/0/com.companyname.SerilogSample/files/appsettings.json'
No matter what I set currentDirectory to.

Related

Send Mail by Azure-Outlook from an App Xamarin.android with App authentication

I'm a beginner in android xamarin programming and I'm trying to develop an app that needs to send a report mail using company cloud services (office365).
The main problem is that the user has no computer skills, therefore it is necessary to authenticate the app on azure (no login done by the user).
what is the correct identification flow? and what is the correct credential provider? ( on windows in a console app i used ClientSecretCredential, and it works fine!)
on android i tried :ClientSecretCredential, InteractiveBrowserCredential and UsernamePasswordCredential but i receive always the same error :
Message: An error occurred sending the request.
I don't understand if the request is not even sent or not answered or what?
here is my code:
async private void Button_buttonSendReport(object sender, EventArgs e)
{
AzureSettings settings = new AzureSettings();
settings.LoadSettings();
GraphHelper.InitializeGraphForAppOnlyAuth(settings);
try
{
await GraphHelper.SendMailAsync("Testing Microsoft Graph", "Hello!", "recipient#inwind.it");
Android.Widget.Toast.MakeText(this, "Mail sent.", Android.Widget.ToastLength.Long).Show();
}
catch (Exception ex)
{
Android.Widget.Toast.MakeText(this, "Error sending mail:"+ex.Message, Android.Widget.ToastLength.Long).Show();
}
}
public class AzureSettings
{
public string? ClientId { get; set; }
public string? ClientSecret { get; set; }
public string? TenantId { get; set; }
public string? AuthTenant { get; set; }
public string? redirectUri { get; set; }
public string[]? GraphUserScopes { get; set; }
public AzureSettings LoadSettings()
{
// Load settings
ClientId = "xxxxxxx-xxxx-xxxxxx-xxxx-xxxxxxxxx";
ClientSecret = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
TenantId = "zzzzzzzz-zzzzz-zzzzz-zzzzzzz-zzzzzzzzzzzz";
AuthTenant = "common";
GraphUserScopes = new[] { "User.Read.All", "User.Read", "Mail.Send" };
redirectUri = "msauth://com.companyname.packageName/<signature_of_app >";
return this;
}
}
this is initialization of client GraphHelper.InitializeGraphForAppOnlyAuth(settings);
public static void InitializeGraphForAppOnlyAuth(AzureSettings settings)
{
_settings = settings;
if (_appClient == null)
{
var options = new InteractiveBrowserCredentialOptions
{
TenantId = _settings.TenantId,
ClientId = _settings.ClientId,
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
RedirectUri = new Uri(_settings.redirectUri),
};
// https://learn.microsoft.com/dotnet/api/azure.identity.interactivebrowsercredential
var interactiveCredential = new InteractiveBrowserCredential(options);
_appClient = new GraphServiceClient(interactiveCredential, _settings.GraphUserScopes);
}
}
this is GraphHelper.SendMailAsync
public static async Task SendMailAsync(string subject, string body, string recipient)
{
// Create a new message
var message = new Microsoft.Graph.Message
{
Subject = subject,
Body = new ItemBody
{
Content = body,ContentType = BodyType.Text
},
ToRecipients = new Recipient[]
{
new Recipient{EmailAddress = new EmailAddress {Address=recipient}}
}
};
await _appClient.Users["kkkkkkkkk-kkkkkk-kkkk-kkkkk-kkkkkkkkk"].SendMail(message).Request().PostAsync();
}

Storing a string on mobile device using gluon mobile plugin

I want to save a string from a TextArea to the device and then reload it after reopening the app. I have tried following the examples (link) but cant get it to work. Main problem arises when i try to read the file and use a StringInputConverter.
private void saveAndLoad(TextArea textArea){
File textFile = new File(ROOT_DIR,"text_file");
String text2 = textArea.getText();
String loadedFile = "none";
if (textFile.exists()){
FileClient fileClient = FileClient.create(textFile);
loadedFile = DataProvider.retrieveObject(fileClient.createObjectDataReader(
new StringInputConverter()));
}
try(FileWriter writer = new FileWriter(textFile)){
writer.write(textArea.getText());
} catch (IOException e) {
e.printStackTrace();
}
textArea.setText(text2);
}
Edit: inserted code which i tried to start reading file with and image of the error i am getting
If you check the DataProvider::retrieveObject documentation:
Retrieves an object using the specified ObjectDataReader. A GluonObservableObject is returned, that will contain the object when the read operation completed successfully.
It returns GluonObservableObject<String>, which is an observable wrapper of the string, not the string itself.
You need to get first the observable, and when the operation ends successfully you can retrieve the string:
if (textFile.exists()) {
FileClient fileClient = FileClient.create(textFile);
GluonObservableObject<String> retrieveObject = DataProvider
.retrieveObject(fileClient.createObjectDataReader(new StringInputConverter()));
retrieveObject.stateProperty().addListener((obs, ov, nv) -> {
if (ConnectState.SUCCEEDED.equals(nv)) {
loadedFile = retrieveObject.get();
}
});
}
This is a quick implementation of this functionality:
public class BasicView extends View {
private static final File ROOT_DIR;
static {
ROOT_DIR = Services.get(StorageService.class)
.flatMap(StorageService::getPrivateStorage)
.orElseThrow(() -> new RuntimeException("Error"));
}
private final File textFile;
private final TextField textField;
private String loadedFile = "none";
public BasicView(String name) {
super(name);
textFile = new File(ROOT_DIR, "text_file");
textField = new TextField();
VBox controls = new VBox(15.0, textField);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(30));
setCenter(controls);
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Basic View");
appBar.getActionItems().add(MaterialDesignIcon.SAVE.button(e -> save()));
appBar.getActionItems().add(MaterialDesignIcon.RESTORE_PAGE.button(e -> restore()));
}
private void save() {
try (FileWriter writer = new FileWriter(textFile)) {
writer.write(textField.getText());
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void restore() {
if (textFile.exists()) {
FileClient fileClient = FileClient.create(textFile);
GluonObservableObject<String> retrieveObject = DataProvider
.retrieveObject(fileClient.createObjectDataReader(new StringInputConverter()));
retrieveObject.stateProperty().addListener((obs, ov, nv) -> {
if (ConnectState.SUCCEEDED.equals(nv)) {
loadedFile = retrieveObject.get();
textField.setText(loadedFile);
}
});
}
}
}

MobileServiceInvalidOperationException: The request could not be completed. (Bad Request) on save images

I am doing a POC application that uses an Android client and a Mobile Azure Service app.
I managed to implement a solution that makes use of a Sql server Compact 4.0 database file in order to store some text and small images (planned maximum 300 Kb for each).
How ever, it only works for really small images (e.g. 2 Kb) that are also compressed in the Jpeg format with the lowest quality
(using this: image.Compress(Bitmap.CompressFormat.Jpeg, 0, stream);).
I do get "Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException: The request could not be completed. (Bad Request)" error for bigger images when trying to save items using the application. I did not find any where in the response any exact mention about request length limit, I assumed this.
So my question would be - is Azure Storage the only way to accomplish this task ? (also, not sure of any free option using Azure Storage).
UI part with content upload (I am using Xamarin)
Choose an image to upload (into _currentImage variable):
protected override void OnActivityResult(int requestCode, Result result, Intent data)
{
base.OnActivityResult(requestCode, result, data);
if (requestCode == 1)
{
if (result == Result.Ok)
{
var selectedImage = data.Data;
var filePath = GetPathToImage(selectedImage);
var photo = BitmapFactory.DecodeFile(filePath);
_currentImage = photo;
ImagePreview.SetImageBitmap(photo);
}
}
}
Save content invoking the Azure Mobile app:
private async void UploadItemContent()
{
using (MobileServiceClient client = new MobileServiceClient(Configuration.Urls.CloudAppUrl))
{
var table = client.GetTable<Item>();
var content = ContentDescriptionEditText.Text;
var header = ContentHeaderEditText.Text;
var stream = new MemoryStream();
_currentImage.Compress(Bitmap.CompressFormat.Jpeg, 0, stream);
var bitmapData = stream.ToArray();
var item = new Item
{
Content = content,
Header = header,
Image = bitmapData,
CreationDate = DateTime.Now.ToUniversalTime()
};
try
{
await table.InsertAsync(item);
ShowSuccessStatus();
}
catch (Exception ex)
{
ShowError(ex.Message);
}
}
}
Backend
Item definition into the Azure Mobile service:
public class Item : ITableData
{
public Item()
{
}
[Key]
[TableColumn(TableColumnType.Id)]
public string Id { get; set; }
public string Header { get; set; }
public string Content { get; set; }
[Column(TypeName = "image")]
public byte[] Image { get; set; }
public DateTime? CreationDate { get; set; }
public DateTime? UpdateDate { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Index(IsClustered = true)]
[TableColumn(TableColumnType.CreatedAt)]
public DateTime? CreatedAt { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[TableColumn(TableColumnType.UpdatedAt)]
public DateTime? UpdatedAt { get; set; }
[TableColumn(TableColumnType.Deleted)]
public bool Deleted { get; set; }
[TableColumn(TableColumnType.Version)]
[Timestamp]
public byte[] Version { get; set; }
[NotMapped]
DateTimeOffset? ITableData.CreatedAt
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
[NotMapped]
DateTimeOffset? ITableData.UpdatedAt
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
ItemController into the Azure Mobile service:
public class ItemController : TableController<Item>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<Item>(context, Request);
}
...
}
You need to use Azure Storage for this. See the recipe in my book here: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter4/recipes/
There is no free option with Azure Storage.

why is the internal file of my android app doubling in size when adding an item to an Arraylist?

Situation: I have an android application. In this android application I have an internal file containing a "User" class (see code below) this user class has an array-list comprised of spendings, of the "Spending" class, comprised of several different basic attributes.
Problem: When I get this user from the internal file, add a "Spending" Object to the array-list of the User, then re-save this user (delete file and recreate) it doubles in size the Spendings array-list.
I even observed it by looking at the files themselves and can see clearly that the entire array-list is doubled every time a spending is added. the user does not seem to be duplicated.
I tried the same process only without adding the spending and it saves just fine without duplication.
the User class:
public class User implements Serializable {
private int id_utilisateur;
private String mail;
private String motDePasse;
private ArrayList<Spending> mySpendings;
public Utilisateur(int id_utilisateur, String mail, String motDePasse) {
this.id_utilisateur = id_utilisateur;
this.mail = mail;
this.motDePasse = motDePasse;
this.mySpendings= new ArrayList<>();
}
//Getters and Setters of all attributes here//
public void addSpending(Spending mySpending) {
mySpendings.add(mySpending);
}
}
My Spending class :
public class Spendingimplements Serializable {
private Integer idSpending;
private Date dateSpending;
private double montant;
private String pieceJoint;
private Magasin magasin;
private String domaine;
private Date garantieDebut;
private Date garantieFin;
private User user;
public Spending(Integer idSpending, Date dateSpending, double montant, User user, String domaine, Magasin magasin, String pieceJoint, Date garantieDebut, Date garantieFin) {
this.idSpending= idSpending;
this.dateSpending= dateSpending;
this.montant = montant;
this.user= user;
this.domaine = domaine;
this.magasin = magasin;
this.pieceJoint = pieceJoint;
this.garantieDebut = garantieDebut;
this.garantieFin = garantieFin;
}
public Spending(Integer idSpending, Date dateSpending, double montant, User user, String domaine, Magasin magasin, String pieceJoint) {
this.idSpending= idSpending;
this.dateSpending= dateSpending;
this.montant = montant;
this.user= user;
this.domaine = domaine;
this.magasin = magasin;
this.pieceJoint = pieceJoint;
this.garantieDebut = null;
this.garantieFin = null;
}
//geters and setters here//
}
My class StorageHelper:
public final class StorageHelper {
public StorageHelper() {}
public static void storeObject(Context context, Object object) {
try {
File dir = context.getFilesDir();
File file = new File(dir, "UserData.data");
file.delete();
FileOutputStream fos = context.openFileOutput("UserData.data", Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(object);
os.close();
fos.close();
} catch (Exception e) {
Log.e("userFile", "Error: Failed to save User into internal storage - \n" + e.toString());
}
}
public static User getUser(Context context) {
User mainUser = null;
try {
FileInputStream fis = context.openFileInput("UserData.data");
ObjectInputStream is = new ObjectInputStream(fis);
mainUser = (User) is.readObject();
is.close();
fis.close();
} catch (Exception e) {
Log.e("userFile", "Error: loading from the internal storage failed - \n" + e.toString());
} finally {
return mainUser;
}
}
}
MainActivity :
StorageHelper storageHelper;
User mainUser = storageHelper.getUser(this.getBaseContext());
mainUser.addSpending(mySpending);
storageHelper.storeObject(this.getBaseContext(), mainUser);

Android apps Logger - for tester

I am an android developer, I develop apps for clients.
However, in client devices, some app crash issue usually happened. But it didn't happen in my device.
But I cannot reach my client, so, here is question:~~~~
Is there any plugin or tools that can implement into my android apps, so when the apps is crashed in my client's device, the crash logs will be sent to my email or somewhere I can check it, so I can debug it.
Please advise me, thanks
Go to https://play.google.com/apps/publish/ and publish your app. When users install your app and it crashes it notifies Google, and when you'll login to this control-panel you'll be able to find such reports under the "Reports" tab and then under "CRASHES & ANRS".
Try taking a look at Crashlytics: http://try.crashlytics.com/sdk-android/
I think you should extend Application class, and redefine Exception Handle with stable code (independed of android version).
Code example:
YourApplication.java
public class YourApplication extends Application {
#Override
public void onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler.inContext(getApplicationContext()));
super.onCreate();
}}
ExceptionHandler.java
final public class ExceptionHandler implements Thread.UncaughtExceptionHandler{
private final DateFormat formatter = new SimpleDateFormat("dd.MM.yy HH:mm");
private final DateFormat fileFormatter = new SimpleDateFormat("dd-MM-yy");
private String versionName = "0";
private int versionCode = 0;
private final String stacktraceDir;
private final Thread.UncaughtExceptionHandler previousHandler;
private ExceptionHandler(Context context, boolean chained) {
PackageManager mPackManager = context.getPackageManager();
PackageInfo mPackInfo;
try {
mPackInfo = mPackManager.getPackageInfo(context.getPackageName(), 0);
versionName = mPackInfo.versionName;
versionCode = mPackInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// ignore
}
if(chained)
previousHandler = Thread.getDefaultUncaughtExceptionHandler();
else
previousHandler = null;
stacktraceDir = String.format("/Android/data/%s/files/", context.getPackageName());
}
static ExceptionHandler inContext(Context context) {
return new ExceptionHandler(context, true);
}
static ExceptionHandler reportOnlyHandler(Context context) {
return new ExceptionHandler(context, false);
}
#Override
public void uncaughtException(Thread thread, Throwable exception) {
final String state = Environment.getExternalStorageState();
final Date dumpDate = new Date(System.currentTimeMillis());
if (Environment.MEDIA_MOUNTED.equals(state)) {
StringBuilder reportBuilder = new StringBuilder();
reportBuilder
.append("\n\n\n")
.append(formatter.format(dumpDate)).append("\n")
.append(String.format("Version: %s (%d)\n", versionName, versionCode))
.append(thread.toString()).append("\n");
processThrowable(exception, reportBuilder);
File sd = Environment.getExternalStorageDirectory();
File stacktrace = new File(
sd.getPath() + stacktraceDir,
String.format(
"stacktrace-%s.txt",
fileFormatter.format(dumpDate)));
File dumpdir = stacktrace.getParentFile();
boolean dirReady = dumpdir.isDirectory() || dumpdir.mkdirs();
if (dirReady) {
FileWriter writer = null;
try {
writer = new FileWriter(stacktrace, true);
writer.write(reportBuilder.toString());
} catch (IOException e) {
// ignore
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
// ignore
}
}
}
}
if(previousHandler != null)
previousHandler.uncaughtException(thread, exception);
}
private void processThrowable(Throwable exception, StringBuilder builder) {
if(exception == null)
return;
StackTraceElement[] stackTraceElements = exception.getStackTrace();
builder
.append("Exception: ").append(exception.getClass().getName()).append("\n")
.append("Message: ").append(exception.getMessage()).append("\nStacktrace:\n");
for(StackTraceElement element : stackTraceElements) {
builder.append("\t").append(element.toString()).append("\n");
}
processThrowable(exception.getCause(), builder);
}}
If your app will crased, you can find a log with StackTrace at /sdcard/android/data/you.package.name/files/ all log files.
Also you can check new log files at this folder at every app start. If you find new files, you can send this to your email.

Categories

Resources