Xamarin Android - Write to file - access denied - xamarin

I'm getting path to save a video .mp4 file by using the following code in PCL project, which return "/storage/emulated/0/DCIM/":
#if __ANDROID__
var dir = Android.OS.Environment
.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim);
var publicPath= dir.AbsolutePath;
var newFilepath = System.IO.Path.Combine(publicPath, fileName);
video.path = newFilepath;
#endif
I've added permissions to Android.Manifest as bellow:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
And this function will get the .mp4 file from internet and save to the path:
public void downloadVideo(VideoInfor video, bool isRetried)
{
if (!video.downloadUrl.Contains(HLS) && !video.downloadUrl.Contains(HLS_1))
{
string filepath = video.path;
if (Uri.IsWellFormedUriString(video.downloadUrl, UriKind.RelativeOrAbsolute) && !video.downloadUrl.Contains("https://v.vnecdn.net/vnexpress/video/video_default.mp4"))
{
video.downloadUrl = video.downloadUrl.Replace("&", "&");
Console.WriteLine("Downloading url: " + video.downloadUrl);
try
{
if (!video.downloadUrl.Contains(".m3u8"))
{
Console.WriteLine("Downloading.....");
var request = (HttpWebRequest)WebRequest.Create(video.downloadUrl);
if (isRetried)
{
request.Timeout = 120000;
}
using (WebResponse response = request.GetResponse())
{
using (Stream source = response.GetResponseStream())
{
using (FileStream target = File.Open(filepath, FileMode.Create, System.IO.FileAccess.Write))
{
var buffer = new byte[1024];
bool cancel = false;
int bytes;
int copiedBytes = 0;
while (!cancel && (bytes = source.Read(buffer, 0, buffer.Length)) > 0)
{
target.Write(buffer, 0, bytes);
copiedBytes += bytes;
}
}
}
}
}
else
{
}
}
catch (Exception ex)
{
Console.WriteLine("Exception download url = " + video.downloadUrl);
Console.WriteLine("Exception download url Exception = " + ex.ToString());
if (!isRetried)
{
downloadVideo(video, true);
}
}
}
}
}
When the program running into "target.Write(buffer, 0, bytes);" I get the exception "Access to the path is denied", so anyone plesae tell me why is this error happening?
I already tried Plugin.permissions:
Dictionary<Permission, PermissionStatus> results =
await CrossPermissions.Current.RequestPermissionsAsync(Permission.Storage);
PermissionStatus status = results[Permission.Storage];
but I got "status = Permission.Unknow"
I already tried to save the video to a private path that only my application can access following code, which return "/data/user/0/com.companyname.VideoDownloader/files/":
var dir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
video.path = System.IO.Path.Combine(dir, filename);
And downloadVideo function can be able to write all the bytes data into the path with no error, but I cannot see any files when browse to that path.
Here is my SDK configuration:
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />

Since your target sdk is 25, you have to request permissions at runtime. You could refer to this blog for how to do it in xamarin.

I had all of the above things about permission enabled in my project and it still wouldn't let me write to files, just read them.
My solution: you have to use Path.Combine in the File.WriteAllText(..) even though you pass a string variable in the File.WriteAllText(path), you still need to write it as File.WriteAllText(Path.Combine(path))

Related

Error comparing current app version number with latest. Version name=production.release.1.0.0.5 and lastest version= [duplicate]

