Can i use Jsoup in Jetpack Compose in kotlin - kotlin

In traditional xml way , i use GlobalScope.launch{} with runOnUiThread {} to work with Jsoup.But in jetpack Compose this not work anymore. It just instant closed when run it.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch{
val url="somewebsite.com"
var doc= Jsoup.connect(url).get()
runOnUiThread {
}
}
setContent {
WannaJsoupTheme {
Surface(color = MaterialTheme.colors.background) {
Greeting("Android")
}
}
}
}
}

Explanation
Based on the above code, apparently you're using the main thread to perform I/O operations. Main thread are generally used for user interface operations. It's better to use background threads for I/O operations. - They are more efficient.
Here's demo app which I created to demonstrate use of JSoup with MVVM architecture.
Link - https://github.com/TheCodeMonks/NYTimes-App
Solution - https://gist.github.com/Spikeysanju/111e061ad82f3b7be6beb419fb18bca4
Quick Solution
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Initiate Coroutine scope
val scope = rememberCoroutineScope()
var text by remember {
mutableStateOf("")
}
JsoupTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
// Initiate coroutine scope will will run on IO thread & write method to get the text from the website
scope.launch(Dispatchers.IO) {
val url = "https://en.wikipedia.org/wiki/Steve_Jobs"
Jsoup.connect(url).get().also {
text = it.text()
}
}
// Fetch result from the state variable [Text]
Text(text = "Result: $text")
}
}
}
}
}

Related

Invocations can only happen from the context of an #composable function using Compose Navigation

Hi Im currently struggling with navigation in Jetpack Compose due to #composable invocations can only happen from the context of an #composable function. I have a function:
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" + response?.error?.errorCode)
}
}
and used with my navigation class shown under I only get the error message shown above, how do I fix it?
#Composable
fun ScreenMain(){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Routes.Vareliste.route) {
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
composable(Routes.ForgotPassword.route) { navBackStack ->
ForgotPassword(navController = navController)
}
composable(Routes.Vareliste.route) { navBackStack ->
Vareliste(navController = navController)
}
composable(Routes.Handlekurv.route) { navBackStack ->
Handlekurv(navController = navController)
}
composable(Routes.Profileromoss.route) { navBackStack ->
Profileromoss(navController = navController)
}
}
}
EDIT WITH COMPLETE CODE
Here is the whole code for the class if you guys wanted to see it!
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
LoginPage()
}
}
}
}
private var user: FirebaseUser? = FirebaseAuth.getInstance().currentUser
private lateinit var auth: FirebaseAuth
#Composable
fun LoginPage() {
Box(modifier = Modifier.fillMaxSize()) {
}
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Velkommen til ITGuys", style = TextStyle(fontSize = 36.sp))
Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) {
Button(
onClick = { signIn() },
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
) {
Text(text = "Logg inn")
}
}
}
}
private fun signIn() {
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build()
)
val signinIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build()
signInLauncher.launch(signinIntent)
}
private val signInLauncher = registerForActivityResult(
FirebaseAuthUIActivityResultContract()
) {
res -> this.signInResult(res)
}
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" + response?.error?.errorCode)
}
}
}
I need to add more text to be allowed to post this much code you can ignore this text cause it is just for being able to post.
As #z.y mentioned, you can pass a lambda with a onFirebaseAuthSuccess. I would also add that as you are passing the navController to the signup screen, the lambda callback you need to pass should look something like
onFirebaseAuthSuccess = { navController.navigate(Routes.Profileromoss.route) } - or whatever route you need
Based on
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
I would assume your signIn screen is called from inside the scope of a composable. If you can add the extract of code containing how you are calling the signInResult function we can be sure about it.
I'm not familiar with Firebase Authentication so I'm not sure where do you call or how you use your signInResult function but you cannot invoke a function that is annotated with #Composable (ScreenMain) from a scope that is not annotated by it such as ordinary function (signInResult).
You can consider adding a lambda callback for signInResult which will be called in the RESULT_OK condition block.
private fun signInResult(result: FirebaseAuthUIAuthenticationResult, onFirebaseAuthSuccess: () -> Unit) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
...
...
onFirebaseAuthSuccess() // this callback
} else {
...
}
}
Edit: #sgtpotatoe has better answer, you can invoke a navigation in your root composable from the lambda callback that will navigate to your target screen.
Ok so, in your MainActivity, you want your navigational component to be at the top:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain()
}
}
}
}
Then add a route to your navhost for the login page:
composable(Routes.LoginPage.route) {
LoginPage(navController = navController)
}
I think its a bit of a major change, but you would have to rely on a view model to make the authentication, so it can handle the calls, not blocking the ui or showing a loading screen, and communicate with the view
It would look something like this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = MyViewModel()
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain(viewModel)
}
}
}
}
In the LoginPage you want to access the viewmodel to start the auth service calls
In the LoginPage you want to access the viewmodel to observe if the call is succesfull, and in that case do the navigation
In the MyViewModel you want to have the authentication calls, and to update the variable that triggers the navigation if auth is succesfull
Here is an example of a sample firebase authentication app in compose, I would use it as a guide https://firebase.blog/posts/2022/05/adding-firebase-auth-to-jetpack-compose-app

How to show rewardedInterstitial ad which was loaded in mainActivity in a compose?

