Task.whenAllSuccess function always gives me the same task - kotlin

I have a small problem with the processing of my task. I have a list (listImg) which contains bitmaps. Now I try to upload each image to Firebase Storage through a forEach loop and then pack it in an UploadTask. In the addOnSuccessListener, however, the same image is called every time. For example, I have 3 images in the listImg. All 3 show up correctly in the loop. I pack these into the task array. However, in the Task.whenAllSuccess function, each URL of the 3 tasks leads to the same image
What is it?
downloadUrls = mutableListOf<String>()
val tasks = mutableListOf<UploadTask>()
listImg.forEach {
if(bitmap!!.byteCount != it.byteCount) {
var spaceRef = storageRef.child("objektImages/"+UUID.randomUUID().toString()+".jpg")
val bitmap = it
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos)
val data = baos.toByteArray()
var uploadTask = spaceRef.putBytes(data)
//Here log gives me different images, which is also intended
Log.d("URL:",bitmap.toString())
tasks.add(uploadTask)
}
}
Tasks.whenAllSuccess<UploadTask.TaskSnapshot>(tasks).addOnSuccessListener { taskSnapshot ->
taskSnapshot!!.forEach {
it.metadata!!.reference!!.downloadUrl.addOnSuccessListener { url->
//But here every downloadUrl leads to the same image.
downloadUrls.add(url.toString())
...
}
}
}

Related

onSensorChanged function is called in an other app but not in mine (Kotlin)

