How to use "Async & Await" here - java

I want to await the other processes until "Getting Username from firestore and putting it into postMap" How can I make them wait? Because if they don't wait username can not uploading to firestore and that cause some problems. I know I can use "Async & Await" method but how? (You can look at the comment lines that I created and see which processes are happening there.)
if(selectedPicture != null){
imageReference.putFile(selectedPicture!!).addOnSuccessListener {
val uploadPictureReference = storage.reference.child("images").child(imageName)
uploadPictureReference.downloadUrl.addOnSuccessListener {
val downloadUrl = it.toString()
if(auth.currentUser != null){
val postMap = hashMapOf<String,Any>()
postMap.put("downloadUrl",downloadUrl)
postMap.put("userEmail",auth.currentUser!!.email!!)
postMap.put("comment",binding.uploadCommentText.text.toString())
postMap.put("date",Timestamp.now())
//Get Username from firestore and put it into postMap
db.collection("UserDetails").addSnapshotListener { value, error ->
if(error!=null){
Toast.makeText(this,error.localizedMessage,Toast.LENGTH_LONG).show()
}else{
if(value!=null){
if(!value.isEmpty){
val documents = value.documents
for (document in documents){
val username = document.get("username")as String
//Put username into postMap
postMap.put("username",username) as String
}
}
}
}
}
//upload postmap to firestore
firestore.collection("Posts").add(postMap).addOnSuccessListener {
finish()
}.addOnFailureListener{
Toast.makeText(this,it.localizedMessage,Toast.LENGTH_LONG).show()
}
}
}
}.addOnFailureListener{
Toast.makeText(this,it.localizedMessage,Toast.LENGTH_LONG).show()
}
}

I don't know 100 % what you are trying to achieve, but by adding the firebase-ktx library, you can use .await() to get your values inside a coroutine.
// Returns true when everything was successful, or false if not
suspend fun getUserNameAndPutInPostMap(selectedPicture: File?): Boolean {
try {
if (selectedPicture == null || auth.currentUser == null) return
imageReference.putFile(selectedPicture!!).await()
val downloadUrl = storage.reference.child("images").child(imageName).downloadUrl.await().toString()
val userName = db.collection("UserDetails").get("username").await().toString()
val postMap = hashMapOf<String,Any>().apply {
put("downloadUrl", downloadUrl)
put("userEmail", auth.currentUser!!.email!!)
put("comment",binding.uploadCommentText.text.toString())
put("date",Timestamp.now())
put("username",username)
}
firestore.collection("Posts").add(postMap).await()
} catch (e: Exception) {
return false
}
}

The call to firestore.collection("Posts").add(postMap) will need to be inside the addSnapshotListener callback, right after you populate the postMap with postMap.put("username",username) as String.
db.collection("UserDetails").addSnapshotListener { value, error ->
if(error!=null){
Toast.makeText(this,error.localizedMessage,Toast.LENGTH_LONG).show()
}else{
if(value!=null){
if(!value.isEmpty){
val documents = value.documents
for (document in documents){
val username = document.get("username")as String
//Put username into postMap
postMap.put("username",username) as String
//upload postmap to firestore
firestore.collection("Posts").add(postMap).addOnSuccessListener {
finish()
}.addOnFailureListener{
Toast.makeText(this,it.localizedMessage,Toast.LENGTH_LONG).show()
}
}
}
}
}
}
I also recommend converting the onSnapshot to a get().addOnCompleteListener( call, because I'm pretty sure only mean to read the user data once.

Related

How to get click event for multiple GeoJsonLayers on Google maps

I am trying to add multiple GeoJsonLayer. When user click on 1 polygon I would like to display data of clicked polygon. Those are some of my polygons:
This is my function in which I get data from API and draw geojson layers on google maps.
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
var geojson = ArrayList<GeojsonResponse>()
val userGerkId: String? = SharedPrefManager.getInstance(applicationContext).user.gerkMID
RetrofitClientLands.instance.getLand(userGerkId).enqueue(object : Callback<GeojsonResponse> {
override fun onResponse(
call: Call<GeojsonResponse>,
response: Response<GeojsonResponse>
) {
if (response.code() == 200) {
val body = response.body()
if (body != null) {
for (i in 0 until body.lands.size) {
val geo = body.lands[i]
val geos = geo.get("geometry")
val properties = geo.get("properties")
//Log.i("Properties", properties.toString())
val geometryJson: JSONObject = JSONObject(geos.toString())
val geoJsonData: JSONObject = geometryJson
val layer = GeoJsonLayer(mMap, geoJsonData)
val style: GeoJsonPolygonStyle = layer.defaultPolygonStyle
style.fillColor = resources.getColor(R.color.darkGray)
style.strokeColor = resources.getColor(R.color.darkerGray)
style.strokeWidth = 2f
layer.addLayerToMap()
layer.setOnFeatureClickListener(
GeoJsonOnFeatureClickListener { feature: Feature ->
Toast.makeText(
applicationContext,
"GeoJSON polygon clicked: $properties",
Toast.LENGTH_SHORT
).show()
})
}
} else {
Log.i("Map-error", response.errorBody().toString())
}
}
}
override fun onFailure(call: Call<GeojsonResponse>, t: Throwable) {
Log.i("Map response", t.message.toString())
Toast.makeText(
applicationContext,
"Prišlo je do napake, na novo zaženite aplikacijo",
Toast.LENGTH_LONG
).show()
}
})
// adding marker
mMap.moveCamera(CameraUpdateFactory.newLatLng(LatLng(45.92757404830929, 15.595209429220395)))
mMap.uiSettings.isZoomControlsEnabled = true
mMap.animateCamera( CameraUpdateFactory.zoomTo( 12.5f ) );
}
I tried to set style.isClickable = false and then add code below, but every time i clicked on layer, it returns the same data (because whole map is clickable ig).
mMap.setOnMapClickListener {
Log.i("Map_clicked", "polygon: $properties")
}
So is there any other way of doing this? This thread has the same problem described.
How to add multiple GeoJsonLayer runtime and get click event in android

