Get current activity from Application.Context - MonoAndroid - android

I am currently developing an application using Xamarin.Forms that will be available on the Android and iOS platforms. When the application is first loaded on device, I check to see if there is an internet connection available on the device. I want to display a dialog box if an internet connection is not available.
Here is the following snippet of code I am using to check the internet on the Xamarin.Forms.ContentPage
if(App.Connectivity.IsNetworkConnectivityAvailable())
{
App.Notification.DisplayLocalNotifications("No Internet", "You need an internet connection to access certain application content");
}
I am using dependency injection to build the appropriate module for handling dialog boxes for each appropriate environment. The Android is throwing the following exception
Android.Views.WindowManagerBadTokenException: Unable to add window --
token null is not for an application Here is the code for the
DisplayLocalNotification method on the Android:
public void DisplayLocalNotification(string title, string content)
{
AlertDialog.Builder builder = new AlertDialog.Builder(Application.Context)
.SetTitle(title)
.SetMessage(content)
.SetCancelable(true)
.SetPositiveButton("OK", (EventHandler<DialogClickEventArgs>) null);
AlertDialog alert = builder.Create();
alert.Show();
var okBtn = alert.GetButton((int)DialogButtonType.Positive);
okBtn.Click += (sender, args) =>
{
alert.Dismiss();
};
}
After doing some research, I need to get pass the current activity to the AlertDialog.Builder constructor instead of the Application.Context. How do I get the current activity object from the application context when you need to the activity outside of the activity context?

Xamarin.Forms Android platform code should assign the current Activity into Forms.Context property. This is the static Forms class and if you debug it you will see that the Forms.Context is an Activity.
public static class Forms
{
public static Context Context { get; }
public static bool IsInitialized { get; }
public static event EventHandler<ViewInitializedEventArgs> ViewInitialized;
public static void Init(Activity activity, Bundle bundle);
}

Related

Difference between DialogPreference before and after AndroidX

