31 October 2017

RxJava Publish Subject is pretty awesome


Hopefully you're all now using RxJava because it's pretty awesome. Now I'm not about to force on you another tutorial, there are plenty out there. I've been using RxJava for ages now and only just discovered a new thing. I love discovering new things, especially about something I've used for ages. So I discovered PublishSubject, this is basically an amazing alternative to creating callbacks all over the place.

Normally if I were to implement a callback from a fragment to an activity I'd do something like this:

public interface IFragmentListener{
    void onFragmentSuccess();
}

private IFragmentListener mFragmentListener;

Then of course my activity would implement that interface and receive callbacks when mFragmentListener is called.

However there's a better way to do things, and that's with publishSubject.

private PublishSubject<String> mSelectionObservable = PublishSubject.create();

mSelectionObservable.onNext("Hello");

public Observable<String> getSelectionObservable() {
    return mSelectionObservable;
}

PublishSubject allows you to declare an observable which you can subscribe to and send callbacks whenever you want. As you can see when I call mSelectionObservable.onNext().

Your activity can subscribe to the publishSubject like this

frag.getSelectionObservable()
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnNext(new Action1<String>() {
    @Override
    public void call(String s) {
        Log.d(TAG, "call: ");
        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
    }
}).subscribe();

Hope that helps make your code more awesome!

18 January 2017

Amazon AWS Key Management Service with Android


I recently came across Amazon AWS's new Key Management Service (KMS). This seemed like a pretty cool idea so I thought I'd give it a go and see if I could get Amazon to manage my keys on Android.

The idea is pretty straightforward, Amazon host your secure keys for you, so you can encrypt and decrypt without having to worry about key management and storage. As far as I know KMS currently supports only symmetric encryption.

Here's how to use the Amazon Web Services console to create a Key Management Service (KMS) key:

  1. Goto AWS management console and click services, under Security and Compliance click IAM
  2. Goto groups on the left menu and click create new group
    1. Create a group and name and click next
    2. Search for “AWSKeyManagementServicePowerUser” and check it, click next
  3. Goto users on the left menu and click add user
    1. Give the user a name “kms_user” or something
    2. Click programatic access
    3. You should see your group in the add user to group section, check this
    4. Click next
  4. Click Encryption Keys at the bottom
    1. Give the key a name
    2. Do not give the key any administrators, for this tutorial the account owner will be the only one who can administer this key. Click next.
    3. Give your kms_user user account use permissions for the key
    4. Click next and finish adding the key
  5. Setup Android Studio
    compile 'com.amazonaws:aws-android-sdk-kms:2.2.+'


You should now have an encryption key and be ready to start coding. For the Android part of this I used two Async tasks. You could do this in a service but it needs to be off the main UI thread as it's a network call.



public class AsyncEncrypt extends AsyncTask<String, String, ByteBuffer>{

    private static final String TAG = AsyncEncrypt.class.getSimpleName();

    public interface AsyncEncryptListener {
        void processFinish(ByteBuffer cipherText);
    }

    private AsyncEncryptListener listener;

    @Override
    protected ByteBuffer doInBackground(String... strings) {

        final AWSCredentials creds = new AWSCredentials() {
            @Override
            public String getAWSAccessKeyId() {
                return "xxx";
            }

            @Override
            public String getAWSSecretKey() {
                return "yyy";
            }
        };

        AWSKMSClient kms = new AWSKMSClient(creds);

        String keyId = "zzzzz";
        ByteBuffer bytePlainText = ByteBuffer.wrap(strings[0].getBytes());

        EncryptRequest req = new EncryptRequest().withKeyId(keyId).withPlaintext(bytePlainText);
        ByteBuffer ciphertext = kms.encrypt(req).getCiphertextBlob();

        Log.d(TAG, "onCreate: " + ciphertext.toString());

        return ciphertext;
    }

    @Override
    protected void onPostExecute(ByteBuffer cipherText) {
        super.onPostExecute(cipherText);
        if(listener != null){
            listener.processFinish(cipherText);
        }
    }

    public void setListener(AsyncEncryptListener listener){
        this.listener = listener;
    }
}




