Codename One: dealing with server/connection unavailability when loading images - codenameone

It seems that if at the time when URLImage.createCachedImage is called to load and the image is not available (no connection, etc.), it won't be called again to reload when the connection comes back.
Description:
When the connection is unavailable, it shows a blank image. And it still shows the same blank image when the Display.getInstance().callSerially(...) is called again. It does not seem like it is invoking the connection to try to load the image but the blank image has apparently become the cache.
I'm not sure if I'm describing my problem properly, but here's the simplified question: how to deal with loading of image and handle events when the connection/server is unavailable? (I thought URLImage.createCachedImage knows if the image is not loaded and will try again.)
I have this piece of code to load Image:
protected static final Image loadImage(String imageAccessLocation) {
int filenameIndex = imageAccessLocation.lastIndexOf("/");
String filename = imageAccessLocation.substring(filenameIndex + 1);
String imageAccessBaseLocation = Application.getInstance().getImagesAccessBaseLocation();
String imageAccessURL = imageAccessBaseLocation + imageAccessLocation;
int displayWidth = Display.getInstance().getDisplayWidth();
EncodedImage imagePlaceholder = EncodedImage.createFromImage(Image.createImage(displayWidth, displayWidth / 5, 0xffff0000), true);
Image image = URLImage.createCachedImage(filename, imageAccessURL, imagePlaceholder, FLAG_RESIZE_SCALE);
return image;
}
protected static final Container prepareImageContainer(Item item) {
Image image = item.getImage();
if(image == null) {
return null;
}
Image scaledImage = image.scaled(50, 50);
Container imageContainer = new Container();
imageContainer.add(scaledImage);
return imageContainer;
}
private final void prepare() {
Container imageContainer = prepareImageContainer(lookslike);
Container textContainer = prepareTextDescription(lookslike);
add(imageContainer);
add(textContainer);
}

You're right. If there is a network problem, URLImage will just silently fail.
If the URLImage hasn't finished downloading the image for whatever reason, its isAnimation() method will return true. This is a way to detect if it hasn't finished downloading. It doesn't tell you whether the download failed or just hasn't completed, but you could combine that with some sort of timeout to check if the image is downloaded, and, if not, replace it with a new URLImage for the same URL.
Not ideal, I know. You can file an RFE in the issue tracker and we'll evaluate the request.

Related

Codename One: show a (secret) Image from an URL without storing and without caching

I have the following code:
EncodedImage placeholder = EncodedImage.createFromImage(FontImage.createImage(size, size, ColorUtil.GRAY), true);
String url = "...";
Date date = new Date();
URLImage qrCode = URLImage.createToStorage(placeholder, date.getTime() + ".png", url, URLImage.RESIZE_SCALE);
qrCode.fetch();
qrCodeLabel.setIcon(qrCode);
The qrCode image contains a secret that should not be saved on the Storage / FileSystem and that should not be cached in any way. It should be shown to the user only one time.
Because these requirements, of course my code doesn't work as I need, because the image is saved and cached. I prefer that the execution of the code stops until the image is downloaded, instead this code firstly shows the placeholder, then shows the image.
So, my question is which code can I use to show an image in a Label, downloading it from an url with these requirements:
no cache;
no storing;
block of the execution until the image is ready (I have a loading Dialog that I want to dispose when the image is ready).
URLImage was designed for caching. You can obviously delete the storage file but it goes a bit against the core purpose of the class.
Just use something like:
ConnectionRequest q = new ConnectionRequest(imageUrl, false) {
public void postResponse() {
EncodedImage qr = EncodedImage.create(getResponseData());
labelForQr.setIcon(qr);
parentForm.revalidate();
}
};
addToQueue(q);

WebBrowser issue (add infinite progress in web browser)

