Building a Vue 3 native Android app with Capacitor on WSL2 (Ubuntu 20.04)
Tags: webdev, android, mobile, vue
Published: 2021-11-14 18:22:00

---
title: Building a Vue 3 native Android app with Capacitor on WSL2 (Ubuntu 20.04)
published: true
date: 2021-11-14 18:22:00 UTC
tags: webdev,android,mobile,vue
canonical_url: https://dico.duba.dev/building-a-vue-3-native-android-app-with-capacitor-on-wsl2-ubuntu-20-04/
license: cc-by-4.0
---

Capacitor is the spiritual successor to Cordova and Phonegap. Unfortunately they took this literally and left out actual instructions on how to compile the app to a native android app, because who needs those... At time of writing, [Vue recommends either Capacitor or NativeScript](https://v3.vuejs.org/guide/mobile.html), but NativeScript requires prior knowledge about native apps I do not have. If you can work with that, [their installation guide](https://docs.nativescript.org/environment-setup.html#linux-android) is a lot clearer than Capacitor's is, and possibly a better fit for you.

Credit to the following resources for providing snippets to get this guide working: [Install Android SDK CLI Ubuntu 20.04 WSL2 (Work in Progress)](https://gist.github.com/steveclarke/d988d89e8cdf51a8a5766d69ecb07e7b) (by steveclarke), [the documentation of NativeScript](https://docs.nativescript.org/environment-setup.html#linux-android) and [a guide to building a react native app in WSL2](https://gist.github.com/bergmannjg/461958db03c6ae41a66d264ae6504ade) by bergmannjg.

## Prerequisites and goals

At the end of this blog you will be able to:
* Set up a Vue 3 app with Capacitor
* Be able to run a test build on a real device from WSL2
* Be able to build a version of your app that can be deployed to the Play Store

Notable things that I will **not** be touching upon are how to make this work with an emulator, and how to debug the app on a real device.

I expect you have already installed node (I have 16.13.0) and npm (I have 8.1.0). If not, go do that using your favourite method. I also assume you use bash (you can check with `ps -p $$`). If you do not use bash, there may be slight differences in syntax. I also expect you to have an android device you can connect to via usb, and where you already have enabled the USB debugger.

## Creating a new Vue app and installing Capacitor

Added here for completeness sake. Chances are you already pulled this from some other guide.

```bash
vue create your-app
cd your-app
npm install @capacitor/core @capacitor/cli
npm install @capacitor/android
npx cap init "My beautiful app" com.something.my_beautiful_app --web-dir=dist
npx cap add android
```

## Installing the Android SDK

After these initial steps the Capacitor documentation stops. You can run an debug build via the following command:

```bash
npx cap run android
```

This will likely yield the error

```
[error] native-run failed with error

        ERR_SDK_NOT_FOUND: No valid Android SDK root found.

        More details for this error may be available online:
        https://github.com/ionic-team/native-run/wiki/Android-Errors
```

### Command line tools

We will tackle this problem one step at a time. First we will install the Android SDK command line tools. Obtain the most up-to-date download link for the command line tools only from [the official android studio download page](https://developer.android.com/studio#downloads). Alternatively just install the version I used at time of writing (November 2021).

```bash
# Install unzip if it is not already installed
sudo apt install unzip

# We will install this in the home directory
cd $HOME
mkdir -p Android/cmdline-tools
wget https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip
unzip commandlinetools-linux-7302050_latest.zip -d Android/cmdline-tools
# This location is important, because otherwise you will get an error that the SDK root could not be determined
mv Android/cmdline-tools/cmdline-tools Android/cmdline-tools/latest

# And clean up after ourselves
rm commandlinetools-linux-7302050_latest.zip
```

While we have the command line tools, other tools like Capacitor relying on it do not know where to find it. For this we need to set the `ANDROID_HOME` environment variable. We also need to update our PATH environment variable so the command line knows where to find the sdkmanager for example.

```bash
# These changes last until the end of the current bash session. We will add a more permanent solution later
export ANDROID_HOME=$HOME/Android
export PATH="$ANDROID_HOME/cmdline-tools/latest:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH"
```

### Installing some build tools and platforms

If you would to run the emulator now (`npx cap run android`), you'll notice it at least doesn't complain that it can't find an Android SDK root. It still doesn't work though. To fix this we will run the sdkmanager with some commands. If the sdkmanager complains with an error like `Error: Could not determine SDK root`. make sure that it is in the correct location. In my version it needs to be in a folder `cmdline-tools/latest/bin` to automatically detect where the root is.

```bash
# Make sure we have the latest information
sdkmanager --update

# We are installing version 31. To figure out what options *you* have, see below
# You'll have to accept licenses by pressing y. Ignore the clause where you sell your soul. That's normal.
sdkmanager "build-tools;31.0.0" "platform-tools" "platforms;android-31" "tools"
```

If you want to find out what other platforms you can use, you can list the options with `sdkmanager --list | grep tools`

**Last but not least you need to accept any remaining licenses that did not pop up during the initial install.**

```bash
sdkmanager --licenses
```

### Making our changes permanent

If all of the above went as expected, we can make our changes to our environment variables more permanent. Otherwise figure out what you needed to change, and persist those changes instead.

Edit the `~/.bashrc` file with an editor of your choice, then add the following lines to the bottom.

```bash
export ANDROID_HOME=$HOME/Android

# Note: I am exporting these from back to front from our previous command
export PATH=$ANDROID_HOME/tools/bin:$PATH
export PATH=$ANDROID_HOME/tools:$PATH
export PATH=$ANDROID_HOME/emulator:$PATH
export PATH=$ANDROID_HOME/platform-tools:$PATH
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH
export PATH=$ANDROID_HOME/cmdline-tools/latest:$PATH
```

You will need to restart your shell for these changes to take effect. Go do that now. Exit out of the shell (ctrl+d) and start it again. Then test if your path and android home variable are set by echo-ing them in the shell.

```bash
# should show a line with something like /home/youruser/Android
echo $ANDROID_HOME
# should show a loooong line, with our Android paths at the start
echo $PATH
```

## Other dependencies
### Gradle

Last but not least we need to make sure that gradle is installed. Gradle is used to build our source code into a nice apk that can be loaded on a device.

At time of writing the version of gradle in the Ubuntu repository is woefully out of date, so remove it if it is installed.

```bash
sudo apt remove gradle
```

Then install it from a different repository

```bash
sudo add-apt-repository ppa:cwchien/gradle
sudo apt-get update
sudo apt install gradle

# And check if it is higher than v4
gradle -v
```

### Java JDK

You'll eventually also need the java jdk. You can check if one is already installed for you.

```bash
sudo apt list --installed | grep jdk
```

The easiest way to install the latest working version for you is probably installing default-jdk-headless, which installed openjdk-11-jdk-headless for me.

```bash
sudo apt install default-jdk-headless
```

### Zipalign

To eventually be able to create a signed apk you can put in the Google Play Store you will need to have zipalign installed.

```bash
sudo apt install zipalign
```

### Apksigner

Similar to zipalign, we will need the apksigner to... sign... the apk... that we want to put in the Google Play Store.

```bash
sudo apt install apksigner
```

## Running a dev build

With the android build tools installed, everything will work fine... right? Right?

Unfortunately when running `npx cap run android`, we quickly figure out that the emulator is not working. We get the following error.

```
[error] native-run failed with error

        ERR_UNSUITABLE_API_INSTALLATION: No suitable API installation found. Use --sdk-info to reveal missing packages
        and other issues.

        More details for this error may be available online:
        https://github.com/ionic-team/native-run/wiki/Android-Errors
```

The proposed switch doesn't reveal anything, and the --verbose flag the link suggests doesn't do anything either. I have not been able to figure out how to get an emulator working on WSL2. You may be able to accomplish this by running adb on Windows instead by following [the instructions on bergmannjg's gist](https://gist.github.com/bergmannjg/461958db03c6ae41a66d264ae6504ade#connect-to-android-virtual-device-in-windows) or an article by Adrien Pellegrini showing [how to use the Android emulator in Windows 10 with WSL2](https://pellea.medium.com/using-the-android-emulator-on-windows-10-with-wsl2-39c2bae30c49). What I did figure out is how to make the command deploy a test build to a connected device. Unfortunately this too takes some work.

### Make usb devices visible to WSL

The first road block to overcome is to make usb devices visible to WSL2. If you run lsusb in WSL, you will notice that there are no usb devices listed.

To solve this problem we need to create a bridge service that sends usb data from Windows to WSL2. It's probably best to follow the instructions from the article that introduces it directly: [Connecting USB devices to WSL](https://devblogs.microsoft.com/commandline/connecting-usb-devices-to-wsl/) by Ben McMorran. In short: install a service called usbipd-win on Windows, two libraries called linux-tools-5.4.0-77-generic and hwdata on WSL2 and finally add /usr/lib/linux-tools/5.4.0-77-generic to secure_path in the /etc/sudoers file. Finally restart the computer so the service starts.

### Connecting a phone to WSL2

This section largely follows what [cjshearer commented on a github issue about adb](https://github.com/dorssel/usbipd-win/issues/60#issuecomment-962121982) and uses some information in the article linked in the previous paragraph. If anything is unclear, you may want to consult these posts.

First, open Powershell on Windows as administrator. Then open a WSL2 command line window. This makes sure that Ubuntu is actually running. Next plug in your Android test device, and run the following command in Powershell.

```bash
usbipd wsl list
```

In my case there is a whole list of usb devices, but the one I want is marked as

```
1-6    SAMSUNG Android ADB Interface, SAMSUNG Mobile USB Modem, ...  Not attached
```

1-6 is the bus id of my phone, so I will bridge it to WSL2 with the following command in Powershell

```bash
usbipd wsl attach --busid 1-6
```

Enter your WSL2 sudo password.

You should now see your device in WSL2 if you execute the lsusb command. Remember that you need to enable this bridge whenever you remove the usb cable from the device and put it back in. It will not remember to bridge the usb (and this is probably for the best).

### Giving adb permissions to the phone

When you now do the `adb devices` command, you'll notice that it shows you a device number and something like

```
no permissions (missing udev rules? user is in the plugdev group); see [http://developer.android.com/tools/device.html]
```

So... we have to set up the udev rules for this device. Izzy does a very good job [in their answer android.stackexchange.com](https://android.stackexchange.com/a/144967) explaining what to do, so I recommend just following that answer. In short, make note of the device id in the output of `lsusb`, craft a line with it in a specific rule file, then restart udev and reload the udevadm service, and replug the device. The group you select for this device should be one you are in. Adb suggests `plugdev` as te group name. You can check your own groups with the groups command.

```
sudo service udev restart
sudo udevadm control --reload
```

You may need to do this after every computer restart.

You can now deploy your app for testing purposes to the connected device. Make sure that you authorize the computer on the phone. `adb devices` should show you the device id followed by "device". If it shows unauthorized, you still need to authorize it on the phone. If it shows no permissions you may need to restart the services listed above, and you may need to replug your phone.

## Building for production

Eventually you probably want to publish your app in the Google Play Store. To do this you will need to generate a signed apk. Since we don't have a GUI in WSL2, we will have to do this from the command line as well. Use the official documentation on [building your app from the command line](https://developer.android.com/studio/build/building-cmdline) as a guideline.

### Preparing a key to sign

To sign an apk with a key, we first need... a key. This key needs to be constant between releases, so generate it once and make sure to save it to your repository. First, navigate to the android subfolder of your project.

```bash
cd /path/to/your/project/android
```

Then generate a key. We use the alias "release", since aliases can be enumerated anyway with the keystore's password so there is no value in making it obscure. Store the password of the keystore securely somewhere **outside** the repository.

```bash
keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
```

### Assemble and sign your build

We first want to assemble an apk that is ready for release, but still needs to be signed. To do this, we just run gradle.

```bash
cd /path/to/your/project/android
gradle assembleRelease
```

Your apk will be generated somewhere deep in the app/build folder. You can quickly get the path with `find . -type f -name '*.apk'` or hope it is in `./app/build/outputs/apk/release/app-release-unsigned.apk` like it is for me.

If you run into problems with an out-of-date version of gradle, scroll up to Other dependencies > Gradle.

Next we zipalign the entire thing:

```bash
zipalign -v -p 4 ./app/build/outputs/apk/release/app-release-unsigned.apk ./app/build/outputs/apk/release/app-release-unsigned-aligned.apk
```

Finally sign the apk. Use the password that you securely stored somewhere several paragraphs ago.

```bash
apksigner sign --ks release.jks --out ./app/build/outputs/apk/release/app-release-signed.apk ./app/build/outputs/apk/release/app-release-unsigned-aligned.apk
```

And finally verify that everything went well.

```bash
apksigner verify ./app/build/outputs/apk/release/app-release-signed.apk
```

Finally copy that file somewhere where you will be able to access it to put it in the Google Play Store.