merge two lists of different objects

i'm using api requests that returns a list.
-the first api request returns a list of object that contains (user_id,content,date,title)
-the second response returns list of object too that contains (user_id,user_name).
i want to merge the two list the display them into one recycler view but keep user name instead of user_id.this image breaks down what i want clearly.
apprecuiate any help i'm really stuck in this and i need it ty .
EDIT
this is the first api call :
followuplist=new ArrayList<>();
Retrofit retrofit = RetrofitInstance.getRetrofitInstance();
final Api api = retrofit.create(Api.class);
Call<List<TraitementTicketModel>> call = api.getfollowup(id, sestoken);
call.enqueue(new Callback<List<TraitementTicketModel>>() {
#Override
public void onResponse(Call<List<TraitementTicketModel>> call, Response<List<TraitementTicketModel>> response) {
if (!response.isSuccessful()) {
Toast.makeText(getApplicationContext(), "Something is wrong !! ", Toast.LENGTH_LONG).show();
Log.e("TAG", "onResponse: something is wrong");
} else if (response.body() == null) {
return;
}
List<TraitementTicketModel> followups = response.body();
for (TraitementTicketModel followup : followups) {
followuplist.add(followup);
}
followuplist.add(firstfollowup());
}
#Override
public void onFailure(Call<List<TraitementTicketModel>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"Pas de connextion internet",Toast.LENGTH_LONG).show();
}
});
this is the second api call :
List<User> userList;
SharedPreferences sp =getApplicationContext().getSharedPreferences("tokenPref", Context.MODE_PRIVATE);
String sestoken = sp.getString("token","");
Retrofit retrofit= RetrofitInstance.getRetrofitInstance();
final Api api= retrofit.create(Api.class);
Call<List<User>> call = api.getUser(sestoken);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.code() != 200){
Log.e("TAG", "onResponse: something is wrong"+response.code() );
}
List<User> users = response.body();
for (User user : users){
userList.add(user);
}
swipeRefreshLayout.setRefreshing(false);
}
so I have two liststhe first one is :
followuplist (user_id,title,content,date)
and the second :
userList(user_id,user_name)
but i didn't know what to do after that to get to my goal
You can do something like that.
In this example UserDetails is the object on the left in your image, UserInfo the one on the right, and MergeData the result.
You should use Kotlin instead of Java, it's far easier to manipulate lists.
List<MergedData> mergeList(
List<UserDetails> listUserDetails,
List<UserInfo> listUserInfo
) {
// Resulting list
final List<MergedData> result = new ArrayList<>();
// We iterate through the first list
for (UserDetails details : listUserDetails) {
// For each element of the list we will try to find one with the same user id in the other list
for (UserInfo info : listUserInfo) {
// if the current element of the second list has the same user id as the current one from the first list, we merge the data in a new object and this object is then added to the result list.
if (details.getUserId().equals(info.getUserId())) {
result.add(
new MergedData(
info.getName(),
details.getContent(),
details.getTitre(),
details.getDate()
)
);
// Once the object is found it is unnecessary to continue looping though the second list, so we break the for loop.
break;
}
}
}
// Once we finished to iterate through the first list, we return the result.
return result;
}
Same example in Kotlin:
fun mergeList(
listUserDetails: List<UserDetails>,
listUserInfo: List<UserInfo>
): List<MergedData> =
listUserDetails.mapNotNull { details ->
listUserInfo
.firstOrNull { it.userId == details.userId }
?.let { info ->
MergedData(
info.name,
details.content,
details.titre,
details.date
)
}
}

Android: Open and read another app location