I need to check current version of Installed Application and Playstore application version. If it is not same app should navigate to Playstore .
im using xam.Plugin.LatestVersion(2.1.0) To get the latestversion number of application from play console. unfortunately not getting latest version number of application from play store. The below code im using.
private async void ChekAppVersion()
{
try
{
latestVersionNumber = await CrossLatestVersion.Current.GetLatestVersionNumber();
installedVersionNumber = CrossLatestVersion.Current.InstalledVersionNumber;
if (installedVersionNumber != latestVersionNumber)
{
await DisplayAlert("New Version", "There is a new version of this app available. Please update now?", "Ok");
await CrossLatestVersion.Current.OpenAppInStore();
ChekAppVersion();
}
else
{
}
}
catch (Exception ex)
{
}
}
Im getting the installedVersionNumber, but im unable to get the latestVersionNumber(Playstore).
Please help on this.
They have removed the version from div, now it's displayed with js, but data is still there inside a <script> tag. My current fixed code is:
private bool _lockCheckUpdates;
public async Task<bool> CheckNeedUpdate()
{
if (Connectivity.NetworkAccess != NetworkAccess.Internet || _lockCheckUpdates)
return false;
_lockCheckUpdates = true;
try
{
HttpClient myClient = CreateClient();
if (Device.RuntimePlatform == Device.Android)
{
var bundle = "com.todo.app"; //ANDROID BUNDLE NAME HERE
string url = $"https://play.google.com/store/apps/details?id={bundle}&hl=en";
string raw = await myClient.GetStringAsync(new Uri(url));
var doc = new HtmlDocument();
doc.LoadHtml(raw);
var scripts = doc.DocumentNode.Descendants()
.Where(n => n.Name == "script" && n.InnerText.Contains("AF_initDataCallback({key: 'ds:4'"))
.ToArray();
var script = scripts.First().InnerText;
var engine = new Jurassic.ScriptEngine();
var eval = "(function() { var AF_initDataCallback = function(p) { return p.data[1][2][140][0][0][0]; }; return " + script + " })()";
var result = engine.Evaluate(eval);
//var json = JSONObject.Stringify(engine, result); //for debug, check in browser console with JSON.parse(json)
var remote = $"{result}".ToDouble();
var local = App.Version.ToDouble();
return local < remote;
}
else if (Device.RuntimePlatform == Device.iOS)
{
var bundle = "com.todo.app";//iOS BUNDLE NAME HERE
string url = $"http://itunes.apple.com/lookup?bundleId={bundle}";
string raw = await myClient.GetStringAsync(new Uri(url));
var dto = JsonConvert.DeserializeObject<AppStoreRecord>(raw);
double local = App.Version.ToDouble();
if (dto.ResultCount > 0)
{
double remote = dto.Results[0].Version.ToDouble();
return remote > local;
}
}
}
catch (Exception e)
{
Logger.Error("CheckNeedUpdate", e);
}
finally
{
_lockCheckUpdates = false;
}
return false;
}
Using nugets
Jurassic to evaluate the script on page,
HtmlAgilityPack to parse html,
Xamarin.Essentials to check if we are online,
and AppoMobi.Specials for .ToDouble() etc
I hope this could also be useful to fix https://github.com/edsnider/latestversionplugin/issues/43 :)
The plugin you are using no longer works for Android https://github.com/edsnider/latestversionplugin/issues/43
You will need to find a new way to get the desired information.
PR has been made on this plugin... it works again ;-)

Rename a recorded file every time I save a record in xamarin

I am saving my records using this code:
string path = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
public string fileName { get; set; }
fileName = Path.Combine(path, "sample.wav");
if (!recorder.IsRecording)
{
recorder.StopRecordingOnSilence = TimeoutSwitch.IsToggled;
//Start recording
var audioRecordTask = await recorder.StartRecording();
BtnDoneRec.IsEnabled = false;
await audioRecordTask;
RecEditor.IsEnabled = true;
BtnDoneRec.IsEnabled = false;
PlayButton.IsEnabled = true;
var filePath = recorder.GetAudioFilePath();
if (filePath != null)
{
var stream = recorder.GetAudioFileStream();
using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
}
else
{
//stop recording ...
await recorder.StopRecording();
}
I want my record to have a specific name which is labeled with my RecEditor
using (var streamReader = new StreamReader(fileName))
{
File.Move("sample.wav", RecEditor.Text + ".wav");
}
So it will rename "sample.wav" to "RecEditor text.wav" every time I click my save button.
But when I click save, it gives me this record
System.IO.FileNotFoundException: 'Could not find file '/sample.wav'.'
The record is stored in /storage/emulated/0/sample.wav
The sample.wav is created in my device but I don't know why it give me 'Could not find file '/sample.wav'.' error. What am i doing wrong here?
I believe that what you're looking is something like this:
if(File.Exists(fileName))
{
var newFileName = Path.Combine(path, $"{RecEditor.Text}.wav");
File.Move(fileName, newFileName);
}
You don't need to open a new Stream as you are doing. Also, you need to put the full file path not only the file name.
You might want to validate that RecEditor.Text is not empty before using its value for the newfileName
Hope this helps.-

Xamarin forms: Picture extension is not saving with path in android when do multiple photo selection

I am following this article for Select Multiple Images From Gallery in Xamarin Forms.
I completed the feature in android part but the picture path contains only the picture name, extensions are missing when saving path.
To upload the image to the server I need the complete image name with extension. So how can I save the complete path of the selected images with the extension?
Following method capture the image path:
public String GetRealPathFromURI(Android.Net.Uri contentURI)
{
try
{
ICursor imageCursor = null;
string fullPathToImage = "";
imageCursor = ContentResolver.Query(contentURI, null, null, null, null);
imageCursor.MoveToFirst();
int idx = imageCursor.GetColumnIndex(MediaStore.Images.ImageColumns.Data);
if (idx != -1)
{
fullPathToImage = imageCursor.GetString(idx);
}
else
{
ICursor cursor = null;
var docID = DocumentsContract.GetDocumentId(contentURI);
var id = docID.Split(':')[1];
var whereSelect = MediaStore.Images.ImageColumns.Id + "=?";
var projections = new string[] { MediaStore.Images.ImageColumns.Data };
cursor = ContentResolver.Query(MediaStore.Images.Media.InternalContentUri, projections, whereSelect, new string[] { id }, null);
if (cursor.Count == 0)
{
cursor = ContentResolver.Query(MediaStore.Images.Media.ExternalContentUri, projections, whereSelect, new string[] { id }, null);
}
var colData = cursor.GetColumnIndexOrThrow(MediaStore.Images.ImageColumns.Data);
cursor.MoveToFirst();
fullPathToImage = cursor.GetString(colData);
}
return fullPathToImage;
}
catch (Exception ex)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "Unable to get path", ToastLength.Long).Show();
}
return null;
}
The extension(.png or .jpg) was missing not from the GetRealPathFromURI(), it happens in ImageHelpers.SaveFile(). So I save the filename to another variable from the path using Path.GetFileName() like below and pass the complete filename when call ImageHelpers.SaveFile().
var fileName = Path.GetFileName(picturepath);

