First of all. Read all before giving down vote.
I am trying to achieve one of biggest challange for me that download OBB file through google market. I have tested/tried almost every single code published in here and read almost every single topic which posted here unfortunately non-of-them solved my problem.
What i did so far? well, i have import sample download activity from SDK directory and changed it to my APP which is currently publishing in Market also created an OBB file and uploaded it and saved it in DRAFT mode.
So, i have followed tutorial in http://my.fit.edu/~vkepuska/ece5570/adt-bundle-windows-x86_64/sdk/docs/guide/google/play/expansion-files.html step by step. My test APP shows up but download is not starting.
This is my BroadcastReceiver:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
public class DownloadFinished extends BroadcastReceiver
{
@Override
public void onReceive(Context arg0, Intent arg1)
{
try
{
DownloaderClientMarshaller.startDownloadServiceIfRequired(arg0, arg1, SampleDownloaderService.class);
}
catch (NameNotFoundException e)
{
e.printStackTrace();
}
}
}
And this is my DownloaderService:
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
public class SampleDownloaderService extends DownloaderService
{
private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQE..........";
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};
@Override
public String getPublicKey()
{
return BASE64_PUBLIC_KEY;
}
@Override
public byte[] getSALT()
{
return SALT;
}
@Override
public String getAlarmReceiverClassName()
{
return DownloadFinished.class.getName();
}
}
And this is my Activity:
public class SplashActivity extends Activity implements IDownloaderClient
{
// OBB DOWNLOAD
static private final XAPKFile[] xAPKS = {new XAPKFile(true, 5, 58814844L)};
static private final float SMOOTHING_FACTOR = 0.005f;
private IStub mDownloaderClientStub;
private IDownloaderService mRemoteService;
private View mDashboard;
private View mCellMessage;
private ProgressBar mPB;
private TextView mStatusText;
private TextView mProgressFraction;
private TextView mProgressPercent;
private TextView mAverageSpeed;
private TextView mTimeRemaining;
private Button mPauseButton;
private Button mWiFiSettingsButton;
private boolean mCancelValidation;
private boolean mStatePaused;
private int mState;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initializeDownloadUI();
if (!expansionFilesDelivered())
{
try
{
Intent launchIntent = SplashActivity.this.getIntent();
Intent i = new Intent(SplashActivity.this, SplashActivity.this.getClass());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null)
for (String category : launchIntent.getCategories())
i.addCategory(category);
// Build PendingIntent used to open this activity from
// Notification
PendingIntent pendingIntent = PendingIntent.getActivity(SplashActivity.this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
// Request to start the download
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SplashActivity.class);
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED)
{
// The DownloaderService has started downloading the files,
// show progress
initializeDownloadUI();
return;
} // otherwise, download not needed so we fall through to
// starting the movie
}
catch (NameNotFoundException e)
{
// Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
e.printStackTrace();
}
}
else
validateXAPKZipFiles();
}
@Override
protected void onStop()
{
if (null != mDownloaderClientStub)
mDownloaderClientStub.disconnect(this);
super.onStop();
}
@Override
protected void onStart()
{
if (null != mDownloaderClientStub)
mDownloaderClientStub.connect(this);
super.onStart();
}
@Override
protected void onDestroy()
{
this.mCancelValidation = true;
super.onDestroy();
}
@Override
public void onServiceConnected(Messenger m)
{
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
@Override
public void onDownloadStateChanged(int newState)
{
setState(newState);
boolean showDashboard = true;
boolean showCellMessage = false;
boolean paused;
boolean indeterminate;
switch (newState)
{
case IDownloaderClient.STATE_IDLE :
// STATE_IDLE means the service is listening, so it's
// safe to start making calls via mRemoteService.
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_CONNECTING :
case IDownloaderClient.STATE_FETCHING_URL :
showDashboard = true;
paused = false;
indeterminate = true;
break;
case IDownloaderClient.STATE_DOWNLOADING :
paused = false;
showDashboard = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_FAILED_CANCELED :
case IDownloaderClient.STATE_FAILED :
case IDownloaderClient.STATE_FAILED_FETCHING_URL :
case IDownloaderClient.STATE_FAILED_UNLICENSED :
paused = true;
showDashboard = false;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION :
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION :
showDashboard = false;
paused = true;
indeterminate = false;
showCellMessage = true;
break;
case IDownloaderClient.STATE_PAUSED_BY_REQUEST :
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_PAUSED_ROAMING :
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE :
paused = true;
indeterminate = false;
break;
case IDownloaderClient.STATE_COMPLETED :
showDashboard = false;
paused = false;
indeterminate = false;
validateXAPKZipFiles();
return;
default :
paused = true;
indeterminate = true;
showDashboard = true;
}
int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;
if (mDashboard.getVisibility() != newDashboardVisibility)
mDashboard.setVisibility(newDashboardVisibility);
int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;
if (mCellMessage.getVisibility() != cellMessageVisibility)
mCellMessage.setVisibility(cellMessageVisibility);
mPB.setIndeterminate(indeterminate);
setButtonPausedState(paused);
}
@Override
public void onDownloadProgress(DownloadProgressInfo progress)
{
mAverageSpeed.setText(getString(R.string.kilobytes_per_second, Helpers.getSpeedString(progress.mCurrentSpeed)));
mTimeRemaining.setText(getString(R.string.time_remaining, Helpers.getTimeRemaining(progress.mTimeRemaining)));
progress.mOverallTotal = progress.mOverallTotal;
mPB.setMax((int) (progress.mOverallTotal >> 8));
mPB.setProgress((int) (progress.mOverallProgress >> 8));
mProgressPercent.setText(Long.toString(progress.mOverallProgress * 100 / progress.mOverallTotal) + "%");
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
}
private void initializeDownloadUI()
{
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SplashActivity.class);
setContentView(R.layout.splash_screen);
mPB = (ProgressBar) findViewById(R.id.progressBar);
mStatusText = (TextView) findViewById(R.id.statusText);
mProgressFraction = (TextView) findViewById(R.id.progressAsFraction);
mProgressPercent = (TextView) findViewById(R.id.progressAsPercentage);
mAverageSpeed = (TextView) findViewById(R.id.progressAverageSpeed);
mTimeRemaining = (TextView) findViewById(R.id.progressTimeRemaining);
mDashboard = findViewById(R.id.downloaderDashboard);
mCellMessage = findViewById(R.id.approveCellular);
mPauseButton = (Button) findViewById(R.id.pauseButton);
mWiFiSettingsButton = (Button) findViewById(R.id.wifiSettingsButton);
mPauseButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
if (mStatePaused)
mRemoteService.requestContinueDownload();
else
mRemoteService.requestPauseDownload();
setButtonPausedState(!mStatePaused);
}
});
mWiFiSettingsButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
}
});
Button resumeOnCell = (Button) findViewById(R.id.resumeOverCellular);
resumeOnCell.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
mRemoteService.requestContinueDownload();
mCellMessage.setVisibility(View.GONE);
}
});
}
void validateXAPKZipFiles()
{
AsyncTask<Object, DownloadProgressInfo, Boolean> validationTask = new AsyncTask<Object, DownloadProgressInfo, Boolean>()
{
@Override
protected void onPreExecute()
{
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
mStatusText.setText(R.string.text_verifying_download);
mPauseButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
mCancelValidation = true;
}
});
mPauseButton.setText(R.string.text_button_cancel_verify);
super.onPreExecute();
}
@Override
protected Boolean doInBackground(Object... params)
{
for (XAPKFile xf : xAPKS)
{
String fileName = Helpers.getExpansionAPKFileName(SplashActivity.this, xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(SplashActivity.this, fileName, xf.mFileSize, false))
return false;
fileName = Helpers.generateSaveFileName(SplashActivity.this, fileName);
ZipResourceFile zrf;
byte[] buf = new byte[1024 * 256];
try
{
zrf = new ZipResourceFile(fileName);
ZipEntryRO[] entries = zrf.getAllEntries();
/**
* First calculate the total compressed length
*/
long totalCompressedLength = 0;
for (ZipEntryRO entry : entries)
totalCompressedLength += entry.mCompressedLength;
float averageVerifySpeed = 0;
long totalBytesRemaining = totalCompressedLength;
long timeRemaining;
/**
* Then calculate a CRC for every file in the Zip file,
* comparing it to what is stored in the Zip directory.
* Note that for compressed Zip files we must extract
* the contents to do this comparison.
*/
for (ZipEntryRO entry : entries)
{
if (-1 != entry.mCRC32)
{
long length = entry.mUncompressedLength;
CRC32 crc = new CRC32();
DataInputStream dis = null;
try
{
dis = new DataInputStream(zrf.getInputStream(entry.mFileName));
long startTime = SystemClock.uptimeMillis();
while (length > 0)
{
int seek = (int) (length > buf.length ? buf.length : length);
dis.readFully(buf, 0, seek);
crc.update(buf, 0, seek);
length -= seek;
long currentTime = SystemClock.uptimeMillis();
long timePassed = currentTime - startTime;
if (timePassed > 0)
{
float currentSpeedSample = (float) seek / (float) timePassed;
if (0 != averageVerifySpeed)
averageVerifySpeed = SMOOTHING_FACTOR * currentSpeedSample + (1 - SMOOTHING_FACTOR) * averageVerifySpeed;
else
averageVerifySpeed = currentSpeedSample;
totalBytesRemaining -= seek;
timeRemaining = (long) (totalBytesRemaining / averageVerifySpeed);
this.publishProgress(new DownloadProgressInfo(totalCompressedLength, totalCompressedLength - totalBytesRemaining,
timeRemaining, averageVerifySpeed));
}
startTime = currentTime;
if (mCancelValidation)
return true;
}
if (crc.getValue() != entry.mCRC32)
{
System.out.println("CRC does not match for entry: " + entry.mFileName);
System.out.println("In file: " + entry.getZipFileName());
return false;
}
}
finally
{
if (null != dis)
dis.close();
}
}
}
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
}
return true;
}
@Override
protected void onProgressUpdate(DownloadProgressInfo... values)
{
onDownloadProgress(values[0]);
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Boolean result)
{
mDashboard.setVisibility(View.VISIBLE);
mCellMessage.setVisibility(View.GONE);
if (result)
{
mStatusText.setText(R.string.text_validation_complete);
mPauseButton.setText(android.R.string.ok);
}
else
{
mStatusText.setText(R.string.text_validation_failed);
mPauseButton.setText(android.R.string.cancel);
}
mPauseButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
finish();
}
});
super.onPostExecute(result);
}
};
validationTask.execute(new Object());
}
boolean expansionFilesDelivered()
{
for (XAPKFile xf : xAPKS)
{
String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
return false;
}
return true; // Always return true;
}
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 void setButtonPausedState(boolean paused)
{
mStatePaused = paused;
int stringResourceID = paused ? R.string.text_button_resume : R.string.text_button_pause;
mPauseButton.setText(stringResourceID);
}
private void setState(int newState)
{
if (mState != newState)
{
mState = newState;
mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
}
}
}
I really appreciate if someone shows me right direction.
EDIT : I am not sure this output is related with my problem 12-08 11:15:12.431: D/OpenGLRenderer(27491): Enabling debug mode 0 12-08 11:15:42.273: D/libEGL(28577): loaded /system/lib/egl/libEGL_adreno200.so 12-08 11:15:42.274: D/libEGL(28577): loaded /system/lib/egl/libGLESv1_CM_adreno200.so 12-08 11:15:42.276: D/libEGL(28577): loaded /system/lib/egl/libGLESv2_adreno200.so
But this is definitely related when i hit back button 12-08 11:18:58.396: E/ActivityThread(28577): Activity com.sample.downloadobb.SplashActivity has leaked ServiceConnection com.google.android.vending.expansion.downloader.DownloaderClientMarshaller$Stub$2@411c1e40 that was originally bound here 12-08 11:18:58.396: E/ActivityThread(28577): android.app.ServiceConnectionLeaked: Activity com.sample.downloadobb.SplashActivity has leaked ServiceConnection com.google.android.vending.expansion.downloader.DownloaderClientMarshaller$Stub$2@411c1e40 that was originally bound here 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.LoadedApk$ServiceDispatcher.(LoadedApk.java:974) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:868) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ContextImpl.bindService(ContextImpl.java:1462) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ContextImpl.bindService(ContextImpl.java:1451) 12-08 11:18:58.396: E/ActivityThread(28577): at android.content.ContextWrapper.bindService(ContextWrapper.java:473) 12-08 11:18:58.396: E/ActivityThread(28577): at com.google.android.vending.expansion.downloader.DownloaderClientMarshaller$Stub.connect(DownloaderClientMarshaller.java:177) 12-08 11:18:58.396: E/ActivityThread(28577): at com.sample.downloadobb.SplashActivity.onStart(SplashActivity.java:113) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1164) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.Activity.performStart(Activity.java:5114) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2272) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ActivityThread.access$600(ActivityThread.java:167) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1276) 12-08 11:18:58.396: E/ActivityThread(28577): at android.os.Handler.dispatchMessage(Handler.java:99) 12-08 11:18:58.396: E/ActivityThread(28577): at android.os.Looper.loop(Looper.java:137) 12-08 11:18:58.396: E/ActivityThread(28577): at android.app.ActivityThread.main(ActivityThread.java:5158) 12-08 11:18:58.396: E/ActivityThread(28577): at java.lang.reflect.Method.invokeNative(Native Method) 12-08 11:18:58.396: E/ActivityThread(28577): at java.lang.reflect.Method.invoke(Method.java:511) 12-08 11:18:58.396: E/ActivityThread(28577): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 12-08 11:18:58.396: E/ActivityThread(28577): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 12-08 11:18:58.396: E/ActivityThread(28577): at dalvik.system.NativeStart.main(Native Method)