Last active
November 12, 2019 04:34
-
-
Save moust/7990925 to your computer and use it in GitHub Desktop.
Example of implementation of APK Expansion Files download process with a PhoneGap application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.phonegap.Sample; | |
import java.io.File; | |
import android.app.AlertDialog; | |
import android.app.AlertDialog.Builder; | |
import android.app.PendingIntent; | |
import android.app.ProgressDialog; | |
import android.content.Intent; | |
import android.content.pm.PackageManager.NameNotFoundException; | |
import android.os.Bundle; | |
import android.os.Environment; | |
import android.os.Messenger; | |
import android.os.StatFs; | |
import android.util.Log; | |
import org.apache.cordova.*; | |
import com.google.android.vending.expansion.downloader.DownloadProgressInfo; | |
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; | |
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; | |
import com.google.android.vending.expansion.downloader.Helpers; | |
import com.google.android.vending.expansion.downloader.IDownloaderClient; | |
import com.google.android.vending.expansion.downloader.IDownloaderService; | |
import com.google.android.vending.expansion.downloader.IStub; | |
public class Sample extends DroidGap implements IDownloaderClient | |
{ | |
private IStub mDownloaderClientStub; | |
private IDownloaderService mRemoteService; | |
private ProgressDialog mProgressDialog; | |
private static final String LOG_TAG = "Sample"; | |
// The shared path to all app expansion files | |
private final static String EXP_PATH = "/Android/obb/"; | |
/** | |
* This is a little helper class that demonstrates simple testing of an | |
* Expansion APK file delivered by Market. You may not wish to hard-code | |
* things such as file lengths into your executable... and you may wish to | |
* turn this code off during application development. | |
*/ | |
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; | |
} | |
} | |
/** | |
* Here is where you place the data that the validator will use to determine | |
* if the file was delivered correctly. This is encoded in the source code | |
* so the application can easily determine whether the file has been | |
* properly delivered without having to talk to the server. If the | |
* application is using LVL for licensing, it may make sense to eliminate | |
* these checks and to just rely on the server. | |
*/ | |
private static final XAPKFile[] xAPKS = { | |
new XAPKFile( | |
true, // true signifies a main file | |
1, // the version of the APK that the file was uploaded against | |
172667765L // the length of the file in bytes | |
) | |
}; | |
/** | |
* Go through each of the APK Expansion files defined in the structure above | |
* and determine if the files are present and match the required size. Free | |
* applications should definitely consider doing this, as this allows the | |
* application to be launched for the first time without having a network | |
* connection present. Paid applications that use LVL should probably do at | |
* least one LVL check that requires the network to be present, so this is | |
* not as necessary. | |
* | |
* @return true if they are present. | |
*/ | |
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 | |
public void onCreate(Bundle savedInstanceState) | |
{ | |
super.onCreate(savedInstanceState); | |
// 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(Sample.this, Sample.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, SampleDownloaderService.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, SampleDownloaderService.class); | |
// Shows download progress | |
mProgressDialog = new ProgressDialog(Sample.this); | |
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); | |
mProgressDialog.setMessage(getResources().getString(R.string.downloading_assets)); | |
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 (NameNotFoundException e) { | |
Log.e(LOG_TAG, "Cannot find own package! MAYDAY!"); | |
e.printStackTrace(); | |
} | |
catch (Exception e) { | |
Log.e(LOG_TAG, e.getMessage()); | |
e.printStackTrace(); | |
} | |
} | |
// Set by <content src="index.html" /> in config.xml | |
super.loadUrl(Config.getStartUrl()); | |
//super.loadUrl("file:///android_asset/www/index.html") | |
} | |
/** | |
* Connect the stub to our service on start. | |
*/ | |
@Override | |
protected void onStart() { | |
if (null != mDownloaderClientStub) { | |
mDownloaderClientStub.connect(this); | |
} | |
super.onStart(); | |
} | |
/** | |
* Connect the stub from our service on resume | |
*/ | |
@Override | |
protected void onResume() { | |
if (null != mDownloaderClientStub) { | |
mDownloaderClientStub.connect(this); | |
} | |
super.onResume(); | |
} | |
/** | |
* Disconnect the stub from our service on stop | |
*/ | |
@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 onDownloadProgress(DownloadProgressInfo progress) { | |
long percents = progress.mOverallProgress * 100 / progress.mOverallTotal; | |
Log.v(LOG_TAG, "DownloadProgress:"+Long.toString(percents) + "%"); | |
mProgressDialog.setProgress((int) percents); | |
} | |
@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(getResources().getString(R.string.preparing_assets)); | |
// 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: | |
Builder alert = new AlertDialog.Builder(this); | |
alert.setTitle(getResources().getString(R.string.error)); | |
alert.setMessage(getResources().getString(R.string.download_failed)); | |
alert.setNeutralButton(getResources().getString(R.string.close), null); | |
alert.show(); | |
break; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.phonegap.Sample; | |
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; | |
import android.content.BroadcastReceiver; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.pm.PackageManager.NameNotFoundException; | |
/** | |
* You should start your derived downloader class when this receiver gets the message | |
* from the alarm service using the provided service helper function within the | |
* DownloaderClientMarshaller. This class must be then registered in your AndroidManifest.xml | |
* file with a section like this: | |
* <receiver android:name=".XAPKAlarmReceiver"/> | |
*/ | |
public class XAPKAlarmReceiver extends BroadcastReceiver { | |
@Override | |
public void onReceive(Context context, Intent intent) { | |
try { | |
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, XAPKDownloaderService.class); | |
} catch (NameNotFoundException e) {S | |
e.printStackTrace(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.phonegap.Sample; | |
import com.google.android.vending.expansion.downloader.impl.DownloaderService; | |
/** | |
* This class demonstrates the minimal client implementation of the | |
* DownloaderService from the Downloader library. | |
*/ | |
public class XAPKDownloaderService extends DownloaderService { | |
// stuff for LVL -- MODIFY FOR YOUR APPLICATION! | |
private static final String BASE64_PUBLIC_KEY = "YOUR_PUBLIC_KEY"; | |
// used by the preference obfuscater | |
private static final byte[] SALT = new byte[] { | |
1, 43, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -108, -33, 45, -1, 84 | |
}; | |
/** | |
* This public key comes from your Android Market publisher account, and it | |
* used by the LVL to validate responses from Market on your behalf. | |
*/ | |
@Override | |
public String getPublicKey() { | |
return BASE64_PUBLIC_KEY; | |
} | |
/** | |
* This is used by the preference obfuscater to make sure that your | |
* obfuscated preferences are different than the ones used by other | |
* applications. | |
*/ | |
@Override | |
public byte[] getSALT() { | |
return SALT; | |
} | |
/** | |
* Fill this in with the class name for your alarm receiver. We do this | |
* because receivers must be unique across all of Android (it's a good idea | |
* to make sure that your receiver is in your unique package) | |
*/ | |
@Override | |
public String getAlarmReceiverClassName() { | |
return XAPKAlarmReceiver.class.getName(); | |
} | |
} |
Good one, thanks
Thanks!
Can you please tell me, how to reduce apk size as it is currently showing apk size+expansion file size on play store which is humongous..Is there any way to do this as in my case it is near about 300 mb??
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is great, thanks!