Monday, December 18, 2017

Service worker and authentication in a Polymer app

In order to sign in with a Google account or to sign out, a web app running on Google App Engine needs to navigate to a URIs that start with /_ah.  If the app is configured to use a service worker, the service worker will intercept these URIs and won't allow the sign-in/sign-out flow to continue.

In order to enable sign-in/sign-out for a Polymer app, polymer.json configuration needs to specify swPrecacheConfig option, e.g.:

"builds": [
  {
    "preset": "es6-unbundled",
    "swPrecacheConfig": "sw-precache-config.js"
  }

And sw-precache-config.js file needs to specify navigateFallbackWhitelist option:

module.exports = {
    // Everything except '/_' prefixes
    navigateFallbackWhitelist: [/^(?!\/_).*/]
};

When built with these options, the generated service worker will allow a Polymer app to successfully navigate to sign-in/sign-out URIs.

See Generating a Service Worker for more information.

Sunday, December 17, 2017

Debugging an AppEngine app locally using IntelliJ Community Edition

This post puts together information that is spread across different GCP documents.

First, configure appengine.run.jvmFlags in build.gradle as described here:

appengine {
    run {
        jvmFlags = [
            '-Xdebug',
            '-Xrunjdwp:transport=dt_socket,' + 
                'server=y,suspend=y,address=5005'
        ]
    }
}

Second, configure remote debug configuration as shown here.

In order to debug the app, start the local server from the Gradle or Maven toolbox in the margin of your IDE, or run

gradle appengineRun or gradle appengineStart

from the command line.

Then choose the remote debug configuration on the top toolbar and click Debug icon.

Monday, December 4, 2017

Leap years in Android CalendarView

Use CalendarView with caution.

MonthView uses Utils.getDaysInMonth() that defines a leap year as follows:

case Calendar.FEBRUARY:
    return (year % 4 == 0) ? 29 : 28;

And this is how a leap year should be defined:

Exactly Which Years Are Leap Years?

We add a Leap Day on February 29, almost every four years. The leap day is an extra, or intercalary, day and we add it to the shortest month of the year, February.

In the Gregorian calendar three criteria must be taken into account to identify leap years:


  • The year can be evenly divided by 4;
  • If the year can be evenly divided by 100, it is NOT a leap year, unless;
  • The year is also evenly divisible by 400. Then it is a leap year.


This means that in the Gregorian calendar, the years 2000 and 2400 are leap years, while 1800, 1900, 2100, 2200, 2300 and 2500 are NOT leap years.

So, 2100 is not a leap year, but according to Android, it is:


There are more problems related to CalendarView.  For example, the control does not allow a user to select February 29, 2100, but it allows to select February 28.  Again, be cautious.

Friday, October 20, 2017

Adding a dependency to compileReleaseSources or compileDebugSources

Simply adding a dependency to compileReleaseSources causes a gradle error.

compileReleaseSources.dependsOn 'someTask'

Could not get unknown property 'compileReleaseSources' for project 

The dependency need to be added in whenTaskAdded closure:

tasks.whenTaskAdded { t ->
    if (t.name in ['compileReleaseSources', 'compileDebugSources']) {
        t.shouldRunAfter 'someTask'
        t.dependsOn 'someTask'
    }
}

Note that you need both t. shouldRunAfter (or t.mustRunAfter) and t.dependsOn.

Wednesday, October 18, 2017

Packaging dependencies into an AAR in Android Studio

When assembling a release AAR, proguard processes external dependencies using -libraryjars directive.  This is because the dependencies are not packaged into the resulting AAR.

In order for proguard to process dependencies using -injars directive, the external dependencies need to by converted to local dependencies by simply copying the JAR files to the project's libs directory and removing explicit external dependencies from dependencies section of your build.gradle file. Then local dependencies will be packaged into the AAR and obfuscated.  If you don't use proguard to minimize/obfuscate AAR, the dependencies will be packaged as is.

Just make sure that you have this line in the dependencies section:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    ...
}

Thursday, September 28, 2017

Delaying enforcement of foreign key constraints until the end of the outermost transaction

Inserting two rows that circularly depend on each other fails after first insert because of foreign key constraint violation:  java.lang.AssertionError: expected:<1> but was:<-1>

