Make a webview search bar that searches Google and URL's - android

I'm 16 and trying to learn Kotlin, I'm very new. I want to create a search bar which searches both Google and can open any URL you type in. I was trying to use an if else statement, for example:
if the first three letters were "www." then use the string url which is equal to "https://",
else use the string start_url which is equal to "google.com/search?q=";.
I just do not know how to do that and I have tried looking for help across the internet I just couldn't.
The URLUtil.isValidUrl(url) will not work because it still only loads Google Search
I am open to any comments to try and help me further learn and improve my code, even if it is not related to my question. Point out any errors or things that could be improved in my code, I know it's not perfect, thank you!
package com.example.corie.quicklinks.mainpages
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.asynclayoutinflater.R.id.text
import android.webkit.WebChromeClient
import android.webkit.WebViewClient
import com.example.corie.quicklinks.R
import com.example.corie.quicklinks.R.string.start_url
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//------------------WEBVIEW-----------------//
webViewOne.webChromeClient = WebChromeClient()
webViewOne.isVerticalScrollBarEnabled = false
webViewOne.run{
webViewOne.loadUrl("https://" + getString(start_url))
}
goBtn.setOnClickListener{
webViewOne.loadUrl("https://www.google.com/search?q=" + editText.text.toString())
}
backBtn.setOnClickListener {
if (webViewOne.canGoBack())
webViewOne.goBack()
}
nextBtn.setOnClickListener {
if (webViewOne.canGoForward())
webViewOne.goForward()
}
//------------------WEBVIEW-----------------//
}
}

Simple with built-in regex pattern:
import android.util.Patterns
val isAddress = Patterns.WEB_URL.matcher(address).matches()
if (isAddress) {
this#WebpageFragment.mBinding.webpageWebView
.loadUrl(address)
} else {
this#WebpageFragment.mBinding.webpageWebView.loadUrl(
"https://www.google.com/search?q=$address"
)
}
Explain: First you need to check if the string is an URL. If it's an URL, you'll use the webview to load that URL; Otherwise, you will need to use that value as google search query.

Related

Trying to find the proper file path for my app, but "context" doesn't seem to exist

I'm trying to export data from the app into a text file, and to do that I understand that I need to discover the appropriate path to my app-specific file storage spot. I discovered that I should use something like context.getExternalFilesDir(). While this seems pretty straightforward, when I try to do val path = context.getExternalFilesDir(), Android Studio tells me that context is a function and can't be called with the arguments supplied. Seems like others don't have this problem, is there an import that I'm missing or something along those lines?
Since I had come across this line context.getExternalFilesDir() to be used to return the path where files should go, I had assumed it would just work, but no such luck. I've dug into it for quite a while but can't seem to figure out what to do in the case where the IDE doesn't want to recognize it.
Here are my imports, in case there's something I'm missing:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.times
import com.example.packandfind.ui.theme.PackAndFindTheme
import java.io.File
Here's the function that contains the attempted call:
fun importData(): SnapshotStateMap<String, List<String>> {
val fileName = "data.txt"
// val path = context.getExternalFilesDir()
val file = File(fileName)
var data = mutableStateMapOf<String, List<String>>()
if (file.exists())
{
file.forEachLine {
val (key, value) = it.split("=")
// var newValue = value.replace("[", "")
// newValue = newValue.replace("]", "")
// newValue = newValue.replace(" ", "")
val listValue = value.split(",")
data[key] = listValue
}
} else {
data = mutableStateMapOf("New box" to listOf("location"))
}
return data
}
You appear to be attempting to use Compose UI. In that case, a #Composable function can use LocalContext.current to obtain a valid Context:
#Composable
fun FruitText(fruitSize: Int) {
// Get `resources` from the current value of LocalContext
val resources = LocalContext.current.resources
val fruitText = remember(resources, fruitSize) {
resources.getQuantityString(R.plurals.fruit_title, fruitSize)
}
Text(text = fruitText)
}
(from the docs)
Since you seem to be unfamiliar with Context... I strongly recommend that either:
You learn conventional Android app development (using the View-based UI system) via some book or course before taking on Compose UI, or
You find some book or course that teaches you Android app development using Compose UI
I would suggest to pass the context as a parameter from the activity you called from and make your function accept it.
fun importData(context: Context): SnapshotStateMap<String, List<String>> {
....
}

I am learning Android studio and doing a rectangle calculator, my kotlin has 19 errors and cannot figure it out

