Friday, February 9, 2018

Android programmatically connect to a wifi network without Internet

Android doesn't like to connect to wifi without Internet. But what about when you want to use a local wifi connection to quickly move large volumes of data between nearby devices? The secret is that you need to obtain the Network object that corresponds to the wifi, and then use it to open URL connections, create a SocketFactory, or even bind the application. How to find out which object that is? The big (undocumented) secret is that the NetworkInfo's getExtraInfo method will return a string that matches the SSID. With thanks to this blogpost by Nishkarsh Sharma.
With

Monday, August 14, 2017

Sending an APK over bluetooth on Android

If you want to allow users to install an app offline, sending the APK file over bluetooth seems like a reasonable way forward. Regrettably, and without any real documentation the issue, some versions of Android block this. Perhaps it's anti-piracy.

APK receive over bluetooth was blocked on:
  • Nexus 4 (Android 5)
  • Nexus Galaxy (Android 4.3)
  • Yoga Tablet 2 Pro (Android 5)
APK receive over bluetooth works fine on:
  • Samsung SM-G313F (Android 4.4.2)
  • Wileyfox Swift (Android 7.1)
  • Moto E2 (Android 6)
The enable installation from unknown sources setting makes no difference. If receiving APK files are blocked on a device, no notification at all appears on the client device (thanks) when one is sent. The sender should receive a notification that sending failed. If sending an APK is blocked, the workaround is to send a zip file. There we are hoping that the phone has something capable of unzipping it. Most manufacturers install something for that, but stock Android has no file browser. You can get to a file manager through settings, storage, explore.

Wednesday, August 9, 2017

Android JUnit @BeforeClass flakiness for longer running routines

Using @BeforeClass in a JUnit test seems like a great way to make tests run more efficiently. Surely it's a natural place to put long-running setup routines!

Unfortunately, Android's test runner in Android Studio will, depending on how it feels, might run the next test, might skip the next test, might skip a couple more tests just for good measure, or it might just give up the tests entirely. Looking for an error message or a hint?

So instead of a nice elegant solution:
@BeforeClass
public static void someSlowSetupRoutine() {
    //run long running code
}
We must put the long running procedure in the test itself and use an if statement to avoid running it multiple times to speed things up:
@Test
public void someTest() {
    if(!ready){
        //run setup
    }
}




Monday, May 29, 2017

AVD emulator mysteriously packed up on ubuntu?

Your Android emulator mysteriously decides to give up for no apparent reason... The Android Studio UI won't tell you anything about why (I guess that interrupts the "experience" of tearing your hair out).

Run it from the command line:

$ ~/Android/Sdk/emulator/emulator -avd Nexus_S_API_21 -gpu mesa

Where Nexus_S_API_21 is the name of the avd we want to run. Those can be listed using the -list-avds argument. The -gpu-mesa argument tells the emulator to use software rendering: which is somewhat slower but supposedly reliable.

If you then see:
X Error of failed request: BadValue (integer parameter out of range for operation)
Major opcode of failed request: 155 (GLX)
Minor opcode of failed request: 24 (X_GLXCreateNewContext)
Value in failed request: 0x0
Serial number of failed request: 39
Current serial number in output stream: 40
QObject::~QObject: Timers cannot be stopped from another thread
Segmentation fault (core dumped)

Then apparently we need to use the libstdc++6 from the system : not the one Android helpfully supplied.
$ sudo apt-get install lib64stdc++6
$ cd ~/Android/Sdk/emulator/lib64/libstdc++
$ mv libstdc++.so.6 libstdc++.so.6.original
$ ln -s /usr/lib64/libstdc++.so.6 ~/Android/Sdk/tools/lib64/libstdc++

All based on this article here - just slightly modified the paths to reflect Android's current directory structure.

Saturday, January 14, 2017

ORM Persistence design for GWT, Android, iOS and J2SE

Our persistence challenge is to have Object Relationship Management (ORM) that works on Android, with GWT RequestFactory, with iOS using j2objc, and using "normal" J2SE for an eventual desktop app.

ORMLite provides a very nice ORM framework that works both using JDBC (e.g. Desktop/Server side java).  GWT requires proxy interfaces so it can manage the back/forth from the server.  SharkORM requires objective c classes.  It all gets very repetitive:

It requires ORM classes like so:

package com.example.helloandroid;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import com.j256.ormlite.field.DatabaseField;

/**
 * A simple demonstration object we are creating and persisting to the database.
 */
public class SimpleData {

	// id is generated by the database and set on the object automagically
	@DatabaseField(generatedId = true)
	int id;
	@DatabaseField(index = true)
	String string;
	@DatabaseField
	long millis;
	@DatabaseField
	Date date;

	SimpleData() {
		// needed by ormlite
	}
}


GWT's Request Factory requires a proxy interface that it uses to send data back and forth like so:

@ProxyFor(SimpleData.class)
public interface SimpleDataProxy extends EntityProxy {

       public int getId();
       public void setId(int id);

       public String getString();
       public void setString(String string);
         
       public Date getDate();
       public void setDate(Date date);
}

