Xamarin forms : Open PDF file in external App - xamarin.forms

I am facing below error when trying to launch a PDF file stored in assets folder of android project in xamarin forms.
"this file could not be accessed. check your connection or make the filename shorter in xamarin forms"
Giving shorter file name didn't help.
Below is my code:
public interface IDocumentView
{
void ShowPDFTXTFromLocal(string filename);
}
[assembly: Xamarin.Forms.Dependency(typeof(DocumentView))]
namespace Portfolio_Pdf.Droid.Platform
{
public class DocumentView : IDocumentView
{
[Obsolete]
public void ShowPDFTXTFromLocal(string filename)
{
string reportSavedPath = "/data/user/0/com.companyname.portfolio_pdf/files/test.pdf";
Java.IO.File file = new Java.IO.File(reportSavedPath);
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
var fileUri = Android.Net.Uri.FromFile(new Java.IO.File(reportSavedPath));
Intent intent = new Intent(Intent.ActionView);
var mimetype = MimeTypeMap.Singleton.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl((string)fileUri).ToLower());
Android.Net.Uri apkURI = FileProvider.GetUriForFile(
Xamarin.Forms.Forms.Context.ApplicationContext,
Xamarin.Forms.Forms.Context.ApplicationContext.PackageName + ".provider", file);
intent.SetDataAndType(apkURI, mimetype);
intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);
intent.SetFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.ClearTop);
intent.AddFlags(ActivityFlags.GrantReadUriPermission | ActivityFlags.ClearTop);
try
{
Xamarin.Forms.Forms.Context.StartActivity(intent);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
});
}
}
}
Android.Manifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
provider_paths:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>

Related

Connect NLog To Database Using Azure KeyVault Connection String In NetCore 3.1

My nlogconfig file is writing fine to a text file. It is also writing to a database when I include the connection string in appsettings.json. Now that we are ready to move to production, I am not going to be housing the connection string in appsettings.json.
However, the problem is that I do not know how to connect mynlogconfig file to a connection string that is located in Azure KeyVault.
How do I take this line of code in nlogconfig
connectionString="${configsetting:item=ConnectionStrings.DefaultConnection}"
and reference the connection string in Azure KeyVault?
My nlogconfig file:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<add assembly="NLog.Appsettings.Standard"/>
</extensions>
<targets>
<!-- local file target -->
<target name="fileTarget"
xsi:type="File"
fileName="C:\Nlog\logs\meLog.txt"
layout="
-------------- ${level} (${longdate}) --------------${newline}
${newline}
Call Site: ${callsite}${newline}
Exception Type: ${exception:format=Type}${newline}
Exception Message: ${exception:format=Message}${newline}
Stack Trace: ${exception:format=StackTrace}${newline}
Additional Info: ${message}${newline}" />
<target xsi:type="Database"
name="dbTarget"
connectionString="${configsetting:item=ConnectionStrings.DefaultConnection}"
commandText="INSERT INTO Logs(CreatedOn,Message,Level,Exception,StackTrace,Logger,Url) VALUES (#datetime,#msg,#level,#exception,#trace,#logger,#url)">
<parameter name="#datetime" layout="${date}" />
<parameter name="#msg" layout="${message}" />
<parameter name="#level" layout="${level}" />
<parameter name="#exception" layout="${exception:format=#}" />
<parameter name="#trace" layout="${stacktrace}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#url" layout="${aspnet-request-url}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Error" writeTo="dbTarget" />
<logger name="*" minlevel="Error" writeTo="fileTarget"/>
</rules>
</nlog>
My Program.cs file which is getting the database connection string from Azure KeyVault:
using Azure.Extensions.AspNetCore.Configuration.Secrets;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using WebApplication6.Models;
namespace WebApplication6
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
string MyClientID = "1MyClientID";
string MyTenantID = "1MyTenantID";
string MyClientSecretID = "1MyClientSecretID";
ClientSecretCredential credential =
new ClientSecretCredential(MyTenantID, MyClientID, MyClientSecretID);
var secretClient = new SecretClient(new Uri("https://somerandomurivault.vault.azure.net/"),
credential);
config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());
config.Build();
})
.ConfigureServices((hostContext, services)=>
{
var databaseConnectionString = hostContext.Configuration.GetValue<string>("databaseConnectionString");
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(databaseConnectionString);
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
I tried to set the connection string to a global variable, but I had no way of referencing this global variable in the nlogconfig file.

How to click webview to leave the application and go to the Map application?

viewOverride as you leave the webview
public boolean shouldOverrideUrlLoading (WebView view, String url)}} coding Saw writing webview did not go out.The application works if I go to google and click on the map. I need to intent the application and go to google map scroll
public class MainActivity extends Activity {
/**
* WebViewClient subclass loads all hyperlinks in the existing WebView
*/
public class GeoWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("tel:")) {
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
return true;
} else if (url.startsWith("mailto:")) {
url = url.replaceFirst("mailto:", "");
url = url.trim();
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("plain/text").putExtra(Intent.EXTRA_EMAIL, new String[]{url});
startActivity(i);
return true;
}else {
if (url.startsWith("geo:")) {
Intent searchAddress = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(searchAddress);
return true;
} else {
view.loadUrl(url);
return true;
}
}
}
}
/**
* WebChromeClient subclass handles UI-related calls
* Note: think chrome as in decoration, not the Chrome browser
*/
public class GeoWebChromeClient extends WebChromeClient {
#Override
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
// Always grant permission since the app itself requires location
// permission and the user has therefore already granted it
callback.invoke(origin, true, false);
}
}
WebView mWebView;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
// Brower niceties -- pinch / zoom, follow links in place
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.getSettings().setBuiltInZoomControls(true);
mWebView.setWebViewClient(new GeoWebViewClient());
// Below required for geolocation
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setGeolocationEnabled(true);
mWebView.setWebChromeClient(new GeoWebChromeClient());
mWebView.loadUrl("http://www.google.com");
}
#Override
public void onBackPressed() {
// Pop the browser back stack or exit the activity
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed();
}
}
}
Correct if you want to write anything in AndroidManifest and have a burqa if you know the answer
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="youngsterus.meo.app" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- We may use GPS but it's not required -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="#drawable/ic_launcher"
android:label="GOOGLE"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true"
android:supportsRtl="true">
<activity
android:name="youngsterus.meo.app.MainActivity"
android:label="GOOGLE"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="geo"/>
<data android:scheme="google.navigation"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<!-- Filter for geo scheme geo -->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="geo" />
</intent-filter>
</activity>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/ic_stat_ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/colorAccent" />
<meta-data
android:name="preloaded_fonts"
android:resource="#array/preloaded_fonts" />
</application>
</manifest>

