Android C# HtmlAgilityPack - android

How do I use HtmlAgilityPack with Android (Mono for Android - C#)? I've added the reference, but I keep getting this error:
Error CS0012: The type 'System.Xml.XPath.IXPathNavigable' is defined
in an assembly that is not referenced. You must add a reference to
assembly 'System.Xml, Version=2.0.0.0

I have incorporated HtmlAglilityPack into the base library assembly I use in all of my MonoDroid projects with great success. I have not even tried to use them in precompiled form, but simply added it's source to my project.
I then shamelessly edited HtmlWeb.cs to throw out the Windows stuff:
lines 893 to 903 (may have somewhat changed, just look around near there):
if (!helper.GetIsDnsAvailable())
{
#if Android
contentType = def;
#else
//do something.... not at full trust
try
{
RegistryKey reg = Registry.ClassesRoot;
reg = reg.OpenSubKey(extension, false);
if (reg != null) contentType = (string)reg.GetValue("", def);
}
catch (Exception)
{
contentType = def;
}
#endif
}
lines 934 to 946 (may have somewhat changed, just look around near there):
if (helper.GetIsRegistryAvailable())
{
#if Android
ext = def;
#else
try
{
RegistryKey reg = Registry.ClassesRoot;
reg = reg.OpenSubKey(#"MIME\Database\Content Type\" + contentType, false);
if (reg != null) ext = (string)reg.GetValue("Extension", def);
}
catch (Exception)
{
ext = def;
}
#endif
}
I then added Android to the conditional compilation symbols of my project.
My references are:
Microsoft.CSharp
Mono.Android
System
System.Core
System.Data
System.Xml
System.Xml.Linq
Please add a comment, telling if you can compile it now, or if you need more information.

Related

How do I read a deployed file in MAUI/Xamarin on Android

I have a small MAUI app i'm testing with. Im trying to read a file that was part of the deployment. I have the code below, which works great in a Windows deploy of the MAUI app, but crashes in Android. What is the proper cross-platform way to do this?
// TODO get from service or xml
var path = AppDomain.CurrentDomain.BaseDirectory;
//var path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
var fullpath = Path.Combine(path, "Services\\questions.json");
var json = File.ReadAllText(fullpath);
MAUI has a new way to access files included with the app: MauiAsset.
Described in blog Announcing .NET MAUI Preview 4, Raw Assets:
.NET MAUI now makes it very easy to add other assets to your project and reference them directly while retaining platform-native performance. For example, if you want to display a static HTML file in a WebView you can add the file to your project and annotate it as a MauiAsset in the properties.
<MauiAsset Include="Resources\Raw\index.html" />
Tip: you can also use wildcards to enable all files in a directory:
... Include="Resources\Raw\*" ...
Then you can use it in your application by filename.
<WebView Source="index.html" />
UPDATE
However, the feature MauiAsset apparently still needs improvement:
open issue - MauiAsset is very hard to use.
There we learn that for now:
Set BuildAction in each file's properties to MauiAsset.
That is, its not recommended to use the "wildcard" approach at this time. Set that build action on each file in solution explorer / your project / the file.
Accessing on Windows requires a work-around:
#if WINDOWS
var stream = await Microsoft.Maui.Essentials.FileSystem.OpenAppPackageFileAsync("Assets/" + filePath);
#else
var stream = await Microsoft.Maui.Essentials.FileSystem.OpenAppPackageFileAsync(filePath);
#endif
NOTE: This will be simplified at some point; follow that issue to see progress.
UPDATE
The current MAUI template is missing some platform-specific flags. For now, add your own flag to identify when the code is running on Windows:
Complete example in ToolmakerSteve - repo MauiSOAnswers. See MauiAssetPage.
MauiAssetPage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiTests.MauiAssetPage">
<ContentPage.Content>
<!-- By the time Maui is released, this is all you will need. -->
<!-- The Init code-behind won't be needed. -->
<WebView x:Name="MyWebView" Source="TestWeb.html" />
</ContentPage.Content>
</ContentPage>
MauiAssetPage.xaml.cs:
using Microsoft.Maui.Controls;
using System.Threading.Tasks;
namespace MauiTests
{
public partial class MauiAssetPage : ContentPage
{
public MauiAssetPage()
{
InitializeComponent();
Device.BeginInvokeOnMainThread(async () =>
{
await InitAsync();
});
}
private async Task InitAsync()
{
string filePath = "TestWeb.html";
#if WINDOWS
var stream = await Microsoft.Maui.Essentials.FileSystem.OpenAppPackageFileAsync("Assets/" + filePath);
#else
var stream = await Microsoft.Maui.Essentials.FileSystem.OpenAppPackageFileAsync(filePath);
#endif
if (stream != null)
{
string s = (new System.IO.StreamReader(stream)).ReadToEnd();
this.MyWebView.Source = new HtmlWebViewSource { Html = s };
}
}
}
}
TestWeb.html:
(whatever html you want)
In Solution Explorer, add TestWeb.html to your project. In its Properties, select Build Action = MauiAsset.
I tried looking for a solution to this for months. I ended up hosting the file online then creating a method to download the file during runtime
public async Task DownloadFile(string fileName)
{
if (File.Exists(FileSystem.Current.AppDataDirectory + $"/{fileName}"))
{
return;
}
else
{
try
{
NetworkAccess networkAccess = Connectivity.Current.NetworkAccess;
if (networkAccess == NetworkAccess.Internet)
{
await Task.Run(() =>
{
var uri = new Uri($"https://myhostedfile.com/{fileName}");
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCallback2);//checking if download is complete
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(MaintainProgress);//event handler to check download progress
webClient.DownloadFileAsync(uri, FileSystem.Current.AppDataDirectory + $"/{fileName}");
});
}
else
await Shell.Current.DisplayAlert("No Internet", "Failed to get some files from the internet, confirm if your internet is" +
"working", "OK");
}
catch (Exception)
{
await Shell.Current.DisplayAlert("Error", "Failed to get some files from the internet, confirm if your internet is" +
"working", "OK");
}
}
}
Then you can access your file URL using:
string filePath = FileSystem.Current.AppDataDirectory + $"/myfile.pdf;

Get mangled name of function when using .so shared library from Android c++ side? (libAudioFlinger.so)

i have been working on this problem for 2 weeks now, i have integrate C++ code into my Voip call recording app, the code is supposed to take care of forcefully setting Input_source of mediaRecorder to same one as from the Voip Call (in my case it is input_source=7 / Voice_Communication).
In order to achieve my goal i load shared library : libaudioflinger.so and attempt to reach SetParameters function, as can be seen from the snipet below :
handleLibAudioFlinger = dlopen("libaudioflinger.so", RTLD_LAZY | RTLD_GLOBAL);
if (handleLibAudioFlinger != NULL) {
func = dlsym(handleLibAudioFlinger, "setParameters"); // i do not know the mangled name of
SetParameters function
if (func != NULL) {
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", "Function is not null");
result = 0;
}
audioSetParameters = (lasp) func;
} else {
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", "Function is null");
result = -1;
}
dlopen does not return null, but dlsym does, reason is that i need to have the exact mangled name of the function setParameters from AudioFlinger.cpp As in Android source code.
i am new to handling android c++ code and dealing with shared libraries,etc... if someone can tell me step by step how to get correct mangled name for the function that i need?

TensorFlow Lite 2.0 advanced GPU using on Android with C++

I am new in TensorFlow. I built TensorFlow Lite libraries from sources. I try to use TensorFlow for face recognition. This one a part of my project. And I have to use GPU memory for input/output e.g. input data: opengl texture, output data: opengl texture. Unfortunately, this information is outdated: https://www.tensorflow.org/lite/performance/gpu_advanced. I tried to use gpu::gl::InferenceBuilder for building gpu::gl::InferenceRunner. And I have problem. I don’t understand how I can get the model in GraphFloat32 (Model>) format and TfLiteContext.
Example of my experemental code:
using namespace tflite::gpu;
using namespace tflite::gpu::gl;
const TfLiteGpuDelegateOptionsV2 options = {
.inference_preference = TFLITE_GPU_INFERENCE_PREFERENCE_SUSTAINED_SPEED,
.is_precision_loss_allowed = 1 // FP16
};
tfGPUDelegate = TfLiteGpuDelegateV2Create(&options);
if (interpreter->ModifyGraphWithDelegate(tfGPUDelegate) != kTfLiteOk) {
__android_log_print(ANDROID_LOG_ERROR, "Tensorflow", "GPU Delegate hasn't been created");
return ;
} else {
__android_log_print(ANDROID_LOG_INFO, "Tensorflow", "GPU Delegate has been created");
}
InferenceEnvironmentOptions envOption;
InferenceEnvironmentProperties properties;
auto envStatus = NewInferenceEnvironment(envOption, &env, &properties);
if (envStatus.ok()){
__android_log_print(ANDROID_LOG_INFO, "Tensorflow", "Inference environment has been created");
} else {
__android_log_print(ANDROID_LOG_ERROR, "Tensorflow", "Inference environment hasn't been created");
__android_log_print(ANDROID_LOG_ERROR, "Tensorflow", "Message: %s", envStatus.error_message().c_str());
}
InferenceOptions builderOptions;
builderOptions.usage = InferenceUsage::SUSTAINED_SPEED;
builderOptions.priority1 = InferencePriority::MIN_LATENCY;
builderOptions.priority2 = InferencePriority::AUTO;
builderOptions.priority3 = InferencePriority::AUTO;
//The last part requires a model
// GraphFloat32* graph;
// TfLiteContext* tfLiteContex;
//
// auto buildStatus = BuildModel(tfLiteContex, delegate_params, &graph);
// if (buildStatus.ok()){}
You may look function BuildFromFlatBuffer (https://github.com/tensorflow/tensorflow/blob/6458d346470158605ecb5c5ba6ad390ae0dc6014/tensorflow/lite/delegates/gpu/common/testing/tflite_model_reader.cc). It creates Interpreter and graph from it.
Also Mediapipe uses InferenceRunner you may find for useful in files:
https://github.com/google/mediapipe/blob/master/mediapipe/calculators/tflite/tflite_inference_calculator.cc
https://github.com/google/mediapipe/blob/ecb5b5f44ab23ea620ef97a479407c699e424aa7/mediapipe/util/tflite/tflite_gpu_runner.cc

How to generate java files from swig?Is this done by gradle or do it with command line?

This is my Build.gradle file:-
project.ext {
// * SWIG options *
// This and the SWIG "task" at the bottom are loosely based on:
//
// http://androiddeveloper.co.il/using-swig/
swigModuleFiles = ['yourfy.i']
swigIncludeDirs = ['src/main/cpp/yourfy/src', 'src/main/cpp/yourfy/src/nxcommon/src/libnxcommon']
swigJavaOutputDir = file("src/main/java/com/yourfy/yourfy/swig").absolutePath
swigCxxOutputDir = file("src/main/cpp/swig").absolutePath
swigModuleFilesAbs = []
swigIncludeDirOpts = []
swigCxxModuleFilesAbs = []
swigModuleFiles.each { moduleFile ->
swigModuleFilesAbs.add(file("src/main/cpp/yourfy/src/yourfy/" + moduleFile).absolutePath)
swigCxxModuleFilesAbs.add(swigCxxOutputDir + "/" + moduleFile + ".cxx")
}
swigIncludeDirs.each { incDir ->
swigIncludeDirOpts.add("-I" + file(incDir).absolutePath)
}
}
// * Generate SWIG wrappers *
// Generate .java and .cxx files for the SWIG bindings.
//
// It has to be done in Gradle (as opposed to the native library's CMakeLists.txt), because
// Gradle calls the Java implementationr BEFORE running the native build, so the .java SWIG files will
// not have been generated yet when the implementationr runs. We have to ensure that they are generated
// before the Java implementationr runs.
//
// Thanks, Gradle!
//
// I would love to do this as a task, but unfortunately, it does not work. For some reason,
// when building from Android Studio (NOT when running Gradle from command line), CMake is
// actually invoked first, before any other tasks, which means that the Gradle-generated SWIG
// CXX source files might still be missing, which CMake then complains about and aborts. For
// now, we will have to run SWIG at the top level all the time to get it working. Right now,
// it's reasonably fast to do so, let's see how long this holds.
// More info:
def swigExec = '/usr/local/bin/swig'
// TODO: Add some auto-detection
if (project.hasProperty('swig.executable')) {
swigExec = project.property('swig.executable')
}
if (swigExec != null && file(swigExec).isFile()) {
file(project.swigJavaOutputDir).mkdirs()
file(project.swigCxxOutputDir).mkdirs()
// Delete previous output files (.cxx, .h and *.java in respective directories)
(file(project.swigJavaOutputDir).listFiles() + file(project.swigCxxOutputDir).listFiles()).each { file ->
if (file.name.toLowerCase().endsWith(".cxx")
|| file.name.toLowerCase().endsWith(".h")
|| file.name.toLowerCase().endsWith(".java")
) {
file.delete()
}
}
[project.swigModuleFilesAbs, project.swigCxxModuleFilesAbs].transpose().each { moduleFile, cxxFile ->
exec {
commandLine(
swigExec,
'-java',
'-c++',
'-package', 'com.yourfy.yourfy.swig',
*project.swigIncludeDirOpts,
'-outdir', project.swigJavaOutputDir,
'-o', cxxFile,
moduleFile
)
}
}
} else {
logger.error('Property swig.executable not set or invalid! You should set it in ' +
'the gradle.properties file of your gradle user home directory, pointing to ' +
'a SWIG > 3.0 executable')
}
How to generate swig wrapper files in android?There are some links added to here but still its not figure out.
Also tried following links also mentioned in the comment:-
Stackoverflow question link
Github link
I was trying both the links but didn't understand but still seems it difficult.

TXW.create() different behavior on jdk and android?

I've created webservice on desktop (jdk, working good) and i'm trying to move it to android (make it running on android device). But i'm having some issues and after debugging for few days i've found different behaviour of the next code:
Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result));
Passed result object is almost empty before invocation and has only systemId property. On JDK created schema has not null nsUri property, but on Android it's just empty!
JDK:
Android:
What's the reason and how can i fix it?
It makes schema invalid and it throws exception in cxf later:
javax.xml.ws.WebServiceException: java.lang.RuntimeException: Invalid schema document passed to AbstractDataBinding.addSchemaDocument, not in W3C schema namespace: schema
Update1:
I'v found TXW code pkg.getAnnotation(XmlNamespace.class) return null in android but instance value in jdk:
if(nsUri.equals("##default")) {
Package pkg = c.getPackage();
if(pkg!=null) {
XmlNamespace xn = pkg.getAnnotation(XmlNamespace.class); // xn = null in android
if(xn!=null)
nsUri = xn.value();
}
}
if(nsUri.equals("##default"))
nsUri = "";
return new QName(nsUri,localName);
The reason was in TXW.java:
Package pkg = c.getPackage();
XmlNamespace xn = pkg.getAnnotation(XmlNamespace.class);
if(xn!=null) // always null in android as it does not support package annotations
nsUri = xn.value();
So i had to hack it - create public static variable and set it before:
public static java.lang.String XmlNamespaceAnnotationValue = null;
...
Package pkg = c.getPackage();
if(pkg!=null) {
if (XmlNamespaceAnnotationValue != null) {
nsUri = XmlNamespaceAnnotationValue;
} else {
XmlNamespace xn = pkg.getAnnotation(XmlNamespace.class);
if(xn!=null)
nsUri = xn.value();
}
}
Before usage:
TXW.XmlNamespaceAnnotationValue = "http://www.w3.org/2001/XMLSchema";

Categories

Resources