Xamarin Android Share Link/Text via social media from custom renderer - xamarin

I wan't to share a link via social media from custom renderer
public class CustomActions : ICustomActions
{
Context context = Android.App.Application.Context;
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
context.StartActivity(Intent.CreateChooser(sharingInt, "Share via"));
}
}
This error occur
Android.Util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
even when I added the below code I still get same error
sharingInt.AddFlags(ActivityFlags.NewTask);

The problem is that Intent.CreateChooser creates yet another Intent. What you want to do is to set the flag on this new intent:
public void ShareThisLink()
{
Intent sharingInt = new Intent(Android.Content.Intent.ActionSend);
sharingInt.SetType("text/plain");
string shareBody = "https://www.google.com";
sharingInt.PutExtra(Android.Content.Intent.ExtraSubject, "Subject");
sharingInt.PutExtra(Android.Content.Intent.ExtraText, shareBody);
var intent = Intent.CreateChooser(sharingInt, "Share via");
intent.AddFlags(ActivityFlags.NewTask);
context.StartActivity(intent);
}
Alternatively to avoid the need to do this, you could cache the MainActivity instance Xamarin.Forms uses:
public MainActivity
{
public static MainActivity Instance {get;private set;}
protected override void OnCreate(Bundle bundle)
{
Instance = this;
...
}
}
And then use the Instance as the Context in your code instead of the Application.Context

Related

Replace default IoC container in MvvmCross

Since MvvmCross v7 sticks on its own IoC container, I would like to replace it with the .NET Core one in order to have an easier life when registering third party libraries such as IHttpClientFactory, Polly, Automapper, etc. through already built-in extensions methods.
To achieve this, I've successfully created a class that implementas MvxSingleton<IMvxIoCProvider> described as follow:
public class HostingAdapter : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider
{
private IServiceProvider ServiceProvider;
private IServiceCollection ServiceCollection;
public HostingAdapter()
{
var host = Host
.ConfigureServices((context, serviceCollection) =>
{
// Configure local services
ConfigureServices(context, serviceCollection);
ServiceCollection = serviceCollection;
ServiceProvider = ServiceCollection.BuildServiceProvider();
})
.Build();
}
public void RegisterType<TFrom, TTo>() where TFrom : class where TTo : class, TFrom
{
ServiceCollection.AddTransient<TFrom, TTo>();
ServiceProvider = ServiceCollection.BuildServiceProvider();
}
public T GetSingleton<T>() where T : class
{
return ServiceProvider.GetRequiredService<T>();
}
public object GetSingleton(Type type)
{
return ServiceProvider.GetRequiredService(type);
}
.. and all the required methods requested by the interface.
Then on the platform specific side I override the IoC creation as follow:
protected override IMvxIoCProvider CreateIocProvider()
{
var hostingAdapter = new HostingAdapter();
return hostingAdapter;
}
The code seems to work but as soon as the app starts Mvx registers its own "extra" services such as the IMvxLoggerProvider, IMvxSettings and so on. And here issues come:
ServiceProvider = ServiceCollection.BuildServiceProvider(); is called during the Host initialization but Mvx still continue to register services after that. This means IServiceProvider is not 'in sync' with IServiceCollection and a new ServiceCollection.BuildServiceProvider(); call is needed. I temporarily solved updating the provider at each collection registration (like the code above) but I'm aware this affects performances. Anyone knows how to workaround this?
There are plenty of Mvx services that are not registered so the app fails to start. These are the IMvxLogProvider, IMvxAndroidLifetimeMonitor, IIMvxSettings, IMvxStart, etc. I just wonder, why? How can let Mvx handle the registration in my container of all what it needs to start? I partially solved some of them such as the logger thing replacing the default with a custom one, but other callbacks like InitializeLifetimeMonitor are called too late for being registered.
Do I need to change anything in my MvxApplication than the most standard implementation?
Am I really forced to replace the standard IoC container? How can I handle the IServiceCollection's extension methods that 3rd party libraries expose like services.AddHttpClient();?
If it needs, I am on Xamarin classic using the Droid platform. Thanks
Deliberately inspired by Unity.Microsoft.DependencyInjection repository I've workarounded this approaching the problem the other way round: instead of replacing the default IoC container, I manually initialize an IServiceCollection instance and I add it to the Mvx's IoC provider.
To achieve this, I've used the following code:
public class App : MvxApplication
{
public override void Initialize()
{
base.Initialize();
InitializeServiceCollection();
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<HomeViewModel>();
}
private static void InitializeServiceCollection()
{
IServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MapServiceCollectionToMvx(serviceProvider, serviceCollection);
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddHttpClient();
}
private static void MapServiceCollectionToMvx(IServiceProvider serviceProvider,
IServiceCollection serviceCollection)
{
foreach (var serviceDescriptor in serviceCollection)
{
if (serviceDescriptor.ImplementationType != null)
{
Mvx.IoCProvider.RegisterType(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationType);
}
else if (serviceDescriptor.ImplementationFactory != null)
{
var instance = serviceDescriptor.ImplementationFactory(serviceProvider);
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, instance);
}
else if (serviceDescriptor.ImplementationInstance != null)
{
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationInstance);
}
else
{
throw new InvalidOperationException("Unsupported registration type");
}
}
}
}