Issue with initializing database with EF

I'm trying to initialize a database with sample data and I get an error:
Failed to set database initializer of type
'Blog.Models.DAL.BlogInitializer,Blog' for DbContext type
'Blog.Models.DAL.BlogContext, Blog' specified in the application
Inner exception is like that:
Can't find a file or an assembly 'Blog' or one of its dependencies.
Can't find the specified file.
I have Blog.Model and Blog.WebUI in separate projects in the same Blog solution.
I have edited Web.config (In Blog.WebUI) file like that:
<connectionStrings>
<add name="BlogContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=Blog;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<entityFramework>
<contexts>
<context type="Blog.Models.DAL.BlogContext, Blog">
<databaseInitializer type="Blog.Models.DAL.BlogInitializer, Blog" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
In Server Explorer I see an accessible database created by it, but no tables inside.
BlogContext class:
namespace Blog.Models.DAL
{
public class BlogContext : DbContext
{
public BlogContext() : base("BlogContext")
{
}
public DbSet<Post> Posts { get; set; }
}
}
And initializer:
namespace Blog.Models.DAL
{
public class BlogInitializer : DropCreateDatabaseIfModelChanges<BlogContext>
{
protected override void Seed(BlogContext context)
{
var posts = new List<Post>
{
new Post { Title="sadasd", Content="asdsa", Date=DateTime.Parse("2016-11-13"), Language="pl" },
new Post { Title="sadasd", Content="asdsa", Date=DateTime.Parse("2016-11-13"), Language="pl" },
new Post { Title="sadasd", Content="asdsa", Date=DateTime.Parse("2016-11-13"), Language="pl" },
new Post { Title="sadasd", Content="asdsa", Date=DateTime.Parse("2016-11-13"), Language="pl" },
new Post { Title="sadasd", Content="asdsa", Date=DateTime.Parse("2016-11-13"), Language="pl" }
};
posts.ForEach(p => context.Posts.Add(p));
context.SaveChanges();
}
}
}
EF failed to instantiate the database initalizer for your DbContext instance named BlogContext.
In the configuration you have configured the correct value for type of your database initializer which is correct:
<contexts>
<context type="Blog.Models.DAL.BlogContext, Blog">
<databaseInitializer type="Blog.Models.DAL.BlogInitializer, Blog" />
</context>
</contexts>
The issue is that you're not using the correct assembly name Blog. You need to go to your project "Properties" via the context menu or Alt + F4 and copy the correct assembly name. Then put it like this:
<contexts>
<context type="Blog.Models.DAL.BlogContext, [The correct assemblby name]">
<databaseInitializer type="Blog.Models.DAL.BlogInitializer, [The correct assemblby name]" />
</context>
</contexts>