I try to load rewarded interestitial ad in Mainactivity and then show it in one composable screen in jetpack compose. I loaded it successfully but in my RewardedShow compose screen rewardedInterstitialAd is null? I used my code from https://developers.google.com/admob/android/rewarded-interstitial
This is my code in MainActivity
class MainActivity : ComponentActivity(){
var rewardedInterstitialAd: RewardedInterstitialAd? = null
private var TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyBeautyAppInJetpackComposeTheme {
MyNavGraph()
MobileAds.initialize(this) {
loadAd()
}
}
}
}
private fun loadAd() {
RewardedInterstitialAd.load(this, "ca-app-pub-
3940256099942544/5354046379",
AdRequest.Builder().build(), object :
RewardedInterstitialAdLoadCallback() {
override fun onAdLoaded(ad: RewardedInterstitialAd) {
Log.d(TAG, "Ad was loaded.")
rewardedInterstitialAd = ad
}
override fun onAdFailedToLoad(adError: LoadAdError) {
Log.d(TAG, adError?.toString())
rewardedInterstitialAd = null
}
})
}
}
and this the RewardedShowCompose screen
#SuppressLint("UnrememberedMutableState")
#Composable
fun RewardedShowCompose( actions: MainActions, maskArg: String) {
val context = LocalContext.current
val loading = mutableStateOf(true)
Surface(modifier = Modifier.fillMaxSize())
{
if (MainActivity.rewardedInterstitialAd != null){
MainActivity.rewardedInterstitialAd?.show(context as Activity, OnUserEarnedRewardListener { actions.gotoFinalShow.invoke(maskArg) })
}
}
}
I made rewardedInterstitialAd Companion and now it works well. like this
companion object {
var rewardedInterstitialAd: RewardedInterstitialAd? = null
}

Why is setContent{} being called twice?

I've recently started my first project using Jetpack Compose (with minimal Android dev experience).
For checking performance, I logged each call to any function / composable, with some unexpected behavior at startup or orientation change (but without further interacting with the app):
I do understand oncreate / super being called (again, in case of orientation change), but why is it that setContent {} is being called twice?
override fun onCreate(savedInstanceState: Bundle?) {
Log.v(tag, "oncreate")
super.onCreate(savedInstanceState)
Log.v(tag, "oncreatesuper")
setContent {
Log.v(tag, "setting content")
Content()
}
}
and then
#Composable
private fun Content() {
val arrayOfNodes = rememberSaveable { mutableListOf<Wurzel>() }
val toggleSizeInputDialog = rememberSaveable { mutableStateOf(false) }
(...some more...)
val currentConfig = LocalConfiguration.current
val title = stringResource(id = R.string.title)
Log.v(tag, "recomposing content")
MyTheme {
when (currentConfig.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
Scaffold(...........)
My project is far to small to see any perfomance issues, however I'd like to find out the reason for this behavior for future reference or whether I severely misunderstood the compose architecture.
setComponent() is not being called twice, but the composable function you pass in it is. It just seems like some event causes recomposition of the content composable.

how do i update configuration in kotlin for change language on radio btn selected in kotlin

how to update this one without using resources.updateConfiguration this commented codes please any can say? and solve it.
when i click th eradio btn an dthe language will have to change
this is mainActivity.kt file
class LanguageChange : AppCompatActivity() {
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_language_change)
radio_group_btn.setOnCheckedChangeListener { _, i ->
when(i){
R.id.radio_btn_fr_id -> {
setLang("fr")
txt_view1_id.setText(R.string.view_one)
txt_view2_id.setText(R.string.view_two)
Log.i("tag","french")
}
R.id.radio_btn_en_id -> {
setLang("en")
txt_view1_id.setText(R.string.view_one)
txt_view2_id.setText(R.string.view_two)
Log.i("tag","english")
}
}
}
}
private fun Context.setLang(lan: String): Context{
// val config = resources.configuration
// config.setLocale(Locale(lan))
// resources.updateConfiguration(config,resources.displayMetrics)
// onConfigurationChanged(config)
val config = resources.configuration
config.setLocale(Locale(lan))
config.setLayoutDirection(Locale(lan))
applyOverrideConfiguration(config)
return createConfigurationContext(config)
}
#RequiresApi(Build.VERSION_CODES.N)
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(ContextWrapper(newBase?.setLang("en")))
}
}
It's a good question. Short answer you can't. Even with updateConfiguration you can't be sure about result. For more details please read this article https://medium.com/swlh/android-app-specific-language-change-programmatically-using-kotlin-d650a5392220. But i say it again there is no 100% method to change language inside app with default strings.

Text in jetpack compose not showing text?

This class simply emit character after 1-sec delay
class MainViewModel: ViewModel() {
var bouncingName = flow<Char> {
val text="hemant"
for (letter in text){
delay(1000L)
emit(letter)
}
}
}
This main class displays string after concatenating string display on the app
class MainActivity : ComponentActivity() {
#SuppressLint("CoroutineCreationDuringComposition")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompleteGuideKotlinFlowTheme {
// A surface container using the 'background' color from the theme
var letter by remember {
mutableStateOf(StringBuilder())
}
val viewModel = viewModel<MainViewModel>()
// var currentValue=viewModel.countDownValue.collectAsState(initial = 10)
lifecycleScope.launch {
viewModel.bouncingName.collect {
letter.append(it)
Log.d("TAG", "Character in main $letter")
}
}
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(
text = letter.toString(),
fontSize = 30.sp
)
}
}
}
}
}
But nothing is showing on the app.
I don't understand why?
StringBuilder is mutable. When you append to it, Compose doesn't get notified and the recomposition doesn't happen. You should use immutable types with MutableState and update it using MutableState's setter:
var letter by remember { mutableStateOf("") }
...
letter = letter + it