Thursday, March 2, 2017

Loading WebView from a resource file on Android

This pattern worked for me:

final Uri uri = Uri.parse("file:///android_res/raw/maptest");
webView.loadUrl(uri.toString());

where maptest.html is a file in res/raw folder.  Replacing maptest with maptest.html also works.

The following works to load a file from assets directory:

final Uri uri = Uri.parse("file:///android_asset/maptest.html");
webView.loadUrl(uri.toString());

Note the use of .html extension when loading from assets directory.  When the extension is removed from the URI, the file won't load.

Tuesday, February 28, 2017

Antlr4, gradle, -package, outputDirectory

First, we enabled antlr4 in IntelliJ/gradle. Next we need to find a location for antlr4 files and correctly place its output files.  There is a couple of ways to configure antlr4 output directory in gradle.

The simplest way is specify package name in @header:

@header {
  package com.example.parser;
}

Then, if we place our grammar files to "$project.projectDir/src/main/antlr/com/example/parser", antlr4 will place its output files to "$project.buildDir/generated-src/antlr/main/com/example/parser".

The problem with specifying package name in the @header is that it ties the grammar to the target language.  There is another way to configure output directory.

First, we remove @header from grammar file to untie the grammar from the target language.
Second, we add the following to build.gradle:

generateGrammarSource {
    outputDirectory file("${project.buildDir}/generated-src/antlr/main/com/example/parser")
    arguments << '-package' << 'com.example.parser'
}

An optional step is to move the grammar files from "$project.projectDir/src/main/antlr/com/example/parser" to "$project.projectDir/src/main/antlr" so that we don't have to look for the files deep down the directory hierarchy.

When all this is setup, antlr4 will place its output files into the same directory as in the first example.

Sunday, February 26, 2017

Testing Content Providers

ProviderTestCase2 and Testing Your Content Provider documents seems not complete.  I followed them and getMockContentResolver() always returned null. The reason for null resolver is that setUp() method is not called when tests run.  Adding the following method to my sub-class of ProviderTestCase2 fixed the problem.

@Before
public void mySetUp() throws Exception {
    setUp();
}

I'm not sure if this is the best solution though.

Sunday, January 29, 2017

Using antlr4 in IntelliJ



Earlier I wrote about configuring JavaCC in Android Studio / IntelliJ. Since I'm migrating from JavaCC to antlr4, I'll show how to integrate antlr4 to IntellJ.

It's actually less work than configuring JavaCC. All you have to do is to add 2 lines:

plugins {
  // ...
  id "antlr"
}

// ...

dependencies {
  antlr "org.antlr:antlr4:4.6"
  // ...
}

One might ask why switching from one LL(*) parser generator to another LL(*)?  There are many reasons, I'll comment on this later.  One note: antlr4 is not exactly LL(*), it is ALL(*), where A stands for Adaptive.  This difference by itself is a big improvement over JavaCC, where Adaptive part requires careful management by hand.

Tuesday, December 27, 2016

Precedence Climbing in JavaCC

Precedence Climbing is described in Parsing Expressions by Recursive Descent.  Here we look at how to implement the highlighted part in JavaCC, namely, how to exit the loop.

Exp( p ) is
    var t : Tree
    t := P
    while next is a binary operator and prec(binary(next)) >= p
       const op := binary(next)
       consume
       const q := case associativity(op)
                  of Right: prec( op )
                     Left:  1+prec( op )
       const t1 := Exp( q )
       t := mkNode( op, t, t1)
    return t

Semantic lookahead looks like a nice solution.  Below is a sample implementation:

public ExprNode Expr(int p) :
{
    ExprNode expr, expr1;
    OpBinary op;
}
{
    (
        expr = ExprPrefix()
        (
            LOOKAHEAD({ nextBinaryOp() != null && 
                nextBinaryOp().getPrecedence() >= p })
            op = BinOp()
            expr1 = Expr(op.getAssoc() == OpBinary.Assoc.RIGHT ?
                op.getPrecedence() : op.getPrecedence() + 1)
            { expr = new ASTExprBinary(op, expr,  expr1); }
        )*
    )
    { return expr; }
} 

()* construct corresponds to the while loop.  Since this construct is a choice point, the false expression value in the LOOKAHEAD simply exits the loop.

We define nextBinaryOp() in the parser class as follows:

private OpBinary nextBinaryOp()
{
    return OpBinary.fromCode(getToken(1).kind);
}

getToken(1) does not modify the input stream and is equivalent to next in the original algorithm.

Definitions of ExprNode, OpBinary, ExprPrefix, BinOp and ASTExprBinary are omitted for brevity.

Friday, November 11, 2016

Formatter/encoder widgets from Bing

These are quite useful when you need to quickly format/encode some strings.








Sunday, July 10, 2016

A novice's guide to installing CM 13 on HTC One A9 - Fail!

In the previous post I described how after following these steps to install CM 13 I ended up in a boot loop.  I also described the recovery process that allowed me to install the official system image provided by HTC.