Decrypt:


public class AsyncDecrypt extends AsyncTask<ByteBuffer, String, String>{

     private static final String TAG = AsyncDecrypt.class.getSimpleName();

     public interface AsyncDecryptListener {
        void processFinish(String plainText);
    }

     private AsyncDecryptListener listener;

     @Override
    protected String doInBackground(ByteBuffer... ciphertextBlob) {

         final AWSCredentials creds = new AWSCredentials() {
            @Override
            public String getAWSAccessKeyId() {
                return "xxx";
            }

             @Override
            public String getAWSSecretKey() {
                return "yyy";
            }
        };

         AWSKMSClient kms = new AWSKMSClient(creds);

         DecryptRequest req = new DecryptRequest().withCiphertextBlob(ciphertextBlob[0]);
        ByteBuffer plainText = kms.decrypt(req).getPlaintext();

         String decoded = new String(plainText.array());
        Log.d(TAG, "onCreate: " + decoded);

         return decoded;
    }

     @Override
    protected void onPostExecute(String plainText) {
        super.onPostExecute(plainText);
        if(listener != null){
            listener.processFinish(plainText);
        }
    }

     public void setListener(AsyncDecryptListener listener){
        this.listener = listener;
    }
}

That's pretty much it. You create a set of AWS credentials supplying the security data given to you in the console for your user, then pass those credentials to the KMS client and make an encryption request.

The only other thing you might want to consider is whether it's worth it or not, in order to use KMS on Android you've got to store your secret key and access key somewhere. If an attacker can get those, they can access your encryption key. It's the classic chicken and egg scenario that distributed systems suffer from again and again. Oh well, it was a neat experiment.

At least it means it's much easier to rotate keys without having to re-release a new app!

30 December 2016

Improving on SimpleDateFormat


I'm a big fan of SimpleDateFormat, but it suffers from one critical problem. Your date format is forced on the user. I've learnt the hard way (as have most developers) that American's have their own date format (mm/dd/yyyy), us Brits have our own format (dd/mm/yy) and of course there are many other countries that also have different ideas.

Using SimpleDateFormat means you pick a format and the user has to like it. In some cases this could even be very frustrating for the user. You could of course allow them to pick their own format, but that's a lot of work.

My point here is Android has a little used method of doing this hard work for you and it utilizes the user's locale as set in the phone settings, to calculate this format. That is java.text.DateFormat.
So if the user has English American locale, then DateFormat will use that, if it has en-UK then that's the format it'll use. Magic!


import java.text.DateFormat;
DateFormat dateTimeFormat;


dateTimeFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault());


Date date = new Date();
dateTimeFormat.format(date);


Next time you're displaying a date or a time, why not try using DateFormat instead of forcing your format on the user?

22 December 2016

Inject Javascript into Android WebView

Here's an interesting one I stumbled across the other day. The ability to "inject" some javascript into a webview and override the existing javascript for a webpage.

Let's say you want to override an existing javascript function, maybe one that's broken or you just want to change functionality. This is possible using onPageFinished.

Now I'm not going to say you *should* do this, nor will I say it is recommended or a good idea. I'm just pointing out that it's possible and saying it's mildly interesting.
Obviously there are warnings that go with enabling javscript on your webview and you should take heed of them over my example here.

Here's my HTML that I will load in a webview. For this example I've loaded it locally from my assets folder. I see no reason why this wouldn't work on a remote page.


<html>
    <head>
        <title>hello</title>
        <script>
            function myFunction() {
                document.getElementById("demo").innerHTML = "Gonzo was here";
            }
        </script>
    </head>
    <body>
        <p>
            <button name="Sumit" label="Submit" value="Submit" id="Submit" onclick="myFunction()">Submit</button>

            <br /><br />
            <div id="demo">Hello</div>
        </p>
    </body>
</html>


Let's try and change that javascript function to do something else:


final WebView webView = (WebView) findViewById(R.id.webView);

webView.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        webView.loadUrl("javascript:function myFunction(){document.getElementById(\"demo\").innerHTML = \"Paragraph changed.\";}");
    }
});

WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.loadUrl("file:///android_asset/test.htm");


