01 May 2019

Kotlin Coroutines

I don't by any means propose to be a master on this topic, but there's been so much chat on this topic on the blog world, I thought I'd give it a try. As is my usual I always like to start with the most insanely simple scenario I can think of.

I started off reading this article which actually gives a really nice overview of what coroutines are and why they're important:
https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb

I started by creating a new Kotlin project, I'm on Android Studio 3.3.2 and Kotlin version 1.3.21 and added the following library:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

Maybe this will be added into standard Kotlin in the future. For me this was quite a surprise
that it needed a separate library.

Now let's jump straight into some reckless coding, I added a TextView to my activity with the ID 

android:id="@+id/hello_world"
Now let's create a function that will run in the background. I'm thinking of this like the main thread on an AsyncTask. For my function, I'm going to pause for ten seconds and then return a string.

suspend fun get(): String {
    //Delay for ten seconds
    delay(10000)
    return "All your base are belong to me"
}
Now a plain vanilla function to show the results
fun show(result: String) {
    val tv = findViewById<TextView>(R.id.hello_world)
    tv.setText(result)
    println("Done!")
}
Now we need to setup the Activity to allow for Coroutines. This in my opinion is a bit of a mess. I've no idea why I need all this boilerplate nonsense. Oh well
class MainActivity : AppCompatActivity(), CoroutineScope {

    private var job: Job = Job()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    override fun onDestroy() {
        super.onDestroy()
        job.cancel()
    }
You'll see we need to implement the CoroutineScope and add some other fluff just to use Coroutines. Lastly we can call our function in the background from our onCreate method

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    launch {
        // Dispatchers.IO
        val result = get()
        // Dispatchers.Main
        show(result)
    }

    println("Start!")
}
So as you can see onCreate launches get in the background which waits ten seconds then returns a string. The launch method then, (when complete) calls the show method with that result. I know that's an incredibly basic example but I hope it helps. I found the concept easy to grasp, but the implementation was a bit fiddly. Hence the blog post.

22 November 2018

Android RxJava .delay() method


This is a weird one but it took up most of my day, so I thought I'd post it here for posterity. This article describes the problem very well:

https://dev.to/dbottillo/rxjava-a-story-about-delay-and-schedulers-j48

.delay(200, TimeUnit.MILLISECONDS)

In short calling .delay on a rxjava observable causes very strange results. The reason is because delay forces a change to the subscribeOn type. Why it does this is a mystery to me. However luckily the solution is very simple.

.delay(200, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())




28 August 2018

Raspberry Pi Photo Slideshow

I decided I wanted to use a Raspberry Pi to run a slideshow device, similar to the old scrolling photo screens. I needed something with an HDMI port that would scroll through my chosen photos with no user interaction. Raspberry Pi was a good choice for me as I already had two, they are low power and cheap. Plus if I lost one it would be considerably less painful than losing a laptop.

So first thing first I needed to sort the hardware out. I already had a Raspberry Pi 2 and a Raspberry Pi 1 so I wanted to use them. I wasn’t initially sure how the 1 would perform, but eager to see. The only other difference should be the SD card in the 1 and the micro SD card in the 2. Thankfully this made no difference to the process at all.

Next thing was the operating system. I downloaded Raspbian stretch with desktop and installed it on the SD card. Then of course fired it up and ran through the setup process. I deliberately didn’t set a password so the raspberry could log straight in. All of this was pretty straightforward.

Next I added the photos. The quickest way to do this was shut the raspberry down and plug the SD card into my laptop and copy the images onto the SD card. I put the images in /home/pi/Pictures/

Next I needed the slideshow part. For this I found a nice little tutorial:

This boils down to the following commands:

sudo apt-get install feh

Feh is a simple image viewer. Next we need a screensaver application that will show the images and scroll through them.

sudo apt-get install xscreensaver

This needs a little configuration. So goto start -> Preferences -> Screen Saver
Set the blank out time to 720. As the above article mentions this is the maximum time that you can force the screensaver can run for.

Test it out by running this command:

feh -Y -x -q -D 5 -B black -F -Z -z -r /home/pi/Pictures/

To exit press q.

Now we need to setup the pi so it starts the slideshow when it boots up, meaning all we have to do is power it up and it’s away. This bit got a bit more tricky. First we create an executable file with the feh command in so it can be run. I created this file at /home/pi/ and called it superscript. I gave it all permissions:

    Sudo chmod 777 superscript

Try running this a few times and make sure it works, it’s easier to diagnose problems now than it is later.

Lastly we need to run out superscript file on startup. To do this goto this folder:

    /etc/xdg/autostart/

And create a file called imageStartup.desktop

Add the following contents:

[Desktop Entry]
Type=Application
Name=imageStartup
Comment=All your base are belong to me
NoDisplay=false
Exec=/home/pi/superscript
NotShownIn=GNOME;KDE;XFCE;

Give this permissions as well and you are all set! On restart your images should loop.

Enjoy.

12 July 2018

Firebase Realtime Database - Custom Rules - Admin Write Permissions


Firebase is cool. Firebase realtime database is also cool. Rules...they are way less cool.

I joke, but protecting your Firebase realtime data is of course important and not to be under-estimated. I wanted to setup Firebase realtime database rules so only the database administrator (me) could enter or edit data. This took me a while to figure out as it involves not only setting the correct rules, but editing the metadata of the user.

Right so let's jump straight in, we're going to set the database rules to allow only edits by an administrator:

"rules":{
        "dinosaurs":{
            ".read":"auth != null",
            ".write":"auth.token.admin === true"
        }


You can see we've granted write permissions only if the user is an administrator. That was pretty easy. Sadly actually configuring which user is an admin is much harder.

First goto your Firebase console and click on the Authentication section, you should be on the user's tab. Locate the user you want to make an administrator and copy their User UUID.

To set this user as an administrator you'll need to use the Firebase Admin SDK. This isn't an Android SDK and so I'm afraid we're going to have to break into some NodeJS.

I'm not going to go into setting up NodeJS here, as it's a huge pain in the neck. Once you've got NodeJS setup, install the firebase Admin SDK as such:
https://firebase.google.com/docs/admin/setup?authuser=0

$ npm install firebase-admin --save

Now you'll need to return to your Firebase console and create a service key. In the settings section, goto service accounts and download a private key. You'll need to create one for NodeJS and you'll need to copy it to the NodeJS folder you're about to create. The path from the index.js file needs to be relative, mine is justin the root. As we won't be sharing this project, it should be fine, but you should never publicly host or share your private key.
Now you should be ready to run a NodeJS script which will set this user as an admin.

Create a standard node project and edit the index.js file. Add the following code:

var admin = require('firebase-admin');

var serviceAccount = require('./service_key.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: '*****'
});


admin.auth().setCustomUserClaims('#######', {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});

Replace the *** with your database url and #### with the UUID you copied earlier and run your nodeJs project.

That's it. Your user should now be an administrator, it can take a while to propagate, so try logging on and off a few times if it doesn't work right away.

What you've done here is set a custom Claim on that user. Basically some meta-data that describes that user as an admin. Your database rules will only allow administrators to edit that data. Here's a bit more info should you wish to take Calims further:
https://firebase.google.com/docs/auth/admin/custom-claims?authuser=0

Good luck, and have fun with Firebase.

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?