i want to implement a step counter in my app, so i search how to make that and i found lot of differents implementations.
I notably found an app on GitHub which works. I have tried to implement this code in my app and in an other "test" app but any of them works and i don't no why.
The problem is caused by the onSensorChanged function of my STEP_COUNTER which is not called.
I have search in all the files of the app and i don't found the problem.
If somebody have a solution...
(I'm french so sorry if it's badly written)
the code i use:
private var sensorManager: SensorManager? = null
// Creating a variable which will give the running status
// and initially given the boolean value as false
private var running = false
// Creating a variable which will counts total steps
// and it has been given the value of 0 float
private var totalSteps = 0f
// Creating a variable which counts previous total
// steps and it has also been given the value of 0 float
private var previousTotalSteps = 0f
//in the onCreate
loadData()
resetSteps()
// Adding a context of SENSOR_SERVICE as Sensor Manager
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
override fun onResume() {
super.onResume()
mainHandler.post(pingRunnable)
binding.map.onResume()
running = true
// Returns the number of steps taken by the user since the last reboot while activated
// This sensor requires permission android.permission.ACTIVITY_RECOGNITION.
// So don't forget to add the following permission in AndroidManifest.xml present in manifest folder of the app.
val stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
if (stepSensor == null) {
// This will give a toast message to the user if there is no sensor in the device
Toast.makeText(this, "No sensor detected on this device", Toast.LENGTH_SHORT).show()
} else {
// Rate suitable for the user interface
sensorManager?.registerListener(this, stepSensor, SensorManager.SENSOR_DELAY_UI)
}
}
override fun onSensorChanged(event: SensorEvent?) {
// Calling the TextView that we made in activity_main.xml
// by the id given to that TextView
var tvStepsTaken = findViewById<TextView>(R.id.step)
if (running) {
totalSteps = event!!.values[0]
// Current steps are calculated by taking the difference of total steps
// and previous steps
val currentSteps = totalSteps.toInt() - previousTotalSteps.toInt()
// It will show the current steps to the user
tvStepsTaken.text = ("$currentSteps")
}
}
private fun resetSteps() {
var resetButton = findViewById<Button>(R.id.reset)
resetButton.setOnClickListener {
// This will give a toast message if the user want to reset the steps
previousTotalSteps = totalSteps
// When the user will click long tap on the screen,
// the steps will be reset to 0
testFragment?.binding?.step?.text = 0.toString()
// This will save the data
saveData()
true
}
}
private fun saveData() {
// Shared Preferences will allow us to save
// and retrieve data in the form of key,value pair.
// In this function we will save data
val sharedPreferences = getSharedPreferences("myPrefs", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putFloat("key1", previousTotalSteps)
editor.apply()
}
private fun loadData() {
// In this function we will retrieve data
val sharedPreferences = getSharedPreferences("myPrefs", Context.MODE_PRIVATE)
val savedNumber = sharedPreferences.getFloat("key1", 0f)
// Log.d is used for debugging purposes
Log.d("MainActivity", "$savedNumber")
previousTotalSteps = savedNumber
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// We do not have to write anything in this function for this app
}

Why does the author add key(task) for the items function in LazyColumn?

The Code A is from the offical sample project.
I don't understand why the author need to add key(task) for the items function in LazyColumn, could you tell me?
I think the Code B can work well and it's simple.
Code A
val allTasks = stringArrayResource(R.array.tasks)
val tasks = remember { mutableStateListOf(*allTasks) }
...
items(count = tasks.size) { i ->
val task = tasks.getOrNull(i)
if (task != null) {
key(task) {
TaskRow(
task = task,
onRemove = { tasks.remove(task) }
)
}
}
}
Code B
...
items(tasks) { task ->
TaskRow(
task = task,
onRemove = { tasks.remove(task) }
)
}
Keys in general are used to avoid unnecessary recompositions. If you have a list of items, and some items are reordered, Compose will find existing components in the composition based on their key and reorder them accordingly instead of recomposing all of them.
That said, items() already supports a key parameter. So code A could indeed be simplified this way if the tasks in the list are non-nullable:
items(tasks, key = { it }) { task ->
TaskRow(
task = task,
onRemove = { tasks.remove(task) },
)
}
But code B would use the items' positions instead of the items themselves as key by default, which is not desirable.
Disclaimer: I'm no expert in JetPack Compose

I want send a filepart image with Vertx, I want replicate this case in code like Postman

PostmanExample
fun sendFileToMatch(path:String){
val client = WebClient.create(vertx);
var form = MultipartForm.create()
.binaryFileUpload("image","imageName" , path, "image/jpeg")
client.post(8888, "localhost", "/search?")
.putHeader("content-type", "multipart/form-data")
.sendMultipartForm(form) { }
}
when I run the code show bad request I have put exactly key "image" and send filepart image
TL;DR - your client code seems fine.
The only suspicious part is the path itself, as you don't specify how exactly you get it in your code, and the fact that you didn't specify how you handle response from the server: you just do {} in your example
Here is a fully working example for you to refer to, though:
val vertx = Vertx.vertx()
val router = Router.router(vertx)
router.route().handler(BodyHandler.create());
router.post("/search").handler {
val uploads: Set<FileUpload> = it.fileUploads()
uploads.forEach { upload ->
println(upload.name()) // "image"
println(upload.fileName()) // "imageName"
println(upload.size()) // 42537
}
it.response().end("OK!")
}
vertx.createHttpServer().requestHandler(router)
.listen(8888)
// We read the PNG file from /resources
val path = object {}.javaClass.getResource("5EWx9.png").path
val form = MultipartForm.create()
.binaryFileUpload("image","imageName" , path, "image/png")
val client = WebClient.create(vertx);
client.post(8888, "localhost", "/search?")
.putHeader("content-type", "multipart/form-data")
.sendMultipartForm(form) {
if (it.succeeded()) {
println(it.result().bodyAsString()) // "OK!"
}
else {
println(it.cause())
}
}
As the file to upload, I used the PostmanExample you've provided, which is a PNG image, which I put in the /resources directory of my project.

Is it possible to combine mapAreas from ArcGISOnline in one offline map on android?

I try to create an offline map using ArcGIS Runtime SDK for Android 100.5.0. I follow preplanned workflow according the guide https://developers.arcgis.com/android/latest/guide/take-map-offline-preplanned.htm. I create mapAreas in ArcGISOnline and try to download them from device. I want to get an offline map, which contains all mapAreas together like in app maps.me(on a big map you have downloaded regions with deeper detailing), but instead I am getting an offline map made from last downloaded area. So I created mapArea "Europe" with scale world - cities and mapArea "Berlin" with scale cities - buildings (both basemaps - openstreetmaps, no feature layers) and downloaded them successfully, see 2 tpk files in a folder, but mobile_map.mmpk and package.info files only contain data related to last loaded area. Is it possible at all to get what I want, combine tpk files in one map?
My code in Kotlin:
val portal = Portal("https://www.arcgis.com/", false)
val portalItem = PortalItem(portal, itemID)
val offlineMapTask = OfflineMapTask(portalItem)
//get all of the preplanned map areas in the web map
val mapAreasFuture = offlineMapTask.preplannedMapAreasAsync
mapAreasFuture.addDoneListener {
try {
// get the list of areas
val mapAreas = mapAreasFuture.get()
val directory = getDirectory()
prepareDir(directory)
// loop through the map areas
var i = 0
for (mapArea in mapAreas) {
mapArea.loadAsync()
mapArea.addDoneLoadingListener {
val downloadJob = offlineMapTask.downloadPreplannedOfflineMap(mapArea, directory)
downloadJob.start()
downloadJob.addJobDoneListener {
i++
if (i == mapAreas.size) {
val offlineMapPackage = MobileMapPackage(path)
offlineMapPackage.addDoneLoadingListener({
if (offlineMapPackage.getLoadStatus() === LoadStatus.LOADED) {
val mobileMap = offlineMapPackage.getMaps().get(0)
myCompletionFuncToShowMap(mobileMap)
} else {
println("PACKAGING FAILED")
}
})
offlineMapPackage.loadAsync()
}
}
}
}
} catch (e: InterruptedException) {
e.printStackTrace()
} catch (e: Exception) {
e.printStackTrace()
}
I duplicated the question on community.esri.com and got an answer, thanks to Luke Smallwood from esri. So, yes, it is possible. The "Europe" map area has to be added as basemap layer if I set mapView.map to Berlin and also every map area has to be saved in its own directory.
Downloading areas:
val portal = Portal("https://www.arcgis.com/", false)
val portalItem = PortalItem(portal, itemID)
val offlineMapTask = OfflineMapTask(portalItem)
//get all of the preplanned map areas in the web map
val mapAreasFuture = offlineMapTask.preplannedMapAreasAsync
mapAreasFuture.addDoneListener {
try {
// get the list of areas
val mapAreas = mapAreasFuture.get()
val directory = getDirectory() //my function returns String path
// loop through the map areas
for (mapArea in mapAreas) {
mapArea.loadAsync()
mapArea.addDoneLoadingListener {
val downloadJob = offlineMapTask.downloadPreplannedOfflineMap(mapArea, directory + "/" + mapArea.portalItem.title)
downloadJob.start()
downloadJob.addJobDoneListener {
val offlineMapPackage = MobileMapPackage(directory + "/" + mapArea.portalItem.title)
offlineMapPackage.addDoneLoadingListener({
if (offlineMapPackage.getLoadStatus() != LoadStatus.LOADED) {
println("PACKAGING FAILED")
}
})
offlineMapPackage.loadAsync()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Files after downloading:
.../files/maps/Berlin/p13/129294b8-3a70-4d79-a421-24ff14cb19fc.tpk
.../files/maps/Berlin/p13/mobile_map.mmap
.../files/maps/Berlin/package.info
.../files/maps/Europa/p13/2547a985-c98f-49be-a187-5ae3b7a9da09.tpk
.../files/maps/Europa/p13/mobile_map.mmap
.../files/maps/Europa/package.info
Displaying an offline map:
val path = getDirectory() + "/Berlin"
val offlineMapPackage = MobileMapPackage(path)
offlineMapPackage.addDoneLoadingListener {
if (offlineMapPackage.getLoadStatus() == LoadStatus.LOADED && !offlineMapPackage.getMaps().isEmpty()) {
mapView.map = offlineMapPackage.getMaps().get(0)
val cache = TileCache(getDirectory() + "/Europa/p13/2547a985-c98f-49be-a187-5ae3b7a9da09.tpk")
val layer = ArcGISTiledLayer(cache)
mapView.map.basemap.baseLayers.add(layer)
mapView.map.minScale = 1.8489297737236E7
mapView.map.maxScale = 2256.994353
} else {
println("NO MAP FILES")
}
}
offlineMapPackage.loadAsync()
It is important to adjust mapView.map.minScale to include Europe levels, otherwise on device the map only allows to scale between Berlin levels. The scale levels are listed here https://www.esri.com/arcgis-blog/products/product/mapping/web-map-zoom-levels-updated/ .

Copying sitecore rendering to new template programmatically using renderingDefinition.ItemId?

I have a custom sitecore button which changes the template of the current item, simple enough.
However as part of this I'm trying to also migrate the renderings of the old layout to a new layout if it's of a certain sublayout type by ItemId. However the ItemId that is returned is always null, the only value I get back from the RenderingDefinition is the UniqueId.
What am I doing wrong?
I have used this blog post as a guide.
The Code
public class ConvertToNewTemplateCommand : Command
{
protected void Run(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item item = Context.ContentDatabase.Items[args.Parameters["id"]];
if (args.IsPostBack)
{
if (args.Result == "yes")
{
//Get current layout details
var originalLayoutXml = item[FieldIDs.LayoutField];
//Get new template
TemplateItem hubTemplate = Context.ContentDatabase.GetTemplate("some guid...");
//Change template
item.ChangeTemplate(hubTemplate);
//Reset laytout
ResetLayout(item);
//Get reset layout
var newLayoutXml = item[FieldIDs.LayoutField];
//Add all the module containers to the new layout in the central column
MoveModuleContainers(item, originalLayoutXml, newLayoutXml);
}
}
}
private void MoveModuleContainers(Item item, string oldXml, string newXml)
{
var oldLayout = LayoutDefinition.Parse(oldXml);
var newLayout = LayoutDefinition.Parse(newXml);
bool updated = false;
var oldRenderings = (oldLayout.Devices[0] as DeviceDefinition).Renderings;
var newRenderings = (newLayout.Devices[0] as DeviceDefinition).Renderings;
foreach (RenderingDefinition rendering in oldRenderings)
{
// Here is where the rendering.ItemID is always null
if (rendering != null && !String.IsNullOrEmpty(rendering.ItemID) && new Guid(rendering.ItemID) == new Guid("matching guid..."))
{
rendering.Placeholder = "middlecolumn";
newRenderings.Add(rendering);
updated = true;
}
}
if (updated)
{
// Save item...
}
}
}
I got onto Sitecore support in the end which informed me that I should use:
Sitecore.Data.Fields.LayoutField.GetFieldValue(item.Fields[Sitecore.FieldIDs.LayoutField])
instead of:
item[FieldIDs.LayoutField]
to get the items layoutField correctly. This results in the rendering values being parsed correctly and as they say the rest is history.