I am learning Android studio and doing a rectangle calculator. My Kotlin has 19 errors, and I cannot figure it out. I keep getting unresolved errors for btn, functions that cannot be called, and expecting an element. I am trying to do a calculator that takes height and width and then calculates area and perimeter. Just need guidance on what I am doing wrong and not looking for someone to give me new code.
MainActivity.kt
package com.example.calculator
import android.annotation.SuppressLint
import android.icu.text.DecimalFormat
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.*
class MainActivity : AppCompatActivity() {
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_calculate.setOnClickListener {
calculate()
btn_calculate.onEditorAction(EditorInfo.IME_ACTION_DONE)
}
btn_reset.setOnClickListener {
reset()
}
}
private fun calculate() {
val formatter = DecimalFormat("#.##")
val editNum1 = (EditText) editNum1.text.toString()
val editNum2 = (EditText) editNum2.text.toString()
val Area = DecimalFormat(editNum1.toDouble() * editNum2.toDouble())
val Perimeter = DecimalFormat(2* ( (editNum1.toDouble()) + (editNum2.toDouble()))
}}
If you want to access the views from your XML file directly in your activity you should use Kotlin Android Extension. But this plugin is deprecated. You should migrate to View Binding.
https://developer.android.com/topic/libraries/view-binding/migration

Issue in Display view dynamically code failed

I am very new to Android development and actually stuck with code in tutorial for Kotlin programming for android. The code below is not working and I have tried to find alternative but no luck.
Will appreciate if somebody can help we with the alternative code
below is a code:
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addSomeViews(count = 5)
}
fun addSomeViews(count: Int) {
for (i in 1..count) {
val textView = TextView(this)
textView.text = "Hey, learner # $i"
textView.textSize = 20f
my_layout.addView(textView)
}
val button = Button(this)
button.text = "Click me!"
my_layout.addView(button)
}
}
That kotlinx.synthetic stuff is deprecated - it doesn't work anymore. Instead of just referencing my_layout directly (the synthetics are supposed to look it up for you and create that variable) you need to find it yourself:
fun addSomeViews(count: Int) {
// lookup the layout viewgroup and create a variable for it
val my_layout = findViewById<ViewGroup>(R.layout.my_layout)
for (i in 1..count) {
val textView = TextView(this)
textView.text = "Hey, learner # $i"
textView.textSize = 20f
// now this variable exists
my_layout.addView(textView)
}
}
Aside from that... this isn't how a beginner should be learning Android imo. Creating views like this is kind of an advanced thing, mostly you never have to do it, and if you do there's a bunch of configuration you need to do on the views to make them display correctly (like the appropriate LayoutParams)
You can do it, but mostly you create your layouts in XML using the layout editor. And Compose is the new thing for writing UI in code, which is probably worth learning. It's up to you obviously, I just wanted to warn you that it's a strange thing for a beginner to be learning, and you might be better trying the Codelabs stuff instead

How can I check to see if JSON data is null without an infinite loop?