How do I get log4net to decrypt an encrypted connection string from the web.config?

The web application I'm working on uses log4net for logging. A requirement of the project is that the connections strings should be encrypted. How do I tell log4net to use the decrypted value?
For example:
<log4net>
<root>
<level value="Debug"/>
<appender-ref ref="AdoNetAppender"/>
</root>
<appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
<bufferSize value="1"/>
<connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<connectionString value="encryptedconnectionstringhere=="/>
Is there a way to accomplish this?
When implementing drumboog's answer, I ran into stackoverflow exceptions due to an infinitely recursive method call. This is essentially what I ended up using.
public class CustomAdoNetAppender : AdoNetAppender
{
private string _connectionString;
protected override string ResolveConnectionString(out string connectionStringContext)
{
if(string.IsNullOrEmpty(_connectionString))
{
var decrypt = new MyDecyptionLib();
_connectionString = decrypt.MyDecryptionFunction(ConfigurationManager.AppSettings["Connection"]);
}
connectionStringContext = _connectionString;
return connectionStringContext;
}
}
...and in the log4net config section
<appender name="AdoNetAppender" type="My.Name.Space.To.CustomAdoNetAppender">
Aside from writing a custom appender, you could encrypt the entire configuration section:
http://msdn.microsoft.com/en-us/library/zhhddkxy.aspx
Programmatically encrypting a config-file in .NET
Edit:
log4net is open source, so you can also try looking through their code and customizing their appender to fit your needs... maybe something like this:
public class DecryptConnectionStringAdoNetAppender : AdoNetAppender
{
protected override string ResolveConnectionString(out string connectionStringContext)
{
string result = base.ResolveConnectionString(out connectionStringContext);
if (String.IsNullOrEmpty(result))
{
return result;
}
else
{
Decrypt(result);
}
}
private string Decrypt(string encryptedValue)
{
// Your code goes here.
}
}
Then update the type attribute of the appender element in the config file:
<appender name="AdoNetAppender" type="Your.Namespace.DecryptConnectionStringAdoNetAppender">

Unity [Dependency] attribute doesn't resolve

I have the following code, and when I try to run it, I can see that the BrokerProvider is not being resolved. Here is my code:
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
new TestBroker().RunTestBroker();
}
class TestBroker
{
private IBrokerProvider brokerProvider;
public void RunTestBroker()
{
List<IPortfolio> portfolios = BrokerProvider.GetPortfolios();
}
[Dependency]
public IBrokerProvider BrokerProvider
{
get { return brokerProvider; }
set { brokerProvider = value; }
}
}
The related config
<unity>
<typeAliases>
<typeAlias alias="string" type="System.String, mscorlib" />
<typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
<typeAlias alias="IBrokerProvider" type="PA.Common.Interfaces.IBrokerProvider, PA.Common" />
<typeAlias alias="PManager" type="PA.BrokerProviders.PManager, PA.BrokerProviders" />
</typeAliases>
<containers>
<container>
<types>
<type type="IBrokerProvider" mapTo="PManager">
<lifetime type="singleton" />
</type>
</types>
</container>
</containers>
</unity>
Another question: Do I need to repeat the same 3 lines of code that I have under main in every other class that I would like to use unity or setting it up once is enough?
That's because are creating TestBroker directly by calling operator new on it:
new TestBroker().RunTestBroker();
In order for unity to resolve your dependencies you need to call the framework like so:
var broker = container.Resolve<TestBroker>();
IUnityContainer is the interface that is going to be doing all the work for you - i.e. resolving types to instances. You only need to create it once and then just pass it around the application where ever you need it.

Resources