My Website(use laravel) call window.open method on this code
window.open('My_URL','_blank');
and my Android Webview open new window on dialog. code is
override fun onCreateWindow(view:WebView, isDialog:Boolean,
isUserGesture:Boolean, resultMsg:Message):Boolean {
// set dialog webview
val dialog = Dialog(context)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.activity_webview)
val newWebView = dialog.findViewById<WebView>(R.id.WebView)
newWebView.settings.javaScriptEnabled = true
newWebView.loadUrl(view?.url)
// open dialog full screen
val window = dialog.window
val wlp : WindowManager.LayoutParams = window.attributes
wlp.gravity = Gravity.CENTER
wlp.flags = WindowManager.LayoutParams.FLAG_BLUR_BEHIND
window.attributes = wlp
dialog.window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT)
dialog.show()
(resultMsg.obj as WebView.WebViewTransport).setWebView(newWebView)
resultMsg.sendToTarget()
newWebView.setWebViewClient(object: WebViewClient() {
})
newWebView.setWebChromeClient(object:WebChromeClient() {
override fun onCloseWindow(window:WebView) {
dialog.dismiss()
window.destroy()
newWebView.destroy()
//webview.removeView(newWebView)
}
})
return true
}
it was work successful just one time.
what should I do?
Not sure if it will help you or not, but as far as I know the android application should provide handling of opening and closing window requests from javascript. I mean it does work out of the box, but very often not how it is expected/needed.
It is possible to show separate web window as a dialog, but as I know dialogs can't stack and appear on top of each other. Such approach fits case when you need to open (and then close) only one separate window. Please read more about dialogs here.
What I suggest is to show separate WebView on every window.open() request. Please take a look at this answer, where similar solution was solved. In your case (where any amount of opened windows possible) you can use a LinkedList of WebViews, pushing new one on window.open() and popping top ones on window.close.
Related
I am showing a programmatically created webview in a bottomsheet fragment. The webview is persisted on a singleton that outlives the fragment
The webview is created like so:
webView = WebView(context).let { webView ->
webView.layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
webView.clearCache(true)
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.measure(100, 100)
webView.settings.useWideViewPort = true
webView.settings.loadWithOverviewMode = true
webView.addJavascriptInterface(AndroidJavascriptInterface(environment), webViewHook)
webView.webViewClient = webViewClient
webView.webChromeClient = webViewChromeClient
webView.loadUrl("file:///android_asset/snippet.html")
webView
}
and then present the webview in a bottomsheet fragment like so:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (webController == null) {
// activity restarted without original context
dismiss()
} else {
webController?.webView?.visibility = View.VISIBLE
webController?.let {
fragment_web_view?.addView(webController?.webView)
}
}
}
I observe these undesirable behaviors:
The webview's network requests all get stuck at pending when being backgrounded for around 2 min
If I persist the webview using a singleton, the webview stops making network request after the webview is presented and then dismissed
If I never present the fragment, the webview works fine no matter how long I wait
Requests start normal but stays pending after a while
But in these two scenarios the webview works fine.
If I keep the webview on screen all requests work fine.
If I keep presenting and dismissing the webview, all requests works fine.
Is there something that I don’t understand about the android webview that may have caused this issue? In order to refresh the webview, I need to reinitialize it to make the requests work again.
Looks like this is an issue with lates version of WebView. Can you open play market on the device find Android System WebView and try to downgrade it. Form me everything works fine on version 74 and stuck in pending on version 99. Let me know if it works same for you or you already found a reason?
In Android app, we have custom AppCompatDialog class and need to open the soft input method (keyboard) on EditText after dialog was dismissed.
For showing the keyboard, this function is used:
fun View.showKeyboard() = context.inputMethodManager?.let {
// Find the really focused EditText in this root view.
val focusedView = if (this is ViewGroup) this.findFocus() else this
it.showSoftInput(focusedView ?: this, InputMethodManager.SHOW_IMPLICIT)
}
In dialog OnDismissListener, when calling this code below in Fragment, the code produces the expected outcome like in 70% of cases, but in order to work always in emulator and real devices, arbitrary 50ms delay time was needed to add for postDelayed.
view?.post {
binding.input.apply {
// binding.input is our custom Widget that contains EditText
requestFocus()
showKeyboard()
}
}
I would prefer to not have this magic number in the codebase and also I'm afraid, this might not work in some Android distributions.
Is there any better proven solution, that will work on most Android versions and vendor specific distributions?
Thanks for any help
I have a problem I have not yet been able to understand, nor solve.
On my android project, I have a BaseActivity. There, among other functions, I decided to add a function to show or hide a loading view when necessary. It works as intended, but sometimes an error happens.
I will try to raise some important info about my project I think can be useful. My app is integrated with an external login app. I call it when the services I call need to refresh it's token. When the user logs in on the app, it calls a listener and give me back control on mine.
The problem is the next one:
I come to an activity that needs to call a service and I have the token all right, but then I lock the phone. After a long period of time, I unlock the phone and, from the same activity, I call again the service. My activity shows de loader as intended and, as my token is expired, I call the login app from it's SDK.
When I come back to my app, and I call the service I wanted successfully, the app tries to hide the loader. This is where the fail comes, as I can't change the visibility to GONE. I looked for it on the view hierarchy and find it, but with visibility = VISIBLE.
Here is the piece of code from the loader, hope someone can find where I'm making the mistake!
abstract class BaseActivity : DaggerAppCompatActivity(){
// These are the IDS of the Views I'm adding to the activity, so I can track them and change their visibility
var imgLoadingID = -1
var rvLoadingID = -1
fun showLoading() {
// If the views are added I show them
if (imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.VISIBLE
imageView.visibility = VISIBLE
imageView.isClickable = false
imageView.isFocusable = false
} else {
// else I create them and show them
val imgLoading = ImageView(this)
imgLoading.id = View.generateViewId()
imgLoadingID = imgLoading.id
val maxpx = CustomUtils.ViewUtils.converIntToDps(65, this)
Glide.with(this).asGif().load(R.mipmap.loading).into(imgLoading)
val relativeLayout = RelativeLayout(this)
relativeLayout.id = View.generateViewId()
rvLoadingID = relativeLayout.id
var params = RelativeLayout.LayoutParams(maxpx, WRAP_CONTENT)
params.addRule(RelativeLayout.CENTER_IN_PARENT)
relativeLayout.addView(imgLoading, params)
relativeLayout.background = getDrawable(R.color.pure_white_97)
relativeLayout.isClickable = true
relativeLayout.isFocusable = true
findViewById<ViewGroup>(android.R.id.content).addView(relativeLayout, RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
imgLoading.visibility = VISIBLE
}
// I lock the back button so people don't cancel my requests
esBackPressedBlocked = true
}
// Here I find the views and change their visibility.
fun hideLoading(){
if(imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.GONE
imageView.visibility = View.GONE
}
esBackPressedBlocked = false
}
}
I deleted some logs I added to the whole function, but when it fails, it enters on the hideLoading() function, even on the relativeLayout.visibility = View.GONE part.
The function used to be with the Views as an whole object instead of their ids, but I found it more reliable this way, and saving the views instead of their Id's had the same problem.
My main concern is how Android manages my application while the phone is locked for this period of time (the fails happened after 8-10 hours of inactivity). I think something there can be creating this issue. I thought also about the external Login app, since it's sdk is launching their app's intent and calling me from a listener when coming back, but, since my code is being executed, I think Android it's managing my views on an strange way. Or maybe I try to hide the loading view before I'm on the resumed activity... I don't really know.
BTW, I know there are easier solutions on showing a loader, but I wanted to create it the cleanest way. If you have any cleaner approach I'm open to any solution.
If there is anything unclear let me know in the comments, and I hope my English was clear enough to express myself, it's a tricky problem that I can't understand, so it's difficult for me to explain it.
Thanks!!
I am wondering how I go about (if it is possible) creating a modal popup in a mono for droid application.
Scenario: The application talks to the customers hosted web server (so this location will be different customer to customer). To use the app the user must specify the connection string of their web server. So when the application starts and it hits the main activity, the first task I do is check if there is a connection string set in the devices application settings. If not I want to throw up a simple modal popup that allows the user to specify a connection to their server.
I dont really want to start a normal activity because the user will be able to click the back button and just go back to the main menu and the app is than in an invalid state because it doesnt know what server to talk to.
Any ideas on how I go about this?
Or should I be structuring the activity chain so that the connection string is entered on the first activity so that if they click back it actually goes out of the app?
Im a little confused.
Thanks in advance
This is possible with AlertDialog. It can create dialogs for simple input with lists, checkboxes, yes/no buttons and custom views.
There is a sample in the Xamarin Sample Repository for different type of dialogs and in the bottom you can find one where a custom view with a username and password field has been added.
So first define your custom view you want to put in the AlertDialog. alert_dialog_connection_entry.xml and is a Layout:
Somewhere in your activity add the code:
var connection_string_view = LayoutInflater.Inflate(Resource.Layout.alert_dialog_connection_entry, null);
var builder = new AlertDialog.Builder(this);
builder.SetTitle("Connection String");
builder.SetView(connection_string_view);
builder.SetPositiveButton("OK", OkClicked);
builder.SetNegativeButton("Cancel", CancelClicked);
builder.Create();
builder.Show();
Add some handlers for the buttons:
private void CancelClicked(object sender, DialogClickEventArgs dialogClickEventArgs)
{
//Todo
}
private void OkClicked(object sender, DialogClickEventArgs dialogClickEventArgs)
{
var dialog = sender as AlertDialog;
if (null != dialog)
{
var connectionEdit = dialog.FindViewById(Resource.Id.connectionstring_edit) as EditText;
if (null != connectionEdit)
Console.WriteLine("Connection String: {0}", connectionEdit.Text);
}
}
That should be it. You should be able to put any kind of custom view in the dialog.
If you just want to display a modal popup for letting users put their connection string, you could try this.
First, you need to have a simple layout for how the dialog is presented. In this case, a TextView displaying something like "Connection string:" and an EditText to let the user put connection string is probably enough to go.
Then, you can put this code somewhere in your MainActivity, like after checking application settings or something similar.
var builder = new AlertDialog.Builder(this);
var view = LayoutInflater.Inflate(Resource.Layout.ModalDialog, null);
builder.SetView(view);
string connectionString = view.FindViewById<EditText>(Resource.Id.ConnectionString).Text;
AlertDialog alert = builder.Create();
alert.SetCancelable(false); //This prevents the dialog from being dismissed by either hit back button or hit out side of the dialog
alert.SetButton("OK", (s,e)=> ToDo(connectionString)); //Now you have the connection string, to do whatever you want.
alert.Show();
As you said, the alternative could be allowing user specify the connection string in the first screen. This is a good approach too. I assume you know how to do it, so I didn't post code here.
I'm creating an android app where user loads some data from an online server when he hits a button
I need to add the following :
when user hits the button a pop-up screen shows on top of current screen & shows something like
"loading" "spinning loader" or anything like this
any thoughts ?
Have a look at this Android Developer page at the section "Creating a ProgressDialog". Android offers such a dialog out of the box.
You can try a function like showProgressDialog(Activity activity)with this context:
if ((mySpinnerDialog== null) || (!mySpinnerDialog.isShowing())) {
mySpinnerDialog= new Dialog(activity);
mySpinnerDialog.getWindow().getCurrentFocus();
mySpinnerDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mySpinnerDialog.setContentView(R.layout.some_layout);
mySpinnerDialog.setCancelable(false);
mySpinnerDialog.setOwnerActivity(activity);
mySpinnerDialog.show();
} else {
mySpinnerDialog.setOwnerActivity(activity);
}
and dismiss this with mySpinnerDialog.dismiss();. Handle Illegalargumentexception on dismiss()