As you can see we've injected a custom function that overrides the results with a different output. Interesting huh?

13 November 2016

Android Round Launcher Icon


So the Google Pixel now allows you to use a round icon as your launcher icon. I've seen incredibly few apps implement this, even most Google apps have yet to update. That got me wondering how to achieve this new amazing (not at all copied from Apple) roundness?

The answer is this new tag in your manifest application:

<application
    android:icon="@mipmap/ic_launcher"
    android:roundIcon="@mipmap/ic_round_launcher"

round launcher icon

square launcher icon


Android is clever enough to apply this only to phones that support it and lets you
provide a normal boring square icon for old phones.

You are given this error in the IDE which is annoying:
  
    Resources referenced from the manifest cannot vary by configuration 
    (except for version qualifiers, e.g. -v21.) Found variation 
    in hdpi, mdpi, xhdpi, xxhdpi, xxxhdpi
  
It can safely be ignored or suppressed though.

25 October 2016

Google Pixel Review


Everyone and their dog seems to be putting out Google Pixel reviews so I thought I'd chip in with my thoughts and findings.

First thing I discovered is you need a nano sim. Grr, hadn't thought about that. Thankfully BT were amazing and I had one less than 24 hours later.

The phone is smaller than I expected, much smaller. I hate big phones and that's part of the reason why I've stuck with my Nexus 4 for four years. I was genuinely worried about it, but within a few hours I had gotten used to the Pixel's larger length and I have no regrets. It's incredibly small in depth and with is small too, so the larger height is well compensated for.

The phone itself is beautiful, the curves are nice and it feels very comfortable. The metal finish is a joy to feel and the screen blends well into the case. The back glass surrounding the camera and fingerprint reader is odd, I don't quite get it. It spoils the design a little and I don't know why it's not all metal, but it's not of any real consequence.

The screen is stunning, absolutely incredible, again I'm comparing most of this to my Nexus 4 so it is worlds apart. The colours and display are crystal clear and I sometimes find myself just staring at it.

The speed is lightening fast and it seems to cope with whatever I can throw at it with ease. I haven't really pushed it yet but it is so responsive and quick I can't see it struggling. The battery is good, compared to my four year old phone it lasts infinitely longer, but it's no more or less than I'd expect from a modern phone. It doesn't last weeks but it'll get me through a couple of days.

The USB C is a cool feature, but sadly it's not new nor unique, it works and it charges fast, actually it charges really really fast. Plus you're less likely to destroy your phone by ramming the charging cable in upside down. The fingerprint reader isn't new either, but frankly that's rocked my world! I love it.

Now the OS is a difficult one, it's fine and I have no complaints. However ...I am an Android developer and have used phones by every manufacturer you care to name and every Android OS extensively. I think we're well beyond the point where an OS update makes any real difference. In terms of speed and battery use, they've pretty much done all they can. What we see now is minor updates and UI tweaks like the settings changes. Not since Material design has anything really made much difference to the user. I'm not unimpressed, it just hasn't changed my interactions with the phone much at all.

There are a few things with confuse me and they are largely the things they've "borrowed" from Apple:

  • The round icons, not sure it makes a lot of difference, but what do we developers do? Can we release with round and square icons? If we switch to square, what happens to the old OSs that aren't prepared for round icons?
  • Quick Tap or whatever you call it where you can long press on a launcher icon? That's just a blatant rip off, and it adds nothing to the user experience. Bah!


Lastly is the Google Assistant, this is impressive! Its learnt my voice and ignores my girlfriend's, which I love! It understands easily what I'm asking it and responds quickly and generally with a surprising insight. That said....I'm still not going to talk to it!

So there are my highlights, in short it's a fantastically well put together phone and I'm really enjoying it. Go get one.

13 October 2016

Android exported provider


This is something simple, but I don't feel like it gets enough press:
https://developer.android.com/guide/topics/manifest/provider-element.html#exported

If you create a provider in your manifest and your minSdkVersion or targetSdkVersion is less than 17. The default value for exported is true! That means other applications on the device can access your provider! The safe way to always deal with this is always set exported="false".