What I'm looking for is a way to dismiss the keyboard while maintaining focus on the Entry field so an external keyboard can be used without giving up all that screen real estate.
I can't seem to implement the code in How to dismiss keyboard on button press in Xamarin Forms I'm calling it in the OnFocused of an Entry field.
Android.App.Application.Context as Activity;
is returning null Without the "as activity it works just fine, but I can't run
.CurrentFocus?.WindowToken;
on a Context.
This is what my code looks like.
[assembly: Dependency (typeof (KeyboardInteractions))] namespace namespace.Droid
{
public class KeyboardInteractions : IKeyboardInteractions
{
public void HideKeyboard()
{
var activity = Android.App.Application.Context as Activity;
var token = activity?.CurrentFocus?.WindowToken;
var i = (InputMethodManager)Android.App.Application.Context.GetSystemService(Context.InputMethodService);
if (token != null) {
i.HideSoftInputFromWindow(token, 0);
}
}
}
}
Use the CurrentActivityPlugin.
Once you have the plugin setup you can use it like this :
var activity = CrossCurrentActivity.Current?.Activity;
In case of queries feel free to revert.
Related
I am working on a Xamarin Forms project for which one requirement is to recognize certain key presses to trigger hot key actions. The devices that we will be deploying the application to have physical keyboards attached. For now, Android is the only platform that is being targeted.
From some research that I did yesterday afternoon, it sounds as though a custom page renderer is what is required. As I played with this concept this morning, I stumbled upon the On* key methods of the Activity class.
I tried adding the following to the MainActivity class in the Android project:
public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
return base.OnKeyUp(keyCode, e);
}
Placing a breakpoint on this method seems to show that this code is what is needed (read, this method is fired whenever I press a key on the keyboard).
The issue is that this method is also fired when an Entry control on the page has focus. Shouldn't the key press be handled by the Entry control and not bubbled up to the page?
Generally speaking, is this the right approach for what I am trying to accomplish? Are there other approaches that someone can point me to that might work better?
When I was working with hardware devices, I had to do something similar. I created a custom renderer for an entry on the Xamarin.android side. This captures the Enter key press for both hard and soft key in different events. I think creating custom render for a page like you did could work too but this only captures key presses for elements that are in focus. This works for me as I have the entry in focus when user presses the hardware Enter key.
[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Project.Droid.Controls {
public class CustomEntryRenderer : EntryRenderer {
public CustomEntryRenderer(Context context) : base(context) {
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) {
base.OnElementChanged(e);
Control.EditorAction += Control_EditorAction;
Control.KeyPress += NativeEditText_KeyPress;
}
// Fires only for Soft Keyboard
private void Control_EditorAction(object sender, Android.Widget.TextView.EditorActionEventArgs e) {
if (e.ActionId == ImeAction.Done) {
// your code
e.Handled = true;
}
}
// Fires for Hard Keyboard
private void NativeEditText_KeyPress(object sender, KeyEventArgs e) {
if (e.KeyCode == Keycode.Enter && e.Event.Action == KeyEventActions.Up) {
// your code
e.Handled = true;
}
else
e.Handled = false;
}
}
}
FYI: I also tried the MainActivity event that you are using and it did not work for me. I cannot recall why.
I'm trying to make use of the new SearchHandler implemented as part of Xamarin Forms 4. I've found it pretty easy so far to get suggestions populated but now I want to raise an event, or follow the suggested method of handling when a search is confirmed.
public class FoodSearchHandler: SearchHandler
{
IFoodDataStore dataStore = new FoodDataStore();
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
// What to do here?
}
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);
if(!string.IsNullOrWhiteSpace(newValue)
{
// Populate suggestions
ItemsSource = dataStore.GetSuggestions(newValue);
}
else
{
ItemsSource = null;
}
}
}
public partial class FoodsPage : ContentPage
{
ObservableCollection<Food> Foods = new ObservableCollection<Food>();
public ItemsPage()
{
InitializeComponent();
// Wire up the search handler
Shell.SetSearchHandler(this, new FoodSearchHandler());
BindingContext = this;
}
}
Unfortunately, althought the alpha docs mention the search handler they don't contain any details on how to use it and the sample apps only demonstrate populating the suggestions.
Does anyone out there have a pointer to offer on how I should be notifying my ContentPage that my SearchHandler confirmed a search?
So, after reading the Shell docs some more, it seems what I want to do in this situation is use of Shell's new Navigation and navigate to a route passing the search text as a query, for example:
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
var shell = Application.Current.MainPage as Shell;
shell.GoToAsync($"app:///fructika/search?query={Query}", true);
}
N.B. It doesn't look like passing data works right now or if it does I'm doing it wrong but I'll raise a separate question about that.
I have a cross-platform mobile app (Android/iOS) which implements the generic WebView control. This works well for most circumstances, but some iOS users complain that, when attempting to load a certain resource-intensive web page, the app "goes black" and then focus returns to the main menu view. My suspicion is that the app is choking due to the amount of content and processing overhead of the web page, but frankly this is a blind guess and I don't have the resources (such as an iPhone at my disposal) in order to verify this. Using an iPhone simulator on a Mac does not reproduce the "black screen" issue.
Therefore, I am attempting to implement in parallel WkWebView for iOS devices at version 8.0 and above as this is presumably more performant and might alleviate the problem. It is just about working, but there seems to be a disconnect between the ViewController and ContentPage which is supposed to host the WkWebView control which I have been unable to rectify.
Below is the general implementation:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WkWebViewPage : ContentPage
{
public WkWebViewPage (string url, string title)
{
InitializeComponent();
Title = title;
App.GetWkWebView(this, url);
}
}
The markup for WkWebPageView has no inner content.
The method App.GetWkWebView is a delegate of type Action implemented as a static property in the main App class. This is assigned in the FinishedLaunching method of the AppDelegate class (iOS project) to a static method in the static class I am using to manage invoking the WkWebView. This class is implemented as such:
public static class WkWebViewController
{
private static WKWebView _wkWebView;
public static void GetWkWebView(Page parentView, string url)
{
if(_wkWebView == null)
{
// INSERT ATTEMPTED APPROACHES BELOW HERE
var frame = view.Frame;
var cgRect = new CoreGraphics.CGRect(frame.X, frame.Y, frame.Width, frame.Height);
_wkWebView = new WKWebView(cgRect, new WKWebViewConfiguration());
view.AddSubview(_wkWebView);
// NavigationDelegate is a custom class; not germane to the issue
_wkWebView.NavigationDelegate = new NavigationDelegate();
}
var nsUrl = new NSUrl(url);
var request = new NSUrlRequest(nsUrl);
_wkWebView.LoadRequest(request);
}
}
Here is where the trouble begins. I have tried two approaches to obtaining the appropriate ViewController -- and more pertinently, the UIView object:
1)
var renderer = Platform.GetRenderer(parentView);
if (renderer == null)
{
renderer = Platform.CreateRenderer(parentView);
Platform.SetRenderer(parentView, renderer);
}
var view = renderer.ViewController.View;
This results in the following:
(source: aquaspy.com)
The content area is white/blank. The http request is submitted successfully as a 200 response is received. Note that the navigation bar above the content area properly displays.
2)
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
var view = vc.View;
Which results in:
(source: aquaspy.com)
In this case, the web page displays; however, the WkWebView control takes up the entire screen, obscuring the navigation bar (and seemingly the Status Bar).
Any suggestions would be greatly appreciated!
I have a MainPage in PCL which then navigates to a Platform Specific
Login Page on LoginButton Click Event
The LoginPage is inherited from
platform specific PageRenderer as it needs platform specific
Authentication to Social Providers (Facebook, Google, Twitter,
Microsoft, etc.)
I am using Xamarin.Auth to do the authentication.
Inside the OnElementChanged event of the LoginPage, it instantiates
the OAuth2Authenticator object.
Upon successful instantiation (based
on provider and app details), it needs to call the UI of the specific
provider.
To do that, I call the auth.GetUI where auth is
Xamarin.Auth.OAuth2Authenticator object.
I have two questions:
In UWP, how do I navigate to the provider login UI? More specifically, what is the equivalent in UWP of the following code snippets in iOS and Android? In iOS, the following code is used:
PresentViewController(auth.GetUI(), true, null);
where auth is Xamarin.Auth.OAuth2Authenticator object.
In Android the following is used:
activity.StartActivity(auth.GetUI(activity));
I am looking for the equivalent code in UWP. Please bear in mind that these calls are made from the LoginPage which is derived from Platform specific PageRenderer
How do I navigate back to my MainPage (which is in PCL) upon successful authentication?
The code is based off of a sample from the following source:
http://www.c-sharpcorner.com/article/oauth-login-authenticating-with-identity-provider-in-xamarin-forms/
Here is my code for the LoginPage:
using System;
using Valufy;
using Xamarin.Forms.Platform.UWP;
using System.ComponentModel;
using Valufy.UWP;
using Valufy.AuthConfiguration;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(ProviderLoginPage), typeof(LoginRenderer))]
namespace Valufy.UWP
{
class LoginRenderer: PageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);
//Get and Assign ProviderName from ProviderLoginPage
ProviderLoginPage loginPage = (ProviderLoginPage)Element;
string providername = loginPage.ProviderName;
//Create OauthProviderSetting class with Oauth Implementation .Refer Step 6
OAuthProviderSetting oauth = new OAuthProviderSetting();
Xamarin.Auth.OAuth2Authenticator auth = oauth.LoginWithProvider(providername);
// After facebook,google and all identity provider login completed
auth.Completed += Auth_Completed;
Type page_type = auth.GetUI();
//////THIS IS WHERE I AM STUCK...HOW DO I GO TO THE PROVIDER AUTH UI ////////////
//this.Frame.Navigate(page_type, auth);
//parentPage.Navigation.PushModalAsync(auth.GetUI());
}
}
private void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
OAuthConfig.User = new UserDetails();
// Get and Save User Details
OAuthConfig.User.Token = e.Account.Properties["oauth_token"];
OAuthConfig.User.TokenSecret = e.Account.Properties["oauth_token_secret"];
OAuthConfig.User.TwitterId = e.Account.Properties["user_id"];
OAuthConfig.User.ScreenName = e.Account.Properties["screen_name"];
/////NOW, HOW GO I GO BACK TO THE CALLING PAGE IN PCL ///////////////////
}
else
{
// The user cancelled
/////NOW, HOW GO I GO BACK TO THE CALLING PAGE IN PCL ///////////////////
}
}
}
}
Here is the code to navigate to the provider login for UWP:
WindowsPage windowsPage = new WindowsPage();
_frame = windowsPage.Frame;
if (_frame == null)
{
_frame = new Windows.UI.Xaml.Controls.Frame
{
Language = global::Windows.Globalization.ApplicationLanguages.Languages[0]
};
windowsPage.Content = _frame;
SetNativeControl(windowsPage);
}
Type pageType = auth.GetUI();
_frame.Navigate(pageType, auth);
To navigate back to my page upon successful authentication, here is the code:
private async void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
var request = new OAuth2Request("GET", new Uri("https://login.microsoftonline.com/common/oauth2/V2.0/token?oauth2_access_token=" + e.Account.Properties["access_token"]), null, e.Account);
try
{
string response = await MSGetUserInfo(e.Account);
}
catch (System.OperationCanceledException)
{
}
catch (Exception ex)
{
}
this.Element.Navigation.PushModalAsync(new MainPage());
}
else
{
// The user cancelled
}
}
1- In UWP, how do I navigate to the provider login UI
You need to create a UWP Page object, the same object you are will use to display in the renderer is the one you will use to do the navigation.
Type page_type = auth.GetUI();
page = new MyUWPLoginPage();
page.Frame.Navigate(page_type, auth);
2- How do I navigate back to my MainPage (which is in PCL) upon successful authentication?
There are many ways to do this, the easiest one is creating a public method in your ProviderLoginPage class and from the renderer classes call this method and pass-in the parameters.
public class ProviderLoginPage: ContentPage
{
......
public void AuthenticationCompleted(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
// Do your logic
}
}
In your Renderer using the Element:
private void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
var xamElement = Element as ProviderLogin;
xamElement?.AuthenticationCompleted(sender, e);
}
Move all the logic to the PCL class so you don't have to repeat it on each renderer.
This should help.
Now I am using Gluon plugin and it is very helpful to start working with JavaFXPorts. I have my application ready and I can use it on computers and phones. I tested on phone with system android. Application is working good but only with my server.
I take care of the different resolutions and I think that it is good for now.
I want to have Turn-based Multiplayer Application but I have still big problem with using Google Play Service. Tutorial which show how to use this services for turn-based multiplayer application is written in pure android and use Activity. My question is maybe very simple but If I have my application view from "fxml" how to use it as an tutorial Activity?
I want to do auto-matching for my application and next I want override method takeTurn() to suit it to my application.
For example, how can I change thing like that (code below) to application in JavaFX?
I must use google services from my JavaFX(src/main/java folder) class in addition AndroidPlatformProvider.java and all methods must be in src/android/java folder. I know that I must use PlatformService and PlatformProvider. I did it as in the examples: HelloPlatform and SMSTracker .
I use methods from my interface PlatformProvider but application still crashes. :(
I only use Provider from my code of JavaFX and I don't have android Activity. I don't know how to use these method without Activity or View:
- public void onActivityResult(int request, int response, Intent data)
- public void playTurn(View view)
Can I call from google service methods to methods from my controller for view (fxml).
I don't know how these methods should working with JavaFX.
public class TbmpGameActivity extends Activity {
...
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
...
if (request == RC_SELECT_PLAYERS) {
if (response != Activity.RESULT_OK) {
// user canceled
return;
}
// Get the invitee list.
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// Get auto-match criteria.
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
TurnBasedMatchConfig tbmc = TurnBasedMatchConfig.builder()
.addInvitedPlayers(invitees)
.setAutoMatchCriteria(autoMatchCriteria)
.build();
// Create and start the match.
Games.TurnBasedMultiplayer
.createMatch(mGoogleApiClient, tbmc)
.setResultCallback(new MatchInitiatedCallback());
}
}
}
or something like that:
// Call this method when a player has completed his turn and wants to
// go onto the next player, which may be himself.
public void playTurn(View view) {
// Get the next participant in the game-defined way, possibly round-robin.
String nextParticipantId = getNextParticipantId();
// Get the updated state. In this example, we simply retrieve a
// text string from the view. In your game, there may be more
// complicated state.
mTurnData = mDataView.getText().toString();
// At this point, you might want to show a waiting dialog so that
// the current player does not try to submit turn actions twice.
showSpinner();
// Invoke the next turn. We are converting our data to a byte array.
Games.TurnBasedMultiplayer
.takeTurn(mGoogleApiClient, mMatch.getMatchId(),
mTurnData.getBytes(Charset.forName("UTF-16")),
nextParticipantId)
.setResultCallback(this);
}
Latest version of the jfxmobile plugin (1.0.3) includes recent changes in Dalvik SDK that contains the port of JavaFX 8 to android.
In FXActivity class, it has been added a very convenient method to listen to results of intents: setOnActivityResultHandler().
This means you shouldn't add new activities to your app, and only use FXActivity. You should add an Intent to it, and set a proper result handler to the FXActivity.
Have a look at this recent post at Gluon's site. It explains how to access native services, based on the case of taking a picture with the device's camera, and waiting to get the result of it to resume the app.
Basically, this is what it is required in this case:
public void takePicture() {
// create intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
...
// Add result handler
FXActivity.getInstance().setOnActivityResultHandler((requestCode, resultCode, data) -> {
if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK) {
...
}
});
// launch activity
FXActivity.getInstance().startActivityForResult(intent, TAKE_PICTURE);
}
Try this approach with your app.