I've created an Android application that uses Expansion file. When the Expansion file downloads along with the apk, it works good. But when it doesn't, and the app needs to download the Expansion itselg, it gets complicated.
DownloadService.java - I copied *Base64-encoded RSA public key from Developer Console here and I inserted completely random bytes to SALT (I made them up myself).
public class DownloadService extends DownloaderService {
// TODO You must use the public key belonging to your publisher account
public static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0rC8XLNwlpDn0Ezc7LBF08eds9VLvbT6Bbl3B7h6uSqa703HXEv44LLBLAmBz2QM2iwzrf3WH7Tjp80II9rQxaVsUIukBHtnE5r9CBHn/pQl4OkZplmI3/Fvl2WPWyvXqVi2BungmW29FBqIWVQED0UepT1eXVr8X8sPAhcDwfI2WERIWHzm32oiz/a2dpMBs1DA/ERO1kWgtDKJuwjik9tHTeo2xfRBC+ytZQ5jqKFlmLqlDXpcDCUXVXhV2idtujV9cPD1PdEgElPTbqro7LQdrsMm4hn0olPK2Iy6hhdzO/Tll/viH03GtUkMOjpFA+OAVsA+b+oM1MfHiHTbwIDAQAB";
// TODO You should also modify this salt
public static final byte[] SALT = new byte[] { 7, 23, -47, -8, 24, 72,
-28, -83, 47, 1, -12, -8, 28, 3, -101, -109, -20, 47, -2, 92
};
@Override
public String getPublicKey() {
return BASE64_PUBLIC_KEY;
}
@Override
public String getAlarmReceiverClassName() {
return BroadCastReceiver.class.getName();
}
@Override
public byte[] getSALT() {
return SALT;
}
}
BroadcastReceiver.java
public class BroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, DownloadService.class);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
SplashActity.java - the first activity that starts with the app, here is where I implemented the download process
public class SplashActivity extends Activity implements IDownloaderClient {
int SPLASH_DURATION = 2000;
TextView title1, title2, loading;
// APK EXPANSIONS
private IStub mDownloaderClientStub;
private IDownloaderService mRemoteService;
private ProgressDialog mProgressDialog;
private static final String LOG_TAG = "LOG";
// The shared path to all app expansion files
private final static String EXP_PATH = "/Android/obb/";
private static class XAPKFile {
public final boolean mIsMain;
public final int mFileVersion;
public final long mFileSize;
XAPKFile(boolean isMain, int fileVersion, long fileSize) {
mIsMain = isMain;
mFileVersion = fileVersion;
mFileSize = fileSize;
}
}
private static final XAPKFile[] xAPKS = {
new XAPKFile(
true, // true signifies a main file
2, // the version of the APK that the file was uploaded against
52948873L // the length of the file in bytes
)
};
boolean expansionFilesDelivered() {
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
// Log.v(LOG_TAG, "XAPKFile name : " + fileName);
if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false)) {
Log.e(LOG_TAG, "ExpansionAPKFile doesn't exist or has a wrong size (" + fileName + ").");
return false;
}
}
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
// Views
title1 = (TextView) findViewById(R.id.textView2);
title2 = (TextView) findViewById(R.id.textView3);
loading = (TextView) findViewById(R.id.textView);
// Font
Typeface tf = Typeface.createFromAsset(getApplicationContext().getAssets(), "aressence.ttf");
title1.setTypeface(tf);
title2.setTypeface(tf);
loading.setTypeface(tf);
// Check if expansion files are available before going any further
if (!expansionFilesDelivered()) {
try {
Intent launchIntent = this.getIntent();
// Build an Intent to start this activity from the Notification
Intent notifierIntent = new Intent(SplashActivity.this, SplashActivity.this.getClass());
notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
notifierIntent.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
notifierIntent.addCategory(category);
}
}
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Start the download service (if required)
Log.v(LOG_TAG, "Start the download service");
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, DownloadService.class);
// If download has started, initialize activity to show progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
Log.v(LOG_TAG, "initialize activity to show progress");
// Instantiate a member instance of IStub
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, DownloadService.class);
// Shows download progress
mProgressDialog = new ProgressDialog(SplashActivity.this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMessage(getResources().getString(R.string.state_downloading));
mProgressDialog.setCancelable(false);
mProgressDialog.show();
return;
}
// If the download wasn't necessary, fall through to start the app
else {
Log.v(LOG_TAG, "No download required");
}
}
catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
e.printStackTrace();
}
catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
e.printStackTrace();
}
}
else
{
// 2sec handler
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
finish();
}
}, SPLASH_DURATION);
}
// Set by <content src="index.html" /> in config.xml
//super.loadUrl(Config.getStartUrl());
//super.loadUrl("file:///android_asset/www/index.html")
}
@Override
protected void onStart() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onStart();
}
@Override
protected void onResume() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onResume();
}
@Override
protected void onStop() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.disconnect(this);
}
super.onStop();
}
@Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
@Override
public void onDownloadStateChanged(int newState) {
Log.v(LOG_TAG, "DownloadStateChanged : " + getString(Helpers.getDownloaderStringResourceIDFromState(newState)));
switch (newState) {
case STATE_DOWNLOADING:
Log.v(LOG_TAG, "Downloading...");
break;
case STATE_COMPLETED: // The download was finished
// validateXAPKZipFiles();
mProgressDialog.setMessage("");
// dismiss progress dialog
mProgressDialog.dismiss();
// Load url
//super.loadUrl(Config.getStartUrl());
break;
case STATE_FAILED_UNLICENSED:
case STATE_FAILED_FETCHING_URL:
case STATE_FAILED_SDCARD_FULL:
case STATE_FAILED_CANCELED:
case STATE_FAILED:
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("error");
alert.setMessage("download failed");
alert.setNeutralButton("close", null);
alert.show();
break;
}
}
@Override
public void onDownloadProgress(DownloadProgressInfo progress) {
long percents = progress.mOverallProgress * 100 / progress.mOverallTotal;
Log.v(LOG_TAG, "DownloadProgress:"+Long.toString(percents) + "%");
mProgressDialog.setProgress((int) percents);
}
}
I found this related to licensing in the LogCat right after the app tried to download Expansion files.
08-19 23:39:16.641 25920-25920/com.mhacinapps.sleepwithnaturesounds I/LicenseChecker﹕ Binding to licensing service.
08-19 23:39:16.758 25920-25920/com.mhacinapps.sleepwithnaturesounds I/LicenseChecker﹕ Calling checkLicense on service for com.mhacinapps.sleepwithnaturesounds
08-19 23:39:16.758 25920-25920/com.mhacinapps.sleepwithnaturesounds I/LicenseChecker﹕ Start monitoring timeout.
08-19 23:39:26.766 25920-25939/com.mhacinapps.sleepwithnaturesounds I/LicenseChecker﹕ Check timed out.
When the Expansion file is not present on the device, the download bar is at 0/100 for a few seconds and then it says that it didn't find the resources and downlaod failed.
Could someone please help me and let me know where I went wrong?
Thank you!