We are currently migrating to Androidx namespace with our Android app project. However I noticed that not only the namespace seems to have changed. For DialogPreference also some interfaces which were using before are now missing
new interfaces: https://developer.android.com/reference/androidx/preference/DialogPreference
old interfaces: https://developer.android.com/reference/kotlin/android/preference/DialogPreference
For example the following methods seem to be missing: onBindDialogView, showDialog, onDialogClosed.
Since we use some of these methods to influence the default behavior of the dialog, it is unclear to me how I should realize this functionality now. For example we are validating the input before closing the dialog, we are saving the value in a database instead of the sharedpreferences and adding some dynamic elements to the dialog.
Has anyone else already encountered this problem and found a solution? Did I miss anything in the documentation? Is there another concept that we can / should use?
It would be possible to use Fragments instead of DialogPreference but for small amounts of content (e.g. a list of tree items, where the user can choose from) this seems to be a lot of overhead for me...
Starting from androidx source files, I've migrated custom classes based on old DialogPreference to new androidx.preference.DialogPreference with the following procedure:
Step 1
The old custom dialog class (e.g. CustomDialogPreference) based on legacy DialogPreference should be split into two separate classes:
One class (e.g. CustomPreference) should extend androidx.preference.DialogPreference and will contain only the code related to preference handling (data management).
Another class (e.g. CustomDialog) should extend androidx.preference.PreferenceDialogFragmentCompat and will contain only the code related to dialog handling (user interface), including onDialogClosed. This class should expose a static method newInstance to return an instance of this class.
Step 2
In the main fragment handling preferences based on PreferenceFragmentCompat the onDisplayPreferenceDialog method should be overridden to show the custom dialog, e.g.:
private static final String DIALOG_FRAGMENT_TAG = "CustomPreference";
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (getParentFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
if (preference instanceof CustomPreference) {
final DialogFragment f = CustomDialog.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
Instead of using DialogPreference, you can write your own custom Preference with an AlertDialog.
This may be a workaround for those who don't want to deal with the DialogPreference and PreferenceDialogFragmentCompat.
import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
public class CustomDialogPreference extends Preference {
private final Context context;
public CustomDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
#Override
protected void onClick() { //what happens when clicked on the preference
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("TITLE")
.setMessage("message")
.setPositiveButton("OK", (dialog, which) -> {
String preferenceKey = getKey(); //You can update the SharedPreference with the key
//....
})
.setNegativeButton("CANCEL", (dialog, which) -> {
//....
})
.create().show();
}
}
onClick() and getKey() methods belong to the Preference class. The context object comes with the constructor and so on..
The key can be defined, as other preferences, in xml file or programmatically in the PreferenceFragment.
<com.myApp.CustomDialogPreference
android:key="my_preference_key"
android:summary="..."
android:title="..." />
A little hack for everyone who (like me) do not completely understand how we should combine androidx.preference.DialogPreference and androidx.preference.PreferenceDialogFragmentCompat:
Step 1:
In your DialogFragment's onAttach() method get the value of your desired SharedPreference (get the key either from newInstance() method or just hardcore it inside) and save it as a variable. On the other hand, save your new value in SharedPreference before closing your DialogFragment. By doing so, you have created your "custom Preference".
Step 2:
Create empty androidx.preference.DialogPreference and use it inside your PreferenceScreen. Then combine it with your DialogFragment as described in 2nd step by #Livio:
private static final String DIALOG_FRAGMENT_TAG = "CustomPreference";
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
if (preference instanceof CustomPreference) {
final DialogFragment f = CustomDialog.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
By doing so, you will get the same result with only difference that you need to deal with SharedPreference yourself inside your DialogFragment.

How to display a dialog from a Library?

I'm writing a custom library, and I ran into a problem. I need to display a dialog to prompt a user to login one time. I can't seem to be able to display an AlertDialog from the Library. I get Unable to add window -- token is not for an application. I am getting the context of the application, by extending a custom library application class. This is the application class in the library:
public class MyCustomApplication extends Application {
private static MyCustomApplication instance;
/**
* Constructor.
*/
public MyCustomApplication () {
instance = this;
}
/**
* Gets the application context.
*
* #return the application context
*/
public static Context getContext() {
return instance;
}
And this is the code in the app that is using the library:
public class MyApplication extends MyCustomApplication {
#Override
public void onCreate(){
super.onCreate();
}
This is the code in the library that attempts to display a dialog:
public static void displayTerminatePopup(String title, String message){
int size = 14;
AlertDialog.Builder aboutBuilder = new AlertDialog.Builder(MyCustomApplication.getContext());
aboutBuilder
.setTitle(title)
.setMessage(message)
.setCancelable(false);
.setPositiveButton("Accept",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
dialog.cancel();
}
});
final AlertDialog aboutAlert = aboutBuilder.create();
aboutAlert.show();
TextView textView = (TextView) aboutAlert.findViewById(android.R.id.message);
textView.setTextSize(size);
}
The 'MyCustomApplication.getContext() is where I'm 99% sure the hang up is. I also tried passing a Context directly to the library, and that didn't work, either. Any help on how to show a dialog to allow a user to login via a library would be much appreciated. I've been searching and searching and havn't been able to find anything.
Thanks!

How can I display error messages in android with a custom class?

As the title suggests, I'm trying to create a class that will display an error message in android and my reasoning is that, instead of creating a pop up wherever it was needed as well as declaring the preset error strings in the MainActivity, it would be cleaner to create a separate class containing the preset info and the code necessary to actually display the message. The only parameter it would need would be the actual exception.
The way I tried to achieve this was by extending the AlertDialog.Builder class as follows:
public class showError extends AlertDialog.Builder
{
private enum exString
{
UnknownHostException, IOException,
ConnectException, Exception,
NoTimeTablesFoundException
}
private String UHE;
private String IOE;
private String CE;
private String NTTFE;
private String CNFE;
private String title;
private String E;
private String message ="";
private String exception="";
Context cxt;
exString enumExc;
public showError(Exception ex, Context cxt)
{
super(cxt);
this.cxt = cxt;
initString();
try
{
exception = ex.getClass().getName().toString();
exception = exception.substring(exception.lastIndexOf(".")+1);
enumExc = exString.valueOf(exception);
}
catch (IllegalArgumentException err)
{
enumExc = exString.valueOf("Exception");
}
setType();
setTitle(title);
setMessage(message);
setPositiveButton("OK", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
}
});
setIcon(android.R.drawable.ic_dialog_alert);
//show();
}
private void initString()
{
String UHE = cxt.getString(R.string.errorUHE);
String IOE = cxt.getString(R.string.errorIOE);
String CE = cxt.getString(R.string.errorCE);
String NTTFE = cxt.getString(R.string.errorNTTFE);
String title = cxt.getString(R.string.errorTitle);
String E = cxt.getString(R.string.errorUnknown);
}
private void setType()
{
switch (enumExc)
{
case UnknownHostException:
{ message = UHE; break; }
case IOException:
{ message = IOE; break; }
case ConnectException:
{ message = CE; break; }
case NoTimeTablesFoundException:
{ message = NTTFE; break; }
case Exception:
{ message = E + exception; break; }
}
}
}
The way I would like for this to work is:
new showError(whateverException, this);
However, for some reason I don't understand, it constantly gives a WindowLeaked exception?
I commented the show() command in the class and, instead, started using
new showError(whateverException, this).show();
and this seems to work sometimes but this also throws the WindowLeaked exception from time to time.
First of all, can someone shed some light as to why, since it is the same context, does it make a difference where I call the show command (in the constructor of the class itself, or by calling the .show() method)?
Why does calling show in the constructor always throw an exception, whereas calling it by using .show() of the instantiated class sometimes work?
I understand that this exception is thrown when the activity that creates the dialog ends before the dialog is dismissed. Is this correct?
And if it is, when I try using MainActivity as the context, am I supposed to believe that the MainActivity ends? How can it end while the app is still running?
ALso, if these dialogs are asynchronous (they are on a seperate thread from the UI thread so as to not block the UI thread, is my understanding correct?) how come they need to be dismissed before the activity ends?
Because, it seems, in order for my code to work, I would actually need to have the app wait for the dialog to be dismissed (sort of how you cannot use the main form of a Windows desktop app while a MessageBox is being displayed), but it is my understanding that android doesn't do that by design.
Even more confusing is if instead of using this approach, I create and show an AlertDialog.Builder in every catch block, that seems to work perfectly each time... why?
Second, I'm not exactly trying to find out how to fix the flaws of my design but what I actually am looking for is understanding why my design is flawed and what approach would be appropriate to achieve what I want?
Or better yet, what is the industry standard for handling exceptions and displaying the appropriate error messages to the user so that he knows what has gone wrong (whenever possible)?
Thank you for your time and excuse the lengthy read.

Get JavaScript variable in webview in Android

I am working on an e-learning project using SCROM APIs, but now I got stuck at one point: how to get server-side JavaScript value in my core Android activity IN WEBVIEW from scrom API?
I am trying below code:
public class MyJavaScriptInterface
{ Context mContext;
/** Instantiate the interface and set the context */
MyJavaScriptInterface(Context c) {
mContext = c;
}
/** retrieve the ids */
public void getbookmark(final String bookmarkId) {
webView.loadUrl("javascript:Android.getbookmark(BOOKMARKED_PAGE);");
//getWindow().requestFeature();
}
#SuppressWarnings("unused")
public void showHTML(String html)
{
new AlertDialog.Builder(myApp)
.setTitle("HTML")
.setMessage(html)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.create()
.show();
}
}
Do I have to take the value of onpagefinished() function of WebView?
You'll want to add a javascript interface:
mWebView.addJavascriptInterface(new MyJavaScriptInterface(getApplicationContent(), "JSInterface");
Add a method in your interface you want to call and ensure you have the #JavascriptInterface annotation so Android makes it callable:
#JavascriptInterface
public void showHTML(String html)
{
new AlertDialog.Builder(myApp)
.setTitle("HTML")
.setMessage(html)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.create()
.show();
}
Then follow the approach you are doing at the moment of calling a method in javascript:
webView.loadUrl("javascript:Android.getbookmark(BOOKMARKED_PAGE);");
And the javascript method would look something like:
window.Android.getbookmark = function(variable) {
var returnValue = getSomeValue(variable);
if(!window.JSInterface) {
console.error('window.JSInterface not defined - Did you inject the javascript interface in the native app?');
}
window.JSInterface.showHTML(returnValue);
};
Notice the reason we have window.JSInterface is because we added it with that name in:
mWebView.addJavascriptInterface(new MyJavaScriptInterface(getApplicationContent(), "JSInterface");
NOTE: In KitKat it is more efficient to use evaluateJavascript() than loadUrl, simplest form shown below, allow you can pass in a callback to get a return value (Demo in the sample code)
webView.evaluateJavascript("Android.getbookmark(BOOKMARKED_PAGE);", null);
There is a full JS Interface sample here which includes the new API's in KitKat: https://github.com/GoogleChrome/chromium-webview-samples
you can use javascript bridge for your requirement
you can find the source , this too

Using Android AlertDialog

I want to create a single class which I can call when I need to show an AlertDialog with the parameters and son on I want.
The problem is I dont know if that class have to be an Activity... the alertDialog needs an context, but I can send the current one, because what I want is to show the alert on the actual activity (not to create a new one, I want to show the alert on the actual screen). But I cant get it. I get errors sending the context of the actual activity...
Only I get working it when I create that class like an Activity, but with that, the alertDialog appears alone without the actual screen behind.
What Can I do? I don't know if I understand the contexts...
Thanks
Your class does not need to extend anything to produce a dialog. You can try this way to produce a static method that creates a dialog for you.
Make sure when you call your method you use THIS and not getApplicationContext()
MyDialogClass.getDialog(this); //good!
MyDialogClass.getDialog(getApplicationContext()); //results in error
That is likely the cause of your error
Example class:
public class MyDialogClass
{
public static AlertDialog getDialog(Context context)
{
Builder builder = new Builder(context);
builder.setTitle("Title").setMessage("Msg").setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id)
{
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id)
{
}
});
return builder.create();
}
}
AynscTask does not need the Context; and no, it doesn't need to be an activity.
http://developer.android.com/reference/android/os/AsyncTask.html
Anyways, you should be able to get the context anytime with no problems. Just do this:
public class MyApplication extends Application{
private static Context context;
public void onCreate(){
super.onCreate();
context = getApplicationContext();
}
public static Context getAppContext() {
return context;
}
}
Then you can get the context from wherever you want with MyApplication.getAppContext(); and pass it on and it should work.

Categories

Resources