How to download and open a file with NS 6.0 - nativescript

I'have migrate to NativeScript 6.0 and need some help on how to download and open a file with Android support lib (AndroidX) in the Downloads folder.
Actually, in NS 5.x, i have used FileProvider from Android support lib (android.support.v4.content.FileProvider) and works great. After the migration, using (androidx.core.content.FileProvider), i have errors opening the App.
But in Android docs, i can't find any method or information to migrate the code for Native download and Open (Downloads Folder).
Previous Method:
private openFile(fileName: string, mimeType: string, extension: string) {
try {
if (isAndroid) {
const intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
const context = applicationModule.android.context;
console.log("android.ctx=", context);
const nativeFile = new java.io.File(fileName);
console.log("nativeFile=", nativeFile);
const uri = android.support.v4.content.FileProvider.getUriForFile(context, "com.otisw.gescon.app.provider", nativeFile);
intent.setDataAndType(uri, mimeType);
intent.addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION);
const choosedIntent = android.content.Intent.createChooser(intent, "Open file...");
console.log("choosedIntent=>", choosedIntent);
context.startActivity(choosedIntent);
} else {
// const documents = fs.knownFolders.currentApp();
// const file = this.documents.getFile(fileName);
const open = utils.ios.openFile(fileName);
}
} catch (e) {
console.log(e);
}
}
Tried:
private openFile(fileName: string, mimeType: string, extension: string) {
try {
if (isAndroid) {
const intent = androidx.core.content.IntentCompat.makeMainSelectorActivity(
"android.content.Intent.ACTION_VIEW",
"??"
);
File reference.d.ts:
/// <reference path="./node_modules/tns-core-modules/tns-core-modules.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/android/androidx-26.d.ts" />
Does anyone tries to upgrade the code for Download and Open from NativeScript to new AndroidX or knows a workaround to do this ?
Thanks!

You will have to use androidx.core.content.FileProvider on AndoridX and android.support.v4.content.FileProvider in lower versions.
With the release of Android 9.0 (API level 28) there is a new version
of the support library called AndroidX which is part of Jetpack. The
AndroidX library contains the existing support library and also
includes the latest Jetpack components.
You can continue to use the support library. Historical artifacts
(those versioned 27 and earlier, and packaged as android.support.*)
will remain available on Google Maven. However, all new library
development will occur in the AndroidX library.
We recommend using the AndroidX libraries in all new projects. You
should also consider migrating existing projects to AndroidX as well.

Related

FilePicker class cannot be found in Xamarin project

I'm creating a Xamarin forms app which enables user uploads. I have installed the latest version of the Xamarin.Essentials package but the classes and methods which I would expect to be available cannot be found. I can move ahead with the xamarin.plugins.filepicker package but this is not well documented and I would prefer to use the standard library. Any assistance with this would be greatly appreciated! The default is below.
'''
async Task<FileResult> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.PickAsync();
if (result != null)
{
Text = $"File Name: {result.FileName}";
if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
var stream = await result.OpenReadAsync();
Image = ImageSource.FromStream(() => stream);
}
}
}
catch (Exception ex)
{
// The user canceled or something went wrong
}
}
'''
For the Xamarin.Essentials package, update to the latest version on both Xamarin.Form NuGet Package and Android NuGet Package. After that you could fix the errors like below.
Error CS0246 The type or namespace name 'PickOptions' could not be found (are you
missing a using directive or an assembly reference?)
For the usage of Xamarin.Essentials: File Picker, you could check the MS document.
https://learn.microsoft.com/en-us/xamarin/essentials/file-picker?tabs=android
If you wanna the source file, you could download from the link below. https://github.com/xamarin/Essentials/tree/main/Xamarin.Essentials/FilePicker

Download path in Xamarin iOS

I am using Xamarin.Forms and written the code to download the file for the iOS
platform. It is downloading the file successfully without any error. But after downloading it, I am not able to find the downloaded file in my apple device.
During debugging I found that it is showing
/var/mobile/Containers/Data/Application/1234567A-B8CD-9EF9-C850-9G73587DC7C/Documents/XF_Downloads/hausmann_abcd.jpg
path. So at which location file get saved? below is the image for the same.
I have written below code for this
public class IosDownloader : IDownloader
{
public event EventHandler<DownloadEventArgs> OnFileDownloaded;
public void DownloadFile(string url, string folder)
{
string pathToNewFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), folder);
Directory.CreateDirectory(pathToNewFolder);
try
{
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
string pathToNewFile = Path.Combine(pathToNewFolder, Path.GetFileName(url));
webClient.DownloadFileAsync(new Uri(url), pathToNewFile);
}
catch (Exception ex)
{
if (OnFileDownloaded != null)
OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
}
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
if (OnFileDownloaded != null)
OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
}
else
{
if (OnFileDownloaded != null)
OnFileDownloaded.Invoke(this, new DownloadEventArgs(true));
}
}
}
When a file is saved on an application it is a temporary url within the app's sandbox. To make this image file be publicly accessible through Apple's Photos, You'll have to use native code to do a request to add a new PHImageAsset to the photos library.
In forms you would need to access the native frameworks and therefore run native code. There are plenty of examples of doing this online if you don't know how to already. But here is an introduction if you want to run native code within a shared code framework. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/introduction
Here is a sample of taking that temp file URL and saving it to Photos Framework once you can code w/ native Xamarin.iOS frameworks:
public void AddImageUrlToPhotosLibrary(string urlToSaveToPhotos)
{
PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => {
//This is bound to native https://developer.apple.com/documentation/photokit/phassetchangerequest/1624060-creationrequestforasset
//Parameter is an NSURL of path to image.
PHAssetChangeRequest.FromImage(new NSUrl(urlToSaveToPhotos));
}, (completed, error) => {
if (completed)
{
if (error != null)
{
Console.WriteLine($"Failed saving image asset {error.LocalizedDescription}");
} else
{
Console.WriteLine($"Successfully saved image to photos library.");
}
}
});
}
I am not able to find the downloaded file in my apple device.
If you use a simulator, you can directly find the folder in the mac by entering the path in the
your mac --> select Finder --> Then open the Go menu --> Click Go to Folder as I described in this thread:
Where can I find the MyDocuments folder on iPhone Simulator?
If you installed the app in a real device. You can browse the file in
Xcode --> Devices and Simulators --> download container as described in this thread:
Browse the files created on a device by the iOS application I'm developing, after downloading the container, you can right click it and choose Show Package Contents. Then you can see the folders.
You can also access the file by the path in the project.

