I'm making a download App and to keep the user inform about the progress I use an UIView on top of the main View.
I've got trouble with updating my labels and progressBars.
I try different way to do it like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// do some task
dispatch_async(dispatch_get_main_queue()) {
// update some UI
}
}
And also:
self.lblSpeedDownload.setNeedsDisplay()
But it does not update the UI every time it's call
So I tried this https://stackoverflow.com/a/26744884/2410264
For now it's look like it is the best solution I made but it's still not enough quickly update: it's look like the more I call update function, the more it's take time to update UI (1-2-10-20 seconds...)
Here is my code:
A class variable:
let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
Call when I start download:
dispatch_source_set_event_handler(source) {
[unowned self] in
self.updateDownloadView(self.mediaIndex, numberOfMedia: self.numberOfMedia)
}
dispatch_resume(source)
Call when I want to update UI:
dispatch_source_merge_data(source, 1);
And the update function:
func updateDownloadView(mediaIndex : Float, numberOfMedia:Int32){
if downloadPopup != nil {
var tempText:String
if self.selectedDirectories.count == 1 {
tempText = "Folder "
} else {
tempText = "Folders "
}
self.lblFolders.text = tempText + (self.downloadIndex+1).description + "/" + self.selectedDirectories.count.description + " :"
if numberOfMedia == 1 {
tempText = "Image "
} else {
tempText = "Images "
}
self.lblImages.text = tempText + Int(floor(mediaIndex)+1).description + "/" + numberOfMedia.description + " :"
let images_progress = mediaIndex/Float(numberOfMedia)
let folders_progress = Float(self.downloadIndex)+images_progress/Float(self.selectedDirectories.count)
self.progressFolders.setProgress(folders_progress, animated: true)
self.progressImages.setProgress(images_progress, animated: true)
self.lblSpeedDownload.text = Util.getByteString(self.downloadPopup.getDownloadSpeedBySecond())+"/s"
}
}
Do you find something wrong with my code?
Do you have other way to update UI?
Thank you for reading me!
Related
I am making an app for ios using nativescript angular. I am selecting and converting a file to base64 to send to server easily. Everything works correctly but if a file has space in it eg: 'myFile 1' then the path of that file is read as 'myFile%201' and I think this is the reason that readSync() is not working. Here is my Code.
const files = await openFilePicker({
multipleSelection:true,
// extensions:['application/pdf','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','png','jpeg']
// extensions:['application/pdf','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document']
// extensions:[]
})
for(let result of files['files'])
{
// extra line to see console easily/
console.log("");
console.log("result is : ",result)
let selectedFile:File;
if(isAndroid)
{
selectedFile = File.fromPath(result);
}
else
{
selectedFile = File.fromPath(result.replace("file://",""));
}
console.log("");
console.log("\n\nselected File : ", selectedFile)
if(this.isExtensionAllowed(selectedFile['_extension']))
{
if(this.maxAttachmentCount <= 0)
{
this.maxAttachmentCount=0;
}
else
{
this.maxAttachmentCount--;
}
let data = selectedFile.readSync();
console.log("")
console.log("data is : ",data)
let base64File = "";
if(isIOS)
{
base64File = data.base64EncodedStringWithOptions(0);
}
else
{
base64File = android.util.Base64.encodeToString(data,android.util.Base64.NO_WRAP);
}
console.log("");
console.log("base64 is : ", base64File)
let attachmentInfo = {name:selectedFile['_name'],extension:'.'+selectedFile['_extension'],
base64:base64File};
if(this.base64Attachments.length == 4)
{
this.base64Attachments.shift();
}
(<any[]>this.base64Attachments).push(attachmentInfo);
}
}
And here is a screenshot of file name without space which is working:
And here is a screenshot of file name with space which is not working:
I finally had to go to ChatGPT for answer. Apparently I had to use decodeURI(path) to get decoded path and then use File.fromPath.
if(isAndroid)
{
selectedFile = File.fromPath(result);
}
else
{
result = decodeURI(result);
selectedFile = File.fromPath(result.replace("file://",""));
}
I am using Firebase for a very simple purpose-- just to create a real-time button for WordPress that disappears on-click. There are no users/ authentication.
How can I make security rules that prevent someone from tampering with the database, but allow the button to work? The simple way I know how is to set the security write": false, but then the button won't work as the Firebase variables cannot be altered/updated. Documentation seems to be more directed at apps with users, but does seem to mention being able to set limits on read/write/etc for certain paths.
Perhaps I can make it so that only the used Firebase paths/variables used can be updated?
var database = firebase.database();
//firebase queue
//**************
///timer fb
let timestamp;
let now = new Date().getTime();
let endTimeRef = firebase.database().ref("server");
let endTime;
let minutes;
let secondsDisplay;
let distance= endTime-now;
let buttonDisplay= document.getElementById("queue");
let timerDisplay = document.getElementById("timer_fb");
let message= document.getElementById("timer_div");
function displayTimer(){
minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
secondsDisplay= Math.floor((distance % (1000 * 60)) / 1000);
let s;
if (minutes >= 1 || secondsDisplay >= 1) {
if (minutes >= 1 && secondsDisplay >= 10) {
s = "Time Until Next Person: " + minutes + ":" + secondsDisplay;
} else if (minutes >= 1) {
s = "Time Until Next Person: " + minutes + ":0" + secondsDisplay;
} else {
s = "Time Until Next Person: " + secondsDisplay + "s";
}
} else {
s = "";
}
document.getElementById("timer_fb").innerHTML = s;
}
function setTimer() {
let interval = setInterval(function() {
now = new Date().getTime();
distance= endTime-now;
//update timer display
displayTimer();
if (distance <= 0) {
buttonDisplay.style.display = "block";
timer_div.style.display = "block";
document.getElementById("timer_div").innerHTML = "This session is open!";
clearInterval(interval);
timerState.set('off');
endTimeRef.set(0);
}
}, 1000);
}
//display timer
//state machine
let timerState = firebase.database().ref("timerState");
let timer;
timerState.on("value", function(snapshot) {
timer= snapshot.val();
if(timer == "on"){
buttonDisplay.style.display = "none";
message.style.display = "none";
endTimeRef.once("value", function(snap) {
let endStamp = snap.val();
console.log("stored button snap value check: ", snap.val());
now = new Date().getTime();
endTime= endStamp +100000;
distance= endTime-now;
//show display right after button is pressed
displayTimer();
setTimer();
});
}
});
//********
//*****on click handler
document.getElementById("queue").onclick = function() {
//hide button
buttonDisplay.style.display = "none";
message.style.display = "none";
//set time button comes back
now = new Date().getTime();
endTime= now + 100000;
endTimeRef.set(now);
//firebase state machine
timerState.set('on');
//setInterval handler, used for timer/countdown
//need to wrap interval in function so that it can be reused
};
I don't understand what do you mean only the used Firebase paths/variables used can be updated?
But if you are not using authentication how do you distinguish which users able to access this value which users can not.Or you want make all user can read this value only in some case can modify?
I've this method :
func stepThree() {
operation = "prDatas"
let entries = self.data.componentsSeparatedByString("|***|")
total = entries.count
for entry in entries {
++current
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {
self.registerDB(entry)
})
}
status.setProgress(Float(current/total), animated: true)
finishAll()
}
I want to perform registerDB function and update my progressBar when complete.
I tested several way but never succeed
EDIT 1
Implementing #Russell proposition, work perfectly, but calculating value inside dispatch_async block always result to 0
Is there an issue about operations and multithread ?
method :
func stepThree() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
var current = 0
var total = 0
self.operation = "prDatas"
let entries = self.data.componentsSeparatedByString("|***|")
total = entries.count
for entry in entries {
++current
self.registerDB(entry)
dispatch_async(dispatch_get_main_queue(), {
print("value of 'current' is :" + String(current))
print("value of 'total' is :" + String(total))
print("Result is : " + String(Float(current/total)))
self.updateV(Float(current/total))
})
}
})
}
Console output :
value of 'current' is :71
value of 'total' is :1328
Result is : 0.0
Your code will update the status bar immediately - so the job will not have finished.
You need to move the update so that it actually follows the registerDB function, and then you have to make the call on the main thread. Here's an example - using dummy functions instead of your function calls, so that I can ensure it works as expected
func stepThree()
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let total = 5 // test data for demo
for entry in 0...total
{
// dummy function - just pause
sleep(1)
//self.registerDB(entry)
// make UI update on main queue
dispatch_async(dispatch_get_main_queue(),
{
self.setProgress(Float(entry)/Float(total))
})
}
})
}
func setProgress(progress : Float)
{
progressView.progress = progress
lblProgress.text = String(format: "%0.2f", progress)
}
new to actionscript and looking at the GeolocationEvent.UPDATE examples, having some unexpected results with .appendText() and an array.push --I didn't know whether they might both be just the phone not keeping up with the updates?
first, the text problem is that it's overwriting rather than replacing the last write, so after a couple minutes of the app running on the phone, you can't read the numbers any more. --using this.removeChild() and then addChild() was about trying to get it to remove the last write before writing again.
and then second, the problem with the array is that it's outputting random .length numbers in the trace() --the length looks to occasionally reset to 2 before counting up again, and counts up to seemingly random numbers. I know that I don't want the overhead of an array in the final version, but I'm trying to learn from why it's not working.
I've commented out the different things I've tried --sorry if I've missed something basic here
var format:TextFormat = new TextFormat();
format.color = 0xff0066;
format.font = "Lucida Console";
format.size = 20;
var fl_GeolocationDisplay:TextField = new TextField();
fl_GeolocationDisplay.defaultTextFormat = format;
fl_GeolocationDisplay.x = 10;
fl_GeolocationDisplay.y = 20;
fl_GeolocationDisplay.selectable = false;
fl_GeolocationDisplay.autoSize = TextFieldAutoSize.LEFT;
//fl_GeolocationDisplay.text = "Geolocation is not responding. Verify the device's location settings.";
fl_GeolocationDisplay.text = " ";
addChild(fl_GeolocationDisplay);
var gpsArray:Array = [42.09646417];
if(!Geolocation.isSupported)
{
fl_GeolocationDisplay.text = "Geolocation is not supported on this device.";
}
else
{
var fl_Geolocation:Geolocation = new Geolocation();
fl_Geolocation.setRequestedUpdateInterval(60000); //android overrides setRequestedUpdateInterval()
fl_Geolocation.addEventListener(GeolocationEvent.UPDATE, fl_UpdateGeolocation);
fl_Geolocation.addEventListener(StatusEvent.STATUS, gpsStatusHandler);
}
function fl_UpdateGeolocation(event:GeolocationEvent):void
{
//gpsArray.push(event.latitude);
//gpsArray[gpsArray.length] = event.latitude;
gpsArray.unshift(event.latitude);
var speed:Number = event.speed * 2.23693629;
if (gpsArray[gpsArray.length - 2] != gpsArray[gpsArray.length - 1])
{
trace(gpsArray.length + "|" + gpsArray[gpsArray.length - 2] + "|" + gpsArray[gpsArray.length - 1]);
trace(gpsArray[1] + "|" + gpsArray[0]);
trace(gpsArray[gpsArray.length - 2] - gpsArray[gpsArray.length - 1]);
}
//this.removeChild(fl_GeolocationDisplay);
fl_GeolocationDisplay.parent.removeChild(fl_GeolocationDisplay);
//fl_GeolocationDisplay = null; //TypeError: Error #2007: Parameter child must be non-null.
addChild(fl_GeolocationDisplay);
fl_GeolocationDisplay.text = (event.latitude.toString() + " | " + event.timestamp.toString());
//fl_GeolocationDisplay.text = (event.latitude.toString() + "\n");
//fl_GeolocationDisplay.appendText(event.latitude.toString() + "\n");
//fl_GeolocationDisplay.appendText(event.longitude.toString() + "\n");
}
function gpsStatusHandler(event:StatusEvent):void {
if (fl_Geolocation.muted) {
fl_GeolocationDisplay.text = "Please verify the device's location settings.";
}
}
I really can't understand what it is that you are trying to do, I mean you say one thing but your code seem to say something different.
There is also a serious issue about where the different code snippets are located? It Seems like the top part is inside a constructor. And then the bottom part are their own functions? If that is the case, make sure that the constructor is not run multiple times (which seems to be the issue and explaining why items are "overwritten" on top of each other.
Also your question states something about appendText but it seems like you want to replace the text inside the textfield? AppendText will add extra text inside that text field.
Anyways, I did an implementation from your code that gets the "longitude|lattitude" from the update event and then appends these to the textfield on a new line. Maybe this is what you wanted to do? I commented out the gps-array since I had no idea what it was that you tried to achieve by doing this:
package {
import flash.events.GeolocationEvent;
import flash.events.StatusEvent;
import flash.sensors.Geolocation;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class Foobar extends MovieClip {
var gpsArray:Array = [42.09646417];
var format:TextFormat = new TextFormat();
var fl_GeolocationDisplay:TextField = new TextField();
var fl_Geolocation:Geolocation = new Geolocation();
public function Foobar() {
format.color = 0xff0066;
format.font = "Lucida Console";
format.size = 20;
fl_GeolocationDisplay.defaultTextFormat = format;
fl_GeolocationDisplay.x = 10;
fl_GeolocationDisplay.y = 20;
fl_GeolocationDisplay.selectable = false;
fl_GeolocationDisplay.autoSize = TextFieldAutoSize.LEFT;
//fl_GeolocationDisplay.text = "Geolocation is not responding. Verify the device's location settings.";
fl_GeolocationDisplay.text = " ";
addChild(fl_GeolocationDisplay);
if(!Geolocation.isSupported) {
trace("unsupported");
fl_GeolocationDisplay.text = "Geolocation is not supported on this device.";
} else {
trace("supported");
fl_Geolocation.setRequestedUpdateInterval(500); //android overrides setRequestedUpdateInterval()
fl_Geolocation.addEventListener(GeolocationEvent.UPDATE, fl_UpdateGeolocation);
fl_Geolocation.addEventListener(StatusEvent.STATUS, gpsStatusHandler);
}
}
function fl_UpdateGeolocation(event:GeolocationEvent):void {
/*gpsArray.unshift(event.latitude);
var speed:Number = event.speed * 2.23693629;
if (gpsArray[gpsArray.length - 2] != gpsArray[gpsArray.length - 1]) {
trace(gpsArray.length + "|" + gpsArray[gpsArray.length - 2] + "|" + gpsArray[gpsArray.length - 1]);
trace(gpsArray[1] + "|" + gpsArray[0]);
trace(gpsArray[gpsArray.length - 2] - gpsArray[gpsArray.length - 1]);
}*/
fl_GeolocationDisplay.appendText(event.latitude.toString() + "|" + event.longitude.toString() + "\n");
}
function gpsStatusHandler(event:StatusEvent):void {
if (fl_Geolocation.muted) {
fl_GeolocationDisplay.text = "Please verify the device's location settings.";
}
}
}
}
I have a Google Calendar for a school website I'm working on and am using the Google API to display the next five calendar events. One problem is that the time displays on a 24 hour clock instead of AM and PM, but that's not my main problem. The main problem is that while the events display the correct time on the website, when you click on the event to view it in the calendar event view, it will only display GMT time instead of Eastern Time. While logged into the Google account, the events display the right time zone, but whenever you view it while not logged in, it defaults to GMT.
I have tried changing it to another time zone and change it back, didn't fix it.
I also made sure all settings in both the calendar and the account were set to Eastern time zone, at least everywhere I could find it.
I've seen a lot of people with similar problems on Google sites using the ical or other feeds, but I haven't seen anyone with the problem using a code similar to mine.
The website is live: http://fletcheracademy.com. And here is the main javascript code that pulls it.
There's probably some details I'm missing, let me know if there's anything else you need to know. Thanks so much!
<script type="text/javascript">
google.load("gdata", "2.x");
function init() {
google.gdata.client.init(handleGDError);
loadDeveloperCalendar();
}
function loadDeveloperCalendar() {
loadCalendarByAddress('fletcheracademycalendar#gmail.com');
}
function padNumber(num) {
if (num <= 9) {
return "0" + num;
}
return num;
}
function loadCalendarByAddress(calendarAddress) {
var calendarUrl = 'https://www.google.com/calendar/feeds/' +
calendarAddress + '/public/full';
loadCalendar(calendarUrl);
}
function loadCalendar(calendarUrl) {
var service = new
google.gdata.calendar.CalendarService('gdata-js-client-samples-simple');
var query = new google.gdata.calendar.CalendarEventQuery(calendarUrl);
query.setOrderBy('starttime');
query.setSortOrder('ascending');
query.setFutureEvents(true);
query.setSingleEvents(true);
query.setMaxResults(5);
service.getEventsFeed(query, listEvents, handleGDError);
}
function handleGDError(e) {
document.getElementById('jsSourceFinal').setAttribute('style', 'display:none');
if (e instanceof Error) {
alert('Error at line ' + e.lineNumber + ' in ' + e.fileName + '\n' + 'Message: ' + e.message);
if (e.cause) {
var status = e.cause.status;
var statusText = e.cause.statusText;
alert('Root cause: HTTP error ' + status + ' with status text of: ' + statusText);
}
} else {
alert(e.toString());
}
}
function listEvents(feedRoot) {
var entries = feedRoot.feed.getEntries();
var eventDiv = document.getElementById('events');
if (eventDiv.childNodes.length > 0) {
eventDiv.removeChild(eventDiv.childNodes[0]);
}
var ul = document.createElement('ul');
//document.getElementById('calendarTitle').innerHTML =
// "Calendar: " + feedRoot.feed.title.$t;
var len = entries.length;
for (var i = 0; i < len; i++) {
var entry = entries[i];
var title = entry.getTitle().getText();
var startDateTime = null;
var startJSDate = null;
var times = entry.getTimes();
if (times.length > 0) {
startDateTime = times[0].getStartTime();
startJSDate = startDateTime.getDate();
}
var entryLinkHref = null;
if (entry.getHtmlLink() != null) {
entryLinkHref = entry.getHtmlLink().getHref();
}
var dateString = (startJSDate.getMonth() + 1) + "/" + startJSDate.getDate();
if (!startDateTime.isDateOnly()) {
dateString += " " + startJSDate.getHours() + ":" +
padNumber(startJSDate.getMinutes());
}
var li = document.createElement('li');
if (entryLinkHref != null) {
entryLink = document.createElement('a');
entryLink.setAttribute('href', entryLinkHref);
entryLink.appendChild(document.createTextNode(title));
li.appendChild(entryLink);
li.appendChild(document.createTextNode(' - ' + dateString));
} else {
li.appendChild(document.createTextNode(title + ' - ' + dateString));
}
ul.appendChild(li);
}
eventDiv.appendChild(ul);
}
google.setOnLoadCallback(init);
</script>
Try this!
Where you have:
var calendarUrl = 'https://www.google.com/calendar/feeds/' + calendarAddress + '/public/full';
you should add something like:
&ctz=Europe/Lisbon
Check here for the correct name of your timezone.