Xamarin Android 16 Jelly Bean / 4.1 create folder and file - c#

I know c#; have developed for windows mobile; I now have an android project with constraints that I cannot change; the most important being cannot run above API 16 due to the devices the app will be running on. These devices are already purchased. I’m aware of the age of these devices, and how old 4.1 is; hands tied.
I’ve started a new Xamarin (not forms) project for android only; compile using 8.1 Oreo; Min version 4.1, target version 8.1. I’m aware this is not ideal however doing so I’ve managed to get lots of other needed features working including camera and barcode scanner. Changing compile version down to 4.1 causes numerous errors which won’t compile.
I’m testing the device using usb-debugging on the actual device and even though it’s 4.1, the code runs and features work – camera, scanner etc.
I’m stuck trying to create a folder and then write/read a file in that folder. I’d like this folder to be accessible via windows explorer when plugged into computer.
I’ve got code like this to write a file:
string FileContents = "Text file contents";
Java.IO.File SaveFolder = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory("Documents") + Java.IO.File.Separator + "FolderName");
Boolean Success = false;
if (!SaveFolder.Exists())
{
Success = SaveFolder.Mkdirs();
}
string FName = "test.txt";
string FTogether = System.IO.Path.Combine(SaveFolder.Path, FName);
Java.IO.FileWriter fw = new Java.IO.FileWriter(FTogether);
fw.Write(FileContents);
fw.Close();
SaveFolder.Dispose();
And code like this to read the file:
Java.IO.File SaveFolder = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory("Documents") + Java.IO.File.Separator + "FolderName");
string FName = "test.txt";
string FTogether = System.IO.Path.Combine(SaveFolder.Path, FName);
StreamReader sr = new StreamReader(FTogether);
string FileContents = sr.ReadToEnd();
Debugging reveals SaveFolder’s AbsolutePath to be /storage/sdcard0/Documents/FolderName
I think this is part of the android’s internal storage which the app has access to but nothing else.
How do I get a folder which is accessible from outside the app ?
Environment.DirectoryDocuments
cannot be used because it’s null at runtime on API 16.