I have a viewmodel and data classes that fetch the NASA api for photos of Mars. The user should be displayed images from a random date queried. I always need an image url (imgSrc in Photo class) returned. If no url (imgSrc) is found, refresh data until one is found and display it. This logic would need to return an imgSrc following launch of the application as well as after swiperefreshlayout if the user chooses to swipe to refresh. I have been stuck on this for a week with no resolve. What is the best way to handle this? Even if I have to refactor my code I would like to be pointed in the right direction.
Here is the actual project on github.
JSON that I want to fetch
JSON returning no imgSrc
viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dev20.themarsroll.models.MarsPhotos
import com.dev20.themarsroll.models.Photo
import com.dev20.themarsroll.repository.MarsPhotoRepository
import com.dev20.themarsroll.util.Resource
import kotlinx.coroutines.launch
import retrofit2.Response
class MarsPhotoViewModel(
private val marsPhotoRepository: MarsPhotoRepository
): ViewModel() {
val marsPhotos: MutableLiveData<Resource<MarsPhotos>> = MutableLiveData()
init {
getRandomPhotos()
}
fun getCuriosityPhotos(solQuery: Int, roverQuery: Int, camera: String) = viewModelScope.launch {
marsPhotos.postValue(Resource.Loading())
val response = marsPhotoRepository.getCuriosityPhotos(solQuery, roverQuery, camera)
marsPhotos.postValue(handlePhotosResponse(response))
}
private fun handlePhotosResponse(response: Response<MarsPhotos> ) : Resource<MarsPhotos> {
if(response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
fun getRandomPhotos() {
getCuriosityPhotos((1..2878).random(), 5, "NAVCAM")
}
fun savePhoto(photo: Photo) = viewModelScope.launch {
marsPhotoRepository.upsert(photo)
}
fun getSavedPhotos() = marsPhotoRepository.getSavedPhotos()
fun deletePhoto(photo: Photo) = viewModelScope.launch {
marsPhotoRepository.deletePhoto(photo)
}
}
CuriosityFragment
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.View
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.dev20.themarsroll.R
import com.dev20.themarsroll.adapters.MarsPhotoAdapter
import com.dev20.themarsroll.util.Resource
import com.dev20.ui.MarsActivity
import com.dev20.ui.MarsPhotoViewModel
import kotlinx.android.synthetic.main.fragment_curiosity.*
class CuriosityFragment : Fragment(R.layout.fragment_curiosity) {
lateinit var viewModel: MarsPhotoViewModel
lateinit var marsPhotoAdapter: MarsPhotoAdapter
val TAG = "CuriosityFragment"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as MarsActivity).viewModel
setupRecyclerView()
swipeLayout.setOnRefreshListener {
viewModel.getRandomPhotos()
swipeLayout.isRefreshing = false
}
marsPhotoAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("photo", it)
}
findNavController().navigate(
R.id.action_curiosityFragment_to_cameraFragment,
bundle
)
}
viewModel.marsPhotos.observe(viewLifecycleOwner, { response ->
when(response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { curiosityResponse ->
marsPhotoAdapter.differ.submitList(curiosityResponse.photos)
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An Error occurred: $message")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
}
private fun hideProgressBar() {
curiosityPaginationProgressBar.visibility = View.INVISIBLE
}
private fun showProgressBar() {
curiosityPaginationProgressBar.visibility = View.VISIBLE
}
private fun setupRecyclerView() {
marsPhotoAdapter = MarsPhotoAdapter()
rvCuriosityPhotos.apply {
adapter = marsPhotoAdapter
layoutManager = LinearLayoutManager(activity)
}
}
}
MarsPhoto data class
data class MarsPhotos(
val photos: MutableList<Photo>,
val camera: MutableList<Camera>
)
Photo data class
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.google.gson.annotations.SerializedName
import java.io.Serializable
#Entity(
tableName = "photos"
)
#TypeConverters
data class Photo(
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
#SerializedName("earth_date")
val earthDate: String,
#SerializedName("img_src")
val imgSrc: String,
val sol: Int,
#SerializedName("rover_id")
val rover: Int,
) : Serializable
There are many potential solutions here that I can think of. However, given the app needs to have predictable and reasonable user experience, herein I'm scoping out the issues first.
Since a random resource is being requested each time, there's always a chance of it being null. Hence, multiple round-trips cannot be done away with (but can be reduced).
Multiple HTTP round-trips, that too with the unpredictability of returning null several times, can be really frustrating user experience.
Below are the potential ways (in increasing order of complexity) this can be dealt with.
The simplest solution is to implement logic on the repository level, wherein the function getCuriosityPhotos is responsible to request the api resource indefinitely till it responds with a not null data. This will solve the core issue that the user will eventually be shown something (but it might take a hell lot of time).
(PS- You'll also need to delegate the random number generation as a potential service available to the repository.)
To reduce the number of requests and hence wait-time for the user, you can save to the in-app database, the request params as well as the response. Thus, your database can act as a single source of truth. Hence, before making a request, you can query the database to check if the app had previously requested the same params earlier. If it did not, dispatch the request else if it did, then there's no need to request again and you can use the previous result. If it was null, regenerate another random number and try again. If it was not null, serve the data from the database. (This is a good enough solution and as more and more requests & responses are saved, user wait time would continually reduce)
(Note: In case the endpoints do not respond with static data and the data keeps changing, prefer using an in-memory database than a persistent database such as SQLite)
The app can run a background service that continually (by iterating over all possible combinations of the request params) requests and saves the data into the database. When the user requests random data, the app should display a random set of data from within the database. If the database is empty/does not meet a threshold of having at least n number of rows in the database, the app can perhaps show an initialization setup UI.
Pro-tip: Ideally (and in case you are building a product/service), mobile apps are meant to be very very predictable and have to be mindful of a user's time. Hence, the very task of requesting data from such resources should be a task of a backend server and database which operate some sort of service to fetch and store data and in-turn the app would request this server to fetch data amongst this subset which does not have any null values.
I've answered this question from a perspective of solving the problem with varying granularity. In case you need help/advice on the technical implementation part, let me know, I'll be happy to help!

Calling REST api on android emulator causes app to crash

I am relatively new to android studio. I am trying to build a very simple app that fetches the price of cryptocurrencies using the Bittrex exchanges api. However every time I try to get the info from the URL, my app crashes. I am using Kotlin by the way. I'm having trouble solving this because I don't know how to run the emulator in debug mode, just the compiler. Here is my code:
package com.example.sebastian.cryptoapp
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import java.net.URL
import java.net.MalformedURLException
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
fun search(): String {
//read in value
var market = searchBar.getText().toString()
//output text from URL query
val result = URL("https://bittrex.com/api/v1.1/public/getticker?market="
+ market).readText()
return result
}
fun getPrice(): String {
//calling search function
var info = search()
//split the string into a list
var list: List<String> = info.split(":", "}")
//access 7th index of list for last traded price
return list[6]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
text_output.setText(getPrice())
}
}
}
This line here:
val result = URL("https://bittrex.com/api/v1.1/public/getticker?market=" + market).readText() looks like it is being run on the main thread. This will cause the app to crash with a a NetworkOnMainThreadException.
You can read more on this exception here.
Also, make sure you have the following in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Check out these answers for more information on how to run this in the background:
How to fix android.os.NetworkOnMainThreadException?
Android - android.os.NetworkOnMainThreadException
Alternatively in Kotlin you could also Anko or Coroutines.

Categories

Resources