tag:blogger.com,1999:blog-9897143595092653882024-02-21T07:22:11.840-08:00Mike's Coding OdditiesAnonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.comBlogger16125tag:blogger.com,1999:blog-989714359509265388.post-69101666309769743032018-02-09T11:50:00.000-08:002018-02-09T11:50:47.282-08:00Android programmatically connect to a wifi network without Internet<div dir="ltr" style="text-align: left;" trbidi="on">
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 <a href="https://developer.android.com/reference/android/net/Network.html" target="_blank">Network</a> 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 <b>big</b> (undocumented) secret is that the NetworkInfo's getExtraInfo method will return a string that matches the SSID. With thanks to <a href="http://www.intentfilter.com/2016/08/programatically-connecting-to-wifi.html">this blogpost</a> by Nishkarsh Sharma.<br/>
</div>
<script src="https://gist.github.com/mikedawson/b4f4175613d1ca9bddb2b4dccbb761c9.js"></script>
WithAnonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com1tag:blogger.com,1999:blog-989714359509265388.post-79481035801908150532017-08-14T05:01:00.002-07:002017-08-14T05:01:35.748-07:00Sending an APK over bluetooth on Android<div dir="ltr" style="text-align: left;" trbidi="on">
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. <br />
<br />
<div style="text-align: left;">
APK receive over bluetooth was blocked on:</div>
<ul style="text-align: left;">
<li>Nexus 4 (Android 5)</li>
<li>Nexus Galaxy (Android 4.3)</li>
<li>Yoga Tablet 2 Pro (Android 5)</li>
</ul>
APK receive over bluetooth works fine on:<br />
<ul style="text-align: left;">
<li>Samsung SM-G313F (Android 4.4.2)</li>
<li>Wileyfox Swift (Android 7.1)</li>
<li>Moto E2 (Android 6)</li>
</ul>
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. </div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-64220124093347400502017-08-09T05:33:00.000-07:002017-08-09T05:33:13.514-07:00Android JUnit @BeforeClass flakiness for longer running routines<div dir="ltr" style="text-align: left;" trbidi="on">
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!<br />
<br />
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? <br />
<br />
So instead of a nice elegant solution:<br />
<blockquote>
@BeforeClass<br />
public static void someSlowSetupRoutine() {<br />
//run long running code<br />
}
</blockquote>
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:<br />
<blockquote>
@Test<br />
public void someTest() {<br />
if(!ready){<br />
//run setup<br />
}<br />
}
</blockquote>
<br />
<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-8295818018476259112017-05-29T22:33:00.000-07:002017-05-29T22:33:51.354-07:00AVD emulator mysteriously packed up on ubuntu?<div dir="ltr" style="text-align: left;" trbidi="on">
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).<br />
<br />
Run it from the command line:<br />
<br />
<blockquote>
<span style="font-family: "courier new" , "courier" , monospace;">$ ~/Android/Sdk/emulator/emulator -avd <i>Nexus_S_API_21 </i>-gpu mesa</span></blockquote>
<br />
Where <i>Nexus_S_API_21</i> 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.<br />
<br />
If you then see:<br />
<blockquote>
<span style="font-family: "courier new" , "courier" , monospace;">X Error of failed request: BadValue (integer parameter out of range for operation)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Major opcode of failed request: 155 (GLX)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Minor opcode of failed request: 24 (X_GLXCreateNewContext)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Value in failed request: 0x0</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Serial number of failed request: 39</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Current serial number in output stream: 40</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">QObject::~QObject: Timers cannot be stopped from another thread</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Segmentation fault (core dumped)</span></blockquote>
<br />
Then apparently we need to use the libstdc++6 from the system : not the one Android helpfully supplied.<br />
<blockquote style="font-family: "Courier New",Courier,monospace;">
$ sudo apt-get install lib64stdc++6<br />
$ cd ~/Android/Sdk/emulator/lib64/libstdc++<br />
$ mv libstdc++.so.6 libstdc++.so.6.original<br />
$ ln -s /usr/lib64/libstdc++.so.6 ~/Android/Sdk/tools/lib64/libstdc++</blockquote>
<br />
All based on this article <a href="https://charmie11.wordpress.com/2016/08/10/error-of-android-studio-cannot-launch-avd-in-emulator/" target="_blank">here</a> - just slightly modified the paths to reflect Android's current directory structure.</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-17751171827155384452017-01-14T07:12:00.002-08:002017-01-14T07:12:31.778-08:00ORM Persistence design for GWT, Android, iOS and J2SE<div dir="ltr" style="text-align: left;" trbidi="on">
Our persistence challenge is to have Object Relationship Management (ORM) that works on Android, with <a href="http://www.gwtproject.org/doc/latest/DevGuideRequestFactory.html" target="_blank">GWT RequestFactory</a>, with iOS using j2objc, and using "normal" J2SE for an eventual desktop app. <br />
<br />
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:<br />
<br />
It requires ORM classes like so:<br />
<br />
<pre>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
}
}
</pre>
<pre></pre>
<pre></pre>
GWT's Request Factory requires a proxy interface that it uses to send data back and forth like so:<br />
<br />
<pre>@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);
}
</pre>
<br />
SharkORM wants something like (haven't tested this yet - but the gist is correct):<br />
<br />
<pre>// Header File : SimpleData.h
#import "SharkORM.h"
@interface SimpleData : SRKObject</pre>
<pre><pre>@property int id;</pre>
@property NSString* string;
@property long date;</pre>
<pre>@end
</pre>
<pre></pre>
<pre>// Source File : SimpleData.m
#import "Person.h"
@implementation Person
@dynamic id,string,date;
@end
</pre>
<br />
Enter <a href="http://github.com/forge/roaster" target="_blank">Roaster</a> where you can parse and generate java code. In <a href="https://www.github.com/UstadMobile/NanoLRS" target="_blank">NanoLRS</a> (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 <a href="https://github.com/UstadMobile/NanoLRS/blob/master/nanolrs-entitygen/src/main/java/com/ustadmobile/nanolrs/entitygen/EntityGeneratorOrmLite.java" target="_blank">EntityGeneratorOrmLite class</a> like so:<br />
<br />
<pre>JavaInterfaceSource proxyInterface = Roaster.parse(JavaInterfaceSource.class, proxyStr);
Iterator<MethodSource<JavaInterfaceSource>> iterator = proxyInterface.getMethods().iterator();
</pre>
<br />
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.<br />
<br />
<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com1tag:blogger.com,1999:blog-989714359509265388.post-6086961549473733222015-12-19T10:02:00.002-08:002015-12-19T10:02:30.819-08:00The HTTP dance required make Chrome play video tags<div dir="ltr" style="text-align: left;" trbidi="on">
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?<br />
<br />
In our app we run the excellent NanoHTTPD to serve up content from epub (zipped) files. Chrome has some (seemingly undocumented) requirements:<br />
<br />
<br />
<ol style="text-align: left;">
<li>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).<br /></li>
<li>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.</li>
</ol>
<br />
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 <a href="https://github.com/UstadMobile/UstadMobile/blob/master/ports/android/src/main/java/com/ustadmobile/port/android/impl/http/EmbeddedHTTPD.java" target="_blank">EmbeddedHTTPD</a> and <a href="https://github.com/UstadMobile/UstadMobile/blob/master/ports/android/src/main/java/com/ustadmobile/port/android/impl/http/RangeInputStream.java" target="_blank">RangeInputStream</a> .</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-72771414683420870732015-11-30T07:20:00.002-08:002015-11-30T07:49:19.296-08:00The ways around J2ME JSR-135 OutOfMemoryException<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
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 <b>always </b>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.<br />
<br />
<b>Partial Solution</b>: 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?<br />
<br />
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.<br />
<br />
<br /></div>
<pre>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"
</pre>
<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
Now let's consider some workarounds one might have in mind: and how unfortunately they get thwarted:<br />
<br />
<br />
<ul style="text-align: left;">
<li>If I know the duration of a stream: slow the stream down to the rate of playback. </li>
<ul>
<li>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.</li>
</ul>
<li>Use a DataSource / SourceStream for Manager.createPlayer</li>
<ul>
<li>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"</li>
</ul>
<li>Let's close the stream at some time to force an interruption / replay</li>
<ul>
<li>Getting this to play in the best of circumstances isn't so much fun... and partial streams (e.g. truncated files) aren't understood</li>
</ul>
<li>Let's slow down the stream to internet like speeds: then use an Internal HTTP Server</li>
<ul>
<li>Well this should produce the same performance as running it over the internet: the problem is the implementation of playing over HTTP misbehaves.</li>
</ul>
</ul>
<div>
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). </div>
<br />
For a good overview (that even mentions the let's buffer everything problem) check out this post on InformIT <a href="http://www.informit.com/articles/article.aspx?p=375708" target="_blank">http://www.informit.com/articles/article.aspx?p=375708</a> .</div>
</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-42319205740915182782015-11-25T10:28:00.001-08:002015-11-30T07:20:42.242-08:00Using InputStreams with JSR-135 J2ME Sound Playback<div dir="ltr" style="text-align: left;" trbidi="on">
In our J2ME app we need to be able to play back sounds for the user for files that are already downloaded (albeit inside zipped epubs). So connecting the two using Manager.createPlayer(in, mimeType) seems fairly straightforward. However on the devices themselves with longer files it will croak after a certain amount of time (maybe a buffer under run happens... still not sure). With some other files that we were accessing for performance and to ensure any active file connections are closed properly we simply read the whole content into a byte array. Not a good idea when it comes to long mp3 files: here's our recipe that seems to work:<br />
<br />
<br />
<ol style="text-align: left;">
<li>Obtain an inputstream from the file source (in our case, the file source is inside a zip for which we use the gnu classpath project to unzip)</li>
<li>Also from gnu classpath get the BufferedInputStream class (this is not supplied in java.io on J2ME/CLDC devices).</li>
<li>Feed the player a BufferedInputStream instead of the raw stream itself: e.g.<br />return new BufferedInputStream(src, 20*1024);</li>
</ol>
<div>
I haven't yet played around with the buffer sizes; occasionally it very briefly misses a fraction of a beat on lower end devices (e.g. the Nokia 110)</div>
<div>
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-34864487746994817612015-11-09T12:01:00.001-08:002015-11-30T07:21:01.663-08:00J2ME Images stop loading mystery<div dir="ltr" style="text-align: left;" trbidi="on">
In our app we need to be able to show images from EPUB files on J2ME .. and those images change as the user goes from page to page. We're using the rather neat LWUIT HTMLComponent to render the HTML pages with some custom additions to handle media (published <a href="https://github.com/mikedawson/lwuit-for-series-40" target="_blank">here on GitHub</a>).<br />
<br />
Mysteriously using a Nokia 206 phone we could load about 19 900x900 pixel jpegs (showing one after the other; with a garbage collect System.gc() call in between) before it would give up - and any subsequent request to load an image would throw an IOException. This was being caused by an IOException being thrown inside the ResourceThread as it was trying to load the image. This of course was happening only on the devices: not on the emulator.<br />
<br />
J2ME phones is that they vary widely in resolution and memory - from a Nokia 110 which has a 120 pixel or so wide screen to an Asha which has 240x320. Perhaps images can simply be bounded to a maximum of 320x320 ; or perhaps we need to have different epub files for different phones. Not sure of that yet.<br />
<br />
Note to anyone (still) working with J2ME to avoid tearing hair out : keep images as small as they can be.</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-83519149444441467772015-09-05T09:06:00.000-07:002015-11-30T07:25:37.920-08:00J2ME FileConnection Gotchas: Overwrite and beginning with .<div dir="ltr" style="text-align: left;" trbidi="on">
J2ME gives you all the power of what was in a 1997 PC to put apps on billions of phones: with none of the debugging tools.<br />
<br />
1. Nokia Series 40 will now allow you to create a filename that begins with "." - do that and you will get an exception .<br />
<br />
2. Even in the emulator: if you want to overwrite the entire file when getting an output stream to an existing file: the only way I have seen consistent results is to delete the file first. Incredibly even openOutputStream(0) - to explicitly open the outputstream at the start seemed to overwrite some but not all of the file (e.g. if you want to overwrite with a smaller file than it was originally there will be trailing leftovers from the previous file). Something like this works:<br />
<br />
<pre>public OutputStream openFileOutputStream(String fileURI, boolean autocreate) throws IOException{
FileConnection con = null;
IOException e = null;
try {
con = (FileConnection)Connector.open(fileURI, Connector.READ_WRITE);
if(con.exists()) {
con.delete();
}
con.create();
}catch(IOException e2) {
e = e2;
}finally {
J2MEIOUtils.closeConnection(con);
if(e != null) throw e;
}
OutputStream out = Connector.openOutputStream(fileURI);
return out;
}
</pre>
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-85838168628331753192015-08-28T02:55:00.001-07:002015-11-30T07:26:18.081-08:00No Java 8: No JSON for you says Android: UnsupportedClassVersionError: org/json/JSONObject<div dir="ltr" style="text-align: left;" trbidi="on">
Let's say you are crazy enough to be running the Ubuntu Long Term Support distribution. Maybe you would even rather like to use the jdk package from the Ubuntu repo so it gets automatically updated? Android says: No; not if you feel like using JSON.<br />
<br />
<pre class="console-output">java.lang.UnsupportedClassVersionError: org/json/JSONObject : Unsupported major.minor version 52.0
</pre>
<br />
No you won't be able to just change the JSON version in Maven like others who get stuck with it. You will upgrade to JDK 8 or your code will not run. Developer's using JSON: wow, what an edge case, eh? Who would possibly expect Google to conduct enough testing to figure that one out and say "Hey - you have to use JDK 8 to compile from now on".<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-15476376980489914802015-08-12T05:12:00.000-07:002015-08-12T05:13:31.823-07:00Android emulator SNAFU: Eating 100% CPU when idle<div dir="ltr" style="text-align: left;" trbidi="on">
Fresh from the "who could have expected Google to try that edge case?" list: the case of Android emulators eating 100% of the CPU when Idle on Linux. It's very complex to reproduce:<br />
<br />
<ol style="text-align: left;">
<li>Start any Android emulator on any Ubuntu Linux machine</li>
<li>Watch it eat 100% of a CPU core when doing nothing after bootup has completed</li>
</ol>
<div>
The issue is noted on a <a href="https://code.google.com/p/android/issues/detail?id=18589" target="_blank">bug report</a> where someone from Google looked at it 4 years ago to say "huh I can't make it happen".</div>
<div>
<br /></div>
<div>
The solution? Hope you don't need audio: run <b>export QEMU_AUDIO_DRV=none </b>; then run your emulator.<br />
<br />
SNAFU: Regularly used in Afghanistan, particularly if you speak to anyone who was working with the military. Situation Normal All F***ed Up. Also highly applicable to many Android development situations it seems.<br />
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-13357999496336331682015-05-31T07:45:00.001-07:002015-05-31T12:07:23.008-07:00Android command line project creation and gradle <div dir="ltr" style="text-align: left;" trbidi="on">
Here's another instance where Android's <a href="http://developer.android.com/tools/building/building-cmdline.html" target="_blank">lousy documentation</a> is just plain wrong AGAIN. When you create a project using android project create as <a href="http://developer.android.com/tools/projects/projects-cmdline.html" target="_blank">per their documentation</a> you will see there is no gradle file. I've tested this on my Ubuntu 15.04 running openjdk7.<br />
<br />
It will then say to build or run your project run the non-existant ./gradlew file. What you need to do is as per stackoverflow is to add a --gradle and a --gradle-version to the project create command that is nowhere mentioned in <a href="http://developer.android.com/tools/projects/projects-cmdline.html" target="_blank">google's documentation</a>. Seriously Google, you don't have enough money or staff to run once through your docs to discover that when people RTFM they don't actually work.<br />
<br />
Again one has to find the<a href="http://stackoverflow.com/questions/20801042/how-to-create-android-project-with-gradle-from-command-line" target="_blank"> answers on stackoverflow</a>. I'd find the right place to report it, but someone already told you that you have the wrong packages listed for Ubuntu since years and you never bothered with it.<br />
<br />
<b>Part 1: change how you create the project:</b><br />
<br />
<pre>$ android create project --path FrustratedDev2 --activity FrustratedDev2 --package com.somewhere.killme --target "android-21" --gradle --gradle-version '1.2.3'</pre>
<br />
<b>Part 2: Play with config file it never mentioned that is created wrong by default:</b><br />
<br />
<pre>$ vi gradle/wrapper/gradle-wrapper.properties
</pre>
<br />
Change:
<br />
<br />
<pre>distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
</pre>
<br />
to:
<br />
<br />
<pre>distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip
<div style="font-family: 'Times New Roman'; white-space: normal;">
<b>Part 3: Update proguard minify options that don't work android put there somehow:</b></div>
</pre>
<pre>$ vi build.gradle
</pre>
<br />
Change:
<br />
<br />
<pre>runProguard false</pre>
<pre>proguardFile getDefaultProguardFile('proguard-android.txt')
</pre>
<br />
To:
<br />
<br />
<pre>minifyEnabled false
</pre>
<pre></pre>
<br />
And if everything is in order; now you can build a debug APK file:<br />
<br />
<pre>$ ./gradlew assembleDebug
</pre>
<br />
PS: Even though the docs say you need to run chmod +x gradlew; that's wrong too - you don't have to now ; it's created executable by default.<br />
<br />
PS: Reported it to Google here on their <a href="https://code.google.com/p/android/issues/detail?id=175225" target="_blank">bug report for documentation</a> : Let's see how long it's ignored for.<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-35084364752675013572015-04-24T05:47:00.002-07:002015-04-24T05:54:01.669-07:00Android tablet as a second Ubuntu screen with xrandr and x11vnc<div dir="ltr" style="text-align: left;" trbidi="on">
Let's say you have a laptop, an Android tablet and you're working on debugging something. Wouldn't it be nice to have some extra screen real estate? The second answer here seemed to be going in the right direction... only one gets stuck moving the mouse into the extended screen area.<br />
<br />
So here's a recipe that works (on Ubuntu 14.10 at least). Let's say the actual laptop screen is 1366x768 as in my case; and we want to create another screen of the same resolution:
<br />
<br />
<pre>$ sudo apt-get install x11vnc
$ sudo xrandr --fb 2732x768 --output LVDS1 --panning 2732x768+0+0/2732x768+0+0
$ sleep 3 # wait a moment
$ sudo xrandr --fb 2732x768 --output LVDS1 --panning 1366x768+0+0/2732x768+0+0
</pre>
<br />
<strong>Explanation:</strong> xrandr (X Resize and Rotate) sets the screen size. To make it think the screen is bigger without doing strange things we first tell it to make a panning setup. With panning the section before the / is the area in which that screen can pan around, after the / is the very important tracking area in which the mouse can move.<br />
<br />
Now we can use x11vnc with a clip<br />
<br />
<pre>sudo x11vnc -clip 1366x768+1367+0 -nocursorshape -nocursorpos
</pre>
<pre></pre>
We need to use -nocursorshape and -nocursorpos so the cursor is not dealt with by VNC; the cursor is directly painted on as part of the image.
<br />
<br />
Now you can take your pick of Android VNC apps and then point it at your laptop's IP address:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMzXdaFoV8KcuRnDfcNU3cnmenPLi7QUPqgEiYjE78UkckmUS54d-N-EMJUzZ50jG2hKIas23jlXnDqya4jMPbDgeso4OSHEDrJMZZr92IZEaNLhDrbbWGRgfM_mGs0gKqDnhHNI4jXbIm/s1600/android-vnc.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMzXdaFoV8KcuRnDfcNU3cnmenPLi7QUPqgEiYjE78UkckmUS54d-N-EMJUzZ50jG2hKIas23jlXnDqya4jMPbDgeso4OSHEDrJMZZr92IZEaNLhDrbbWGRgfM_mGs0gKqDnhHNI4jXbIm/s1600/android-vnc.png" height="360" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The new right half of the screen</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
Now there's only two and a bit problems left: VNC is not encrypted; so it should run over an SSH tunnel/VPN or something of the like. Another problem can be lousy WiFi; I'm hoping to kill two bids with one stone by making this run using port forwarding on over the USB cable.</div>
<br />
Thanks to this <a href="http://askubuntu.com/questions/28608/how-do-you-use-an-android-tablet-as-a-second-display" target="_blank">AskUbuntu post</a>.<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com11tag:blogger.com,1999:blog-989714359509265388.post-87473214103326294972015-04-13T04:44:00.004-07:002015-04-13T05:39:47.614-07:00Ubuntu 14.04 'headless' LTS with Jenkins-CI for Android emulation<div dir="ltr" style="text-align: left;" trbidi="on">
So you want one of these magic cheap cloud servers to do handle unit testing and building your Android app? Makes sense if you have a cross platform approach; the tests need to run automatically on each platform. And some of us need to do more customization than the millions of cloudy IDEs let us do (like install an Ubuntu package even)<br />
<br />
<b>Problem 1</b>: Amazon AWS (and the like) servers are virtual themselves and performance will be awful. So bad in fact that Cordova's default timeouts will be exceeded. So you need a real server; in fact with many providers (e.g. Hetzner) they are cheaper for the same performance than what Amazon provides.<br />
<br />
<b>Problem 2</b>: Google can make an OS that's on a billion devices. What they apparently cannot do is maintain documentation on installing an SDK and it's actual requirements. <br />
<br />
<b>Problem 3</b>: Ubuntu minimal (what you normally get with a cloud server) and the Android emulator do not play ball. You will get strange meaningless error messages like can't load swrast. glxinfo will segfault.<br />
<br />
<h2>
Solution</h2>
<div>
Take an Ubuntu 14.04 real dedicated server, add the full ubuntu desktop, add a few extra packages, then use VNC to login exactly as you would 'normally'<br />
<br /></div>
<pre>$ apt-get update && apt-get install ubuntu-desktop
</pre>
<pre>$ apt-get install x11vnc</pre>
<pre>$ adduser <someusername></pre>
<pre>$ init 6</pre>
<pre></pre>
<div>
<br />
...Wait for server to restart with the lightdm desktop manager running, login as root, and then...<br />
<br /></div>
<div>
<pre>$ x11vnc -xkb -noxrecord -noxfixes -noxdamage -display :0 -auth /var/run/lightdm/root/:0 -usepw
</pre>
<pre></pre>
Now use a VNC client to connect to display 0 using the public IP address of the server: you should be able to connect and see the 'normal' ubuntu login. You can now login with the username created before.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNi-pfTzKpSEdqlwnR1iuHIlmfqt_8_5iAhhezxr_wKx72Y4zx3l0cb_TJnBj_SLfxOV0OrPyuDKcmk61GN-eVAMpctLPQpRaD61lpuGggX80IiOeyQZfl6q-YOZVjxSSHXVZIP8jXjOZR/s1600/ci-server-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNi-pfTzKpSEdqlwnR1iuHIlmfqt_8_5iAhhezxr_wKx72Y4zx3l0cb_TJnBj_SLfxOV0OrPyuDKcmk61GN-eVAMpctLPQpRaD61lpuGggX80IiOeyQZfl6q-YOZVjxSSHXVZIP8jXjOZR/s1600/ci-server-1.png" height="223" width="400" /></a></div>
<br />
Note: VNC itself does not use encryption. You'll need to use a VPN like OpenVPN for that.<br />
<br /></div>
<div>
Now use another SSH connection to install what's needed for Jenkins:<br />
<pre>$ apt-get install openjdk-7-jdk
$ wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
$ echo 'deb http://pkg.jenkins-ci.org/debian binary/' > /etc/apt/sources.list.d/jenkins.list
$ apt-get update && apt-get install jenkins
</pre>
<pre>$ nano /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf</pre>
<br />
Add a line so you can login with a graphical desktop as jenkins :<br />
<pre>greeter-show-manual-login=true
</pre>
<pre></pre>
<br />
Restart lightdm to enable the change (this will cut your VNC session):
<br />
<pre>$ service lightdm restart</pre>
<pre></pre>
<br />
Re-run the VNC service:
<br />
<pre>$ x11vnc -xkb -noxrecord -noxfixes -noxdamage -display :0 -auth /var/run/lightdm/root/:0 -usepw</pre>
<br />
Login to the server as jenkins using vnc now and use firefox to download the android development toolkit as you would on a normal laptop/desktop. Before you run it (as root); add the packages Android needs:<br />
<br />
<pre>$ dpkg --add-architecture i386
$ apt-get update
$ apt-get install libncurses5:i386 libstdc++6:i386 zlib1g:i386
</pre>
<pre>$ #install these packages that google didn't tell you about to avoid no such file or directory mystery error message</pre>
<pre>$ apt-get install libc6:i386 libstdc++6:i386 lib32z1</pre>
<br />
Add Jenkins to the KVM group needed to run android vms at a reasonable speed:<br />
<pre>$ adduser jenkins libvirtd
$ adduser jenkins kvm
</pre>
<br />
Now use the android command to install the components as normal and android avd to setup virtual devices. I recommend not using super screen resolutions:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdNGmXisA9ciVOmq3umK5YgQ8GN6OEriwz8V9Gxcla_9xCJoCLvOpnzW8lDuaf_QBbktLJ4CX8C_iaCT9dJPgs6vT2P4l516MP9FXgR8YGrJxURgWTq9vY_d-EYN9euxz95nVoZLsX6TW_/s1600/vm-settings.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdNGmXisA9ciVOmq3umK5YgQ8GN6OEriwz8V9Gxcla_9xCJoCLvOpnzW8lDuaf_QBbktLJ4CX8C_iaCT9dJPgs6vT2P4l516MP9FXgR8YGrJxURgWTq9vY_d-EYN9euxz95nVoZLsX6TW_/s1600/vm-settings.png" height="400" width="321" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Now for continuous integration purposes; you'll need to make sure the system is logged in and add this to your scripts before they need something that launches a window:<br />
<br />
<pre>export DISPLAY=:0 && export XAUTHORITY=/home/$USER/.Xauthority</pre>
<br /></div>
References + Thanks:<br />
<br />
<ul style="text-align: left;">
<li>AskUbuntu Answers: <a href="http://askubuntu.com/questions/229989/how-to-setup-x11vnc-to-access-with-graphical-login-screen">http://askubuntu.com/questions/229989/how-to-setup-x11vnc-to-access-with-graphical-login-screen</a> and <a href="http://askubuntu.com/questions/552064/how-can-kvm-be-located-by-android-studio-on-ubuntu-14-04-lts">http://askubuntu.com/questions/552064/how-can-kvm-be-located-by-android-studio-on-ubuntu-14-04-lts</a></li>
</ul>
</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0tag:blogger.com,1999:blog-989714359509265388.post-57029353135942551482013-10-07T19:28:00.003-07:002013-10-08T08:15:53.669-07:00J2ME Compatibility: LWUIT w/Alcatel 871a<div dir="ltr" style="text-align: left;" trbidi="on">
People think J2ME is dead: reports of it's death are rather exaggerated given the fact feature phones will dominate the market in developing countries (ie the most populous market). The Alcatel 871a is a cheap ($33 USD prepay) with a QUERTY keyboard ready available on ATT in the US.<br />
<br />
Getting an app to run happily across multiple implementations is not going to be huge amounts of fun. So here's hoping I can save someone some headaches:<br />
<ul style="text-align: left;">
<li>Use Nokia's LWUIT Fork worked not the original LWUIT from lwuit.java.net would just give a "this java application is not supported" message. Ironic given I'm trying to get it from being on Nokia only to be more widely supported, and guess what, I need to use Nokia's version... But that looks better maintained these days.</li>
<li>Don't request any permissions from JSRs that are not installed on the phone (e.g. javax.bluetooth) - that will make it show "this java application is not supported" without further explanation</li>
<li>Make sure the Manifest is MIDP-2.0 (not 2.1)</li>
<li>Do not use getClass().getResourceAsStream when the result might not be found and then pump it into an Image object (not good practice anyway but you think inside an exception handler it'd be safe). No that will give an insufficient memory error</li>
<li>The Alcatel 871a does not like 3gp h263 encoded video that comes out of ffmpeg. Does not work with the video player at all. Whatever ffmpeg uses by default in mp4 works.</li>
</ul>
<div>
<br /></div>
</div>
Anonymoushttp://www.blogger.com/profile/14948780221749580024noreply@blogger.com0