The path you have is actually publicly accessible so it's not specific to your application. When you connect your Android device to the computer, you should be able to go to the root folder of the device and see the Documents folder.
Don't let the /storage/sdcard0/ part in the beginning of the path confuse you. For historical reasons, Android simulates an sdcard even if there isn't one physically on the device. In reality, /storage/sdcard0/ is just a symlink to /data/media/0.
For a really good overview of Android storage, I'd recommend you read this very thorough Reddit post: Let's clear up the confusion regarding storage in Android once and for all, including adoptable storage in Marshmallow.
What you can also do is download a file explorer app (in case your phone doesn't already have one) and go to the Documents folder. You should see FolderName there.
Edit: Since you're running on such an old version of you might also suffer from the bug in the MTP protocol, which causes newly created files and folders to be invisibile when attaching the device to a computer via USB.
The fix is to call MediaScannerConnection.scanFile for each new file/folder you've created, as explained here.

Related

Automatically uploading a file to Google Drive File Stream with a Windows Service

I have made a Windows service that;
Downloads a CSV file from a regularly updated internal database through a WebClient every hour.
Put that CSV file into a designated folder.
In the original test case, it was put into a local folder on my desktop (C:).
The test case worked perfectly.
The CSV would replace the old file with the newly downloaded one with the same name.
As listed above. This works perfectly on a local folder. However, we intend for it to work through Google Drive File Stream. As we have a Google Sheet that manipulates and sought the data for us for any CSV file that is under the given name.
This is the current method of downloading and placing the file.
public void CSVDownload()
{
string url = #"YOUR_CSV_URL_HERE";
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileAsync(new Uri(url), #"YOUR_GOOGLE_DRIVE_FILE_STREAM_FOLDER_HERE\NAME.csv");
void client_DownloadFileCompleted(object CSVDownload, AsyncCompletedEventArgs e)
{
eventLog1.WriteEntry("CSV File (NAME.csv) downloaded");
}
}
My question is why can't I currently automatically upload a file to Google Drive File Stream through a Windows Service? Is it because Google Drive File Stream requires certain permissions or actions? Is it because the drive is "virtual" and not physical (H:)? Below is the folder I am trying to upload to in Google Drive File Stream (H:\My Drive\Test).
The code also runs through completely as the log files show that the methods are used. However, there seems to be some block between the download and placing of the CSV file in a Google Drive File Stream folder.
Update: There has been little progress so far. One of my colleagues maybe suggests that there needs to be some sought of user permission to push. Like a username and password. If this is true does anyone know how I could achieve this?
Update #2: In the 'Registry Editor' I found some interesting info.
Press Windows + R
Type regedit.exe
HKEY_CURRENT_USER -> SOFTWARE -> Google -> DriveFS -> Share
As you can see there are two values. 'MountPoint = H' is obviously the drive letter it is mapped to and 'ShellIpcPath = \\.\Pipe\GoogleDriveFSPipe_user.name_shell' which might be useful. I played around with ShellcpPath but to no prevail.
Update #3: #Ben Voigt mentioned to use DriveInfo.GetDrives() to see if the drive is found by the service. I run the code and it looks like it does exist.
Here is what the console spit out:
Drive H:\
Drive type: Fixed
Volume label: Google Drive File Stream
File system: FAT32
Available space to current user: 15987068928 bytes
Total available space: 15987068928 bytes
Total size of drive: 16106127360 bytes
As you can see it exists however it uses File system: FAT32 instead of File system: NTFS which all my other drives use (C:),(D:),etc. So it seems that only Google Drive File Stream uses FAT32.
Test Case:
Works perfectly when placing the CSV files into a local folder.
client.DownloadFileAsync(new Uri(url), #"C:\Users\user.name\Desktop\Test\designtasks.csv");
client.DownloadFileAsync(new Uri(url), #"C:\Users\user.name\Desktop\Test\designjobs.csv");
Does not work when placing into a Google Drive File Stream folder.
client.DownloadFileAsync(new Uri(url), #"H:\My Drive\Test\designtasks.csv");
client.DownloadFileAsync(new Uri(url), #"H:\My Drive\Test\designjobs.csv");
To clarify about the test case. I am actually getting two CSV files. However, the code is basically the same. So I only mention getting one in my original question to keep it cleaner and more straightforward.
I found the solution! It might not be optimal for all cases however it works in my case.
Here is what I did;
Make a project installer with InstallerProjects.vsiz. Obviously, if you are using a different version of VS, this might change. Some of the older version already come with it installed and the newer versions do not automatically include it. You will have to also find the correlating version as the one linked is for VS2017. Here is a tutorial on how to implement an installer on a windows service.
Once you have implemented the installer, right click on serviceProcessInstaller1 (or whatever you decided to call it) and select properties.
In the properties window, look a for the 'Account' field and set it to 'User'.
Save and build you project. That's it.
What does's this actually do? In plain English; It basically makes the service impersonate a user so it can read and write files on the system.
When installing on a different computer from which the service was built on, 'Windows Defender Shield' will block the service from installing. This is just Windows bring cautious. Obviously, if you built the service without ill intent this will not harm your computer.
To continue installation click "More info" on the 'Windows Defender Shield' screen and click "Run anyway".
Before the Service finishes installing, it will stall and ask for user information. In most use cases, giving it your own user information ill suffice.
If you are a user on a domain or anything along those lines. You will need to add the domain prefix to the 'Username' field. Example: DOMAIN\user.name.

Unauthorized access exception while running as admin

I know that there are a lot of questions concerning getting a
"System.UnauthorizedAccessException".
However I couldn't find a solution in any of these questions, as most of the answers refer to one of these Microsoft help pages.
My Situation:
I try to save some user input as .csv, so I can import it when needed.
My Code:
var csv = new System.Text.StringBuilder();
string dir = Path.Combine(Environment.GetFolderPath
(Environment.SpecialFolder.DesktopDirectory), "test.csv");
var newLine = string.Format("{0},{1},{2},{3},{4}", txtFirstName.Text, txtLastName.Text, txtEmail.Text,
txtPhone.Text, txtPlace.Text);
csv.AppendLine(newLine);
if (!File.Exists(dir))
{
using (FileStream fs = File.Create(dir))
{
Byte[] info = new System.Text.UTF8Encoding(true).GetBytes("FirstName,LastName,Email,Phone,Place");
// Add headers to the file.
fs.Write(info, 0, info.Length);
}
}
try
{
File.AppendAllText(dir, csv.ToString());
}
catch (Exception ex)
{
throw ex;
}
As you can see, I'm trying to write everything to my Desktop, in a file called "test.csv". I am running Visual Studio as an Administrator and the file I have on my Desktop is not read-only.
Does anybodoy have an idea why this still fails?
Edit: I'm running this as a Standard UWP-App on a desktop Computer.
From a UWP process file access is restricted. In order to write to the desktop (or any arbitrary location) your app will need to use the file save dialog and let the user confirm/choose the location. Then you will be able to save to the desktop or whatever location the user has decided to select.
In the upcoming Spring 2018 update for Windows 10 we will introduce a new capability ('broadFileSystemAccess') for UWP applications that will make this better. If you declare this capability in your manifest, the app will ask for user consent on first launch for broad file system access, and then you will be able to access all locations that the current user has access to.
If you need a solution that works on earlier versions of Windows 10 (prior to Spring 2018 update) and the file dialog is not a viable option then you can look into adding a fulltrust process to your UWP package that handles the file operations on behalf of your UWP process. You can launch that fulltrust process from the UWP via the FullTrustProcessLauncher API: https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.fulltrustprocesslauncher

ERROR: System.Environment.SpecialFolder' does not contain a definition for 'CommonApplicationData'

I have the code to save a file in a folder in directory
string timestamp = DateTime.Now.ToString("MM-dd-yyyy.HH-mm-ss");
var file = File.Create("Owe-Data.txt" + timestamp);
var com = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase + timestamp + #"\Data" + file;
MessageBox.Show(com);
if (!Directory.Exists(com))
{
Directory.CreateDirectory(com);
}
using (var sw = new StreamWriter(com))
{
sw.WriteLine(InputData);
}
}
i Displayed COM it gives path bt i cant see the Data folder or Owe-Data file at that path
Anybody can tell why this happening, or should i save the Data folder in current directory where this prgram running? bt i dnt know how to reach that path. Any solutions ??
Working on windows phone 5, visual studio 2008 .NET framwork 2.0
As per the Exceptions section of documentation,the above exception is thrown when
ArgumentException ------- folder is not a member of System.Environment.SpecialFolder.
It means the OS where you are running this command does not have Environment.SpecialFolder.CommonApplicationData as one of the special folder.
For knowledge,
Environment.SpecialFolder.ApplicationData is the most common one. This folder holds per-user, non-temporary application-specific data, other than user documents. A common example would be a settings or configuration file.
Environment.SpecialFolder.CommonApplicationData is similar, but shared across users. You could use this to store document templates, for instance.
Environment.SpecialFolder.LocalApplicationData is a non-roaming alternative for ApplicationData. As such, you'd never store important data there. However, because it's non-roaming it is a good location for temporary files, caches, etcetera. It's typically on a local disk.
I think the problem may be that Environment.SpecialFolder.CommonApplicationData is common and shared between different users and the user with which you have logged in is not having rights to access the folder or the Visual Studio has not been started in Admin mode.
EDIT Look at link and try to add a manual registry Common AppData defined in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\
Given you are asking about a .NET Windows Phone application as per the tags
I think your problem is that a .NET Windows Phone application does not have direct access to the file system; it can only access IsolatedStorage this is by design.
I would quote a Microsoft source for this but I can't seem to find one!
EDIT
See this article from MSDN

XML file can't be saved in windows mobile

In simple way, I want to save some data in XML file when i tried this code it ran successfully but,unfortunately i didn't the file in the project folder or anywhere on my computer.When I changed the path also i didn't find it.
The following is the used code
string configFile = #"config.xml";
XmlDocument xml = new XmlDocument();
XmlElement element = xml.CreateElement("Configuration");
xml.AppendChild(element);
//---create the <LastAccess> element---
element = xml.CreateElement("LastAccess");
element.InnerText = DateTime.Now.ToString();
xml.DocumentElement.AppendChild(element);
//---create the <LastSearchString> element---
element = xml.CreateElement("LastSearchString");
element.InnerText = "sushi";
xml.DocumentElement.AppendChild(element);
xml.Save(configFile);
Can you help me in this problem please.
When I wrote this code and ran the application the file must be saved whatever where
XmlDocument xml = new XmlDocument();
XmlElement element = xml.CreateElement("First");
xml.AppendChild(element);
element = xml.CreateElement("Second");
element.InnerText = "LaLa";
xml.DocumentElement.AppendChild(element);
xml.Save(#"XML.xml");
and then i try this code as the file has been saved
element.InnerText = "LaLa";
xml.DocumentElement.AppendChild(element);
xml.Save(#"XML.xml");
I got that exception Could not find file '\XML.xml'.
please can you help me in this problem.
The emulator is, for all intents and purposes of this question, a completely separate device. It does not "share" any part of it's file system with the host PC, so there's no way to "browse" it's files (like the one you created" from the PC's Windows Explorer, or the standard file system APIs.
If you want access to the file from the PC, you need to use one of the following to retrieve it:
Configure the emulator to share a folder with the PC. This is don't in the options of the Emulator itself and allows you to "mount" a Host PC folder. That folder becomes the "\Storage Card" root folder in the emulator, so you'd have to change your code appropriately (i.e. save to "\storage card\config.xml")
Use a tool like Remote Registry Editor (in the Start menu under Visual Studio 22008 remote tools), connect to the emulator and download the file.
Use the Emulator Manager to "dock" the emulator so it shows up in ActiveSync/WMDC. When that occurs, a shell extension will show the device in Windows Explorer - though be aware that it is purely a shell extension. There is still no file API access to that system, so you could not write a desktop app that navigates to "My Device\MyFolder\MyFile.xml".

Passing instructions to windows phone application from console application

I was following this fantastic tutorial. Which showed this bit of code to talk to my windows phone application by writing/reading to a file in the isolatedstore.
object ConManServer = WP7Device.GetType().GetField("mConmanServer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(WP7Device);
FileDeployer f = (FileDeployer)typeof(FileDeployer).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0].Invoke(new object[] { ConManServer });
f.ReceiveFile(#"\Applications\Data\" + appID + #"\data\isolatedstore\Foo.txt", #"\Foo.txt");
However, I get a file access denied. Somebody commented that:
unfortunately Microsoft has removed that feature from the final
release of the CoreCon API. So it's no longer possible to transfer
files from the device in this lovely, easy manner.
I am wondering what would be the alternative to pass instructions to my Windows Phone application from a Console Application?
Maybe using the DevicePacketStream?
The Mango (v7.1) version of the SDK included the Isolated Storage Explorer Tool to make it possible to read and write files from IsolatedStorage.
See http://msdn.microsoft.com/en-us/library/hh286408(v=vs.92).aspx

Categories