SharkORM wants something like (haven't tested this yet - but the gist is correct):

//  Header File : SimpleData.h
#import "SharkORM.h"
@interface SimpleData : SRKObject
@property int               id;
@property NSString* string; @property long date;
@end

// Source File : SimpleData.m
#import "Person.h"
@implementation Person
@dynamic id,string,date;
@end

Enter Roaster where you can parse and generate java code.  In NanoLRS (our in progress implementation of an Experience API server designed for embedding in apps) we just make the proxy interface and the rest is generated by roaster by our EntityGeneratorOrmLite class like so:

JavaInterfaceSource proxyInterface = Roaster.parse(JavaInterfaceSource.class, proxyStr);
Iterator<MethodSource<JavaInterfaceSource>> iterator = proxyInterface.getMethods().iterator();

It's a work in progress - but at least it seems like I've found a way around writing the same code slightly differently by hand three times.




Saturday, December 19, 2015

The HTTP dance required make Chrome play video tags

Sometimes in an app you need to use an embedded HTTP server to make HTML content work properly.  In theory you could serve up content from the FileSystem directly: but then HTML5 localstorage, media, cookies, requests for resources from other directories misbehave.  What does cross-domain mean on a filesystem?

In our app we run the excellent NanoHTTPD to serve up content from epub (zipped) files.  Chrome has some (seemingly undocumented) requirements:


  1. You must support HTTP range requests.  You don't need to support multiple ranges: but you must implement returning of 206 partial content in response to a range request.  Interestingly on Android 5.1 Chrome seems to test this by sending a range request for the first byte (0-1).
  2. You must give chrome a means of validating the file remains the same and hasn't changed: A strong (normal) etag works for this.  Setting the last-modified header might work as well.

When using Java inputstreams this of course means using the skip method: be careful that needs to go in a loop because the skip method often does not skill all the bytes requested.  We implemented range support in our EmbeddedHTTPD and RangeInputStream .

Monday, November 30, 2015

The ways around J2ME JSR-135 OutOfMemoryException

Playing media in J2ME seems relatively straightforward: provide an InputStream or address and the media should play.  Unfortunately Nokia's implementation of Manager.createPlayer(InputStream, mimeType) will attempt to buffer the stream in full in memory.  It will always run out of memory because it will attempt to buffer the full media object before playing anything back.  A slower stream just makes you wait a bit before it runs out of memory and doesn't play.

Partial Solution: If you use Manager.createPlayer("file://E:/fileonsdcard.3gp") instead with the jad attribute progressive_download: enabled the file will play smoothly and it won't run out of memory (hoorah!).  But what if the media you want to play isn't just sitting around in raw form on the file system?

I tested out using Manager.createPlayer("http://server/some/file.3gp").  Playing over a 2G or 3G internet connection my half hour 3GP clip played OK.  But using a Nokia Asha 500 (which runs series 40) over a WiFi network the clip only plays successfully one time in 5 or so.  Again seems like the underlying logic thinks "AH!!! Data - let's download everything!!!  Ehh... I ate too much... collapse...".  When I used mod_ratelimit in the apache server I was running on my laptop to limit the speed to 128kbps (16K) behavior returned to more or less what it was with the Internet: Generates an interesting Apache log showing that it downloads a small chunk, then a large chunk, then some smaller chunks.  Towards the end playback started sputtering cutting out for 10-15seconds at a time, playing back for less than a minute, and repeating that cycle.


192.168.0.102 - - [30/Nov/2015:12:59:57 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 200 6847 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:12:59:57 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 367505 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:12:59:56 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 200 130375 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1 UNTRUSTED/1.0"
192.168.0.102 - - [30/Nov/2015:13:00:20 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 16860717 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:23:06 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 37080 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:23:42 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 90564 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:24:24 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 58872 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:25:01 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 77460 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:25:39 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 30528 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:26:15 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 147324 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:26:57 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 629964 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:28:09 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 81803 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:28:48 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 30527 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"
192.168.0.102 - - [30/Nov/2015:13:29:23 +0100] "GET /stream/bbc_click.3gp HTTP/1.1" 206 159739 "-" "Nokia501/2.0 (11.1.1) Profile/MIDP-2.1 Configuration/CLDC-1.1"

Now let's consider some workarounds one might have in mind: and how unfortunately they get thwarted:


  • If I know the duration of a stream: slow the stream down to the rate of playback.  
    • Unfortunately the duration is not available for players created using an InputStream : and the duration is also not available ; so calculating the bitrate would involve manual file reading and codec work.
  • Use a DataSource / SourceStream for Manager.createPlayer
    • I didn't have any luck with this one: I tested my implementation on the emulator with a wav file - was happy.  Tested it on a Series 40 phone: can't get beyond "Error connecting to data source"
  • Let's close the stream at some time to force an interruption / replay
    • Getting this to play in the best of circumstances isn't so much fun... and partial streams (e.g. truncated files) aren't understood
  • Let's slow down the stream to internet like speeds: then use an Internal HTTP Server
    • Well this should produce the same performance as running it over the internet: the problem is the implementation of playing over HTTP misbehaves.
Which leaves us with one final workaround: Write the entire stream to a file and then use Manager.createPlayer with the created file URL.  In our particular implementation we have files downloaded locally but in epub (zipped) files.  Unzipping an 18MB 3gp file (which plays for 30mins) took 2mins on a Nokia 206 (mid range phone).  

For a good overview (that even mentions the let's buffer everything problem) check out this post on InformIT http://www.informit.com/articles/article.aspx?p=375708 .