How to handle end of conversation to start another dialog in QnA multi turn - Microsoft.Bot.Builder.AI.QnA.Dialogs.QnAMakerDialog

I have implemented multi turn QnA in our bot and using this class Microsoft.Bot.Builder.AI.QnA.Dialogs.QnAMakerDialog.
Now, I want to extend its functionality so that after mutli turn conversation, bot can ask user if the conversation helped or not? if not then bot will ask to log a ticket with help desk.
I am able to catch the end of multi turn dialog by overriding the Dialog.EndDialogAsync method but not able to start another dialog from there. Please help.
public class QnAMultiTurnBase : QnAMakerDialog
{
// Dialog Options parameters
public readonly string DefaultNoAnswer = Configuration.Messages("Troubleshoot", "NoAnswer");//"No QnAMaker answers found.";
public readonly string DefaultCardTitle = Configuration.Messages("Troubleshoot", "DidYouMean");//"Did you mean:";
public readonly string DefaultCardNoMatchText = Configuration.Messages("Troubleshoot", "NoneOfTheAbove");//"None of the above.";
public readonly string DefaultCardNoMatchResponse = Configuration.Messages("Troubleshoot", "Feedback");//"Thanks for the feedback.";
private readonly BotServices _services;
private readonly IConfiguration _configuration;
//private readonly IStatePropertyAccessor<Dictionary<string, string>> troubleshootQuery;
private readonly Dictionary<string, string> qnaPair = new Dictionary<string, string>();
private readonly string qnAMakerServiceName;
/// <summary>
/// Initializes a new instance of the <see cref="QnAMakerBaseDialog"/> class.
/// Dialog helper to generate dialogs.
/// </summary>
/// <param name="services">Bot Services.</param>
public QnAMultiTurnBase(BotServices services, IConfiguration configuration, string qnAMakerServiceName) : base()
{
this._services = services;
this._configuration = configuration;
this.qnAMakerServiceName = qnAMakerServiceName;
}
protected async override Task<IQnAMakerClient> GetQnAMakerClientAsync(DialogContext dc)
{
return this._services?.QnAServices[qnAMakerServiceName];
}
protected override Task<QnAMakerOptions> GetQnAMakerOptionsAsync(DialogContext dc)
{
return Task.FromResult(new QnAMakerOptions
{
ScoreThreshold = DefaultThreshold,
Top = DefaultTopN,
QnAId = 0,
RankerType = "Default",
IsTest = false
});
}
protected async override Task<QnADialogResponseOptions> GetQnAResponseOptionsAsync(DialogContext dc)
{
var noAnswer = (Activity)Activity.CreateMessageActivity();
noAnswer.Text = this._configuration["DefaultAnswer"] ?? DefaultNoAnswer;
var cardNoMatchResponse = MessageFactory.Text(DefaultCardNoMatchResponse);
var responseOptions = new QnADialogResponseOptions
{
ActiveLearningCardTitle = DefaultCardTitle,
CardNoMatchText = DefaultCardNoMatchText,
NoAnswer = noAnswer,
CardNoMatchResponse = cardNoMatchResponse,
};
return responseOptions;
}
public override Task EndDialogAsync(ITurnContext turnContext, DialogInstance instance, DialogReason reason, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
// end of multi turn convversation
// ask if conversation helped the user or not
}
catch (Exception)
{
turnContext.SendActivityAsync(MessageFactory.Text(Configuration.Messages("UnknownError"))).Wait();
throw;
}
return base.EndDialogAsync(turnContext, instance, reason, cancellationToken);
}
}
Add a new dialog and initiate the dialog added using BeginDialogAsync:
AddDialog(new MoreHelp());
return await stepContext.BeginDialogAsync(nameof(MoreHelp), UserInfo, cancellationToken);
You can refer to this documentation where it specifies how to create your own prompts to gather user input. A conversation between a bot and a user often involves asking (prompting) the user for information, parsing the user's response, and then acting on that information.
Dialog actions – ability to control dialogs, BeginDialog, RepeatDialog, GotoDialog, EndDialog, etc.
Please follow the below for multi turn.
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/05.multi-turn-prompt

