I know I should know how to do this, but I don't.
I have some code (from a brilliant map routing packing called Itinero http://docs.itinero.tech/index.html) that reads in a a routerDb file.
It works great (in windows) if I use a fully qualified path as it is absolute, but I have moved the file into the Resources.Raw folder and want to read it properly.
The working code
using (var stream = new FileInfo(#"/path/to/my/file/gb.routerdb").OpenRead())
{
routerDb = RouterDb.Deserialize(stream);
}
How can I use the Maui approach to do the same thing? Such as or simpler
using (var stream = new xxx("gb.routerdb").xxxx)
{
routerDb = RouterDb.Deserialize(stream);
}
I'm looking at https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/storage/file-system-helpers?tabs=windows but I don't get it :(
Thanks for any help.
G.
As document Platform differences of File system helpers mentioned, you can use method FileSystem.OpenAppPackageFileAsync to access file in the Resources\Raw folder as a MauiAsset.
Besides, if you want to access the path of the items in folder Raw, you can follow up the unknown issue about this:
https://github.com/dotnet/maui/issues/7943 .
Update:
As a test, I created a simple html(test.html) in folder Resource\Raw, and set BuildAction to MauiAsset.And I used the following code to read the html, it works well on my side(I tested on android device).
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async Task InitAsync()
{
string filePath = "test.html";
#if WINDOWS
var stream = await FileSystem.OpenAppPackageFileAsync("Assets/" + filePath);
#else
var stream = await FileSystem.OpenAppPackageFileAsync(filePath);
#endif
if (stream != null)
{
string s = (new System.IO.StreamReader(stream)).ReadToEnd();
this.MyWebView.Source = new HtmlWebViewSource { Html = s };
}
}
private void OnClicked(object sender, EventArgs e)
{
InitAsync();
}
}
MainPage.xaml
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<WebView x:Name="MyWebView"
/>
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
Well I certainly appreciate the example code, but for some reason it didn't work for me, but what did work based on your code (#Jessie Zhang) was:
var stream = await FileSystem.OpenAppPackageFileAsync("gb.routerdb");
if (stream != null)
{
routerDb = RouterDb.Deserialize(stream);
}
The conditional didn't work and I'm not sure why, but I'm only using windows so I'll check that out later. Thanks
Related
I am new to react-native and I seek your help please. What I am planning to do is to get the app icon associated with an app that the user has installed on his device.
I did take a look at this code and realized that I have no way of passing it back to my JS.
Here is what I am doing currently.
private List<String> getNonSystemApps() {
List<PackageInfo> packages = this.reactContext
.getPackageManager()
.getInstalledPackages(0);
List<String> ret = new ArrayList<>();
for (final PackageInfo p: packages) {
if ((p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("name", p.packageName);
jsonObject.put("firstInstallTime", p.firstInstallTime);
jsonObject.put("installLocation", p.installLocation);
jsonObject.put("applicationInfo", p.applicationInfo);
jsonObject.put("permissions", getPermissionsByPackageName(p.packageName));
Drawable icon = reactContext.getPackageManager().getApplicationIcon(p.packageName);
ret.add(jsonObject.toString());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return ret;
}
Can you please help me out with this ?
Thanks
Edit
I managed to get it working, based on Aaron's suggestion, I created another private function just to work with the images. This function will generate the base 64 version of an app's icon.
private String getBitmapOfAnAppAsBase64(String packageName) {
if(packageName.isEmpty() ) return new String("");
String base64Encoded = "";
Bitmap bitmap;
try {
Drawable appIcon = this.reactContext.getPackageManager().getApplicationIcon(packageName);
if(appIcon instanceof BitmapDrawable) {
bitmap= ((BitmapDrawable)appIcon).getBitmap();
} else {
bitmap = Bitmap.createBitmap(appIcon.getIntrinsicWidth(), appIcon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
base64Encoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
} catch(Exception e) {
Log.d(TAG,"An error was encounted while getting the package information. The error follows : " + e.toString());
}
return base64Encoded;
}
I then use this generated string in my original function, with the following modifications.
Old Non working code
Drawable icon = reactContext.getPackageManager().getApplicationIcon(p.packageName);
New working code
jsonObject.put("icon", getBitmapOfAnAppAsBase64(p.packageName));
and then in React-Native - its a piece of pie, since it supports base64 already.
"icon" : 'data:image/png;base64,'+installAppObj.icon
Huge thanks to Aaron , for guiding me in the correct direction.
If you haven't already, read through the entire Android Native Modules page. It's not that long and addresses several issues you're likely to run into.
Only these types can be sent to your JS code (via a #ReactMethod):
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
So you effectively have two options:
Encode the Drawable to one of these types, then decode it on the JavaScript side, or
Write the file to disk, and send the path over
I'm not sure what type of Drawable the icon is or if there are guarantees. But any of them should be convertible in some way. For example if it's a Bitmap you can do a Bitmap to Base64 String conversion.
When it was a clickonce program it worked, but then I made an appxpackage and exported it as a centennial app for windows store and the upgrade does not work any more.
Right now I have in App.xaml.cs
protected override void OnStartup(StartupEventArgs e) {
if (myprog.Properties.Settings.Default.UpgradeRequired)
{
myprog.Properties.Settings.Default.Upgrade();
myprog.Properties.Settings.Default.UpgradeRequired = false;
myprog.Properties.Settings.Default.Save();
}
With UpgradeRequired as a bool in user settings. Is that the right place?
I am getting settings reset on each version update. Now I have several of these directories
C:\Users\me\AppData\Local\progname\prog.exe_Url_randomChars
each with several different version of program settings. So after the upgrade another one of those is created, instead a subfolder with x.x.x.x of the current version.
As before, on each version release I increase version in Assembly Information the Assembly Version, File Version, and now I have the same numbers in AppxManifest.xml. I keep the last number group of the version to 0 as advised, and just increase the 3rd number group.
Is there something I am missing?
UWP and Desktop Bridge apps need to store their settings in ApplicationData.LocalSettings:
https://learn.microsoft.com/en-us/windows/uwp/design/app-settings/store-and-retrieve-app-data#local-app-data
You could load the previous user.config file into current settings. This is just a workaround, and can be used to transition to ApplicationData.LocalSettings.
public static void Init()
{
if (myprog.Properties.Settings.Default.UpgradeRequired)
{
LoadPreviousSettings(myprog.Properties.Settings.Default);
myprog.Properties.Settings.Default.UpgradeRequired = false;
myprog.Properties.Settings.Default.Save();
}
}
private static void LoadPreviousSettings(params ApplicationSettingsBase[] applicationSettings)
{
const string companyName = "YOUR_COMPANY_NAME_HERE";
var userConfigXml = GetUserConfigXml(companyName);
Configuration config = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.PerUserRoamingAndLocal);
foreach (ApplicationSettingsBase setting in applicationSettings)
{
try
{
// loads settings from user.config into configuration
LoadSettingSection(setting, config, userConfigXml);
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("userSettings");
}
catch (FileNotFoundException)
{
// Could not import settings.
// Perhaps user has no previous version installed
}
setting.Reload();
}
}
private static void LoadSettingSection(ApplicationSettingsBase setting, Configuration config, XDocument userConfigXml)
{
string appSettingsXmlName = setting.ToString();
var settings = userConfigXml.XPathSelectElements("//" + appSettingsXmlName);
config.GetSectionGroup("userSettings")
.Sections[appSettingsXmlName]
.SectionInformation
.SetRawXml(settings.Single().ToString());
}
private static XDocument GetUserConfigXml(string companyName)
{
var localPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + $#"\{companyName}";
// previous package folder
var previousLocal = GetDirectoryByWriteTime(localPath, 1);
// previous version, e.g. 1.2.0
var prevousVersion = GetDirectoryByWriteTime(previousLocal, 0);
// application settings for previous version
return XDocument.Load(prevousVersion + #"\user.config");
}
private static string GetDirectoryByWriteTime(string path, int order)
{
var direcotires = new DirectoryInfo(path).EnumerateDirectories()
.OrderBy(d => d.LastWriteTime)
.Reverse()
.ToList();
if (direcotires.Count > order)
{
var previous = direcotires[order];
return previous.FullName;
}
throw new FileNotFoundException("Previous config file not found.");
}
There is a working answer here.
Basically you need to create a duplicate version using UWP's ApplicationData.Settings and then loading it at the beginning of the app. It is very straightforward when your settings are strings, bools, etc. But not so if you have unique settings.
To elaborate more from the answer in the link, when you have settings consisting of custom types/classes, when creating UWP's duplicate version, you can use Newtonsoft.Json to serialise the custom setting:
try
{
ApplicationData.Current.LocalSettings.Values[value.Name] = value.PropertyValue;
}
catch
{
string serialised = JsonConvert.SerializeObject(value.PropertyValue);
ApplicationData.Current.LocalSettings.Values[value.Name] = serialised;
}
Then when loading your custom setting:
if (s.Name == "MyCustomSetting")
{
var deserialised = JsonConvert.DeserializeObject<MyCustomClass>((string)setting.Value);
s.PropertyValue = deserialised;
}
so I have an mvc 5 application with 3 display modes, desktop (default), mobile, and tablet. I'm using WURFL to figure out devices. Here's the code called from global.cs to register:
public static void RegisterDisplayModes(IList<IDisplayMode> displayModes){
var datafile = HttpContext.Current.Server.MapPath(WurflDataFilePath);
var configurer = new InMemoryConfigurer().MainFile(datafile);
var manager = WURFLManagerBuilder.Build(configurer);
HttpContext.Current.Cache[WURFLMANAGER_CACHE_KEY] = manager;
bool mobileEnabled = ConfigurationManager.AppSettings["EnableMobileSite"] == "true";
bool tabletEnabled = ConfigurationManager.AppSettings["EnableTabletSite"] == "true";
var modeDesktop = new DefaultDisplayMode("") {
ContextCondition = (c => c.Request.IsDesktop())
};
var modeMobile = new DefaultDisplayMode("mobile"){
ContextCondition = (c => c.Request.IsMobile())
};
var modeTablet = new DefaultDisplayMode("tablet"){
ContextCondition = (c => c.Request.IsTablet())
};
displayModes.Clear();
if (mobileEnabled) displayModes.Add(modeMobile);
if (tabletEnabled) displayModes.Add(modeTablet);
displayModes.Add(modeDesktop);
}
I'm using some extension methods to HttpRequestBase, as discussed in http://msdn.microsoft.com/en-us/magazine/dn296507.aspx:
public static bool IsDesktop(this HttpRequestBase request){
return true;
}
public static bool IsMobile(this HttpRequestBase request) {
return IsMobileInternal(request.UserAgent) && !IsForcedDesktop(request);
}
public static bool IsTablet(this HttpRequestBase request) {
return IsTabletInternal(request.UserAgent) && !IsForcedDesktop(request);
}
public static void OverrideBrowser(this HttpRequestBase request, bool forceDesktop){
request.RequestContext.HttpContext.Cache[OVERRIDE_BROWSER_CACHE_KEY] = forceDesktop;
}
public static bool IsForcedDesktop(this HttpRequestBase request){
var isForced = request.RequestContext.HttpContext.Cache[OVERRIDE_BROWSER_CACHE_KEY];
return isForced != null ? isForced.ToString().ToBool() : false;
}
private static bool IsMobileInternal(string userAgent) {
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
if (device.IsTablet() == true) {
return false;
} else {
return device.IsMobile();
}
}
private static bool IsTabletInternal(string userAgent) {
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
return device.IsTablet();
}
It all works fine for a while, but then after an hour or so, mobile and tablet devices start displaying the desktop views, and the desktop view starts showing the ViewSwitcher shared view (I assume most people are familiar with it, it just allows you to show the desktop view from a mobile device). It's almost like that caching bug in mvc4. I have tried removing my code to register the display modes, and just went with the default mvc mobile support, and it works fine it has the same issue! So clearly there's a problem in here somewhere... can anyone see anything obvious? Almost impossible to debug cause problems only start coming up after a long time, and even then only on the live system really! Any ideas?
Thanks heaps... been on this issue for way too long now...
Cheers
Andy
EDIT: even stripping it right back to the default implementations creates the issue. I added some debugging code to make sure I'm actually running mvc5, but it appears I am. I've also tried the initially recommended workaround for the issue on mvc4 by disabling the cache, still no joy. Is there really no one with info on this?
So I finally figured it out. Very simple as usual. For some reason I used RequestContext.HttpContext.Cache to save the status when someone wants the full view as opposed to the mobile view. I've never used HttpContext.Cache, I'm pretty sure I would have taken that from a blog somewhere - can't find it anymore though. So all that happened was that it would switch the view for everyone, not just the one person. Can't believe it took weeks to figure that out. Hope it helps someone else at some point.
I have a problem with the MediaElement in my windows phone (8) application. It's working fine until I navigate to some other page and come back on the page containing the MediaElement.
I have the following code to play a stream coming from Bing Translator APIs:
private void TranslationService_SpeakComplete(object sender, SpeakCompleteEventArgs e)
{
var stream = e.Stream;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
MediaElement.Stop();
MediaElement.Source = null;
string filename = "FlipNLearnItAudio";
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
bool fileExists = isf.FileExists(filename);
if (fileExists)
{
isf.DeleteFile(filename);
}
var isfs = isf.CreateFile(filename);
using (isfs)
{
Helpers.SaveFile(stream, isfs);
isfs.Position = 0;
MediaElement.AutoPlay = true;
MediaElement.SetSource(isfs);
}
}
}
catch (Exception ex)
{
//TODO: log exception
}
});
}
The biggest problem is that there's no exception.... the "MediaElement.SetSource(isfs);" is called normally without any exception!
Do you have any idea of what can happen?
Thanks for any help!
Bastien
Ok, I find a solution to my problem. I put the MediaElement in the App.xaml resources and now it's working fine...
But it doesn't explain why it was not working with the MediaElement in the MainPage.xaml.
If somebody has the answer, I would be very interested to hear it ;-).
So I've downloaded the samples from http://archive.msdn.microsoft.com/ManagedMediaHelpers.
I've got my code working using MP3MediaStreamSource. However, I don't fully understand the code would like some explanation.
public partial class MainPage : PhoneApplicationPage
{
private static string mediaFileLocation = "http://file-here.mp3";
private static HttpWebRequest request = null;
private static Mp3MediaStreamSource mss = null;
public MainPage()
{
InitializeComponent();
}
private void RequestCallback(IAsyncResult asyncResult)
{
HttpWebResponse response = request.EndGetResponse(asyncResult) as HttpWebResponse;
Stream s = response.GetResponseStream();
mss = new Mp3MediaStreamSource(s, response.ContentLength);
Deployment.Current.Dispatcher.BeginInvoke(
() =>
{
this.wp7AudioElement.Volume = 100;
this.wp7AudioElement.SetSource(mss);
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
request = WebRequest.CreateHttp(MainPage.mediaFileLocation);
// NOTICE
// Makes this demo code easier but I wouldn't do this on a live phone as it will cause the whole
// file to download into memory at once.
//
// Instead, use the asynchronous methods and read the stream in the backgound and dispatch its
// data as needed to the ReportGetSampleCompleted call on the UIThread.
request.AllowReadStreamBuffering = true;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(this.RequestCallback), null);
}
}
It's really just the last method I need explained, I don't understand the Notice as to why it's a bad idea and how to do it differently?
Basically, it is trying to tell you that you are downloading 1 file COMPLETELY before it plays. It is not a good idea, since if the file is 10 MB, it may take a while before it completely downloads.
A better idea would be to chunk the file using Encoders, and read it in on need basis.