Kotlin - Uncaught TypeError: X is not a function - JavascriptInterface with WebViews - android

I have a back application with a cshtml file that is basically used to get authentified.
Now, in my Android app in Kotlin, I'm using a WebView and a JavascriptInterface to call the URL that I need and to call my method in the back.
There are the useful code lines:
Login activity:
val myWebView: WebView = findViewById(R.id.connexionWebview)
myWebView.setWebViewClient(object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
view.loadUrl(url)
return false
}
})
myWebView.settings.javaScriptEnabled = true
myWebView.addJavascriptInterface(AuthenticatedHandler(this), "AndroidAuthenticatedManager")
myWebView.loadUrl("HiddenURL")
AuthenticatedHandler.kt:
class AuthenticatedHandler(private val mContext: Context): AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
#JavascriptInterface
fun onAuthenticated(token: String, email: String, userId: Int) {
val intent = Intent(this, Home::class.java)
val extras = Bundle()
extras.putString("token", token)
startActivity(intent)
}
}
}`
In my back, Authenticated.cshtml:
AndroidAuthenticatedManager.onAuthenticated('#token.AccessToken', '#email', #userId);
And this is the error that I got:
I/chromium: [INFO:CONSOLE(14)] "Uncaught TypeError: AndroidAuthenticatedManager.onAuthenticated is not a function", source: http://HidenURL:5000/Account/Authenticated (14)
What do you think about it? I tried several little changes and searched about it on internet but nothing fixes it...
Thanks for your reading,
Tanguy.

Related

JavaScriptInterface in a WebView is not called on Lollipop and Marshmallow

I have tried it on android 7.0, it works, but on android 5,6 it does not work, i could not find any clue on what maybe going wrong.
The URL redirect to payment portal and at the end it calls Android.postMessage("success") from Javascript, but it does not get called in native this problem only persist in android 5 and maybe on android 6 too.
My minimum SDK level is 21.
class PaymentActivity : BaseActivity<ActivityPaymentBinding, IPaymentMvvm.ViewModel>(), IPaymentMvvm.View {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
setAndBindContentView(savedInstanceState, R.layout.activity_payment)
viewModel.initVM()
binding.web.webViewClient = Web(viewModel)
binding.web.settings.javaScriptEnabled = true
binding.web.settings.loadWithOverviewMode = true
binding.web.settings.useWideViewPort = true
binding.web.addJavascriptInterface(JSBridge(this, this), "Android")
val url = intent.extras.getString("url")
if (url == null)
finish()
binding.web.loadUrl(url)
}
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
android.R.id.home ->
finish()
}
return super.onOptionsItemSelected(menuItem)
}
class Web(val viewModel: IPaymentMvvm.ViewModel) : WebViewClient() {
init {
viewModel.setProgress(true)
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
view?.loadUrl(request?.url.toString())
return true
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
viewModel.setProgress(false)
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
super.onReceivedError(view, request, error)
viewModel.setProgress(false)
Timber.e(error.toString())
}
}
}
class JSBridge(val context: Context, val activity: Activity) {
#JavascriptInterface
fun postMessage(message: String) {
// here we return true if we handled the post.
Timber.i(message)
context.toast(message)
val intent = Intent(context, OrderThanksActivity::class.java)
intent.putExtra(Enums.contactDetail.contactNumber.name, activity.intent.getStringExtra(Enums.contactDetail.contactNumber.name))
intent.putExtra(Enums.contactDetail.phoneNumber.name, activity.intent.getStringExtra(Enums.contactDetail.phoneNumber.name))
intent.putExtra(Enums.contactDetail.cellNumber.name, activity.intent.getStringExtra(Enums.contactDetail.cellNumber.name))
activity.startActivity(intent)
activity.finish()
}
}
It was the problem of JavaScript script from web, which threw an error in a script running in web view before my interface function even get called, but the weird behavior was that script only got terminated on Android 5,6 while Android 7 web view does not terminate execution and execute the whole script regarding error that's why the interface function got called in Android 7.

My app keep stopping when I use the API cat facts

I did an tutorial about using API to load sentence into my app.Develop a Chuck Norris Facts Android App with KotlinDevelop a Chuck Norris Facts Android App with Kotlin
but I found when I want to use another API (cat facts) my app keeps stopping and I don't know why.
cat facts API
This is my code:
class MainActivity : AppCompatActivity() {
val URL = "https://catfact.ninja/fact"
var okHttpClient: OkHttpClient = OkHttpClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadRandomFact()
}
private fun loadRandomFact() {
val request: Request = Request.Builder().url(URL).build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call?, e: IOException?) {
}
override fun onResponse(call: Call?, response: Response?) {
val json = response?.body()?.string()
// we get the joke from the Web Service
val txt = (JSONObject(json).getJSONObject("fact")).toString()
// we update the UI from the UI Thread
runOnUiThread {
// we use Html class to decode html entities
CatFactsResponseView.text = Html.fromHtml(txt)
}
}
})
}
}

Failed getStringExtra Kotlin mvvm

I managed to move activty from recyclerview to detail, but when I added data transmission via intent, the data always failed to be taken in the activity detail
error
enter image description here
MainDetail
private lateinit var viewModel: MainDetailModel
var idAnime: String = "34134"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_detail)
idAnime = intent.getStringExtra("idAnime")
println("idanime $idAnime")
setupFragment()
setupViewModel()
}
ViewModel
class MainViewModel(context : Application,private val appRepository: AppRepository,private val contexts : Context) : AndroidViewModel(context),MainItemClickAction {
override fun onItemClicked(detailModel: DetailModel) {
var intent = Intent(contexts, MainDetailActivity::class.java)
intent.putExtra("idAnime",detailModel.mal_id )
contexts.startActivity(intent)
}
Check if your field "detailModel.mal_id" mal_id in that case is string, because you're requesting string in details activity. If it's string check also if this "mal_id" is null. Other issues from code you provided can't be seen.

Kotlin application doesn't show my json array

I just started to learn Kotlin and I have tried to make my first android application with Kotlin.
I have a webservice which built on Restful API and it returns a json array for me like the image below.
My Kotlin code for showing json array via gson library !
class MainActivity : AppCompatActivity() {
val TAG:String="TipCalculatorActivity"
var adapter:ArrayAdapter<Category>?=null
var dsSP: MutableList<Category> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
adapter=ArrayAdapter(this,android.R.layout.simple_list_item_1,dsSP)
lvCategory.adapter=adapter
}
override fun onResume() {
super.onResume()
var task=CategoryTask()
task.execute()
}
inner class CategoryTask:AsyncTask<Void, Void, MutableList<Category>>() {
override fun onPostExecute(result: MutableList<Category>?)
{
super.onPostExecute(result)
dsSP.clear()
dsSP.addAll(result!!)
}
override fun doInBackground(vararg p0: Void?): MutableList<Category>
{
var dsFromServer:MutableList<Category> = mutableListOf()
try {
var url:URL=URL("192.168.31.86/shop/api/category")
var conn: HttpURLConnection = url.openConnection() as HttpURLConnection
conn.requestMethod="GET"
conn.addRequestProperty("content-type","application/json;charset:UTF-8")
var isr= InputStreamReader(conn.getInputStream(), "UTF-8")
var gson=Gson()
dsFromServer=gson.fromJson(isr,object:TypeToken<MutableList<Category>>(){}.type)
isr.close()
}
catch (ex:Exception)
{
ex.printStackTrace()
}
return dsFromServer
}
override fun onPreExecute()
{
super.onPreExecute()
dsSP.clear()
}
}
}
Of course, I declared all attributes of json array on another class , exactly like webservice
The result has nothing to show. I don't know why.
I just found the solution for my problem . I forgot to add http:// before the link. I just use Logging function and I got a message " no protocol"
Just a newbie mistake. Sorry you guys!

Kotlin - Define variable globally for WebView

I'm trying to define a variable globally which is of the class WebView. In Android Java it could be easy done by writing the it
Java for global variable
< ClassName > < variableName >
But in Kotlin I'm facing issue with its declaration.
class MainActivity : AppCompatActivity() {
var mywebview : WebView //<- This shows Property must be initialized or be abstract
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
mywebview = findViewById(R.id.webViewGyrix) as WebView
mywebview.setWebViewClient(object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
view?.loadUrl(url);
return true
}
} )
mywebview.loadUrl("http://www.example.com/")
}
This shows Property must be initialized or be abstract
Then initialize it i.e. to null. This is not a final value and you will be able to change it later:
var mywebview : WebView? = null
Alternatively, you can use lateinit feature of Kotlin, which would let you prevent having nullable mywebview if not needed:
lateinit var webView: WebView
You can use late initialization - you don't have to make WebView nullable
lateinit var webView: WebView
for a global variable, this means it shouldn't be overridden accidentally , so you should instead lazyload it using kotlin's lazy which create the variable on the first call, other calls will just reference the lazy loaded variable
private val webview:WebView by lazy{
findViewById<WebView>(R.id.webview)
}
That should come before the onCreate method
Below code worked for me:
val mywebviewURL = "https://www.google.com"
override fun onStart() {
super.onStart()
events_webview.setWebViewClient(object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
view?.loadUrl(url);
return true
}
})
events_webview.loadUrl(mywebviewURL)
}
Add internet permission to AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>

Resources