"GattDeviceServicesResult" can not be found

I am trying to create client side app using C# for BluetoothLE in VisualStudio 2015 on Windows-10 laptop.
I have problem using Windows.Devices.Bluetooth.GenericAttributeProfile, the issue is my code has compile error saying GattDeviceServicesResult can not be found.
-> I have added package UwpDesktop 10.0.14393.3 by Valdimir Postel... (before installing this even "using Wndows.Devices.Bluetooth" was not working)
-> Then I added SDK, windows Kit that was recommended by VisualStudio when I tried to open one of the example (So I accept the recommendation to build that project and VS installed packages of around 9GB)
-> now I can use some of the Bluetooth api's I can scan and connect to a BLE device, but I can not use classes to deal with services and characteristics because GattDeviceServicesResult and GattCharacteristicsResult types are not found. Although these are mentioned on MSDN website
-> searching in forums I came to know I need to add one more reference System.Runtime.WindowsRuntime.dll, I browsed to proper folder through add reference utility of VS, I am trying to add this and it does nothing, after I select the dll and click 'Add' just nothing happens. (Add reference is not adding this dll).
Just for example if I select some other dll and try to add, that works fine!
Could somebody please help me with this,
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Bluetooth.Advertisement;
Int16 uuid_count = 0;
BluetoothLEAdvertisement[] ble_adv = new BluetoothLEAdvertisement[5];
BluetoothLEAdvertisementReceivedEventArgs[] ble_received_adv = new BluetoothLEAdvertisementReceivedEventArgs[5];
BluetoothLEDevice bluetooth_LE_Device;
GattDeviceServicesResult result_service;// This line does not compile
//Error: CS0246 the type name 'GattDeviceServicesResult ' could not be found
// I am adding reference to "System.Runtime.WindowsRuntime" as mentioned in some solutions
// the reference does not seems to be added at first when I click add button, but I can see the reference dll being mentioned in solution explorer (assuming it's been added)
// using this to scan available devices
private void scann_ble()
{
var watcher = new BluetoothLEAdvertisementWatcher();
watcher.Received += Watcher_Received;
watcher.AdvertisementFilter.Advertisement.ServiceUuids.Clear();
watcher.Start();
while (true)
{
Thread.Sleep(10000);
break;
}
watcher.Stop();
}
// receiver event to collect addresses of available devices
private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
bool update_adv = true;
Int16 i = 0;
if(uuid_count < 5)
{
if (uuid_count > 0)
{
while (i < uuid_count)
{
if (ble_received_adv[i].BluetoothAddress == args.BluetoothAddress)
update_adv = false;
i++;
}
}
if(update_adv != false)
ble_received_adv[uuid_count++] = args;
}
}
// now connecting and checking available services
// as per "https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client"
private async void BLE_connect_button_Click(object sender, EventArgs e)
{
int i = 0;
i = BLE_device_grid_view.CurrentCell.RowIndex; // getting index from item selected in gridView
bluetooth_LE_Device = await BluetoothLEDevice.FromBluetoothAddressAsync(ble_received_adv[i].BluetoothAddress);
// Connection works fine, I can see it on my peripheral device
//get services - This is not working
result_service = bluetooth_LE_Device.GetGattServicesAsync();
if (result_service.Status == await GattCommunicationStatus.Success)
{
var services = result_service.Services;
// ...
}
}
I am using UwpDesktop package.
I had the same problem. My problem was solved after I changed the reference to windows.winmd from C:\Program Files (x86)\Windows Kits\10\UnionMetadata\windows.winmd to C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.17763.0\windows.winmd.
I'm using BLE 4.0 in a WinForm application and I'm working with Visual Studio 2017.
It is very likely that the directory 10.0.17763.0 doesn't exist on every computer, but you may look what versions of windows.winmd exists on your computer.

