Sharing Apps on Android via Bluetooth

There are times when you just want to share a particular application on your phone to a friend and as developer, I encountered this problem several times when I want to let my friend try out an application on his/her phone.

One thing that you can do to share an app is to use 3rd-party applications that can backup an application to sdcard. I wrote one very simple application that can do this few years back. It’s called APKoptic. After storing the APK to your sdcard, you can now download it to your pc and install the app to your friend’s phone using ADB or send it to email and let your friend download and install. This is cumbersome as you still need a computer to do this simple thing of sharing. So I decided to write a very simple Proof-of-Concept app that will allow you to share an Android application to any other Android phone that supports Bluetooth connectivity.

List all installed applications

First, we need to retrieve all the installed applications on the phone using the PackageManager API. The following AsyncTask will retrieve all the installed apps and filter out the system applications as well as those protected apps.



class LoadAppsTask extends AsyncTask> {

    private ProgressDialog pd;

    @Override
    protected void onPreExecute() {
        pd = new ProgressDialog(BluetoothAppShareActivity.this);
        pd.setMessage("Loading applications");
        pd.show();
    }

    @Override
    protected List doInBackground(Void... params) {
        List packages = pm.getInstalledPackages(0);

        filterProtectedApps(packages); // because we cannot access their
                       // apks
        return packages;
    }

    private void filterProtectedApps(List packages) {
        Iterator i = packages.iterator();
        while (i.hasNext()) {
        PackageInfo info = i.next();
        if (info.versionName == null || info.applicationInfo.publicSourceDir.startsWith("/system")) {
            i.remove();
            continue;
        }
        }
    }

    @Override
    protected void onPostExecute(List result) {
        if (pd != null && pd.isShowing()) {
        pd.dismiss();
        }

        if (result != null && result.size() > 0) {
        appsList.setAdapter(new AppsListAdapter(BluetoothAppShareActivity.this, result));
        appsList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        }
    }

    }

The method filterProtectedApps checks if the publicSourceDir of the application (where the APK resides) is in the system folder. If it is, then we will remove it from the list of applications to be displayed to the user.

Prepare the ListView

After retrieving all the applications, it will then be displayed on a ListView with choice mode set to ListView.CHOICE_MODE_MULTIPLE.

appsList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

We need to set the selection mode to multiple so that we can send multiple applications at once. After the user selects the applications that he want to share, a “Share” button will initiate the sharing which will call the method shareApps.

Sharing



public void shareApps(View v) {
    SparseBooleanArray arr = appsList.getCheckedItemPositions();

    int size = arr.size();

    Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
    intent.setType("application/vnd.android.package-archive");

    ArrayList uris = new ArrayList();
    for (int i = 0; i < size; i++) {
        int key = arr.keyAt(i);
        boolean checked = arr.get(key);

        if (checked) {
        PackageInfo info = ((PackageInfo) appsList.getAdapter().getItem(key));
        File apkFile = new File(info.applicationInfo.publicSourceDir);

        if (apkFile != null && apkFile.exists() && apkFile.getName().endsWith(".apk")) {
            uris.add(Uri.fromFile(apkFile));
        }
        }
    }

    intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
    startActivity(intent); 

    }

If you are not familiar with SparseBooleanArray yet, it is just a map for index position and a boolean that represents whether that particular row is checked or not. For example:

Row 1 = checked

Row 2 = not checked

Row 3 = checked

SparseBooleanArray will contain the following values:

[0, true]

[1, false]

[2, true]

Next thing you’ll notice is that we instantiated an Intent to invoke the built-in Intent chooser of Android.


Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setType("application/vnd.android.package-archive");

 The Intent.ACTION_SEND_MULTIPLE simply tells the receiver that it is sending multiple files and should handle it properly. The MIME type of an Android APK is {% codeblock lang:java %}application/vnd.android.package-archive {% endcodeblock %}

which also helps the receiver to determine how to receive the data from the caller.

After calling startActivity(intent), you’ll see a list of applications that can handle data with the given mime-type.

UPDATE: Sharing of APKs via bluetooth doesn’t work on ICS anymore.

comments powered by Disqus