I need to develop an app which monitors a list of apps and, if the system is rebooted, return to the last open page of the monitored apps. So, say that android was on the youtube app playing lofi hip hop radio - beats to relax/study to. How could my app send android back to this given video? Also, is there a way for me to know where in the app the user is? For example, can I know that the user was on youtube playing lofi and open it back again for them?
I already know how to open another app as well as detect whichever app is currently open, but I need to know the its uri as well. I can use adb and shell script if it is needed.
I have the following code to launch another app:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val intent = Intent(this, BackAppListenerService::class.java)
startService(intent)
openApp(this, "com.google.android.youtube")
}
/** Open another app.
* #param context current Context, like Activity, App, or Service
* #param packageName the full package name of the app to open
* #return true if likely successful, false if unsuccessful
*/
fun openApp(context: Context, packageName: String?): Boolean {
val manager = context.packageManager
return try {
val i = manager.getLaunchIntentForPackage(packageName!!)
if (i == null) {
println("Activity not found")
return false;
//throw new ActivityNotFoundException();
}
//throw new ActivityNotFoundException();
i.addCategory(Intent.CATEGORY_LAUNCHER)
context.startActivity(i)
true
} catch (e: ActivityNotFoundException) {
println(e)
false
}
}
}
And with this I get the current foreground app
class BackAppListenerService : Service() {
private var isRunning = false
private var lastApp = ""
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate() {
isRunning = true
Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
Thread(Runnable {
while (true) {
try {
Thread.sleep(10)
} catch (e: Exception) {
}
val currentForegroundApp = getForegroundApp()
val currentApp = currentForegroundApp.first
if (currentApp != lastApp) {
// New app on front
lastApp = currentApp
println("Current App $lastApp")
}
}
}).start()
return START_STICKY
}
// Must Have Usage Access Permission
fun getForegroundApp(): Pair<String, UsageStats> {
var currentApp = "NULL"
var currentAppInfo: UsageStats? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val usm = this.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val time = System.currentTimeMillis()
val appList =
usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time)
if (appList != null && appList.size > 0) {
val mySortedMap: SortedMap<Long, UsageStats> =
TreeMap<Long, UsageStats>()
for (usageStats in appList) {
mySortedMap.put(usageStats.lastTimeUsed, usageStats)
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
currentAppInfo = mySortedMap[mySortedMap.lastKey()]!!
currentApp = mySortedMap[mySortedMap.lastKey()]!!.packageName
}
}
} else {
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val tasks = am.runningAppProcesses
currentApp = tasks[0].processName
}
return Pair(currentApp.split(".").last(), currentAppInfo!!)
}
[...]
}
Any help is much appreciated.

Migrate from FirebaseInstanceId.getInstance().getId() to FirebaseInstallations.getInstance().getId()

I have to migrate from the deprecated synchronous api FirebaseInstanceId.getInstance().getId(); to the new async firebase api:
FirebaseInstallations.getInstance().getId().addOnCompleteListener( task -> {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID
String id = task.getResult();
});
My problem is that in my old project, in many different point of my code a method like the following one has being called, and all the called expecting a response synchrounus in-line without callback:
public static String getDeviceId() {
if (deviceId == null) {
initDeviceId();
}
return deviceId;
}
private static void initDeviceId() {
deviceId = FirebaseInstanceId.getInstance().getId();
}
How can migrate the code without rewriting all the project ?
I have thought to to edit the above methods in this way:
public static String getDeviceId() {
if (deviceId == null) {
deviceId = workerThread.submit(initDeviceId()).get()
}
return deviceId;
}
private fun initDeviceId():Callable<String?>{
return Callable {
val latch = CountDownLatch(1)
var result:String ?= null
FirebaseInstallations.getInstance().id.addOnCompleteListener{
task -> result = task.result
latch.countDown()
}
latch.await()
result
}
}
but in this way I risk to block mainthread.

Firebase Dynamic Link Not getting Query parameter like Utm Source when using custom Domain

Here I am not getting GoogleAnalyticsParameters like soource ,medium when i am using custom domain
FirebaseDynamicLinks.getInstance().getDynamicLink(intent).addOnSuccessListener {
if (it != null) {
deepLink = it.link
}
}.addOnCompleteListener {
callCampaignApi(deepLink)
if (!appUtils.readStringFromPref(Constant.KEY_TOKEN).isNullOrBlank() && deepLink != null) {
try {
//means user is allready logged in
//source is used for screenname
var source = deepLink!!.getQueryParameter("utm_source")
// campaign is used for additional data like agentId or feedId
var campaign = deepLink!!.getQueryParameter("utm_campaign")
// utm _medium to track external sources
var medium = deepLink!!.getQueryParameter("utm_medium")
handleDynamicLinks(this, source ?: "", campaign ?: "", medium ?: "", deepLink!!)
this.finish()
} catch (ex: Exception) {
Log.e("DEEPLINK EXCEPTIONS", ex.message)
mDelayHandler!!.postDelayed(mRunnable, SPLASH_DELAY)
}
} else {
//take him to login or Registration with deeplink
if (deepLink != null) {
var source = deepLink!!.getQueryParameter("utm_source")
var campaign = deepLink!!.getQueryParameter("utm_campaign")
if (source?.equals(Constant.SCREEN_SEND_INVITATION)!! && campaign != null) {
//it means it has cp code /// send that value of cpcode to registration screen
launchLoginActivity(campaign)
} else {
//regular flow
mDelayHandler!!.postDelayed(mRunnable, SPLASH_DELAY)
}
} else {
//regular flow
mDelayHandler!!.postDelayed(mRunnable, SPLASH_DELAY)
}
}
}
Expected Result is utm source,campaign gets appended in link automatically

Categories

Resources