In a situation where a BlackBerry application is installed to a user's device via OTA (BIS), and that application has a "Check for updates" button, one simple approach would be to launch the browser with the address of the .jad file which would then present the user with the "You have version 1.0 installed, would you like to download and install version 1.1?" dialog. But even if there are no updates, the user would get the "You have 1.0, would you like to replace it with 1.0 dialog", which is a hassle and is meaningless.
Is there an better method for doing this in a more seamless manner? For example, are there accepted ways for the application to check the server for an update (with user's permission), inform the user if an update is available, and install the update OTA without going through the browser/jad/ota/replace/restart device loop?
Targeting RIM OS 4.1+
Thank you.
One way would be to fetch the JAD file using an HTTP connection in your app, parse for the version available on the server and only launch the browser if there is a newer version available, or after additionally asking the user if the upgrade is desired.
There are elements of the API that would allow you to also fetch the COD file(s) and install the modules yourself, but that seems like just increasing potential bug space unless you really need to avoid using the Browser OTA install.
A similar method but one that I find a bit better than Richard's thought above because the client does not need a hard-coded JAD path this way (important since JAD files may differ for different BB OS versions):
create a simple web page (php, jsp, servlet, cgi, whatever) that accepts app name and current app version as input; if you need it, also include OS version in the input.
This URL will be constructed by the client by obtaining the appropriate data (details below) and appending it to the known base URL.
the web page will parse the information, and calculate the proper version to run.
Note that you might not need all of the information above: if you only have one downloadable version of your app, you would really only need the device to send the client software version and nothing else. The calculation of proper version can be a simple hard-coded check (if ($version != LATEST_VERSION)) or something more complex, involving lookup into a database or elsewhere.
This page will output plain text, non-HTML. It will write three values, one per line:
"y" if an update is required , "n" if not.
The current-most version for this client to use. This is only necessary if you want the client to display it.
the download URL for the correct JAD.
The client application will parse that data, and if the first flag is "Y" will display message "The current version is (contents of second line). Would you like to update?" When update is selected, it will launch the URL provided in the third line.
Reference
Obtaining Application Version
import net.rim.device.api.system.ApplicationDescriptor;
...
// Returns current app version in the format Major.Minor.Minor.Build, eg 1.5.1.123
String version = ApplicationDescriptor.currentApplicationDescriptor().getVersion();
Obtaining Hardware and Platform Info
import net.rim.device.api.system.ApplicationDescriptor;
...
// Obtain the platform version string in the format A.B.C.DDD, eg 5.0.0.464
String softwareVersion = DeviceInfo.getSoftwareVersion();
// Obtain the hardware name:
String hardwareName = DeviceInfo.getDeviceName();
Launch HTTP URL
import net.rim.blackberry.api.browser.Browser;
Browser.getDefaultSession().displayPage("http://example.com");
Read HTTP file
String url = "full/url/assembled/with/data/above"
// YOU assemble "url" value - and include more error handling than is here in this sample:
HttpConnection conn;
try {
conn = ConnectionHelper.getHttpConnection(url);
LineInputStream stream = new LineInputStream(conn.openInputStream());
String lineOneYesNo = stream.readLine(true);
String lineTwoCurrentVersion = stream.readLine(true))
String lineThreeDownloadURL = stream.readLine(true))
// ***
// * Parse the data above and handle as described.
// ***
return data;
} catch (IOException e) {
// Add appropriate erorro handling here
return;
}
getHttpConnection Implementation
public static HttpConnection getHttpConnection(String URL) throws IOException {
HttpConnection c = null;
StringBuffer conn = new StringBuffer(URL);
// *** IMPORTANT ***
// YOU must define this method below, as it will append
// values to the connection string based on connection
// type (MDS, TCP, WIFI, WAP2, etc)
//
configureConnectionString(conn);
c = (HttpConnection) Connector.open(conn.toString());
int rc = c.getResponseCode();
if (rc != HttpConnection.HTTP_OK) {
throw new IOException("HTTP Error: " + rc);
}
return c;
}
Reference: Simple LineInputStream implementation
http://svn.bbssh.org/trunk/BBSSH_Common/src/org/bbssh/io/LineInputStream.java
Sample Input URL 1
This URL is constructed by the client and sent to the server:
http://example.com/versioncheck.do/app-name/hardware-name/os-version/app-version
e.g. http://example.com/versioncheck.do/MyApplication/Bold9000/5.0.466/1.5.1.0
Sample Input URL 2
Alternative format for the same thing:
http://example.com/versioncheck.php?appName=A&hardwareName=B&osVersion=C&appVersion=D
e.g. http://example.com/versioncheck.php?appName=?MyApplication&hardwareName=Bold9000?osVersion=5.0.466&appVersion=1.5.1.0
Sample Output
y
1.3.1.125
http://example.com/ota/5.0.0/MyApp.jad
Related
I have written a java code of connecting to server mode
p.setProperty("server.database.3",
"file:G:/SERVERMODE/soamware;user=soamware;password=123#123");
p.setProperty("server.dbname.3", "soamware");
server.setProperties(p);
server.setLogWriter(null); // can use custom writer
server.setErrWriter(null); // can use custom writer
server.start();
try {
//Registering the HSQLDB JDBC driver
Class.forName("org.hsqldb.jdbc.JDBCDriver");
con = DriverManager.getConnection("jdbc:hsqldb:hsql://ip/soamware;
file:G:/SERVERMODE/soamware;user=soamware;password=123#123");
this code is working fine in netbeans with jdk8 and hsqldb2.5.1, however the console shows the build is not terminated and its still running. While when i connect to SwingDatabaseManager
with same url, username and password as mentioned in java code. It throws above mentioned exception. Kindly clarify also, why my program doesnt exit. I am not adding "server.shutdownCatalogs(1);" statement at end because then I cannot perform multiple operations in one session.
Because you are starting the server with only one database, you should set database.0 properties. You shouldn't use the # character at all on a connection string because it has a special meaning. You shouldn't use the file path when connecting to a server database. Use the dbname.0 value only. Edited code below:
p.setProperty("server.database.0", "file:G:/SERVERMODE/soamware;user=soamware;password=123x123");
p.setProperty("server.dbname.0", "soamware");
server.setProperties(p);
server.setLogWriter(null); // can use custom writer
server.setErrWriter(null); // can use custom writer
server.start();
try {
Class.forName("org.hsqldb.jdbc.JDBCDriver");
con = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/soamware", "soamware", "123x123");
Created a webextension for firefox (currently using Nightly 52), that uses native messaging to launch a java program on Linux (Ubuntu 14, 32x).
The webextension loads, reads the .json file and reads the path which points to a script that starts the java program. The JSON and the path are correct as when I use:
var native = browser.runtime.connectNative("passwordmanager");
console.log("native.name" + native.name); //outputs passwordmanager.
native.onDisconnect.addListener(function(m) { console.log("Disconnected"); });
The above code prints the name of the native port and also prints "Disconnected". So I m guessing the native app is terminating for some reason.
The application is only skeleton right now, that just does sysout and reads sysin and works correctly if Launch it directly through the shell script.
While debugging the webextension, I am not able to step into the call to connectNative, as it just steps-over that call instead of doing step-in. So kind of out of options whats' going wrong.
Please let me know if anyone is able to create a native messaging app based on FF webextension and any pointers on what I might be doing wrong.
Thanks
This solution here shows you how to detect onConnect and onFail. It should help you out to figure out your real problem.
So I don't think you can do proper error handling with connectNative from the JS side alone. You can do somewhat error handling if you get the exe side involved, but you can't get a string for "error reason" when an error occurs. The error is only logged to console.
First make sure to set your deeloper prefs, so messages show in your browser console. You can use this addon - https://addons.mozilla.org/en-US/firefox/addon/devprefs/ - or read that addon description it gives you the MDN page with the prefs to set.
Then this is how you can do some sort of error handling (without error reason) (pseudo-code - i might need a .bind in the callbcks):
function connectNative(aAppName, onConnect, onFail) {
var listener = function(payload) {
if (!connected) {
connected = true;
port.onDisconnect.removeListener(failedConnect);
onConnect();
} else {
// process messages
}
}
var failedConnect = function() {
onFail('failed for unattainable reason - however see browser console as it got logged there');
}
var connected = false;
var port = chrome.runtime.connectNative(aAppName);
port.onMessage.addListener(listener);
port.onDisconnect.addListener(failedConnect);
return port;
}
Now in your exe, as soon as it starts up, make it write to stdout something. That will trigger the onConnect.
Environment: Windows 10 Professional 64-Bit.
I want to build / install a go-project (twitterbeat as you can see).
C:\apps\Go_workspace\src\github.com\buehler\twitterbeat>go build
# github.com/buehler/twitterbeat/beater
beater\twitterbeat.go:62: b.Events undefined (type *beat.Beat has no field or method Events)
Here you can see line 62 of the file:
func (bt *Twitterbeat) Setup(b *beat.Beat) error {
logp.Info("Setup waitduration and api keys")
bt.events = b.Events
var err error
bt.period, err = time.ParseDuration(*bt.beatConfig.Period)
if err != nil {
return err
}
anaconda.SetConsumerKey(*bt.beatConfig.Twitter.ConsumerKey)
anaconda.SetConsumerSecret(*bt.beatConfig.Twitter.ConsumerSecret)
bt.api = anaconda.NewTwitterApi(*bt.beatConfig.Twitter.AccessKey, *bt.beatConfig.Twitter.AccessSecret)
return nil
}
I don't think that the code is wrong, because I donwloaded it directly from Github.
Because I am not on a linux / unix system (and I had problems with the proxy), i couldn't run "glide". Instead I donwloaded all dependencies by myself.
What can I do to build twitterbeat?
When you download the dependencies by hand, you need to make sure that they are the same version as in the glide.yaml file. The current version of beat.Beat in github.com/elastic/libbeat/beat/beat.go is newer than the one in the glide.yaml and doesn't have an Events field any more.
It's not your problem,but a fault of the library you are using.
As the code shows,it used github.com/elastic/beats/libbeat/beat,then we jump to the source of beat,the Beat struct is:
type Beat struct {
Name string // Beat name.
Version string // Beat version number. Defaults to the libbeat version when an implementation does not set a version.
UUID uuid.UUID // ID assigned to a Beat instance.
BT Beater // Beater implementation.
RawConfig *common.Config // Raw config that can be unpacked to get Beat specific config data.
Config BeatConfig // Common Beat configuration data.
Publisher *publisher.Publisher // Publisher
filters *filter.FilterList // Filters
}
It doesn't have Events field anymore!
You can use the old version of the library github.com/elastic/beats/libbeat/beat,or you can push a issue to the owner of github.com/buehler/twitterbeat to inform him to fix this bug.
I'm trying to return an mp4 file from my Grails controller so that it can be played in the browser. The following is the simplest version of what I have:
def file = new File(<path to mp4 file>)
response.outputStream << file.newInputStream()
The strange thing is that this works when hitting it from a desktop (Chrome on my MacBook), works on an Android phone, but does not work on an iPad Air.
The one header that's different in the iOS request is for "range" of "0-1", but it looks like that might not be causing a problem (tested by adding that request on my laptop).
The exception says:
ERROR errors.GrailsExceptionResolver - SocketException occurred when processing request: [GET]
and further down it says
getOutputStream() has already been called for this response.
I've found many others with similar errors, but they talk about webRequest.setRenderView(false), flushing and closing the outputstream, and many other options. I've tried all of those, but nothing seems to work.
The part that really gets me is that it works on everything except iOS.
Any thoughts would be greatly appreciated. Thanks in advance!
UPDATE 1
Per Graeme's answer below, the accept header from Chrome is:
accept -> text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
And iOS produces multiple requests, which have the following accept headers:
accept -> text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept -> */*
The second accept header, */* is the what occurs during the exception.
I have also created a JIRA issue for Grails:
http://jira.grails.org/browse/GRAILS-11325
Might be related to the Accept header that gets sent, as Grails has some parsing depending on the Accept header. If you could post an example in a JIRA with steps to reproduce that would help.
http://jira.grails.org/browse/GRAILS
This turned out to be an iOS specific issue. The range header is required to be implemented, and if you try to return the entire file content for the response of a range request, iOS will not make additional requests.
The following is the code I used:
try {
def rangeValue = request.getHeader("range")
log.debug("rangeValue: ${rangeValue}")
if (rangeValue != null) {
// Get start and end string, substring(6) removes "bytes="
def (start, end) = rangeValue.substring(6).split("-")
def startInt = start.toLong()
def endInt = end.toLong()
def fileSize = file.length()
response.reset()
response.setStatus(206)
response.setHeader("Accept-Ranges", "bytes")
// WARNING: Do not sent Content-length, as it appears to prevent videos from working in iOS
response.setHeader("Content-range", "bytes ${start}-${end}/"+Long.toString(fileSize))
response.setContentType("video/quicktime")
def bytes = new byte[endInt-startInt+1]
def inputStream = file.newInputStream()
// Skip to the point in the inputStream that the range is requesting
inputStream.skip(startInt)
// Read a chunk of the input stream into the bytes array
inputStream.read(bytes, 0, bytes.length)
response.outputStream << bytes
}
else {
response.outputStream << file.newInputStream()
}
} catch (ClientAbortException e) {
log.error("User aborted download")
}
There are a few important notes:
If the Content-length header is returned in the response, iOS will not play the video. Seems like this could be related to content being gzipped - https://stackoverflow.com/a/2359184/2601060
When using the inputStream.read() function, it will always start reading at the beginning of the stream, so make sure to skip() to the proper position in the file (the startInt)
A response can be reset() to make sure that anything that has already been written is not included (this may not be required, but prevents automatic grails actions from providing default behavior)
I am trying to make use of the JavaMail jar on my blackberry app so I can access my GMail emails. I usually make use of this sort of function:
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
String html_email = "";
try {
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore("imaps");
store.connect("imap.gmail.com", email, password);
However, eclipse is complaining about the Properties object as I am not making use of the standard JRE but rather the blackberry JRE.
Is there anyway, I can work around this with minimal changes to my function as far as the Properties is concerned?
In addition, I am having trouble with the use of System:
The method getProperties() is
undefined for the type System.
Is this even likely to work?!
Thanks all
Hmm, looks like JavaMail is not built for use on JavaME.