Auto-updates to Electron

I'm looking to deploy an auto-update feature to an Electron installation that I have, however I am finding it difficult to find any resources on the web.
I've built a self contained application using Adobe Air before and it seemed to be a lot easier writing update code that effectively checked a url and automatically downloaded and installed the update across Windows and MAC OSX.
I am currently using the electron-boilerplate for ease of build.
I have a few questions:
How do I debug the auto update feature? Do I setup a local connection and test through that using a local Node server or can I use any web server?
In terms of signing the application I am only looking to run apps on MAC OSX and particularly Windows. Do I have to sign the applications in order to run auto-updates? (I managed to do this with Adobe Air using a local certificate.
Are there any good resources that detail how to implement the auto-update feature? As I'm having difficulty finding some good documentation on how to do this.
I am also new to Electron but I think there is no simple auto-update from electron-boilerplate (which I also use). Electron's auto-updater uses Squirrel.Windows installer which you also need to implement into your solution in order to use it.
I am currently trying to use this:
https://www.npmjs.com/package/electron-installer-squirrel-windows
And more info can be found here:
https://github.com/atom/electron/blob/master/docs/api/auto-updater.md
https://github.com/squirrel/squirrel.windows
EDIT: I just opened the project to try it for a while and it looks it works. Its pretty straightforward. These are pieces from my gulpfile.
In current configuration, I use electron-packager to create a package.
var packager = require('electron-packager')
var createPackage = function () {
var deferred = Q.defer();
packager({
//OPTIONS
}, function done(err, appPath) {
if (err) {
gulpUtil.log(err);
}
deferred.resolve();
});
return deferred.promise;
};
Then I create an installer with electron-installer-squirrel-windows.
var squirrelBuilder = require('electron-installer-squirrel-windows');
var createInstaller = function () {
var deferred = Q.defer();
squirrelBuilder({
// OPTIONS
}, function (err) {
if (err)
gulpUtil.log(err);
deferred.resolve();
});
return deferred.promise;
}
Also you need to add some code for the Squirrel to your electron background/main code. I used a template electron-squirrel-startup.
if(require('electron-squirrel-startup')) return;
The whole thing is described on the electron-installer-squirrel-windows npm documentation mentioned above. Looks like the bit of documentation is enough to make it start.
Now I am working on with electron branding through Squirrel and with creating appropriate gulp scripts for automation.
You could also use standard Electron's autoUpdater module on OS X and my simple port of it for Windows: https://www.npmjs.com/package/electron-windows-updater
I followed this tutorial and got it working with my electron app although it needs to be signed to work so you would need:
certificateFile: './path/to/cert.pfx'
In the task config.
and:
"build": {
"win": {
"certificateFile": "./path/to/cert.pfx",
"certificatePassword": "password"
}
},
In the package.json
Are there any good resources that detail how to implement the auto-update feature? As I'm having difficulty finding some good documentation on how to do this.
You don't have to implement it by yourself. You can use the provided autoUpdater by Electron and just set a feedUrl. You need a server that provides the update information compliant to the Squirrel protocol.
There are a couple of self-hosted ones (https://electronjs.org/docs/tutorial/updates#deploying-an-update-server) or a hosted service like https://www.update.rocks
Question 1:
I use Postman to validate that my auto-update server URLs return the response I am expecting. When I know that the URLs provide the expected results, I know I can use those URLs within the Electron's Auto Updater of my Application.
Example of testing Mac endpoint with Postman:
Request:
https://my-server.com/api/macupdates/checkforupdate.php?appversion=1.0.5&cpuarchitecture=x64
JSON Response when there is an update available:
{
"url": "https:/my-server.com/updates/darwin/x64/my-electron=app-x64-1.1.0.zip",
"name": "1.1.0",
"pub_date": "2021-07-03T15:17:12+00:00"
}
Question 2:
Yes, your Electron App must be code signed to use the auto-update feature on Mac. On Windows I'm not sure because my Windows Electron app is code signed and I did not try without it. Though it is recommended that you sign your app even if the auto-update could work without it (not only for security reasons but mainly because otherwise your users will get scary danger warnings from Windows when they install your app for the first time and they might just delete it right away).
Question 3:
For good documentation, you should start with the official Electron Auto Updater documentation, as of 2021-07-07 it is really good.
The hard part, is figuring out how to make things work for Mac. For Windows it's a matter of minutes and you are done. In fact...
For Windows auto-update, it is easy to setup - you just have to put the RELEASES and nupkg files on a server and then use that URL as the FeedURL within your Electron App's autoUpdater. So if your app's update files are located at https://my-server.com/updates/win32/x64/ - you would point the Electron Auto Updater to that URL, that's it.
For Mac auto-update, you need to manually specify the absolute URL of the latest Electron App .zip file to the Electron autoUpdater. So, in order to make the Mac autoUpdater work, you will need to have a way to get a JSON response in a very specific format. Sadly, you can't just put your Electron App's files on your server and expect it to work with Mac just like that. Instead, the autoUpdater needs a URL that will return the aforementioned JSON response. So to do that, you need to pass Electron's Auto Updater feedURL the URL that will be able to return this expected kind of JSON response.
The way you achieve this can be anything but I use PHP just because that's the server I already paid for.
So in summary, with Mac, even if your files are located at https://my-server.com/updates/darwin/x64/ - you will not provide that URL to Electron's Auto Updater FeedURL. Instead will provide another URL which returns the expected JSON response.
Here's an example of my main.js file for the Electron main process of my App:
// main.js (Electron main process)
function registerAutoUpdater() {
const appVersion = app.getVersion();
const os = require('os');
const cpuArchitecture = os.arch();
const domain = 'https://my-server.com';
const windowsURL = `${domain}/updates/win32/x64`;
const macURL = `${domain}/api/macupdates/checkforupdate.php?appversion=${appVersion}&cpuarchitecture=${cpuArchitecture}`;
//init the autoUpdater with proper update feed URL
const autoUpdateURL = `${isMac ? macURL : windowsURL}`;
autoUpdater.setFeedURL({url: autoUpdateURL});
log.info('Registered autoUpdateURL = ' + (isMac ? 'macURL' : 'windowsURL'));
//initial checkForUpdates
autoUpdater.checkForUpdates();
//Automatic 2-hours interval loop checkForUpdates
setInterval(() => {
autoUpdater.checkForUpdates();
}, 7200000);
}
And here's an example of the checkforupdate.php file that returns the expected JSON response back to the Electron Auto Updater:
<?php
//FD Electron App Mac auto update API endpoint.
// The way Squirrel.Mac works is by checking a given API endpoint to see if there is a new version.
// If there is no new version, the endpoint should return HTTP 204. If there is a new version,
// however, it will expect a HTTP 200 JSON-formatted response, containing a url to a .zip file:
// https://github.com/Squirrel/Squirrel.Mac#server-support
$clientAppVersion = $_GET["appversion"] ?? null;
if (!isValidVersionString($clientAppVersion)) {
http_response_code(204);
exit();
}
$clientCpuArchitecture = $_GET["cpuarchitecture"] ?? null;
$latestVersionInfo = getLatestVersionInfo($clientAppVersion, $clientCpuArchitecture);
if (!isset($latestVersionInfo["versionNumber"])) {
http_response_code(204);
exit();
}
// Real logic starts here when basics did not fail
$isUpdateVailable = isUpdateAvailable($clientAppVersion, $latestVersionInfo["versionNumber"]);
if ($isUpdateVailable) {
http_response_code(200);
header('Content-Type: application/json;charset=utf-8');
$jsonResponse = array(
"url" => $latestVersionInfo["directZipFileURL"],
"name" => $latestVersionInfo["versionNumber"],
"pub_date" => date('c', $latestVersionInfo["createdAtUnixTimeStamp"]),
);
echo json_encode($jsonResponse);
} else {
//no update: must respond with a status code of 204 No Content.
http_response_code(204);
}
exit();
// End of execution.
// Everything bellow here are function declarations.
function getLatestVersionInfo($clientAppVersion, $clientCpuArchitecture): array {
// override path if client requests an arm64 build
if ($clientCpuArchitecture === 'arm64') {
$directory = "../../updates/darwin/arm64/";
$baseUrl = "https://my-server.com/updates/darwin/arm64/";
} else if (!$clientCpuArchitecture || $clientCpuArchitecture === 'x64') {
$directory = "../../updates/darwin/";
$baseUrl = "https://my-server.com/updates/darwin/";
}
// default name with version 0.0.0 avoids failing
$latestVersionFileName = "Finance D - Tenue de livres-darwin-x64-0.0.0.zip";
$arrayOfFiles = scandir($directory);
foreach ($arrayOfFiles as $file) {
if (is_file($directory . $file)) {
$serverFileVersion = getVersionNumberFromFileName($file);
if (isVersionNumberGreater($serverFileVersion, $clientAppVersion)) {
$latestVersionFileName = $file;
}
}
}
return array(
"versionNumber" => getVersionNumberFromFileName($latestVersionFileName),
"directZipFileURL" => $baseUrl . rawurlencode($latestVersionFileName),
"createdAtUnixTimeStamp" => filemtime(realpath($directory . $latestVersionFileName))
);
}
function isUpdateAvailable($clientVersion, $serverVersion): bool {
return
isValidVersionString($clientVersion) &&
isValidVersionString($serverVersion) &&
isVersionNumberGreater($serverVersion, $clientVersion);
}
function getVersionNumberFromFileName($fileName) {
// extract the version number with regEx replacement
return preg_replace("/Finance D - Tenue de livres-darwin-(x64|arm64)-|\.zip/", "", $fileName);
}
function removeAllNonDigits($semanticVersionString) {
// use regex replacement to keep only numeric values in the semantic version string
return preg_replace("/\D+/", "", $semanticVersionString);
}
function isVersionNumberGreater($serverFileVersion, $clientFileVersion): bool {
// receives two semantic versions (1.0.4) and compares their numeric value (104)
// true when server version is greater than client version (105 > 104)
return removeAllNonDigits($serverFileVersion) > removeAllNonDigits($clientFileVersion);
}
function isValidVersionString($versionString) {
// true when matches semantic version numbering: 0.0.0
return preg_match("/\d\.\d\.\d/", $versionString);
}

has no authority to load: sdk/window/utils (Firefox addon SDK)

In my Main.js
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var chromewin = getMostRecentBrowserWindow();
chromewin.PrintUtils.printPreview(chromewin.PrintPreviewListener);
Browser console warning: "Module: .../main has no authority to load: sdk/window/utils"
Your addon contains a corrupted or otherwise incomplete harness-options.json. Either rebuild your addon with a fresh copy of Addon SDK, or (more tricky) manually edit harness-options.json so that under the requirements section all required modules are listed.
Hmm I would try this instead:
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
getMostRecentBrowserWindow().PrintUtils.printPreview(getMostRecentBrowserWindow().PrintPreviewListener);
My guess is that you are trying to use your chromewin ref after that window has closed.

Resources