I'm trying to add an auto update functionality to a Xamarin Android app.
When it starts, the app checks whether the current version is allowed and if not retrieves all the packages to update (one or more) from the database.
Then it downloads all the packages and saves them to disk. Once all packages have been downloaded, I prompt android to install them in sequence as such:
foreach (UpdatedApp app in apps)
{
InstallPackage(app);
}
private void InstallPackage(UpdatedApp app)
{
Java.IO.File file = new Java.IO.File(app.Path);
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
Intent intent = new Intent(Intent.ActionInstallPackage);
intent.SetData(FileProvider.GetUriForFile(this, $"{PackageName}.fileprovider", file));
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
StartActivity(intent);
}
else
{
Intent intent = new Intent(Intent.ActionView);
intent.SetDataAndType(Android.Net.Uri.FromFile(file), "application/vnd.android.package-archive");
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
It works great when there is only one package to install, but when there are at least two, I basically prompt android to install the packages simultaneously and only the first package will be installed.
I added a small bit of logic to circumvent this issue:
Get the current version of the Nth package.
Prompt install
Every x seconds for y seconds, get the package version again and compare it to the original
If different it means the package has successfully been installed and repeat the process for the N+1st package
Otherwise cancel any further installation
Here's the updated code:
foreach (UpdatedApp app in apps)
{
string versionName = GetPackageVersion(app);
InstallPackage(app);
bool installed = await HasPackageBeenInstalled(app, versionName);
if (!installed)
break;
}
private async Task<bool> HasPackageBeenInstalled(UpdatedApp app, string oldVersion)
{
int count = 0;
do
{
await Task.Delay(1000);
string newVersion = GetPackageVersion(app);
if (newVersion != oldVersion)
return true;
count++;
}
while (count < 20);
return false;
}
private string GetPackageVersion(UpdatedApp app)
{
try
{
PackageInfo packageInfo = PackageManager.GetPackageInfo(app.PackageName, 0);
return packageInfo.VersionName;
}
catch (PackageManager.NameNotFoundException e)
{
return null;
}
}
It ensures I only prompt to install a package after the previous one has been installed.
I was wondering if there was a better way to do that? I had two things in mind:
Use StartActivityForResult instead of StartActivity. This assumes Android natively returns a result, which is not clear to me.
Use a BroadcastReceiver. This assumes Android natively sends a broadcast message when any app is successfully installed.
Could any of my ideas work? Or anything else?
You can use BroadcastReceiver to detect the PACKAGE_ADDED, PACKAGE_REMOVED message::
In your manifest:
<receiver android:name=".AppStatusReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
</intent-filter>
</receiver>
And Ccreate a Broadcast Receiver:
[BroadcastReceiver(Enabled = true, Exported = false)]
public class SampleReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
// Do stuff here.
}
}
Please Note that the newly installed package does not receive this broadcast.
Related
I am trying to read data from an NFC credit card. The app is written using the .NET Maui framework. Right now I am trying to use the Plugin.NFC NuGet package because it seems to be the only one.
Here is the code I have so far:
private TagInfo tmpData;
private static void ReadTagEvent(ITagInfo data)
{
tmpData = (TagInfo)data;
}
public static NFC_READ_ERROR ReadNFC(ref TagInfo result, Page page)
{
if (!IsReady())
{
return NFC_READ_ERROR.NOT_ENABLED;
}
tmpData = null;
CrossNFC.Current.StartListening();
CrossNFC.Current.OnMessageReceived += ReadTagEvent;
page.DisplayAlert("Info", "Starting listening for NFC...", "Ok");
while (tmpData == null)
{
Thread.Sleep(50);
}
page.DisplayAlert("Info", "tmpData != null", "Ok");
CrossNFC.Current.StopListening();
result = tmpData;
if (tmpData.IsEmpty)
{
return NFC_READ_ERROR.NO_DATA;
}
return NFC_READ_ERROR.SUCCESS;
}
This runs and displays the first alert. But the result of tagInfo saved to tmpData is still null. Because ReadTagEvent is never triggered. After the Start listening is called. And the device is in close proximity to the android phone it beeps so I know that it is listening. I have confirmed that the device has data and is working properly by testing it on another app. The second alert is never displayed. By the way, this code is run inside a Thread in the app's Display Page. Any help and code would be appreciated. Thanks.
Add NFC Permissions in your AndroidManifest.xml
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
Add the line CrossNFC.Init(this) in your OnCreate().
protected override void OnCreate(Bundle savedInstanceState)
{
CrossNFC.Init(this);
base.OnCreate(savedInstanceState);
[...]
}
3. Add the line CrossNFC.OnResume() in your OnResume()
// Plugin NFC: Restart NFC listening on resume (needed for Android 10+)
4. Add the line CrossNFC.OnNewIntent(intent) in your OnNewIntent()
// Plugin NFC: Tag Discovery Interception
Plugin.NFC https://github.com/franckbour/Plugin.NFC
I found a similar question, but it probably didn't help me much.
I'm learning to access drive devices via UWP, in the first two days, the program was working fine, but today, UWP error close when open device, the error message is "Access is denied. Exception from HRESULT: 0x80070005"
I don't know if something was modified during the update process that caused this problem. I only have a snapshot of the virtual machine that can open the drive device. On this snapshot, when I update the UWP application, I can also open the drive device, but after update the driver, the UWP application can no longer open the driver device.
Does this mean the problem is with the driver?
After that, I created a new KMDF project and a UWP project, only printed some information, and the same error occurred after installing to the virtual machine.
It is worth noting that when I saw the UWP access to the drive device in DbgView, the EvtDeviceFileCreate event callback was triggered, but the EvtDeviceClose event callback was triggered immediately, I think, there should be no problem with the device interface.
Also, in the beginning, I used the device manager to "Add legacy hardware" to load the driver, then I tried to install the driver using devcon, both can install the driver, but it seems to be slightly different, I am not sure if this also It will have an impact, although both installation methods currently do not solve the problem I am encountering.
Below are some settings for UWP app and drivers.
Register device interface and add custom capability:
In this part, I refer to Microsoft's official sample kmdf_fx2.
WDFSTRING symbolicLinkString = NULL;
UNICODE_STRING symbolicLinkName = SymbolicName;
DEVPROP_BOOLEAN isRestricted;
status = WdfDeviceCreateDeviceInterface(
hDevice,
(LPGUID)&GUID_INTERFACE_HSADRVSAMPLE1,
NULL); // Reference String
if (!NT_SUCCESS(status)) {
KdPrint(("WdfDeviceCreateDeviceInterface Fail\n"));
goto Error;
}
if (g_pIoSetDeviceInterfacePropertyData != NULL) {
status = WdfStringCreate(NULL,
WDF_NO_OBJECT_ATTRIBUTES,
&symbolicLinkString);
if (!NT_SUCCESS(status)) {
KdPrint(("WdfStringCreate Fail\n"));
goto Error;
}
status = WdfDeviceRetrieveDeviceInterfaceString(
hDevice,
(LPGUID)&GUID_INTERFACE_HSADRVSAMPLE1,
NULL,
symbolicLinkString);
if (!NT_SUCCESS(status)) {
KdPrint(("WdfDeviceRetrieveDeviceInterfaceString Fail\n"));
goto Error;
}
WdfStringGetUnicodeString(symbolicLinkString, &symbolicLinkName);
isRestricted = DEVPROP_TRUE;
status = g_pIoSetDeviceInterfacePropertyData(
&symbolicLinkName,
&DEVPKEY_DeviceInterface_Restricted,
0,
0,
DEVPROP_TYPE_BOOLEAN,
sizeof(isRestricted),
&isRestricted);
if (!NT_SUCCESS(status)) {
KdPrint(("g_pIoSetDeviceInterfacePropertyData Fail\n"));
goto Error;
}
#if defined(NTDDI_WIN10_RS2) && (NTDDI_VERSION >= NTDDI_WIN10_RS2)
static const wchar_t customCapabilities[] = L"ColinTest.HSADrvSample_2022051717171\0";
status = g_pIoSetDeviceInterfacePropertyData(
&symbolicLinkName,
&DEVPKEY_DeviceInterface_UnrestrictedAppCapabilities,
0,
0,
DEVPROP_TYPE_STRING_LIST,
sizeof(customCapabilities),
(PVOID)&customCapabilities);
if (!NT_SUCCESS(status)) {
KdPrint(("g_pIoSetDeviceInterfacePropertyData Fail\n"));
goto Error;
}
#endif
WdfObjectDelete(symbolicLinkString);
}
Error:
if (symbolicLinkString != NULL) {
WdfObjectDelete(symbolicLinkString);
}
Driver INF:
;
; HSADrvSample1.inf
;
[Version]
Signature="$WINDOWS NT$"
Class=Test ; TODO: edit Class
ClassGuid={160303BD-D84C-4819-B962-9B1BDBB52310} ; TODO: edit ClassGuid
Provider=%ManufacturerName%
CatalogFile=HSADrvSample1.cat
DriverVer = 05/19/2022,9.21.50.151
PnpLockDown=1
[DestinationDirs]
DefaultDestDir = 12
HSADrvSample1_Device_CoInstaller_CopyFiles = 11
; ================= Class section =====================
[ClassInstall32]
Addreg=SampleClassReg
[SampleClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-5
[SourceDisksNames]
1 = %DiskName%,,,""
[SourceDisksFiles]
HSADrvSample1.sys = 1,,
WdfCoInstaller01011.dll=1 ; make sure the number matches with SourceDisksNames
;*****************************************
; Install Section
;*****************************************
[Manufacturer]
%ManufacturerName%=Standard,NTamd64
[Standard.NTamd64]
%HSADrvSample1.DeviceDesc%=HSADrvSample1_Device, Root\HSADrvSample1 ; TODO: edit hw-id
[HSADrvSample1_Device.NT]
CopyFiles=Drivers_Dir
[Drivers_Dir]
HSADrvSample1.sys
;-------------- Service installation
[HSADrvSample1_Device.NT.Services]
AddService = HSADrvSample1,%SPSVCINST_ASSOCSERVICE%, HSADrvSample1_Service_Inst
; -------------- HSADrvSample1 driver install sections
[HSADrvSample1_Service_Inst]
DisplayName = %HSADrvSample1.SVCDESC%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %12%\HSADrvSample1.sys
;
;--- HSADrvSample1_Device Coinstaller installation ------
;
[HSADrvSample1_Device.NT.CoInstallers]
AddReg=HSADrvSample1_Device_CoInstaller_AddReg
CopyFiles=HSADrvSample1_Device_CoInstaller_CopyFiles
[HSADrvSample1_Device_CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000, "WdfCoInstaller01011.dll,WdfCoInstaller"
[HSADrvSample1_Device_CoInstaller_CopyFiles]
WdfCoInstaller01011.dll
[HSADrvSample1_Device.NT.Wdf]
KmdfService = HSADrvSample1, HSADrvSample1_wdfsect
[HSADrvSample1_wdfsect]
KmdfLibraryVersion = 1.11
[Strings]
SPSVCINST_ASSOCSERVICE= 0x00000002
ManufacturerName="Colin" ;TODO: Replace with your manufacturer name
ClassName="Test" ; TODO: edit ClassName
DiskName = "HSADrvSample1 Installation Disk"
HSADrvSample1.DeviceDesc = "HSADrvSample1 Device"
HSADrvSample1.SVCDESC = "HSADrvSample1 Service"
SCCD File:
The file is configured as per the official documentationHardware Support App (HSA): Steps for App Developers, the file is already set to content.
<?xml version="1.0" encoding="utf-8"?>
<CustomCapabilityDescriptor xmlns="http://schemas.microsoft.com/appx/2018/sccd" xmlns:s="http://schemas.microsoft.com/appx/2018/sccd">
<CustomCapabilities>
<CustomCapability Name="ColinTest.HSADrvSample_2022051717171"></CustomCapability>
<AuthorizedEntities>
<AuthorizedEntity AppPackageFamilyName="Colin.HSA.APP_4a8dxf1xdwf12" CertificateSignatureHash="2d6e4f1418bc13447bb8c10067d0af418cb8a2f9e14c014e03a1e0ec811735af"></AuthorizedEntity>
</AuthorizedEntities>
<Catalog>FFFF</Catalog>
</CustomCapabilityDescriptor>
Part of Package.appxmanifest:
<Capabilities>
<Capability Name="internetClient" />
<uap4:CustomCapability Name="ColinTest.HSADrvSample_2022051717171"/>
</Capabilities>
Part of UWP Code:
string selector = CustomDevice.GetDeviceSelector(DeviceInterfaceGuid);
DeviceInformationCollection enumDevice = await DeviceInformation.FindAllAsync(selector);
string DeviceID = enumDevice[0].Id;
try
{
CustomDevice Device = await CustomDevice.FromIdAsync(DeviceID, DeviceAccessMode.ReadWrite, DeviceSharingMode.Shared);
if (null != Device)
{
Note.Text = "Open successfully";
}
}
catch (Exception ex)
{
Note.Text = "Failed to open!" + ex.Message;
}
I would like to know at which step am I getting the error? How to solve?
At the suggestion of #Nico Zhu - MSFT, I checked the CustomCapabilityName again.
There are three places in my code related to CustomCapabilityName, but they are all the same.
To test if the problem lies here, I reset the name.
Driver Code
static const wchar_t customCapabilities[] = L"Test.hsaTestCustomCapability_1653011401000\0";
CustomCapability.SCCD
<?xml version="1.0" encoding="utf-8"?>
<CustomCapabilityDescriptor xmlns="http://schemas.microsoft.com/appx/2018/sccd" xmlns:s="http://schemas.microsoft.com/appx/2018/sccd">
<CustomCapabilities>
<CustomCapability Name="Test.hsaTestCustomCapability_1653011401000"></CustomCapability>
</CustomCapabilities>
<AuthorizedEntities AllowAny="true"/>
<Catalog>0000</Catalog>
</CustomCapabilityDescriptor>
Package.appxmanifest
<Capabilities>
<Capability Name="internetClient" />
<uap4:CustomCapability Name="Test.hsaTestCustomCapability_1653011401000"/>
</Capabilities>
After that, I uninstalled the previously installed apps and drivers, restarted and installed the latest version of the program, but I still can't access it.
My SDK and WDK version is 10.0.19041.0, test machine OS version is Windows 10 Pro Build 19041.vb_release.191206-1406
I think I made a low-level mistake, the function g_pIoSetDeviceInterfacePropertyData that sets the device interface properties needs to be set in the DriverEntry.
But I didn't, which caused the CustomCapabilityName I set to not take effect.
UNICODE_STRING funcName;
RtlInitUnicodeString(&funcName, L"IoSetDeviceInterfacePropertyData");
g_pIoSetDeviceInterfacePropertyData = (PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA)(ULONG_PTR)
MmGetSystemRoutineAddress(&funcName);
However, it is still an unsolved mystery why the driver works properly in the most initial version...
I am pretty new in WindowsPhone applications development. I am currently developing a Unity application for Windows Phone 8.0. Inside this app I would like to open a PDF using the appropriate application on the phone (Acrobat Reader, Windows Reader, etc...)
First, I tried this :
void PDFButtonToggled(bool i_info)
{
Dispatcher.BeginInvoke(() =>
{
DefaultLaunch();
});
}
async void DefaultLaunch()
{
// Path to the file in the app package to launch
string PDFFilePath = #"Data/StreamingAssets/ImageTest.jpg";
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(PDFFilePath);
if (file != null)
{
// Set the option to show the picker
var options = new Windows.System.LauncherOptions();
options.DisplayApplicationPicker = true;
// Launch the retrieved file
bool success = await Windows.System.Launcher.LaunchFileAsync(file, options);
if (success)
{
// File launched
}
else
{
throw new Exception("File launch failed");
}
}
else
{
throw new Exception("Could not find file");
}
}
It returned me an exception so I searched why. I found that topic (written in 2013 :/) about async functions / threads : LINK. To sumarize, here the answer of Unity staff :
It will only work on Windows Store Apps, and you'll have to wrap the code in #if NET_FX/#endif. On other platforms, you cannot use async/.NET 4.5 code in scripts. If you want to use it for windows phone, you'll have to write that code in separate visual studio solution and compile it to DLL, so unity can use it as a plugin.
So I decided to create the double DLL solution described in the Unity Manual here : LINK. But when I complete the class of the first DLL with the "async void DefaultLaunch()" function given above I don't have references about Windows.ApplicationModel.etc... and Windows.System.etc... .
And here I am, a little bit lost between WP, Unity, 8.0 apps, StoreApps, etc...
If anyone has advices, questions, anything that can help me, it's welcome. :)
Crèvecoeur
I found a solution by myself but on a WindowsPhone8.1 application.
Here it is :
async void PDFButtonToggled(bool i_info)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
DefaultLaunchFile();
});
}
async void DefaultLaunchFile()
{
StorageFolder dataFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Data");
StorageFolder streamingAssetsFolder = await dataFolder.GetFolderAsync("StreamingAssets");
// Path to the file in the app package to launch
string filePath = "PDFTest.pdf";
var file = await streamingAssetsFolder.GetFileAsync(filePath);
if (file != null)
{
// Launch the retrieved file
bool success = await Windows.System.Launcher.LaunchFileAsync(file);
if (success)
{
// File launched
}
else
{
throw new Exception("File launch failed");
}
}
else
{
throw new Exception("File not found");
}
}
I've developed a free UWP app with IAP included.
I developed this app for Windows 10 Mobile. I published an IAP to the store named customBackground, and published my app as well as the IAP to the store.
After both of them has published, I downloaded my published app from the store and try to buy the IAP.
However it returned an system popup saying "This in-app item is no longer availble in MY-APP-NAME". I don't know why this happened.
There are problems:
When I was trying in debug mode in visual studio using CurrentAppSimulator class, it doesn't popup the purchase state selection on my phone or emulator, but it pops up on desktop version. it only reads the stored state in WindowsStoreProxy.xml.
I've also tried using CurrentApp class in debug/release mode after the IAP is published, but no luck, it returns the same error as the store version.
internet permission in Package.appxmanifest is enabled.
Here's the code in my released app in the store:
private async void buy_Click(object sender, RoutedEventArgs e)
{
LicenseInformation licenseInformation = CurrentApp.LicenseInformation;
if (!licenseInformation.ProductLicenses["customBackground"].IsActive)
{
Debug.WriteLine("Buying this feature...");
try
{
await CurrentApp.RequestProductPurchaseAsync("customBackground");
if (licenseInformation.ProductLicenses["customBackground"].IsActive)
{
var purchaseStat = LocalizedStrings.GetString(LocalizedStringEnum.havePurchased);
var b = new MessageDialog(purchaseStat);
b.ShowAsync();
Debug.WriteLine("You bought this feature.");
isIAPValid = true;
}
else
{
var purchaseStat = LocalizedStrings.GetString(LocalizedStringEnum.notPurchased);
var b = new MessageDialog(purchaseStat);
b.ShowAsync();
Debug.WriteLine("this feature was not purchased.");
}
}
catch (Exception)
{
var purchaseStat = LocalizedStrings.GetString(LocalizedStringEnum.purchaseFailed);
var b = new MessageDialog(purchaseStat);
b.ShowAsync();
Debug.WriteLine("Unable to buy this feature.");
}
}
else
{
var purchaseStat = LocalizedStrings.GetString(LocalizedStringEnum.alreadyOwned);
var b = new MessageDialog(purchaseStat);
b.ShowAsync();
Debug.WriteLine("You already own this feature.");
isIAPValid = true;
}
}
Thanks!
Later after one day, I found my iap is working.
Seems like a temporary problem, Microsoft does need to improve their store system.
I'm trying to edit Skype's config.xml file from code. It works fine, but after change Skype delete it and generate another one, undoing all my changes. For example, code:
public Core()
{
try
{
var processes = Process.GetProcessesByName("Skype");
if (processes.Length == 0)
{
AddRegistryKeys();
RemovePlaceholder();
}
else
{
RestartSkypeAndRun(processes[0],
() =>
{
AddRegistryKeys();
RemovePlaceholder();
});
}
Environment.Exit(0);
}
catch (Exception ex)
{
MessageBox.Show(string.Format("{0} - {1}", ex.GetType(), ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(-1);
}
}
private static void RemovePlaceholder()
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string skypePath = Path.Combine(appDataPath, "Skype");
foreach (var configPath in Directory.EnumerateFiles(skypePath, "config.xml", SearchOption.AllDirectories))
{
string userConfig = File.ReadAllText(configPath);
string fixedConfig = userConfig.Remove("<AdvertPlaceholder>1</AdvertPlaceholder>");
File.Move(configPath, configPath + ".bak");
File.WriteAllText(configPath, fixedConfig);
}
}
private static void RestartSkypeAndRun(Process skypeProc, Action action)
{
string skypeExePath = skypeProc.Modules[0].FileName;
skypeProc.Kill();
skypeProc.WaitForExit();
Thread.Sleep(TimeSpan.FromMilliseconds(500)); //just in case
action();
Process.Start(skypeExePath);
}
So how can it be done? I have no idea, except blocking file modification, e.g. change ACL and other permissions for file, set readonly attribute e.t.c.
See https://www.safaribooksonline.com/library/view/skype-hacks/0596101899/ch04s04.html
"Always stop Skype from running (by right-clicking on Skype in the system tray and choosing Quit) before making any changes to config.xml (or shared.xml), because even though your editor may tell you it has saved your updated version of config.xml, you may find that Skype ignores your changes and they are missing when you reopen config.xml. The procedure for editing any of Skype's configuration files should go like this: quit Skype (that is, stop it from running), edit (or delete) the configuration file, save the changes, and restart Skype."
C:\Documents and Settings\Username\Application Data\Skype\Skypename\config.xml
"There is another file, shared.xml, from which Skype obtains configuration information that is common to all users of Skype on the same Windows machine... You also can edit this file to tweak how Skype behaves, but the scope for tweaking is far more limited than for config.xml. You typically can find shared.xml in these locations on each platform:
Windows (version 1.3 and before)
C:\Documents and Settings\All Users\Application Data\Skype\shared.xml
Windows (version 1.4 and after)
C:\Documents and Settings\Username\Application Data\Skype\shared.xml
"