Hello I am trying to retrieve url, shared from another app and toast it as well as open it in WebViewTab.
But instead id of the app is displayed in toast.
here is my code:
val extras = intent.extras
if (extras != null) {
for (key in activity.intent.extras!!.keySet()) {
CopyKey = key
val value: String? = activity.intent.extras!!.getString(CopyKey)
Toast.makeText(applicationContext, value, Toast.LENGTH_LONG).show()
TabInfo.addTab(value.toString())
}
val url = intent.extras!!.getString("query")
if (url.toString().startsWith("http")) {
TabInfo.addTab(url.toString())
intent.removeExtra("query")
}
}
Thanks in advance
This solved my issue:
val extras = intent.extras
if (extras != null) {
val externalUrl: Uri? = intent?.data //retrieves the shared text from another app.
val url = intent.extras!!.getString("query")
if (url.toString().startsWith("http")) {
TabInfo.addTab(url.toString())
intent.removeExtra("query")
}
else {
TabInfo.addTab(externalUrl.toString())
}
}
Check the detail below code snippets
Note:
Error handling & data null check can be handled separately.
Assuming success case.
val URL_FINDER_REGEX = "((http:\\/\\/|https:\\/\\/|ftp:\\/\\/|file:\\/\\/)?(www.)?(([a-zA-Z0-9-]){2,2083}\\.){1,4}([a-zA-Z]){2,6}(\\/(([a-zA-Z-_\\/\\.0-9#:?=&;,]){0,2083})?){0,2083}?[^ \\n]*)"
fun test(intent: Intent?) {
intent?.let {
it.extras?.let { extras ->
val urlQuery = extras.getString("query")
urlQuery?.let {
val links = getUrlsFromText(urlQuery)
println(links)
// TODO("Your Business logic")
}
}
}
}
fun getUrlsFromText(text: String): ArrayList<URI> {
val availableLinks = ArrayList<URI>()
val matcher: Matcher = Pattern.compile(URL_FINDER_REGEX, Pattern.CASE_INSENSITIVE).matcher(text)
while (matcher.find()) {
try {
val url = URI(matcher.group())
availableLinks.add(url)
} catch (e: Exception) {
println(e)
}
}
return availableLinks
}
This is similar to some old queries.
Ref.
Regular expression to find URLs within a string
Detect and extract url from a string?
Related
When I use the code below I can get data from firebase but when I want to access it with MVVM it returns null.
database.collection("Order")
.get()
.addOnCompleteListener { it ->
if (it.isSuccessful) {
val itemName = it.result.documents[0].data?.get("itemName")
val id = it.result.documents[0].data?.get("id")
It returns null inside Order.kt. I don't realize what the problem is there. I can't find any similar questions here.
FirebaseOrderService.kt
object FirebaseOrderService {
private const val TAG = "FirebaseOrderService"
suspend fun getOrderData(): Order? {
val db = FirebaseFirestore.getInstance()
return try {
db.collection("Order")
.document().get().await().toOrder()
} catch (e: Exception) {
Log.e(TAG, "Error getting order details", e)
FirebaseCrashlytics.getInstance().log("Error getting order details")
FirebaseCrashlytics.getInstance().setCustomKey("id", "1")
FirebaseCrashlytics.getInstance().recordException(e)
null
}
}
SuccessShoppingViewModel.kt
class SuccessShoppingViewModel: ViewModel() {
private val _orderList = MutableLiveData<Order>()
val order: LiveData<Order> = _orderList
init {
viewModelScope.launch {
_orderList.value = FirebaseOrderService.getOrderData()
_orderList
}
}
Order.kt
#Parcelize
data class Order(
val id: String = "",
val picUrl: String = "",
val itemName: String = "",
val itemPrice: Double = 0.0,
val itemAmount: String = "",
val itemQuantatiy: Int = 0
) : Parcelable {
companion object {
fun DocumentSnapshot.toOrder(): Order? {
return try {
val id = getString("id")!!
val picUrl = getString("picUrl")!!
val itemName = getString("itemName")!!
val itemPrice = getLong("itemPrice")?.toDouble()!!
val itemAmount = getString("itemAmount")!!
val itemQuantatiy = getLong("itemQuantatiy")?.toInt()!!
Order(id, picUrl, itemName, itemPrice, itemAmount, itemQuantatiy)
} catch (e: Exception) {
Log.e(TAG, "Error converting order", e)
FirebaseCrashlytics.getInstance().log("Error converting order")
FirebaseCrashlytics.getInstance().setCustomKey("id", id)
FirebaseCrashlytics.getInstance().recordException(e)
null
}
}
private const val TAG = "Order"
}
}
You're getting null because of the following line of code:
db.collection("Order")
.document().get().await().toOrder()
When you are using the above line of code, it means that you are creating a reference to a document with a random ID. Calling CollectionReferenc#document() method, without passing any arguments:
Returns a DocumentReference pointing to a new document with an auto-generated ID within this collection.
So what you're actually doing, you're creating a reference that points to a document that doesn't exist. To solve this problem, you have to pass the ID of the document to the document() function like this:
db.collection("Order")
.document("eBW6...zIO1").get().await().toOrder()
// 👆
I have a ThreadActivity with two functions, saveContacts and loadContacts. They both use sharedpreferences and Gson to save an ArrayList consisting of Objects called SimpleContacts. Somehow it cannot retrieve data from sharedpreferences once I start the Activity from somewhere else. (I tried loading instantly after saving and that works, but not if I close the Activity and re-open it)
The save function:
private fun saveContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
try {
val editor = sharedPreferences.edit()
val json = gson.toJson(participants)
editor.putString(threadId.toString()+"_Contact", json)
editor.apply()
} catch(e: Exception) {
e.printStackTrace()
}
}
The load function:
private fun loadContact() {
val gson = Gson()
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val type = object : TypeToken<ArrayList<SimpleContact?>?>() {}.type
try {
val json = sharedPreferences.getString(threadId.toString()+"_Contact", "")
participants = gson.fromJson(json, type)
} catch(e: Exception) {
e.printStackTrace()
}
}
I have 2 Activities that can open this ThreadActivity, if I start it from the same one, it all works perfectly fine. But when I use the other Activity to start it, the sharedPrefs are empty.
Launch Activity that works (I don't know if its because its the way the Intent is build so I will write them both here):
private fun launchThreadActivity(phoneNumber: String, name: String) {
hideKeyboard()
val text = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
val numbers = phoneNumber.split(";").toSet()
val number = if (numbers.size == 1) phoneNumber else Gson().toJson(numbers)
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, getThreadId(numbers))
putExtra(THREAD_TITLE, name)
putExtra(THREAD_TEXT, text)
putExtra(THREAD_NUMBER, number)
if (intent.action == Intent.ACTION_SEND && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URI, uri?.toString())
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE && intent.extras?.containsKey(Intent.EXTRA_STREAM) == true) {
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
putExtra(THREAD_ATTACHMENT_URIS, uris)
}
startActivity(this)
}
}
Start Activity that does not work:
Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, (it as Conversation).threadId)
putExtra(THREAD_TITLE, it.title)
putExtra("fromMain", true)
startActivity(this)
}
Nevermind, it was my mistake.
When saveContact was called the threadId was not initialized yet. So basically the keys were always different.
New to RxJava.
I want to get UserInfo from cache or Net. when cache is null, then get it from net.
I use two RxJava.Single, try to use concatWith, but it doesn't work.
cacheX is a Single<UserInfo> , when null throw DatabaseItemNotFoundException(self defined.)
userService.getUserInfo is also a Single<UserInfo>
fun getUserInfoFromCacheOrServer(
context: Context, disposables: CompositeDisposable,
token: String?, forceUpdate: Boolean, callback: (UserInfo?, String?) -> Unit
) {
if (token.isNullOrBlank())
return
var save = false
val cacheRepository = CacheRepository(context)
val cacheX = Single.fromCallable {
val cache = if (forceUpdate) null else
cacheRepository.getCacheRx<UserInfo>(KEY_USER_INFO, null, 24 * 60)
Timber.d("cache=$cache")
cache ?: let {
save = true
throw DatabaseItemNotFoundException("cache is null or expired.")
}
}
// I want to get UserInfo from net, when cache is null.
val net = userService.getUserInfo(token.trim()).also { save = true }
cacheX.concatWith(net)
RxJavaResponseUtils.processSingle("getUserInfo", disposables, cacheX) { info, errMsg ->
if (save && info != null) {
cacheRepository.saveCache(KEY_USER_INFO, null, info)
}
callback(info, errMsg)
}
}
I am struggling with firebase to run one query to take the truckDocumentId and after that to run another query to take the routesByDateDocumentIdand at the end I am using both document ids to run the function "sendGpsPosition", my problem is that the first query finds truckDocumentId but sometimes the second query does not execute and that is why the applications stops. The code below is for Kotlin.
If I am on Debug then most of the time works.. if I switch off the debug it almost shows the error below =>
And because the query does not execute I got this error: java.lang.IllegalArgumentException: Invalid document reference. Document references must have an even number of segments, but trucks has 1
suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks").whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "PEUGEOT").get().await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
suspend fun getRouteReferenceId() {
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date").get().await()
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.id
}
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
runBlocking { getTruckId() } // if I get this DocumentID
runBlocking { getRouteReferenceId() } // this here maybe will be not found or maybe will be found.. the async is not done correct not sure how to do it.
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("live_route")
.add(LatLong(Timestamp.now(), lat, long))
}
**I solved it this way.**
private suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks")
.whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "VW")
.get()
.await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
private suspend fun getRouteReferenceId() {
val currentTime = Timestamp.now()
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.get()
.await() // here will be better to look for data by delivery_day
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.documents[0].id
}
}
private fun addGpsDataInDatabase(lat: Double, long: Double, imageRef: String? = null) {
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("planned_route") //planned_route or live_route depends if we want to show current state of a truck of make a route plan
.add(LatLong(Timestamp.now(), lat, long))
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
GlobalScope.launch {
val truckDocId = async { getTruckId() }
truckDocId.await()
val routeDocId = async { getRouteReferenceId() }
routeDocId.await()
addGpsDataInDatabase(lat, long, imageRef)
}
}
I am new to Android and Kotlin and am currently working on a centralized API router class.
To achieve this I am using the Fuel Framework.
For the doAsync function, I use the Anko for Kotlin library.
To retrieve an authorization token from the API I currently use this method:
private fun Login(username: String, password: String, callback: (Map<Boolean, String>) -> Unit) {
"/auth/token.json".httpPost()
.header(mapOf("Content-Type" to "application/json"))
.body("""{"username":"$username", "password":"$password"}""", Charsets.UTF_8)
.response { request, response, result ->
request.headers.remove("Accept-Encoding")
when (result) {
is Result.Failure -> {
// val data = result.get()
val ex = result.getException()
val serverResponseJson = response.data.toString(Charsets.UTF_8)
var exceptionMessage = ex.message
val jelement = JsonParser().parse(serverResponseJson)
val jobject = jelement.asJsonObject
val serverResponseError = if (jobject.has("Error")) jobject.get("Error").asString else jobject.get("detail").asString
callback(mapOf(Pair(false, serverResponseError)))
}
is Result.Success -> {
val data = result.get()
val returnJson = data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "RESULT_LOGIN", returnJson)
callback(mapOf(Pair(true, returnJson)))
}
}
}
}
I invoke this login method at
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
}[30, TimeUnit.SECONDS]
var test = Constants.id;
}
In a separate Constants class, I store the token and id like this:
class Constants {
companion object {
val baseUrl: String = "BASE_URL_TO_MY_API"
val contentTypeJson = "application/json"
lateinit var STOREAGE_PATH: String
// current user details
lateinit var id: String
lateinit var token: String
lateinit var refresh_token: String
// logged in User
lateinit var user: User
}
How do I make sure that the test variable is set after the asynchronous task is done? Currently, I run into
lateinit property id has not been initialized
I have come across the option to limit the task to a timeout such as I have done with [30, TimeUnit.SECONDS], unfortunately, this did not help.
Thanks for the help! Cheers.
I think the problem is where you want to access the result:
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
var test: String? = null
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
test = Constants.id // here test variable surely set if result was successful, otherwise it holds the null value
test?.let{
resultDelivered(it)
}
}[30, TimeUnit.SECONDS]
}
fun resultDelivered(id: String){
// here we know that the async job has successfully finished
}