Brother Printer SDK in Xamarin.Android Printing Issue

I'm Working on Brother Label Printer QL-710W using Xamarin.Android.
I have created a Android bindings project and Added jar files and .so files as shown below.
I'm using Visual Studio.
Jars >
BrotherPrintLibrary.jar; BuildAction: EmbeddedJar
MobilePrintLib.jar; BuildAction: ReferenceJar
NativeLibraries > armeabi >
libAndrJFPDFEMB.so; BuildAction: EmbeddedNativeLibrary
libcreatedata.so; BuildAction: EmbeddedNativeLibrary
Additions
ClassTest.cs
public bool PrintFile(string fileurl, NetPrinter printer)
{
Task <bool> printTask = new Task<bool>(() => {
bool success = true;
try
{
Printer myPrinter = new Printer();
PrinterInfo myPrinterInfo = new PrinterInfo();
PrinterStatus status = new PrinterStatus();
LabelInfo mLabelInfo = new LabelInfo();
myPrinterInfo.PrinterModel = PrinterInfo.Model.Ql710w;
myPrinterInfo.IpAddress = printer.IpAddress;
myPrinterInfo.MacAddress = printer.MacAddress;
myPrinterInfo.Port = PrinterInfo.PortEnum.Net;
myPrinterInfo.PrintMode=PrinterInfo.PrintModeEnum.FitToPage;
myPrinterInfo.PaperSize = PrinterInfo.PaperSizeEnum.Custom;
myPrinterInfo.Orientation = PrinterInfo.OrientationEnum.Portrait;
myPrinterInfo.LabelNameIndex = LabelInfo.QL700.W62.Ordinal();
myPrinterInfo.ScaleValue = 1;
mLabelInfo.LabelNameIndex = LabelInfo.QL700.ValueOf("W62").Ordinal();
mLabelInfo.IsAutoCut = true;
mLabelInfo.IsEndCut = true;
myPrinter.SetPrinterInfo(myPrinterInfo);
myPrinter.SetLabelInfo(mLabelInfo);
myPrinter.StartCommunication();
status = myPrinter.PrintFile(fileurl);
if (status.ErrorCode != PrinterInfo.ErrorCode.ErrorNone)
success = false;
myPrinter.EndCommunication();
}catch(Exception ex)
{
Console.WriteLine("ERROR : {0}",ex.Message);
success = false;
}
return success;
});
printTask.Start();
var isSuccess = printTask.Result;
return isSuccess;
}
I'm getting printers list successfully from network. But when I'm calling above method it is getting exception at myPrinter.SetPrinterInfo(myPrinterInfo); as "Couldn't load createdata from loader dalvik.system.PathClassLoader[DexPathList[[zip file \"/data/app/PocAndroidArchieve.PocAndroidArchieve-2.apk\"],nativeLibraryDirectories=[/data/app-lib/PocAndroidArchieve.PocAndroidArchieve-2, /vendor/lib, /system/lib]]]: findLibrary returned null"
Please suggest me if any one has idea to work with jar's and dependent .so files.
Thanks in advance.

How to extract dot app(.app) mac file from a zip programmatically?

I am working on a Unity project & facing an issue.
I have a zip file containing mybuild.app (mac) file.
I am using SharpZipLib to uncompress the zip file. Issue is, when lib uncompressing, it actually taking mybuild.app as a folder & create a directory with same name & its sub files & folders. After uncompressing mybuild.app is not being able to start.
I think the issue is with .app signature or something. I shouldn't extract .app file content separately.
Please tell me how can i get mybuild.app file from zip which actually works.
I am using Unity 3.5.6 on mac.
Here is my code:
using (ZipInputStream s = new ZipInputStream(File.OpenRead(a_filePath)))
{
ZipEntry theEntry;
while ((theEntry = s.GetNextEntry()) != null)
{
Console.WriteLine(theEntry.Name);
string directoryName = Path.GetDirectoryName(theEntry.Name);
string fileName = Path.GetFileName(theEntry.Name);
// create directory
if (directoryName.Length > 0)
{
Debug.Log("Creating Director: " + directoryName);
Directory.CreateDirectory(directoryName);
}
if (fileName != String.Empty)
{
string filename = a_extractPath;
filename += theEntry.Name;
Debug.Log("Unzipping: " + filename);
using (FileStream streamWriter = File.Create(filename))
{
int size = 2048;
byte[] fdata = new byte[2048];
while (true)
{
size = s.Read(fdata, 0, fdata.Length);
if (size > 0)
{
streamWriter.Write(fdata, 0, size);
}
else
{
break;
}
}
}
}
}
}

Resources