It takes a couple of seconds to load an url in webbrowser. Sometimes it takes more time to load. How to add infinite progress like that of connectionRequest in webbrowser?
#Override
protected void postWebView(Form f) {
WebBrowser wb = new WebBrowser();
if (businessWebsiteUrl != null && !businessWebsiteUrl.equals("")) {
wb.setURL("http://" + businessWebsiteUrl);
f.add(BorderLayout.CENTER, wb);
} else {
}
f.revalidate();
}
What I did but doesnot work
protected void beforeWebView(Form f) {
ip = new InfiniteProgress();
f.add(BorderLayout.CENTER, FlowLayout.encloseCenterMiddle(ip));
}
That's inherently problematic since a browser is effectively a peer component and while we can now paint on top of peers in Android this is still not portable.
Even if it would I'm not sure if it would be a good idea.
Overall you have the following options:
Place a progress indicator above/below the browser component
Fetch the data separately as HTML with a progress and set the HTML which should be instant
Use JavaScript and potentially an iframe to indicate progress from within the browser component itself

How to test Android toast messages in Appium ( selenium Java)

I am using Selenium with Java to run scripts on android (thru Appium server).
I see that it is not possible to locate a toast by using selenium's
driver.findElement(By.LinkText("User not logged in")
in Appium
But can be used in Selendroid to capture toast messages.
I there a way I can use both Selendroid and Appium in the same script?
Finally, we are able to read the toast message without the need of taking screenshots and performing OCR.
I have tested this on Appium 1.15.1.
Toast messages comes under com.package.system.
Normally, Xpath for this will be "/hierarchy/android.widget.Toast".
And, Class Name will be "android.widget.settings"
You can confirm this by refreshing element inspector screen when toast message is displayed.
WebDriverWait waitForToast = new WebDriverWait(driver.25);
waitForToast.until(ExpectedConditions.presenceOfElementLoacted(By.xpath("/hierarchy/android.widget.Toast")));
String toastMessage = driver.findElement((By.xpath("/hierarchy/android.widget.Toast")).getText();
System.out.println(toastMessage);
Method 1: from Appium version 1.6.4 supports toast messages, for that you need to use automationName:'uiautomator2'.
toast = driver.find_element(:xpath, "//android.widget.Toast[1]")
if toast.text == "Hello"
But i don't recommend this because uiautomator2 is not stable yet.
Method 2:
Trigger text message on the screen
Capture screenshots
Convert image to text file
def assettoast(string)
sname = (0...8).map { (65 + rand(26)).chr }.join
$driver.driver.save_screenshot("#{sname}")
# Make sure tesseract is installed in the system. If not you can install using "brew install tesseract" in mac
system ("tesseract #{sname} #{sname}")
text_file="#{sname}.txt"
var= get_string_from_file(string, text_file)
raise if var != true
end
Check whether toast message is there in text file
def get_string_from_file(word, filename)
File.readlines(filename).each do |line|
return true if line.include?(word)
end
end
Looks like you can't switch driver type within same session.
If you trying to switch to Selendroid only for toast verification - you could use OSR image recognition engine.
Check this answer w/ Ruby bindings
Idea is quite simple:
make toast message to appear
take few screenshots
iterate over taken screenshots and look for required text
Here is nice and simple example of OCR usage in Java: tess4j example (make sure that Tesseract engine installed)
Step 1:
File scrFile=null;
String path1 = null;
BufferedImage originalImage=null;
BufferedImage resizedImage=null;
System.out.println("Starting\n\n\n\n");
scrFile = ((TakesScreenshot) appiumDriver).getScreenshotAs(OutputType.FILE);
System.out.println("after scrfile\n\n\n\n");
originalImage = ImageIO.read(scrFile);
System.out.println("after originalFile\n\n\n");
BufferedImage.TYPE_INT_ARGB : originalImage.getType();
resizedImage = CommonUtilities.resizeImage(originalImage, IMG_HEIGHT, IMG_WIDTH);
ImageIO.write(resizedImage, "jpg", new File(path + "/"+ testCaseId + "/img/" + index + ".jpg"));
Image jpeg = Image.getInstance(path + "/" + testCaseId + "/img/"+ index + ".jpg");
Step 2:
BufferedImage pathforToast= original image;
Step 3:
System.setProperty("jna.library.path","C:/Users/Dell/workspace/MOBILEFRAMEWORK/dlls/x64/");
Tesseract instance = Tesseract.getInstance();
`enter code here`ImageIO.scanForPlugins();
String result=null;
result = instance.doOCR(pathforToast);`enter code here`
System.out.println(result);`enter code here`
Appium 1.6.4#beta latest version supports toast messages
Take screen shot of Toast Message page and try to convert the image file in to Text and verify the text using the below code.
public void imageconversion(String filePath) throws IOException,
{
ITesseract instance = new Tesseract();
//file path is the image which you need to convert to text
File imageFile = new File(filePath);
BufferedImage img = null;
img = ImageIO.read(imageFile);
BufferedImage blackNWhite = new BufferedImage(img.getWidth(),img.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics2D graphics = blackNWhite.createGraphics();
graphics.drawImage(img, 0, 0, null);
//path where your downloaded tessdata exists
instance.setDatapath("E://ocr//data");
//What language you required to convert,( e.g. English)
instance.setLanguage("eng");
String result = instance.doOCR(blackNWhite);
System.out.println(result);
}
Appium Directly does not give any API to read toast message we need to do it using tess4j jar. First we need to take screen shot and then we need to read the text from screen shot using tess4j API.
static String scrShotDir = "screenshots";
File scrFile;
static File scrShotDirPath = new java.io.File("./"+ scrShotDir+ "//");
String destFile;
static AndroidDriver driver = null;
public String readToastMessage() throws TesseractException {
String imgName = takeScreenShot();
String result = null;
File imageFile = new File(scrShotDirPath, imgName);
System.out.println("Image name is :" + imageFile.toString());
ITesseract instance = new Tesseract();
File tessDataFolder = LoadLibs.extractTessResources("tessdata"); // Extracts
// Tessdata
// folder
// from
// referenced
// tess4j
// jar
// for
// language
// support
instance.setDatapath(tessDataFolder.getAbsolutePath()); // sets tessData
// path
result = instance.doOCR(imageFile);
System.out.println(result);
return result;
}
/**
* Takes screenshot of active screen
*
* #return ImageFileName
*/
public String takeScreenShot() {
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy__hh_mm_ssaa");
new File(scrShotDir).mkdirs(); // Create folder under project with name
// "screenshots" if doesn't exist
destFile = dateFormat.format(new Date()) + ".png"; // Set file name
// using current
// date time.
try {
FileUtils.copyFile(scrFile, new File(scrShotDir + "/" + destFile)); // Copy
// paste
// file
// at
// destination
// folder
// location
} catch (IOException e) {
System.out.println("Image not transfered to screenshot folder");
e.printStackTrace();
}
return destFile;
}
For more details Refer this video - https://www.youtube.com/watch?v=lM6-ZFXiSls
I found three ways to capture a Toast message and verify them.
To get the page source and verify the toast message from it.
public void verifyToastMessageUsingPageSource(String toastmsg) throws InterruptedException {
boolean found = false;
for(int i =0 ; i <8; i++){
if(getDriver().getPageSource().contains("class=\"android.widget.Toast\" text=\""+toastmsg+"\"")){
found = true;
break;
}
Thread.sleep(300);
}
Assert.assertTrue(found,"toast message "+toastmsg+" is present");
}
Similar can be found using Xpath: //android.widget.Toast[1]
Using the grep command, wait for a toast message in uiautomator events.
run the command before clicking and toast message will be varified.
adb shell uiautomator events | grep "ToastMessgae"
This is tricky and needs more code to run.
start capturing screenshots thread.
perform click action
stop screen capturing thread.
extract text from the captured images using OCR and verify the toast message is present in the captured images.
I prefer the 1st and 2nd option, it provides validation in less time with less code.
comment if you need code for 2nd and 3rd point.
Appium with version number>=1.6.4 supports toast notification with UiAutomator2.
In Javascript with webdriver you can do like this
let toast=await driver1.elements("xpath","/hierarchy/android.widget.Toast");
let data=await toast[0].text();
console.log(data)

Progress bar for HttpClient uploading

I want to run asynchronous uploads with a progress bar in WPF (and preferably use PCL for code reuse in Xamarin and SL too.)
I've been trying to use System.Net.HttpClient.
Unfortunately, PutAsync doesn't provide any progress notifications. (I know that it does for Windows.Web.Http.HttpClient, but that's not available for WPF, nor in the PCL).
For downloading, its fairly easy to implement your own progress bar, as described here. You just pass the ResponseHeadersRead option, which makes the stream available as soon as the headers are returned, and then you read it in chunk by chunk, incrementing your progress bar as you go. But for uploading, this technique doesn't work - you need to pass all your upload data into PutAsync in one go, so there's no chance to increment your counter.
I've also wondered about using HttpClient.SendAsync instead. I'd hoped I could just treat this like an asynchronous HttpWebRequest (in which you can increment the counter as you write to the HttpWebRequest.GetRequestStream as described here). But unfortunately HttpClient.SendAsync doesn't give you writeable stream, so that doesn't work.
So does HttpClient support uploads with a non-blocked UI and a progress bar? It seems like a modest need. Or is there another class I should be using? Thanks very much.
Assuming that HttpClient (and underlying network stack) isn't buffering you should be able to do this by overriding HttpContent.SerializeToStreamAsync. You can do something like the following:
const int chunkSize = 4096;
readonly byte[] bytes;
readonly Action<double> progress;
protected override async Task SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context)
{
for (int i = 0; i < this.bytes.Length; i += chunkSize)
{
await stream.WriteAsync(this.bytes, i, Math.Min(chunkSize, this.bytes.Length - i));
this.progress(100.0 * i / this.bytes.Length);
}
}
In order to avoid being buffered by HttpClient you either need to provide a content length (eg: implement HttpContent.TryComputeLength, or set the header) or enable HttpRequestHeaders.TransferEncodingChunked. This is necessary because otherwise HttpClient can't determine the content length header, so it reads in the entire content to memory first.
On the phone 8 you also need to disable AllowAutoRedirect because WP8 has a bug in the way it handles redirected posts (workaround is to issue a HEAD first, get the redirected URL, then send the post to the final URL with AllowAutoRedirect = false).
Simple way to upload a file with progress
I had the same need, and after some tries found out that you can easily get byte-accurate upload progress by tracking the Position of the FileStream of the file that you are going to upload.
Here is one way to do that...
FileStream fileToUpload = File.OpenRead(#"C:\test.mp3");
HttpContent content = new StreamContent(fileToUpload);
HttpRequestMessage msg = new HttpRequestMessage{
Content=content,
RequestUri = new Uri(--yourUploadURL--)
}
bool keepTracking = true; //to keep tracking thread running
new Task(new Action(() => { progressTracker(fileToUpload, ref keepTracking); })).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //to stop the tracking thread
the function progressTracker() is defined as,
void progressTracker(FileStream streamToTrack, ref bool keepTracking)
{
int prevPos = -1;
while (keepTracking)
{
int pos = (int)Math.Round(100 * (streamToTrack.Position / (double)streamToTrack.Length));
if (pos != prevPos)
{
Console.WriteLine(pos + "%");
}
prevPos = pos;
Thread.Sleep(100); //only update progress every 100ms
}
}

Properly cancel an image download in Silverlight

I have a set of Image elements that I use to download pictures. All the pictures have to be downloaded, but I wish to download the picture the user is looking at in the first place. If the user changes the viewed picture, I wish to cancel the downloads in progress to get the viewed picture as fast as possible.
To start a download I write: myImage.Source = new BitmapImage(theUri);.
How should I cancel it?
myImage.Source = null; ?
act on the BitmapImage ?
a better solution ?
I don't wish to download the picture by code to keep the benefit of the browser cache.
This is definitely doable -- I just tested it to make sure. Here is a quick class you can try:
public partial class Page : UserControl
{
private WebClient m_oWC;
public Page()
{
InitializeComponent();
m_oWC = new WebClient();
m_oWC.OpenReadCompleted += new OpenReadCompletedEventHandler(m_oWC_OpenReadCompleted);
}
void StartDownload(string sImageURL)
{
if (m_oWC.IsBusy)
{
m_oWC.CancelAsync();
}
m_oWC.OpenReadAsync(new Uri(sImageURL));
}
void m_oWC_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
BitmapImage oBMI = new BitmapImage();
oBMI.SetSource(e.Result);
imgMain.Source = oBMI;
}
}
This works just like you wanted (I tested it). Everytime you call StartDownload with the URL of an image (presumably whenever a user clicks to the next image) if there is a current download in progress it is canceled. The broswer cache is also definitely being used (I verified with fiddler), so cached images are loaded ~ instantly.

Resources