MVVMCross 5.3.2 UWP: Where to Get IMvxWindowsFrame for MvxFormsUwpViewPresenter

I'm working out of the Xamarin Forms for MVVMCross 5 Solution Template and updated the packages to the latest version (5.3.2 for MVVMCross). Doing that changes some namespaces around particularly in the UWP project.
It seems that I need to resolve IMvxViewPresenter as MvxFormsUwpViewPresenter which takes a IMvxWindowsFrame as an argument. In the setup file method of Setup.cs there's a XamlControls.Frame rootFrame passed as an argument but I'm not sure if that's suppose to be cast somehow as IMvxWindowsFrame.
Where can you pull the object that implements IMvxWindowsFrame from or is there another way to turn the rootFrame into an IMvxWindowsFrame legitimately.
public class Setup : MvxFormsWindowsSetup
{
private readonly LaunchActivatedEventArgs _launchActivatedEventArgs;
public Setup(XamlControls.Frame rootFrame, LaunchActivatedEventArgs e) : base(rootFrame, e)
{
_launchActivatedEventArgs = e;
// Mvx.RegisterSingleton<IMvxWindowsFrame>(rootFrame);
}
protected override void InitializeFirstChance()
{
base.InitializeFirstChance();
Mvx.RegisterSingleton<Core.Services.ILocalizeService>(new Services.LocalizeService());
Mvx.RegisterSingleton<ISettings>(CrossSettings.Current);
Mvx.RegisterType<IMvxViewPresenter, MvxFormsUwpViewPresenter>();
}
protected override MvxFormsApplication CreateFormsApplication()
{
return new Core.FormsApp();
}
protected override IMvxApplication CreateApp()
{
return new Core.MvxApp();
}
protected override IMvxTrace CreateDebugTrace()
{
return new Core.DebugTrace();
}
}
public sealed partial class MainPage : WindowsPage
{
public MainPage()
{
this.InitializeComponent();
var start = Mvx.Resolve<IMvxAppStart>();
start.Start();
var presenter = Mvx.Resolve<IMvxViewPresenter>() as MvxFormsUwpViewPresenter;
LoadApplication(presenter.FormsApplication);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}
}
EDIT: I've been looking more into the class MvxFormsWindowsSetup in the source code at https://github.com/MvvmCross/MvvmCross/blob/develop/MvvmCross-Forms/MvvmCross.Forms.Uwp/Platform/MvxFormsWindowsSetup.cs. It appears that in the method CreateViewPresenter that the IMvxViewPresenter is registered as a singleton with the MvxWrappedFrame already inside but by default the code does not resolve when calling var presenter = Mvx.Resolve() as MvxFormsUwpViewPresenter; in the windows page. Possible bug? Trying to see if I can resolve it myself.
Looks like it fails to resolve even if I put the code right after when Mvx is suppose to register the type / singleton
protected override IMvxWindowsViewPresenter CreateViewPresenter(IMvxWindowsFrame rootFrame)
{
var presenter = new MvxFormsUwpViewPresenter(rootFrame, FormsApplication);
Mvx.RegisterSingleton<IMvxFormsViewPresenter>(presenter);
var presenter2 = Mvx.GetSingleton<IMvxViewPresenter>() as MvxFormsUwpViewPresenter;
return presenter;
}
When updating to MvvmCross 5.3.2 for UWP, the presenter needs to resolve as IMvxFormsViewPresenter rather than IMvxViewPresenter. Change the interface type and it should load properly.
public MainPage()
{
this.InitializeComponent();
var start = Mvx.Resolve<IMvxAppStart>();
start.Start();
var presenter = Mvx.Resolve<IMvxFormsViewPresenter>() as MvxFormsUwpViewPresenter;
LoadApplication(presenter.FormsApplication);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}

How to monitor the changes in the Bluetooth Connectivity state changes from Xamarin.Forms