Then I tried to repeat the same CM 13 installation steps expecting different results (is this what is called insanity?) but ended up in the same boot loop.  I recovered again back to the official HTC system image (now I know how to do it), but then made the same mistake one more time and ended up in a third boot loop.

After that I decided to test the effect of every step.  First, I rebooted the phone to download mode and installed custom recovery image:

fastboot flash recovery twrp-3.0.2-0-hiae.img

I rebooted to system and verified that the official HTC system still worked.  Then I rebooted the phone to recovery mode.  The first thing TWRP asked be was to enter password to decrypt the storage.  Since I haven't setup any password yet and I didn't know the default password, after reading this thread I setup my password and told the system to ask for the password at boot time.  While I was able to boot the phone with the new password successfully, TWRP did not recognize it. From the thread above it seems to be a known issue: TWRP only recognizes the password on certain device/OS version combinations.

I booted to recovery mode again and pressed Cancel when TWRP asked for the password.  Then I Swiped to Allow for Modifications, pressed Wipe and Swiped to Factory Reset.  Then rebooted to System.  At this point the official HTC system was still there, only all the settings were lost, so the phone asked me to set it up again.  I didn't, powered it down and booted to recovery mode again.

While in recovery mode, I pushed CM 13 and Google apps to sdcard:

adb push cm-13.0-20160710-NIGHTLY-hiaeul.zip /sdcard/
adb push open_gapps-arm64-6.0-nano-20160704.zip /sdcard/

Then I tried to back up the System, Data, and Boot (together and each separately) without any luck.  TWRP would give me Unable to mount storage error message.  This is probably due to the same not recognizing password issue mentioned earlier.

Not being able to back up the system, I simply clicked on Install, added cm-13.0-20160710-NIGHTLY-hiaeul.zip. Then I clicked on Add more Zips, added open_gapps-arm64-6.0-nano-20160704.zip and Swiped to confirm Flash. Fingers crossed...

TWRP installed both ZIP files but at the end displayed an error saying Unable to mount storage - the same error when I tried to back up the system.  I went on and clicked on Reboot System and ended up in the same boot loop: a white screen, a green HTC logo, and a message in red: 

This  build is for development purposes only.  Do not distribute outside of HTC without HTC's permission, Failure to comply may lead to legal action. 

Then reboot.  Then the same message. Then reboot again...

I thought that installation of CM 13 failed again, but after the third reboot the phone showed CyanogenMod logo.  Finally!  But then the system asked me to enter password to decrypt the storage.  Since I didn't have any password after the system was wiped earlier, I typed my previous password that I used before wiping the system.  It did not work of course.  The system said that I needed to reset to factory defaults in order to continue using it.  I clicked the presented button and the phone rebooted to recovery mode.

In recovery mode TWRP asked me to Enter Password.  I clicked on Cancel since I did not have any password.  Swiped to Allow Modifications. Wipe. Swipe to Factory Reset. Reboot System. The same boot loop again, this time around without any luck.  CyanogenMod did not boot on the third time, on the fourth time, and so on.  I decided to let the phone sit in the boot loop for a while and watch what happens.

I went to a supermarket (about 20 minutes trip) - the phone was still sitting in the boot loop.  It's time to do something else.  Waited until the phone starts rebooting, pushed Volume Down and Power keys, booted into download mode, rebooted to bootloader mode and then to recovery mode.  In TWRP, Swiped to Allow Modifications,  Wipe, Swipe to Factory Reset.   Pushed cm-13.0-20160710-NIGHTLY-hiaeul.zip and open_gapps-arm64-6.0-nano-20160704.zip as earlier,  Install, chosen both ZIP files as before, Swiped to confirm Flash, then clicked on Reboot System. 

Boot loop again...  After about 15-20 reboots CyanogenMod logo appeared.  Wow!  Let's wait.  Android is starting... Optimizing app 78 of 106.  I haven't seen this before!  Looks like we're finally getting there!  Oops... Reboot, boot loop again... 

Washed dishes, came back - the phone was still rebooting.  Volume Down + Power when the phone rebooted.  Entered download mode.  Unzipped cm-13.0-20160710-NIGHTLY-hiaeul.zip, found boot.img.   Ran

fastboot flash boot boot.img

Rebooted and entered the same boot loop.  Somebody does not want me to install CyanogenMod.  Either CyanogenMod developers, TWRP developers or HTC developers.  Or they simply joined forces against me...

Back to recovery mode through download and bootloader modes.  Wipe, Advanced Wipe, then wiped Dalvic/ART Cache, System, Cache and Data.  Then installed cm-13.0-20160710-NIGHTLY-hiaeul.zip and open_gapps-arm64-6.0-nano-20160704.zip.  Reboot System.  Boot loop...

I've already seen CyanogenMod logo twice, that's enough for today.  At least I know how to recover to the official HTC system.  Not sure if I continue exploring CyanogenMod any longer.  Why does it have to be so hard?