@Test
public void testCycle() {
    db.execSQL("pragma foreign_keys=true");
    db.execSQL("create table A(id integer primary key, bId integer references B(id))");
    db.execSQL("create table B(id integer primary key, aId integer references A(id))");
    final ContentValues cvA = new ContentValues();
    cvA.put("id", 1);
    cvA.put("bId", 2);
    final long aId = db.insert("A", null, cvA);
    assertEquals(1, aId);
    final ContentValues cvB = new ContentValues();
    cvB.put("id", 2);
    cvB.put("aId", 1);
    final long bId = db.insert("B", null, cvB);
    assertEquals(2, bId);
}

Scoping by a transaction does not help, we are getting the same error.

@Test
public void testCycle() {
    db.execSQL("pragma foreign_keys=true");
    db.execSQL("create table A(id integer primary key, bId integer references B(id))");
    db.execSQL("create table B(id integer primary key, aId integer references A(id))");
    db.beginTransaction();
    try {
        final ContentValues cvA = new ContentValues();
        cvA.put("id", 1);
        cvA.put("bId", 2);
        final long aId = db.insert("A", null, cvA);
        assertEquals(1, aId);
        final ContentValues cvB = new ContentValues();
        cvB.put("id", 2);
        cvB.put("aId", 1);
        final long bId = db.insert("B", null, cvB);
        assertEquals(2, bId);
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

Appending "deferrable initially deferred" after "... bId integer references B(id)" fixes the second test.  Another option is to issue "PRAGMA defer_foreign_keys=true" right after the beginning of a transaction.


Dealing with circular data dependencies in SQLite-based content providers

By default SQLite does not defer enforcing foreign key constraints.  This means that inserting into tables that circularly depend on each other does not work by default even if insertions are scoped by a transaction. There are two options to make insertions scoped by a transaction work:

  • Declare foreign key constraints as DEFERRABLE INITIALLY DEFERRED
  • Execute PRAGMA defer_foreign_keys=true at the beginning of each transaction that deals with circular dependencies
When using ContentProvider API, applyBatch() is the only option to deal with circular data dependencies.  By default applyBatch() method does not scope individual operations by a transaction.  SQLite-based content provider should overwrite applyBatch() method to run the operations in a transaction scope:

@NonNull
@Override
public ContentProviderResult[] applyBatch(
    final @NonNull ArrayList<ContentProviderOperation> operations) 
    throws OperationApplicationException {

    final SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    db.execSQL("PRAGMA defer_foreign_keys=true;");
    try {
        final ContentProviderResult[] results =
            super.applyBatch(operations);
        db.setTransactionSuccessful();
        return results;
    } finally {
        db.endTransaction();
    }
}

Worth mentioning: ContentProvider.applyBatch() calls individual methods, such as insert(), update(), and delete().  If the individual methods are already scoped by transactions, we'll end up with nested transactions. The individual methods can use SQLiteDatabase.inTransaction() to detect this situation if nested transactions are not desired.

Tuesday, September 26, 2017

Enforcing foreign key constraints in SQLite-based content providers

By default SQLite does not enforce foreign key constraints starting since version 3.6.19.  To change this behavior we need to run PRAGMA foreign_keys=true when configuring the database:

class MyDbHelper extends SQLiteOpenHelper {
    @Override
    public void onConfigure(SQLiteDatabase db) {
        super.onConfigure(db);
        db.execSQL("PRAGMA foreign_keys=true");
    }
}

Click here for more information.

Sunday, June 4, 2017

Configuring and accessing flatDir repositories in gradle

Lets assume there are two projects: Project1/module1 and Project2/module2, and module2 depends on a JAR file built by module1.

Step 1: Declare repository in Project2/module2/build.gradle

repositories {
    flatDir {
        dirs new File(project.rootDir, 
                      "/../Project1/module1/build/libs")
    }
}

Step 2: Declare dependency in Project2/module2/build.gradle

dependencies {
    compile ':module1-1.0-SNAPSHOT'
}

All you need to do now is to run JAR gradle task in Project1/module1.  It'll place module1-1.0.SNAPSHOT.jar into Project1/module1/build/libs folder.  After that Project2/module2 will be able to access that jar.  Note that you need a colon (':') in front of 'module1-1.0-SNAPSHOT'.

This quick solution works really well if you need to iterate fast and don't want to setup a local maven repository.

Wednesday, May 31, 2017

Delimiters in ST4's imports

A rule: 

Template files that are being imported should explicitly specify delimiters.  

If they don't, the delimiters of the file that has import statement will be used. If these delimiters are different from the delimiters of the imported file, the template will not be properly rendered.

Sunday, May 21, 2017

Testing Content Providers with Robolectric

There are many samples of testing content providers with Robolectric that seem incorrect.  They would instantiate content provider directly, manually call onCreate(), and then register the provider with the shadow content resolver.  But when you do this, the provider's getContext() will return null if you call it from within onCreate() method.

What seems to work is the following:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class ProviderTestJUnit {
    private static final String AUTHORITY = “com.example.provider";
    private Context context;

    @Before
    public void setUp() {
        context = RuntimeEnvironment.application;
        ShadowContentResolver.registerProviderInternal(AUTHORITY,
                Robolectric.setupContentProvider(Provider.class));
    }

    @Test
    public void simpleQuery() {
        final ContentResolver cr = context.getContentResolver();
        final Uri uri = new Uri.Builder()
                .scheme(ContentResolver.SCHEME_CONTENT)
                .authority(AUTHORITY).build();
        final Cursor cursor = cr.query(uri, null, null, null, null);
        assertNotNull(cursor);
    }
}

When you call ShadowContentResolver.registerProviderInternal(), the content provider is created, properly initialized, and then its onCreate() is called.  Calling getContext() from within onCreate() will return non-null context.

After simple setUp() initialization the test will run.  The test code looks the same as if you tested your provider on an emulator or a device.


Saturday, May 20, 2017

Testing Content Providers (again)

This earlier post describes how to fix content provider tests.  Another way to fix them is to make setUp() method public and annotate it with @Before attribute:

@Before
@Override
public void setUp() throws Exception {
    setContext(InstrumentationRegistry.getTargetContext());
    super.setUp();
}

Note that the method should be implemented as described Testing Your Content Provider.

Listing folders first in OSX Finder

A short version of the original post:


Saturday, April 8, 2017

Loaders 1.2.0 is released

A new version of the loaders library, 1.2.0, which now also works with Loaders from Android support library, is released.
A sample application that demonstrates the usage of the library was also released today.

Saturday, March 18, 2017

SELECT LIKE FROM LIKE WHERE LIKE LIKE LIKE

SQLite documentation clearly says about not using keywords as the names of named objects.  For example, using a keyword as a table name

CREATE TABLE UNIQUE (
    a TEXT
);

leads to the following syntax error:

Error while executing SQL query on database 'provider': near "UNIQUE": syntax error

But this is not true for all keywords.  Let's try LIKE as a table name:

CREATE TABLE LIKE (
    a TEXT
);

No error, the table is created.  Let's try LIKE as a column name:

CREATE TABLE LIKE (
    LIKE TEXT
);

No problem.

INSERT INTO LIKE (LIKE) VALUES('LIKE');

Also works.  And now is the best example:

SELECT LIKE FROM LIKE WHERE LIKE LIKE LIKE;

Compiles, runs and returns correct results.  You don't even need to put quotes around the last LIKE.


Friday, March 17, 2017

Making Antlr4 fail on a syntax error

When Antlr4 encounter a syntax error, it emits an error message and then attempts to recover and continue parsing.  This default strategy is good for normal use but does not work for unit testing where we want the parser to throw an exception on syntax error so that the unit test actually fails.

To make Antlr4 fail on syntax error, we need to extend BaseErrorListener as follows:

class FailingErrorListener extends BaseErrorListener {
    @Override
    public void syntaxError(Recognizer<?, ?> recognizer,
            Object offendingSymbol, int line,
            int charPositionInLine, String msg,
            RecognitionException e)
            throws ParseCancellationException {
        throw new ParseCancellationException("line " + line + 
            ":" + charPositionInLine + " " + msg);
    }
}

Then, we need to add this error listener to both a lexer and a parser:

final ANTLRInputStream input = new ANTLRInputStream(stream);
final FailingErrorListener errorListener = new FailingErrorListener();

final MyLexer lexer = new MyLexer(input);
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);

final CommonTokenStream tokens = new CommonTokenStream(lexer);

final MyParser parser = new MyParser(tokens);
parser.removeErrorListeners();
parser.addErrorListener(errorListener);

After these changes the parser starts failing on as soon as it encounters a syntax error.

Wednesday, March 15, 2017

One of the 'BEST' markdown preview extensions for Atom

By default Atom editor comes with markdown-preview package which, unfortunately, does not handle relative references: you cannot click on a relative reference inside Atom editor to verify that it works.
 
Luckily, there is markdown-preview-enhanced package that deals with relative references without problems.  The package description says "One of the 'BEST' markdown preview extensions for Atom editor!", and this is not an exaggeration.

Sunday, March 12, 2017

Forwarding GoDaddy domain to Google app engine

If you purchased your domain through GoDaddy and want to forward its traffic to an application hosted on Google App Engine, don't start at GoDaddy domains settings, it'll just complicate the process.

Start at Google cloud console. Here is a link to the only instructions you need. The process is simple: Click Add Custom Domain under  {my app} -> App Engine -> Settings -> Custom Domains and follow the prompts.  Google will verify that you own the domain and then allow you to configure domain forwarding.  After configuration is saved, it'll take up to 24 hours for the changes to take effect, but it's usually faster than that.

At this point sub-domain forwarding should work.  To forward your naked domain, you need to login to GoDaddy, go to My Account -> Domains -> {domain name} -> DNS Zone File and add all A and AAAA records listed on Google cloud console.  Use @ as a host name when adding the records. Give it some time before testing.  In my case it took about 5 minutes for the changes to take effect, but it may take longer.

Saturday, March 11, 2017

Deploying a Polymer app to Google App Engine

The steps that are required to deploy a Polymer app to Google App Engine are documented and there is no need to repeat them here.  However, some changes are required to make everything work end to end.

First, polymer build command creates an unbundled output and places it to build/default folder. GAE seems to serve resources via HTTP/2 with server push just fine, so there is no need to create create a bundled output.

Second, since all output files by default are in build/default folder, you need to change static_files in app.yaml file  from build/bundled/... to build/default/....  Another option is to simply rename build/default folder to build/bundled folder.

Third, appcfg.py -A {project name} update app.yaml will more likely fail.  I had to add --oauth2 option for deployment to succeed:  appcfg.py --oauth2 -A {project name} update app.yaml

Wednesday, March 8, 2017

Tips to reduce mobile data usage

After switching to Project Fi, I started tracking my mobile data usage.  Below is the summary of what I've done so far to reduce my bills.

Google Play Music

Turn on Stream only on Wi-Fi in Settings
If you want to stream over mobile network, set Quality on mobile network to Low.

YouTube

Turn on Limit mobile data usage in Settings -> General.  This will only stream HD videos on Wi-Fi.

Google Photos

Turn off Cellular data back up in Settings -> Back up & sync

Google Play

In Settings (General) set Auto-update apps to Auto-update apps over Wi-Fi only. 

Google Plus

Turn on Conserve data usage in Settings (General)

Google Maps

Turn on Wi-Fi only in Settings.  Click on your offline areas link and save the area around your home.  Google Maps can save areas larger than a half of the state of Washington in size.

I'll update this post when I learn about other apps that consume a lot of data.  Project Fi app reports data usage per app, so it's easy to track apps that abuse mobile data.

Friday, March 3, 2017

Using CKEditor in WebView on Android

CKEditor is a powerful rich text editor for web applications.  This post shows how to integrate the editor into an Android application and load a sample page into a WebView.

  1. Download CKEditor.
  2. Unpack Zip file and move ckeditor folder to $project.projectDir/src/main/assets folder.
  3. Create a WebView in your layout file.
  4. After inflating the layout file, find WebView and enable JavaScript: webView.getSettings().setJavaScriptEnabled(true);
  5. Load sample app into the WebView: webView.loadUrl("file:///android_asset/ckeditor/samples/index.html");
When the application is launched, it'll look something like this:




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.