Need to check the bluetooth connection to a remote device exists or got disconnected. Its basically a Forms which mainly targets Android and UWP.
I tried with the Dependency services and made the implementation in Android as below,
_[assembly: Xamarin.Forms.Dependency(typeof(BluetoothListenerActivity))]
namespace demotool.Droid
{
public class BluetoothListenerActivity : Activity,IBluetoothListener
{
public event EventHandler OnDeviceDisconnected;
public static BluetoothListenerActivity mySelf;
//string device;
public void start()
{
mySelf = this;
BluetoothStatusBroadCast mreceiver = new BluetoothStatusBroadCast();
IntentFilter mfilter = new IntentFilter(BluetoothDevice.ActionAclDisconnected);
Forms.Context.RegisterReceiver(mreceiver,mfilter);
}
public void receivedstatuschangd(string devicename,string state)
{
OnDeviceDisconnected(this, new DeviceDisconnectedEventArgs(name: devicename,status: state));
}
}
}_
BroadcastReceiver:
namespace Demo.Droid
{
[BroadcastReceiver]
class BluetoothStatusBroadCast : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
BluetoothDevice device =(BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
BluetoothListenerActivity.mySelf.receivedstatuschangd(device.Name, intent.Action);
}
}
}
Xamarin Forms Part:
_ protected override void OnStart()
{
IBluetoothListener bluetoothlistener = DependencyService.Get();
bluetoothlistener.start();
bluetoothlistener.OnDeviceDisconnected += Bluetoothlistener_OnDeviceDisconnected;
}
private void Bluetoothlistener_OnDeviceDisconnected(object sender, DeviceDisconnectedEventArgs e)
{
Page page1 = new Page();
page1.DisplayAlert(e.Name+ " " +e.Status, "Alert", "OK");
}_
The Intent Action that I have registered- BluetoothDevice.ActionAclDisconnected, is getting triggered once the Pairing is completed or a connection request is made, which I assume is not the actual Disconnection of the devices
Is there any common plugin which monitors the Bluetooth Connectivity Changes to a remote device. Or could you please tell me the actual Intent Action that I should listen for.
Thanks in Advance !

How to run code on Application opens in Android?

My Android app need the user to create an account to be able to use the app. The account info is stored in SQLite database. When the application starts I check if the user has an account, if not I show a sign up activity for the user.
Now I get reports from users that they sometimes comes to the sign up activity even if they've already created an account. This happens when they've closed the application and reopen it again.
This is the code I'm using and I need to figure out what the problem might be:
//MyApplication.java
public class MyApplication extends Application {
private DataBaseUtility dbu;
public boolean hasAccount;
#Override
public void onCreate() {
super.onCreate();
//Init sqlite database
this.dbu = new DataBaseUtility(this);
//This loads the account data from the database and returns true if the user has already created an account
this.hasAccount = loadAccount();
}
public boolean loadAccount() {
boolean loadedData = false;
String query = "SELECT data FROM tblaccount WHERE tblaccount.deleted=0";
Cursor cursor = this.dbu.getCursor(query);
if (cursor != null) {
while (cursor.moveToNext()) {
loadedData = true;
}
cursor.close();
}
return loadedData;
}
}
//MainActivity.java
public class MainActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyApplication application = (MyApplication)getApplication();
if (!application.hasAccount) {
//Take the user to the sign up activity
}
}
My idea is that maybe sometimes MainActivity.onCreate() runs before MyApplication.onCreate(). Can that be the case?
In application's onCreate, you are checking if the user has an account and setting a boolean.
You are checking in the MainActivity's onCreate if the user has an account through the application's boolean.
application's onCreate() executing before MainActivity's onCreate() is always the case! It is impossible for a different execution path to occur and since application's onCreate() does not have a Runnable it is a 100% garantuee.
Please make sure you're DataBaseUtility does not have any Runnables.
Anyway STILL there are several ways to reproduce the error! I will not state these now but you can know them when you see:
SOLUTION
MainActivity You have forgotten to update application.hasAccount upon successfull sign up~
public class MainActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyApplication application = (MyApplication)getApplication();
if (!application.hasAccount) {
//Take the user to the sign up activity
//if(successful) application.hasAccount = true
}
}
To avoid database exceptions
I use this:
REMARK It would be much better to use more strong persistent status saving for the database -i.e. SharedPreferences
boolean isOpened = false;
//When I need to open
if(!isOpened){
//open
isOpened = true;
}
//When I need to close
if(isOpened){
//close
isOpened = false;
}
onDestroy() { //every onDestroy
if(isOpened){
//close
}
}

Resources