Android: Resync Google expansion.downloader library with upstream, unmodified
Synced with 9ecf54e5ce.
This commit is contained in:
parent
ce60217894
commit
ee5898f58a
17 changed files with 3127 additions and 3048 deletions
|
|
@ -3,7 +3,17 @@
|
||||||
This file list third-party libraries used in the Android source folder,
|
This file list third-party libraries used in the Android source folder,
|
||||||
with their provenance and, when relevant, modifications made to those files.
|
with their provenance and, when relevant, modifications made to those files.
|
||||||
|
|
||||||
## Google's licensing library
|
## com.google.android.vending.expansion.downloader
|
||||||
|
|
||||||
|
- Upstream: https://github.com/google/play-apk-expansion/tree/master/apkx_library
|
||||||
|
- Version: git (9ecf54e, 2017)
|
||||||
|
- License: Apache 2.0
|
||||||
|
|
||||||
|
Overwrite all files under:
|
||||||
|
|
||||||
|
- `src/com/google/android/vending/expansion/downloader`
|
||||||
|
|
||||||
|
## com.google.android.vending.licensing
|
||||||
|
|
||||||
- Upstream: https://github.com/google/play-licensing/tree/master/lvl_library/
|
- Upstream: https://github.com/google/play-licensing/tree/master/lvl_library/
|
||||||
- Version: git (eb57657, 2018) with modifications
|
- Version: git (eb57657, 2018) with modifications
|
||||||
|
|
|
||||||
|
|
@ -18,113 +18,115 @@ package com.google.android.vending.expansion.downloader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the internal constants that are used in the download manager.
|
* Contains the internal constants that are used in the download manager.
|
||||||
* As a general rule, modifying these constants should be done with care.
|
* As a general rule, modifying these constants should be done with care.
|
||||||
*/
|
*/
|
||||||
public class Constants {
|
public class Constants {
|
||||||
/** Tag used for debugging/logging */
|
/** Tag used for debugging/logging */
|
||||||
public static final String TAG = "LVLDL";
|
public static final String TAG = "LVLDL";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expansion path where we store obb files
|
* Expansion path where we store obb files
|
||||||
*/
|
*/
|
||||||
public static final String EXP_PATH = File.separator + "Android" + File.separator + "obb" + File.separator;
|
public static final String EXP_PATH = File.separator + "Android"
|
||||||
|
+ File.separator + "obb" + File.separator;
|
||||||
|
|
||||||
/** The intent that gets sent when the service must wake up for a retry */
|
/** The intent that gets sent when the service must wake up for a retry */
|
||||||
public static final String ACTION_RETRY = "android.intent.action.DOWNLOAD_WAKEUP";
|
public static final String ACTION_RETRY = "android.intent.action.DOWNLOAD_WAKEUP";
|
||||||
|
|
||||||
/** the intent that gets sent when clicking a successful download */
|
/** the intent that gets sent when clicking a successful download */
|
||||||
public static final String ACTION_OPEN = "android.intent.action.DOWNLOAD_OPEN";
|
public static final String ACTION_OPEN = "android.intent.action.DOWNLOAD_OPEN";
|
||||||
|
|
||||||
/** the intent that gets sent when clicking an incomplete/failed download */
|
/** the intent that gets sent when clicking an incomplete/failed download */
|
||||||
public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST";
|
public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST";
|
||||||
|
|
||||||
/** the intent that gets sent when deleting the notification of a completed download */
|
/** the intent that gets sent when deleting the notification of a completed download */
|
||||||
public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE";
|
public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a number has to be appended to the filename, this string is used to separate the
|
* When a number has to be appended to the filename, this string is used to separate the
|
||||||
* base filename from the sequence number
|
* base filename from the sequence number
|
||||||
*/
|
*/
|
||||||
public static final String FILENAME_SEQUENCE_SEPARATOR = "-";
|
public static final String FILENAME_SEQUENCE_SEPARATOR = "-";
|
||||||
|
|
||||||
/** The default user agent used for downloads */
|
/** The default user agent used for downloads */
|
||||||
public static final String DEFAULT_USER_AGENT = "Android.LVLDM";
|
public static final String DEFAULT_USER_AGENT = "Android.LVLDM";
|
||||||
|
|
||||||
/** The buffer size used to stream the data */
|
/** The buffer size used to stream the data */
|
||||||
public static final int BUFFER_SIZE = 4096;
|
public static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
/** The minimum amount of progress that has to be done before the progress bar gets updated */
|
/** The minimum amount of progress that has to be done before the progress bar gets updated */
|
||||||
public static final int MIN_PROGRESS_STEP = 4096;
|
public static final int MIN_PROGRESS_STEP = 4096;
|
||||||
|
|
||||||
/** The minimum amount of time that has to elapse before the progress bar gets updated, in ms */
|
/** The minimum amount of time that has to elapse before the progress bar gets updated, in ms */
|
||||||
public static final long MIN_PROGRESS_TIME = 1000;
|
public static final long MIN_PROGRESS_TIME = 1000;
|
||||||
|
|
||||||
/** The maximum number of rows in the database (FIFO) */
|
/** The maximum number of rows in the database (FIFO) */
|
||||||
public static final int MAX_DOWNLOADS = 1000;
|
public static final int MAX_DOWNLOADS = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of times that the download manager will retry its network
|
* The number of times that the download manager will retry its network
|
||||||
* operations when no progress is happening before it gives up.
|
* operations when no progress is happening before it gives up.
|
||||||
*/
|
*/
|
||||||
public static final int MAX_RETRIES = 5;
|
public static final int MAX_RETRIES = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum amount of time that the download manager accepts for
|
* The minimum amount of time that the download manager accepts for
|
||||||
* a Retry-After response header with a parameter in delta-seconds.
|
* a Retry-After response header with a parameter in delta-seconds.
|
||||||
*/
|
*/
|
||||||
public static final int MIN_RETRY_AFTER = 30; // 30s
|
public static final int MIN_RETRY_AFTER = 30; // 30s
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum amount of time that the download manager accepts for
|
* The maximum amount of time that the download manager accepts for
|
||||||
* a Retry-After response header with a parameter in delta-seconds.
|
* a Retry-After response header with a parameter in delta-seconds.
|
||||||
*/
|
*/
|
||||||
public static final int MAX_RETRY_AFTER = 24 * 60 * 60; // 24h
|
public static final int MAX_RETRY_AFTER = 24 * 60 * 60; // 24h
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of redirects.
|
* The maximum number of redirects.
|
||||||
*/
|
*/
|
||||||
public static final int MAX_REDIRECTS = 5; // can't be more than 7.
|
public static final int MAX_REDIRECTS = 5; // can't be more than 7.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time between a failure and the first retry after an IOException.
|
* The time between a failure and the first retry after an IOException.
|
||||||
* Each subsequent retry grows exponentially, doubling each time.
|
* Each subsequent retry grows exponentially, doubling each time.
|
||||||
* The time is in seconds.
|
* The time is in seconds.
|
||||||
*/
|
*/
|
||||||
public static final int RETRY_FIRST_DELAY = 30;
|
public static final int RETRY_FIRST_DELAY = 30;
|
||||||
|
|
||||||
/** Enable separate connectivity logging */
|
/** Enable separate connectivity logging */
|
||||||
public static final boolean LOGX = true;
|
public static final boolean LOGX = true;
|
||||||
|
|
||||||
/** Enable verbose logging */
|
/** Enable verbose logging */
|
||||||
public static final boolean LOGV = false;
|
public static final boolean LOGV = false;
|
||||||
|
|
||||||
/** Enable super-verbose logging */
|
/** Enable super-verbose logging */
|
||||||
private static final boolean LOCAL_LOGVV = false;
|
private static final boolean LOCAL_LOGVV = false;
|
||||||
public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
|
public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download has successfully completed.
|
* This download has successfully completed.
|
||||||
* Warning: there might be other status values that indicate success
|
* Warning: there might be other status values that indicate success
|
||||||
* in the future.
|
* in the future.
|
||||||
* Use isSucccess() to capture the entire category.
|
* Use isSucccess() to capture the entire category.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_SUCCESS = 200;
|
public static final int STATUS_SUCCESS = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This request couldn't be parsed. This is also used when processing
|
* This request couldn't be parsed. This is also used when processing
|
||||||
* requests with unknown/unsupported URI schemes.
|
* requests with unknown/unsupported URI schemes.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_BAD_REQUEST = 400;
|
public static final int STATUS_BAD_REQUEST = 400;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download can't be performed because the content type cannot be
|
* This download can't be performed because the content type cannot be
|
||||||
* handled.
|
* handled.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_NOT_ACCEPTABLE = 406;
|
public static final int STATUS_NOT_ACCEPTABLE = 406;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download cannot be performed because the length cannot be
|
* This download cannot be performed because the length cannot be
|
||||||
* determined accurately. This is the code for the HTTP error "Length
|
* determined accurately. This is the code for the HTTP error "Length
|
||||||
* Required", which is typically used when making requests that require
|
* Required", which is typically used when making requests that require
|
||||||
|
|
@ -133,101 +135,102 @@ public class Constants {
|
||||||
* accurately (therefore making it impossible to know when a download
|
* accurately (therefore making it impossible to know when a download
|
||||||
* completes).
|
* completes).
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_LENGTH_REQUIRED = 411;
|
public static final int STATUS_LENGTH_REQUIRED = 411;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download was interrupted and cannot be resumed.
|
* This download was interrupted and cannot be resumed.
|
||||||
* This is the code for the HTTP error "Precondition Failed", and it is
|
* This is the code for the HTTP error "Precondition Failed", and it is
|
||||||
* also used in situations where the client doesn't have an ETag at all.
|
* also used in situations where the client doesn't have an ETag at all.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_PRECONDITION_FAILED = 412;
|
public static final int STATUS_PRECONDITION_FAILED = 412;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The lowest-valued error status that is not an actual HTTP status code.
|
* The lowest-valued error status that is not an actual HTTP status code.
|
||||||
*/
|
*/
|
||||||
public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
|
public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The requested destination file already exists.
|
* The requested destination file already exists.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
|
public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some possibly transient error occurred, but we can't resume the download.
|
* Some possibly transient error occurred, but we can't resume the download.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_CANNOT_RESUME = 489;
|
public static final int STATUS_CANNOT_RESUME = 489;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download was canceled
|
* This download was canceled
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_CANCELED = 490;
|
public static final int STATUS_CANCELED = 490;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download has completed with an error.
|
* This download has completed with an error.
|
||||||
* Warning: there will be other status values that indicate errors in
|
* Warning: there will be other status values that indicate errors in
|
||||||
* the future. Use isStatusError() to capture the entire category.
|
* the future. Use isStatusError() to capture the entire category.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_UNKNOWN_ERROR = 491;
|
public static final int STATUS_UNKNOWN_ERROR = 491;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because of a storage issue.
|
* This download couldn't be completed because of a storage issue.
|
||||||
* Typically, that's because the filesystem is missing or full.
|
* Typically, that's because the filesystem is missing or full.
|
||||||
* Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
|
* Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
|
||||||
* and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
|
* and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_FILE_ERROR = 492;
|
public static final int STATUS_FILE_ERROR = 492;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because of an HTTP
|
* This download couldn't be completed because of an HTTP
|
||||||
* redirect response that the download manager couldn't
|
* redirect response that the download manager couldn't
|
||||||
* handle.
|
* handle.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_UNHANDLED_REDIRECT = 493;
|
public static final int STATUS_UNHANDLED_REDIRECT = 493;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because of an
|
* This download couldn't be completed because of an
|
||||||
* unspecified unhandled HTTP code.
|
* unspecified unhandled HTTP code.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
|
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because of an
|
* This download couldn't be completed because of an
|
||||||
* error receiving or processing data at the HTTP level.
|
* error receiving or processing data at the HTTP level.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_HTTP_DATA_ERROR = 495;
|
public static final int STATUS_HTTP_DATA_ERROR = 495;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because of an
|
* This download couldn't be completed because of an
|
||||||
* HttpException while setting up the request.
|
* HttpException while setting up the request.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_HTTP_EXCEPTION = 496;
|
public static final int STATUS_HTTP_EXCEPTION = 496;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because there were
|
* This download couldn't be completed because there were
|
||||||
* too many redirects.
|
* too many redirects.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
|
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed due to insufficient storage
|
* This download couldn't be completed due to insufficient storage
|
||||||
* space. Typically, this is because the SD card is full.
|
* space. Typically, this is because the SD card is full.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
|
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This download couldn't be completed because no external storage
|
* This download couldn't be completed because no external storage
|
||||||
* device was found. Typically, this is because the SD card is not
|
* device was found. Typically, this is because the SD card is not
|
||||||
* mounted.
|
* mounted.
|
||||||
*/
|
*/
|
||||||
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
|
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wake duration to check to see if a download is possible.
|
* The wake duration to check to see if a download is possible.
|
||||||
*/
|
*/
|
||||||
public static final long WATCHDOG_WAKE_TIMER = 60 * 1000;
|
public static final long WATCHDOG_WAKE_TIMER = 60*1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wake duration to check to see if the process was killed.
|
* The wake duration to check to see if the process was killed.
|
||||||
*/
|
*/
|
||||||
public static final long ACTIVE_THREAD_WATCHDOG = 5 * 1000;
|
public static final long ACTIVE_THREAD_WATCHDOG = 5*1000;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package com.google.android.vending.expansion.downloader;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains progress information about the active download(s).
|
* This class contains progress information about the active download(s).
|
||||||
*
|
*
|
||||||
|
|
@ -30,49 +31,50 @@ import android.os.Parcelable;
|
||||||
* as the progress so far, time remaining and current speed.
|
* as the progress so far, time remaining and current speed.
|
||||||
*/
|
*/
|
||||||
public class DownloadProgressInfo implements Parcelable {
|
public class DownloadProgressInfo implements Parcelable {
|
||||||
public long mOverallTotal;
|
public long mOverallTotal;
|
||||||
public long mOverallProgress;
|
public long mOverallProgress;
|
||||||
public long mTimeRemaining; // time remaining
|
public long mTimeRemaining; // time remaining
|
||||||
public float mCurrentSpeed; // speed in KB/S
|
public float mCurrentSpeed; // speed in KB/S
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel p, int i) {
|
public void writeToParcel(Parcel p, int i) {
|
||||||
p.writeLong(mOverallTotal);
|
p.writeLong(mOverallTotal);
|
||||||
p.writeLong(mOverallProgress);
|
p.writeLong(mOverallProgress);
|
||||||
p.writeLong(mTimeRemaining);
|
p.writeLong(mTimeRemaining);
|
||||||
p.writeFloat(mCurrentSpeed);
|
p.writeFloat(mCurrentSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadProgressInfo(Parcel p) {
|
public DownloadProgressInfo(Parcel p) {
|
||||||
mOverallTotal = p.readLong();
|
mOverallTotal = p.readLong();
|
||||||
mOverallProgress = p.readLong();
|
mOverallProgress = p.readLong();
|
||||||
mTimeRemaining = p.readLong();
|
mTimeRemaining = p.readLong();
|
||||||
mCurrentSpeed = p.readFloat();
|
mCurrentSpeed = p.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadProgressInfo(long overallTotal, long overallProgress,
|
public DownloadProgressInfo(long overallTotal, long overallProgress,
|
||||||
long timeRemaining,
|
long timeRemaining,
|
||||||
float currentSpeed) {
|
float currentSpeed) {
|
||||||
this.mOverallTotal = overallTotal;
|
this.mOverallTotal = overallTotal;
|
||||||
this.mOverallProgress = overallProgress;
|
this.mOverallProgress = overallProgress;
|
||||||
this.mTimeRemaining = timeRemaining;
|
this.mTimeRemaining = timeRemaining;
|
||||||
this.mCurrentSpeed = currentSpeed;
|
this.mCurrentSpeed = currentSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<DownloadProgressInfo> CREATOR = new Creator<DownloadProgressInfo>() {
|
public static final Creator<DownloadProgressInfo> CREATOR = new Creator<DownloadProgressInfo>() {
|
||||||
@Override
|
@Override
|
||||||
public DownloadProgressInfo createFromParcel(Parcel parcel) {
|
public DownloadProgressInfo createFromParcel(Parcel parcel) {
|
||||||
return new DownloadProgressInfo(parcel);
|
return new DownloadProgressInfo(parcel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DownloadProgressInfo[] newArray(int i) {
|
||||||
|
return new DownloadProgressInfo[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
|
||||||
public DownloadProgressInfo[] newArray(int i) {
|
|
||||||
return new DownloadProgressInfo[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class binds the service API to your application client. It contains the IDownloaderClient proxy,
|
* This class binds the service API to your application client. It contains the IDownloaderClient proxy,
|
||||||
|
|
@ -58,172 +58,158 @@ import java.lang.ref.WeakReference;
|
||||||
* interface.
|
* interface.
|
||||||
*/
|
*/
|
||||||
public class DownloaderClientMarshaller {
|
public class DownloaderClientMarshaller {
|
||||||
public static final int MSG_ONDOWNLOADSTATE_CHANGED = 10;
|
public static final int MSG_ONDOWNLOADSTATE_CHANGED = 10;
|
||||||
public static final int MSG_ONDOWNLOADPROGRESS = 11;
|
public static final int MSG_ONDOWNLOADPROGRESS = 11;
|
||||||
public static final int MSG_ONSERVICECONNECTED = 12;
|
public static final int MSG_ONSERVICECONNECTED = 12;
|
||||||
|
|
||||||
public static final String PARAM_NEW_STATE = "newState";
|
public static final String PARAM_NEW_STATE = "newState";
|
||||||
public static final String PARAM_PROGRESS = "progress";
|
public static final String PARAM_PROGRESS = "progress";
|
||||||
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
|
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
|
||||||
|
|
||||||
public static final int NO_DOWNLOAD_REQUIRED = DownloaderService.NO_DOWNLOAD_REQUIRED;
|
public static final int NO_DOWNLOAD_REQUIRED = DownloaderService.NO_DOWNLOAD_REQUIRED;
|
||||||
public static final int LVL_CHECK_REQUIRED = DownloaderService.LVL_CHECK_REQUIRED;
|
public static final int LVL_CHECK_REQUIRED = DownloaderService.LVL_CHECK_REQUIRED;
|
||||||
public static final int DOWNLOAD_REQUIRED = DownloaderService.DOWNLOAD_REQUIRED;
|
public static final int DOWNLOAD_REQUIRED = DownloaderService.DOWNLOAD_REQUIRED;
|
||||||
|
|
||||||
private static class Proxy implements IDownloaderClient {
|
private static class Proxy implements IDownloaderClient {
|
||||||
private Messenger mServiceMessenger;
|
private Messenger mServiceMessenger;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadStateChanged(int newState) {
|
public void onDownloadStateChanged(int newState) {
|
||||||
Bundle params = new Bundle(1);
|
Bundle params = new Bundle(1);
|
||||||
params.putInt(PARAM_NEW_STATE, newState);
|
params.putInt(PARAM_NEW_STATE, newState);
|
||||||
send(MSG_ONDOWNLOADSTATE_CHANGED, params);
|
send(MSG_ONDOWNLOADSTATE_CHANGED, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadProgress(DownloadProgressInfo progress) {
|
public void onDownloadProgress(DownloadProgressInfo progress) {
|
||||||
Bundle params = new Bundle(1);
|
Bundle params = new Bundle(1);
|
||||||
params.putParcelable(PARAM_PROGRESS, progress);
|
params.putParcelable(PARAM_PROGRESS, progress);
|
||||||
send(MSG_ONDOWNLOADPROGRESS, params);
|
send(MSG_ONDOWNLOADPROGRESS, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(int method, Bundle params) {
|
private void send(int method, Bundle params) {
|
||||||
Message m = Message.obtain(null, method);
|
Message m = Message.obtain(null, method);
|
||||||
m.setData(params);
|
m.setData(params);
|
||||||
try {
|
try {
|
||||||
mServiceMessenger.send(m);
|
mServiceMessenger.send(m);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Proxy(Messenger msg) {
|
public Proxy(Messenger msg) {
|
||||||
mServiceMessenger = msg;
|
mServiceMessenger = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(Messenger m) {
|
public void onServiceConnected(Messenger m) {
|
||||||
/**
|
/**
|
||||||
* This is never called through the proxy.
|
* This is never called through the proxy.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Stub implements IStub {
|
private static class Stub implements IStub {
|
||||||
private IDownloaderClient mItf = null;
|
private IDownloaderClient mItf = null;
|
||||||
private Class<?> mDownloaderServiceClass;
|
private Class<?> mDownloaderServiceClass;
|
||||||
private boolean mBound;
|
private boolean mBound;
|
||||||
private Messenger mServiceMessenger;
|
private Messenger mServiceMessenger;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
/**
|
/**
|
||||||
* Target we publish for clients to send messages to IncomingHandler.
|
* Target we publish for clients to send messages to IncomingHandler.
|
||||||
*/
|
*/
|
||||||
private final MessengerHandlerClient mMsgHandler = new MessengerHandlerClient(this);
|
final Messenger mMessenger = new Messenger(new Handler() {
|
||||||
final Messenger mMessenger = new Messenger(mMsgHandler);
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_ONDOWNLOADPROGRESS:
|
||||||
|
Bundle bun = msg.getData();
|
||||||
|
if ( null != mContext ) {
|
||||||
|
bun.setClassLoader(mContext.getClassLoader());
|
||||||
|
DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
|
||||||
|
.getParcelable(PARAM_PROGRESS);
|
||||||
|
mItf.onDownloadProgress(dpi);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MSG_ONDOWNLOADSTATE_CHANGED:
|
||||||
|
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
|
||||||
|
break;
|
||||||
|
case MSG_ONSERVICECONNECTED:
|
||||||
|
mItf.onServiceConnected(
|
||||||
|
(Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private static class MessengerHandlerClient extends Handler {
|
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
|
||||||
private final WeakReference<Stub> mDownloader;
|
mItf = itf;
|
||||||
public MessengerHandlerClient(Stub downloader) {
|
mDownloaderServiceClass = downloaderService;
|
||||||
mDownloader = new WeakReference<>(downloader);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
Stub downloader = mDownloader.get();
|
|
||||||
if (downloader != null) {
|
|
||||||
downloader.handleMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_ONDOWNLOADPROGRESS:
|
|
||||||
Bundle bun = msg.getData();
|
|
||||||
if (null != mContext) {
|
|
||||||
bun.setClassLoader(mContext.getClassLoader());
|
|
||||||
DownloadProgressInfo dpi = (DownloadProgressInfo)msg.getData()
|
|
||||||
.getParcelable(PARAM_PROGRESS);
|
|
||||||
mItf.onDownloadProgress(dpi);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MSG_ONDOWNLOADSTATE_CHANGED:
|
|
||||||
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
|
|
||||||
break;
|
|
||||||
case MSG_ONSERVICECONNECTED:
|
|
||||||
mItf.onServiceConnected(
|
|
||||||
(Messenger)msg.getData().getParcelable(PARAM_MESSENGER));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
|
|
||||||
mItf = itf;
|
|
||||||
mDownloaderServiceClass = downloaderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for interacting with the main interface of the service.
|
* Class for interacting with the main interface of the service.
|
||||||
*/
|
*/
|
||||||
private ServiceConnection mConnection = new ServiceConnection() {
|
private ServiceConnection mConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
// This is called when the connection with the service has been
|
// This is called when the connection with the service has been
|
||||||
// established, giving us the object we can use to
|
// established, giving us the object we can use to
|
||||||
// interact with the service. We are communicating with the
|
// interact with the service. We are communicating with the
|
||||||
// service using a Messenger, so here we get a client-side
|
// service using a Messenger, so here we get a client-side
|
||||||
// representation of that from the raw IBinder object.
|
// representation of that from the raw IBinder object.
|
||||||
mServiceMessenger = new Messenger(service);
|
mServiceMessenger = new Messenger(service);
|
||||||
mItf.onServiceConnected(
|
mItf.onServiceConnected(
|
||||||
mServiceMessenger);
|
mServiceMessenger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
// This is called when the connection with the service has been
|
// This is called when the connection with the service has been
|
||||||
// unexpectedly disconnected -- that is, its process crashed.
|
// unexpectedly disconnected -- that is, its process crashed.
|
||||||
mServiceMessenger = null;
|
mServiceMessenger = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(Context c) {
|
public void connect(Context c) {
|
||||||
mContext = c;
|
mContext = c;
|
||||||
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
|
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
|
||||||
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
|
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
|
||||||
if (!c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND)) {
|
if ( !c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND) ) {
|
||||||
if (Constants.LOGVV) {
|
if ( Constants.LOGVV ) {
|
||||||
Log.d(Constants.TAG, "Service Unbound");
|
Log.d(Constants.TAG, "Service Unbound");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mBound = true;
|
mBound = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public void disconnect(Context c) {
|
|
||||||
if (mBound) {
|
|
||||||
c.unbindService(mConnection);
|
|
||||||
mBound = false;
|
|
||||||
}
|
|
||||||
mContext = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Messenger getMessenger() {
|
public void disconnect(Context c) {
|
||||||
return mMessenger;
|
if (mBound) {
|
||||||
}
|
c.unbindService(mConnection);
|
||||||
}
|
mBound = false;
|
||||||
|
}
|
||||||
|
mContext = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
|
public Messenger getMessenger() {
|
||||||
|
return mMessenger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Returns a proxy that will marshal calls to IDownloaderClient methods
|
* Returns a proxy that will marshal calls to IDownloaderClient methods
|
||||||
*
|
*
|
||||||
* @param msg
|
* @param msg
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static IDownloaderClient CreateProxy(Messenger msg) {
|
public static IDownloaderClient CreateProxy(Messenger msg) {
|
||||||
return new Proxy(msg);
|
return new Proxy(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a stub object that, when connected, will listen for marshaled
|
* Returns a stub object that, when connected, will listen for marshaled
|
||||||
* {@link IDownloaderClient} methods and translate them into calls to the supplied
|
* {@link IDownloaderClient} methods and translate them into calls to the supplied
|
||||||
* interface.
|
* interface.
|
||||||
|
|
@ -235,11 +221,11 @@ public class DownloaderClientMarshaller {
|
||||||
* @return The {@link IStub} that allows you to connect to the service such that
|
* @return The {@link IStub} that allows you to connect to the service such that
|
||||||
* your {@link IDownloaderClient} receives status updates.
|
* your {@link IDownloaderClient} receives status updates.
|
||||||
*/
|
*/
|
||||||
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
|
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
|
||||||
return new Stub(itf, downloaderService);
|
return new Stub(itf, downloaderService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the download if necessary. This function starts a flow that does `
|
* Starts the download if necessary. This function starts a flow that does `
|
||||||
* many things. 1) Checks to see if the APK version has been checked and
|
* many things. 1) Checks to see if the APK version has been checked and
|
||||||
* the metadata database updated 2) If the APK version does not match,
|
* the metadata database updated 2) If the APK version does not match,
|
||||||
|
|
@ -262,14 +248,14 @@ public class DownloaderClientMarshaller {
|
||||||
* #DOWNLOAD_REQUIRED}.
|
* #DOWNLOAD_REQUIRED}.
|
||||||
* @throws NameNotFoundException
|
* @throws NameNotFoundException
|
||||||
*/
|
*/
|
||||||
public static int startDownloadServiceIfRequired(Context context, PendingIntent notificationClient,
|
public static int startDownloadServiceIfRequired(Context context, PendingIntent notificationClient,
|
||||||
Class<?> serviceClass)
|
Class<?> serviceClass)
|
||||||
throws NameNotFoundException {
|
throws NameNotFoundException {
|
||||||
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
||||||
serviceClass);
|
serviceClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This version assumes that the intent contains the pending intent as a parameter. This
|
* This version assumes that the intent contains the pending intent as a parameter. This
|
||||||
* is used for responding to alarms.
|
* is used for responding to alarms.
|
||||||
* <p>The pending intent must be in an extra with the key {@link
|
* <p>The pending intent must be in an extra with the key {@link
|
||||||
|
|
@ -281,10 +267,11 @@ public class DownloaderClientMarshaller {
|
||||||
* @return
|
* @return
|
||||||
* @throws NameNotFoundException
|
* @throws NameNotFoundException
|
||||||
*/
|
*/
|
||||||
public static int startDownloadServiceIfRequired(Context context, Intent notificationClient,
|
public static int startDownloadServiceIfRequired(Context context, Intent notificationClient,
|
||||||
Class<?> serviceClass)
|
Class<?> serviceClass)
|
||||||
throws NameNotFoundException {
|
throws NameNotFoundException {
|
||||||
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
||||||
serviceClass);
|
serviceClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import android.os.Message;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used by the client activity to proxy requests to the Downloader
|
* This class is used by the client activity to proxy requests to the Downloader
|
||||||
|
|
@ -38,147 +38,134 @@ import java.lang.ref.WeakReference;
|
||||||
*/
|
*/
|
||||||
public class DownloaderServiceMarshaller {
|
public class DownloaderServiceMarshaller {
|
||||||
|
|
||||||
public static final int MSG_REQUEST_ABORT_DOWNLOAD =
|
public static final int MSG_REQUEST_ABORT_DOWNLOAD =
|
||||||
1;
|
1;
|
||||||
public static final int MSG_REQUEST_PAUSE_DOWNLOAD =
|
public static final int MSG_REQUEST_PAUSE_DOWNLOAD =
|
||||||
2;
|
2;
|
||||||
public static final int MSG_SET_DOWNLOAD_FLAGS =
|
public static final int MSG_SET_DOWNLOAD_FLAGS =
|
||||||
3;
|
3;
|
||||||
public static final int MSG_REQUEST_CONTINUE_DOWNLOAD =
|
public static final int MSG_REQUEST_CONTINUE_DOWNLOAD =
|
||||||
4;
|
4;
|
||||||
public static final int MSG_REQUEST_DOWNLOAD_STATE =
|
public static final int MSG_REQUEST_DOWNLOAD_STATE =
|
||||||
5;
|
5;
|
||||||
public static final int MSG_REQUEST_CLIENT_UPDATE =
|
public static final int MSG_REQUEST_CLIENT_UPDATE =
|
||||||
6;
|
6;
|
||||||
|
|
||||||
public static final String PARAMS_FLAGS = "flags";
|
public static final String PARAMS_FLAGS = "flags";
|
||||||
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
|
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
|
||||||
|
|
||||||
private static class Proxy implements IDownloaderService {
|
private static class Proxy implements IDownloaderService {
|
||||||
private Messenger mMsg;
|
private Messenger mMsg;
|
||||||
|
|
||||||
private void send(int method, Bundle params) {
|
private void send(int method, Bundle params) {
|
||||||
Message m = Message.obtain(null, method);
|
Message m = Message.obtain(null, method);
|
||||||
m.setData(params);
|
m.setData(params);
|
||||||
try {
|
try {
|
||||||
mMsg.send(m);
|
mMsg.send(m);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Proxy(Messenger msg) {
|
public Proxy(Messenger msg) {
|
||||||
mMsg = msg;
|
mMsg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestAbortDownload() {
|
public void requestAbortDownload() {
|
||||||
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
|
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestPauseDownload() {
|
public void requestPauseDownload() {
|
||||||
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
|
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDownloadFlags(int flags) {
|
public void setDownloadFlags(int flags) {
|
||||||
Bundle params = new Bundle();
|
Bundle params = new Bundle();
|
||||||
params.putInt(PARAMS_FLAGS, flags);
|
params.putInt(PARAMS_FLAGS, flags);
|
||||||
send(MSG_SET_DOWNLOAD_FLAGS, params);
|
send(MSG_SET_DOWNLOAD_FLAGS, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestContinueDownload() {
|
public void requestContinueDownload() {
|
||||||
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
|
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestDownloadStatus() {
|
public void requestDownloadStatus() {
|
||||||
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
|
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClientUpdated(Messenger clientMessenger) {
|
public void onClientUpdated(Messenger clientMessenger) {
|
||||||
Bundle bundle = new Bundle(1);
|
Bundle bundle = new Bundle(1);
|
||||||
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
|
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
|
||||||
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
|
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Stub implements IStub {
|
private static class Stub implements IStub {
|
||||||
private IDownloaderService mItf = null;
|
private IDownloaderService mItf = null;
|
||||||
private final MessengerHandlerServer mMsgHandler = new MessengerHandlerServer(this);
|
final Messenger mMessenger = new Messenger(new Handler() {
|
||||||
final Messenger mMessenger = new Messenger(mMsgHandler);
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_REQUEST_ABORT_DOWNLOAD:
|
||||||
|
mItf.requestAbortDownload();
|
||||||
|
break;
|
||||||
|
case MSG_REQUEST_CONTINUE_DOWNLOAD:
|
||||||
|
mItf.requestContinueDownload();
|
||||||
|
break;
|
||||||
|
case MSG_REQUEST_PAUSE_DOWNLOAD:
|
||||||
|
mItf.requestPauseDownload();
|
||||||
|
break;
|
||||||
|
case MSG_SET_DOWNLOAD_FLAGS:
|
||||||
|
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
|
||||||
|
break;
|
||||||
|
case MSG_REQUEST_DOWNLOAD_STATE:
|
||||||
|
mItf.requestDownloadStatus();
|
||||||
|
break;
|
||||||
|
case MSG_REQUEST_CLIENT_UPDATE:
|
||||||
|
mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
|
||||||
|
PARAM_MESSENGER));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private static class MessengerHandlerServer extends Handler {
|
public Stub(IDownloaderService itf) {
|
||||||
private final WeakReference<Stub> mDownloader;
|
mItf = itf;
|
||||||
public MessengerHandlerServer(Stub downloader) {
|
}
|
||||||
mDownloader = new WeakReference<>(downloader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public Messenger getMessenger() {
|
||||||
Stub downloader = mDownloader.get();
|
return mMessenger;
|
||||||
if (downloader != null) {
|
}
|
||||||
downloader.handleMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleMessage(Message msg) {
|
@Override
|
||||||
switch (msg.what) {
|
public void connect(Context c) {
|
||||||
case MSG_REQUEST_ABORT_DOWNLOAD:
|
|
||||||
mItf.requestAbortDownload();
|
|
||||||
break;
|
|
||||||
case MSG_REQUEST_CONTINUE_DOWNLOAD:
|
|
||||||
mItf.requestContinueDownload();
|
|
||||||
break;
|
|
||||||
case MSG_REQUEST_PAUSE_DOWNLOAD:
|
|
||||||
mItf.requestPauseDownload();
|
|
||||||
break;
|
|
||||||
case MSG_SET_DOWNLOAD_FLAGS:
|
|
||||||
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
|
|
||||||
break;
|
|
||||||
case MSG_REQUEST_DOWNLOAD_STATE:
|
|
||||||
mItf.requestDownloadStatus();
|
|
||||||
break;
|
|
||||||
case MSG_REQUEST_CLIENT_UPDATE:
|
|
||||||
mItf.onClientUpdated((Messenger)msg.getData().getParcelable(
|
|
||||||
PARAM_MESSENGER));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stub(IDownloaderService itf) {
|
}
|
||||||
mItf = itf;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Messenger getMessenger() {
|
public void disconnect(Context c) {
|
||||||
return mMessenger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public void connect(Context c) {
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void disconnect(Context c) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a proxy that will marshall calls to IDownloaderService methods
|
* Returns a proxy that will marshall calls to IDownloaderService methods
|
||||||
*
|
*
|
||||||
* @param ctx
|
* @param ctx
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static IDownloaderService CreateProxy(Messenger msg) {
|
public static IDownloaderService CreateProxy(Messenger msg) {
|
||||||
return new Proxy(msg);
|
return new Proxy(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a stub object that, when connected, will listen for marshalled
|
* Returns a stub object that, when connected, will listen for marshalled
|
||||||
* IDownloaderService methods and translate them into calls to the supplied
|
* IDownloaderService methods and translate them into calls to the supplied
|
||||||
* interface.
|
* interface.
|
||||||
|
|
@ -187,7 +174,8 @@ public class DownloaderServiceMarshaller {
|
||||||
* when remote method calls are unmarshalled.
|
* when remote method calls are unmarshalled.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static IStub CreateStub(IDownloaderService itf) {
|
public static IStub CreateStub(IDownloaderService itf) {
|
||||||
return new Stub(itf);
|
return new Stub(itf);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import android.os.StatFs;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.godot.game.R;
|
import com.android.vending.expansion.downloader.R;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
|
@ -40,95 +40,96 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public class Helpers {
|
public class Helpers {
|
||||||
|
|
||||||
public static Random sRandom = new Random(SystemClock.uptimeMillis());
|
public static Random sRandom = new Random(SystemClock.uptimeMillis());
|
||||||
|
|
||||||
/** Regex used to parse content-disposition headers */
|
/** Regex used to parse content-disposition headers */
|
||||||
private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern
|
private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern
|
||||||
.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\"");
|
.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\"");
|
||||||
|
|
||||||
private Helpers() {
|
private Helpers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse the Content-Disposition HTTP Header. The format of the header is defined here:
|
* Parse the Content-Disposition HTTP Header. The format of the header is defined here:
|
||||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
|
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html This header provides a filename for
|
||||||
* content that is going to be downloaded to the file system. We only support the attachment
|
* content that is going to be downloaded to the file system. We only support the attachment
|
||||||
* type.
|
* type.
|
||||||
*/
|
*/
|
||||||
static String parseContentDisposition(String contentDisposition) {
|
static String parseContentDisposition(String contentDisposition) {
|
||||||
try {
|
try {
|
||||||
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
|
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
return m.group(1);
|
return m.group(1);
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException ex) {
|
} catch (IllegalStateException ex) {
|
||||||
// This function is defined as returning null when it can't parse
|
// This function is defined as returning null when it can't parse
|
||||||
// the header
|
// the header
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the root of the filesystem containing the given path
|
* @return the root of the filesystem containing the given path
|
||||||
*/
|
*/
|
||||||
public static File getFilesystemRoot(String path) {
|
public static File getFilesystemRoot(String path) {
|
||||||
File cache = Environment.getDownloadCacheDirectory();
|
File cache = Environment.getDownloadCacheDirectory();
|
||||||
if (path.startsWith(cache.getPath())) {
|
if (path.startsWith(cache.getPath())) {
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
File external = Environment.getExternalStorageDirectory();
|
File external = Environment.getExternalStorageDirectory();
|
||||||
if (path.startsWith(external.getPath())) {
|
if (path.startsWith(external.getPath())) {
|
||||||
return external;
|
return external;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Cannot determine filesystem root for " + path);
|
"Cannot determine filesystem root for " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExternalMediaMounted() {
|
public static boolean isExternalMediaMounted() {
|
||||||
if (!Environment.getExternalStorageState().equals(
|
if (!Environment.getExternalStorageState().equals(
|
||||||
Environment.MEDIA_MOUNTED)) {
|
Environment.MEDIA_MOUNTED)) {
|
||||||
// No SD card found.
|
// No SD card found.
|
||||||
if (Constants.LOGVV) {
|
if (Constants.LOGVV) {
|
||||||
Log.d(Constants.TAG, "no external storage");
|
Log.d(Constants.TAG, "no external storage");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of bytes available on the filesystem rooted at the given File
|
* @return the number of bytes available on the filesystem rooted at the given File
|
||||||
*/
|
*/
|
||||||
public static long getAvailableBytes(File root) {
|
public static long getAvailableBytes(File root) {
|
||||||
StatFs stat = new StatFs(root.getPath());
|
StatFs stat = new StatFs(root.getPath());
|
||||||
// put a bit of margin (in case creating the file grows the system by a
|
// put a bit of margin (in case creating the file grows the system by a
|
||||||
// few blocks)
|
// few blocks)
|
||||||
long availableBlocks = (long)stat.getAvailableBlocks() - 4;
|
long availableBlocks = (long) stat.getAvailableBlocks() - 4;
|
||||||
return stat.getBlockSize() * availableBlocks;
|
return stat.getBlockSize() * availableBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the filename looks legitimate
|
* Checks whether the filename looks legitimate
|
||||||
*/
|
*/
|
||||||
public static boolean isFilenameValid(String filename) {
|
public static boolean isFilenameValid(String filename) {
|
||||||
filename = filename.replaceFirst("/+", "/"); // normalize leading
|
filename = filename.replaceFirst("/+", "/"); // normalize leading
|
||||||
// slashes
|
// slashes
|
||||||
return filename.startsWith(Environment.getDownloadCacheDirectory().toString()) || filename.startsWith(Environment.getExternalStorageDirectory().toString());
|
return filename.startsWith(Environment.getDownloadCacheDirectory().toString())
|
||||||
}
|
|| filename.startsWith(Environment.getExternalStorageDirectory().toString());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete the given file from device
|
* Delete the given file from device
|
||||||
*/
|
*/
|
||||||
/* package */ static void deleteFile(String path) {
|
/* package */static void deleteFile(String path) {
|
||||||
try {
|
try {
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
file.delete();
|
file.delete();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
|
Log.w(Constants.TAG, "file: '" + path + "' couldn't be deleted", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Showing progress in MB here. It would be nice to choose the unit (KB, MB, GB) based on total
|
* Showing progress in MB here. It would be nice to choose the unit (KB, MB, GB) based on total
|
||||||
* file size, but given what we know about the expected ranges of file sizes for APK expansion
|
* file size, but given what we know about the expected ranges of file sizes for APK expansion
|
||||||
* files, it's probably not necessary.
|
* files, it's probably not necessary.
|
||||||
|
|
@ -138,63 +139,65 @@ public class Helpers {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static public String getDownloadProgressString(long overallProgress, long overallTotal) {
|
static public String getDownloadProgressString(long overallProgress, long overallTotal) {
|
||||||
if (overallTotal == 0) {
|
if (overallTotal == 0) {
|
||||||
if (Constants.LOGVV) {
|
if (Constants.LOGVV) {
|
||||||
Log.e(Constants.TAG, "Notification called when total is zero");
|
Log.e(Constants.TAG, "Notification called when total is zero");
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return String.format(Locale.ENGLISH, "%.2f",
|
return String.format("%.2f",
|
||||||
(float)overallProgress / (1024.0f * 1024.0f)) +
|
(float) overallProgress / (1024.0f * 1024.0f))
|
||||||
"MB /" +
|
+ "MB /" +
|
||||||
String.format(Locale.ENGLISH, "%.2f", (float)overallTotal / (1024.0f * 1024.0f)) + "MB";
|
String.format("%.2f", (float) overallTotal /
|
||||||
}
|
(1024.0f * 1024.0f))
|
||||||
|
+ "MB";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a percentile to getDownloadProgressString.
|
* Adds a percentile to getDownloadProgressString.
|
||||||
*
|
*
|
||||||
* @param overallProgress
|
* @param overallProgress
|
||||||
* @param overallTotal
|
* @param overallTotal
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
static public String getDownloadProgressStringNotification(long overallProgress,
|
static public String getDownloadProgressStringNotification(long overallProgress,
|
||||||
long overallTotal) {
|
long overallTotal) {
|
||||||
if (overallTotal == 0) {
|
if (overallTotal == 0) {
|
||||||
if (Constants.LOGVV) {
|
if (Constants.LOGVV) {
|
||||||
Log.e(Constants.TAG, "Notification called when total is zero");
|
Log.e(Constants.TAG, "Notification called when total is zero");
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return getDownloadProgressString(overallProgress, overallTotal) + " (" +
|
return getDownloadProgressString(overallProgress, overallTotal) + " (" +
|
||||||
getDownloadProgressPercent(overallProgress, overallTotal) + ")";
|
getDownloadProgressPercent(overallProgress, overallTotal) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDownloadProgressPercent(long overallProgress, long overallTotal) {
|
public static String getDownloadProgressPercent(long overallProgress, long overallTotal) {
|
||||||
if (overallTotal == 0) {
|
if (overallTotal == 0) {
|
||||||
if (Constants.LOGVV) {
|
if (Constants.LOGVV) {
|
||||||
Log.e(Constants.TAG, "Notification called when total is zero");
|
Log.e(Constants.TAG, "Notification called when total is zero");
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return Long.toString(overallProgress * 100 / overallTotal) + "%";
|
return Long.toString(overallProgress * 100 / overallTotal) + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSpeedString(float bytesPerMillisecond) {
|
public static String getSpeedString(float bytesPerMillisecond) {
|
||||||
return String.format(Locale.ENGLISH, "%.2f", bytesPerMillisecond * 1000 / 1024);
|
return String.format("%.2f", bytesPerMillisecond * 1000 / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getTimeRemaining(long durationInMilliseconds) {
|
public static String getTimeRemaining(long durationInMilliseconds) {
|
||||||
SimpleDateFormat sdf;
|
SimpleDateFormat sdf;
|
||||||
if (durationInMilliseconds > 1000 * 60 * 60) {
|
if (durationInMilliseconds > 1000 * 60 * 60) {
|
||||||
sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
|
||||||
} else {
|
} else {
|
||||||
sdf = new SimpleDateFormat("mm:ss", Locale.getDefault());
|
sdf = new SimpleDateFormat("mm:ss", Locale.getDefault());
|
||||||
}
|
}
|
||||||
return sdf.format(new Date(durationInMilliseconds - TimeZone.getDefault().getRawOffset()));
|
return sdf.format(new Date(durationInMilliseconds - TimeZone.getDefault().getRawOffset()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file name (without full path) for an Expansion APK file from the given context.
|
* Returns the file name (without full path) for an Expansion APK file from the given context.
|
||||||
*
|
*
|
||||||
* @param c the context
|
* @param c the context
|
||||||
|
|
@ -202,33 +205,34 @@ public class Helpers {
|
||||||
* @param versionCode the version of the file
|
* @param versionCode the version of the file
|
||||||
* @return String the file name of the expansion file
|
* @return String the file name of the expansion file
|
||||||
*/
|
*/
|
||||||
public static String getExpansionAPKFileName(Context c, boolean mainFile, int versionCode) {
|
public static String getExpansionAPKFileName(Context c, boolean mainFile, int versionCode) {
|
||||||
return (mainFile ? "main." : "patch.") + versionCode + "." + c.getPackageName() + ".obb";
|
return (mainFile ? "main." : "patch.") + versionCode + "." + c.getPackageName() + ".obb";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the filename (where the file should be saved) from info about a download
|
* Returns the filename (where the file should be saved) from info about a download
|
||||||
*/
|
*/
|
||||||
static public String generateSaveFileName(Context c, String fileName) {
|
static public String generateSaveFileName(Context c, String fileName) {
|
||||||
String path = getSaveFilePath(c) + File.separator + fileName;
|
String path = getSaveFilePath(c)
|
||||||
return path;
|
+ File.separator + fileName;
|
||||||
}
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
static public String getSaveFilePath(Context c) {
|
static public String getSaveFilePath(Context c) {
|
||||||
// This technically existed since Honeycomb, but it is critical
|
// This technically existed since Honeycomb, but it is critical
|
||||||
// on KitKat and greater versions since it will create the
|
// on KitKat and greater versions since it will create the
|
||||||
// directory if needed
|
// directory if needed
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
return c.getObbDir().toString();
|
return c.getObbDir().toString();
|
||||||
} else {
|
} else {
|
||||||
File root = Environment.getExternalStorageDirectory();
|
File root = Environment.getExternalStorageDirectory();
|
||||||
String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
|
String path = root.toString() + Constants.EXP_PATH + c.getPackageName();
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to ascertain the existence of a file and return true/false appropriately
|
* Helper function to ascertain the existence of a file and return true/false appropriately
|
||||||
*
|
*
|
||||||
* @param c the app/activity/service context
|
* @param c the app/activity/service context
|
||||||
|
|
@ -237,72 +241,72 @@ public class Helpers {
|
||||||
* @param deleteFileOnMismatch if the file sizes do not match, delete the file
|
* @param deleteFileOnMismatch if the file sizes do not match, delete the file
|
||||||
* @return true if it does exist, false otherwise
|
* @return true if it does exist, false otherwise
|
||||||
*/
|
*/
|
||||||
static public boolean doesFileExist(Context c, String fileName, long fileSize,
|
static public boolean doesFileExist(Context c, String fileName, long fileSize,
|
||||||
boolean deleteFileOnMismatch) {
|
boolean deleteFileOnMismatch) {
|
||||||
// the file may have been delivered by Play --- let's make sure
|
// the file may have been delivered by Play --- let's make sure
|
||||||
// it's the size we expect
|
// it's the size we expect
|
||||||
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
|
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
|
||||||
if (fileForNewFile.exists()) {
|
if (fileForNewFile.exists()) {
|
||||||
if (fileForNewFile.length() == fileSize) {
|
if (fileForNewFile.length() == fileSize) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (deleteFileOnMismatch) {
|
if (deleteFileOnMismatch) {
|
||||||
// delete the file --- we won't be able to resume
|
// delete the file --- we won't be able to resume
|
||||||
// because we cannot confirm the integrity of the file
|
// because we cannot confirm the integrity of the file
|
||||||
fileForNewFile.delete();
|
fileForNewFile.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int FS_READABLE = 0;
|
public static final int FS_READABLE = 0;
|
||||||
public static final int FS_DOES_NOT_EXIST = 1;
|
public static final int FS_DOES_NOT_EXIST = 1;
|
||||||
public static final int FS_CANNOT_READ = 2;
|
public static final int FS_CANNOT_READ = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to ascertain whether a file can be read.
|
* Helper function to ascertain whether a file can be read.
|
||||||
*
|
*
|
||||||
* @param c the app/activity/service context
|
* @param c the app/activity/service context
|
||||||
* @param fileName the name (sans path) of the file to query
|
* @param fileName the name (sans path) of the file to query
|
||||||
* @return true if it does exist, false otherwise
|
* @return true if it does exist, false otherwise
|
||||||
*/
|
*/
|
||||||
static public int getFileStatus(Context c, String fileName) {
|
static public int getFileStatus(Context c, String fileName) {
|
||||||
// the file may have been delivered by Play --- let's make sure
|
// the file may have been delivered by Play --- let's make sure
|
||||||
// it's the size we expect
|
// it's the size we expect
|
||||||
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
|
File fileForNewFile = new File(Helpers.generateSaveFileName(c, fileName));
|
||||||
int returnValue;
|
int returnValue;
|
||||||
if (fileForNewFile.exists()) {
|
if (fileForNewFile.exists()) {
|
||||||
if (fileForNewFile.canRead()) {
|
if (fileForNewFile.canRead()) {
|
||||||
returnValue = FS_READABLE;
|
returnValue = FS_READABLE;
|
||||||
} else {
|
} else {
|
||||||
returnValue = FS_CANNOT_READ;
|
returnValue = FS_CANNOT_READ;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
returnValue = FS_DOES_NOT_EXIST;
|
returnValue = FS_DOES_NOT_EXIST;
|
||||||
}
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to ascertain whether the application has the correct access to the OBB
|
* Helper function to ascertain whether the application has the correct access to the OBB
|
||||||
* directory to allow an OBB file to be written.
|
* directory to allow an OBB file to be written.
|
||||||
*
|
*
|
||||||
* @param c the app/activity/service context
|
* @param c the app/activity/service context
|
||||||
* @return true if the application can write an OBB file, false otherwise
|
* @return true if the application can write an OBB file, false otherwise
|
||||||
*/
|
*/
|
||||||
static public boolean canWriteOBBFile(Context c) {
|
static public boolean canWriteOBBFile(Context c) {
|
||||||
String path = getSaveFilePath(c);
|
String path = getSaveFilePath(c);
|
||||||
File fileForNewFile = new File(path);
|
File fileForNewFile = new File(path);
|
||||||
boolean canWrite;
|
boolean canWrite;
|
||||||
if (fileForNewFile.exists()) {
|
if (fileForNewFile.exists()) {
|
||||||
canWrite = fileForNewFile.isDirectory() && fileForNewFile.canWrite();
|
canWrite = fileForNewFile.isDirectory() && fileForNewFile.canWrite();
|
||||||
} else {
|
} else {
|
||||||
canWrite = fileForNewFile.mkdirs();
|
canWrite = fileForNewFile.mkdirs();
|
||||||
}
|
}
|
||||||
return canWrite;
|
return canWrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts download states that are returned by the
|
* Converts download states that are returned by the
|
||||||
* {@link IDownloaderClient#onDownloadStateChanged} callback into usable strings. This is useful
|
* {@link IDownloaderClient#onDownloadStateChanged} callback into usable strings. This is useful
|
||||||
* if using the state strings built into the library to display user messages.
|
* if using the state strings built into the library to display user messages.
|
||||||
|
|
@ -310,46 +314,47 @@ public class Helpers {
|
||||||
* @param state One of the STATE_* constants from {@link IDownloaderClient}.
|
* @param state One of the STATE_* constants from {@link IDownloaderClient}.
|
||||||
* @return string resource ID for the corresponding string.
|
* @return string resource ID for the corresponding string.
|
||||||
*/
|
*/
|
||||||
static public int getDownloaderStringResourceIDFromState(int state) {
|
static public int getDownloaderStringResourceIDFromState(int state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case IDownloaderClient.STATE_IDLE:
|
case IDownloaderClient.STATE_IDLE:
|
||||||
return R.string.state_idle;
|
return R.string.state_idle;
|
||||||
case IDownloaderClient.STATE_FETCHING_URL:
|
case IDownloaderClient.STATE_FETCHING_URL:
|
||||||
return R.string.state_fetching_url;
|
return R.string.state_fetching_url;
|
||||||
case IDownloaderClient.STATE_CONNECTING:
|
case IDownloaderClient.STATE_CONNECTING:
|
||||||
return R.string.state_connecting;
|
return R.string.state_connecting;
|
||||||
case IDownloaderClient.STATE_DOWNLOADING:
|
case IDownloaderClient.STATE_DOWNLOADING:
|
||||||
return R.string.state_downloading;
|
return R.string.state_downloading;
|
||||||
case IDownloaderClient.STATE_COMPLETED:
|
case IDownloaderClient.STATE_COMPLETED:
|
||||||
return R.string.state_completed;
|
return R.string.state_completed;
|
||||||
case IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE:
|
case IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE:
|
||||||
return R.string.state_paused_network_unavailable;
|
return R.string.state_paused_network_unavailable;
|
||||||
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
||||||
return R.string.state_paused_by_request;
|
return R.string.state_paused_by_request;
|
||||||
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
|
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
|
||||||
return R.string.state_paused_wifi_disabled;
|
return R.string.state_paused_wifi_disabled;
|
||||||
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
|
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
|
||||||
return R.string.state_paused_wifi_unavailable;
|
return R.string.state_paused_wifi_unavailable;
|
||||||
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED:
|
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED:
|
||||||
return R.string.state_paused_wifi_disabled;
|
return R.string.state_paused_wifi_disabled;
|
||||||
case IDownloaderClient.STATE_PAUSED_NEED_WIFI:
|
case IDownloaderClient.STATE_PAUSED_NEED_WIFI:
|
||||||
return R.string.state_paused_wifi_unavailable;
|
return R.string.state_paused_wifi_unavailable;
|
||||||
case IDownloaderClient.STATE_PAUSED_ROAMING:
|
case IDownloaderClient.STATE_PAUSED_ROAMING:
|
||||||
return R.string.state_paused_roaming;
|
return R.string.state_paused_roaming;
|
||||||
case IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE:
|
case IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE:
|
||||||
return R.string.state_paused_network_setup_failure;
|
return R.string.state_paused_network_setup_failure;
|
||||||
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
|
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
|
||||||
return R.string.state_paused_sdcard_unavailable;
|
return R.string.state_paused_sdcard_unavailable;
|
||||||
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
||||||
return R.string.state_failed_unlicensed;
|
return R.string.state_failed_unlicensed;
|
||||||
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
||||||
return R.string.state_failed_fetching_url;
|
return R.string.state_failed_fetching_url;
|
||||||
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
||||||
return R.string.state_failed_sdcard_full;
|
return R.string.state_failed_sdcard_full;
|
||||||
case IDownloaderClient.STATE_FAILED_CANCELED:
|
case IDownloaderClient.STATE_FAILED_CANCELED:
|
||||||
return R.string.state_failed_cancelled;
|
return R.string.state_failed_cancelled;
|
||||||
default:
|
default:
|
||||||
return R.string.state_unknown;
|
return R.string.state_unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,26 +23,26 @@ import android.os.Messenger;
|
||||||
* downloader. It is used to pass status from the service to the client.
|
* downloader. It is used to pass status from the service to the client.
|
||||||
*/
|
*/
|
||||||
public interface IDownloaderClient {
|
public interface IDownloaderClient {
|
||||||
static final int STATE_IDLE = 1;
|
static final int STATE_IDLE = 1;
|
||||||
static final int STATE_FETCHING_URL = 2;
|
static final int STATE_FETCHING_URL = 2;
|
||||||
static final int STATE_CONNECTING = 3;
|
static final int STATE_CONNECTING = 3;
|
||||||
static final int STATE_DOWNLOADING = 4;
|
static final int STATE_DOWNLOADING = 4;
|
||||||
static final int STATE_COMPLETED = 5;
|
static final int STATE_COMPLETED = 5;
|
||||||
|
|
||||||
static final int STATE_PAUSED_NETWORK_UNAVAILABLE = 6;
|
static final int STATE_PAUSED_NETWORK_UNAVAILABLE = 6;
|
||||||
static final int STATE_PAUSED_BY_REQUEST = 7;
|
static final int STATE_PAUSED_BY_REQUEST = 7;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Both STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION and
|
* Both STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION and
|
||||||
* STATE_PAUSED_NEED_CELLULAR_PERMISSION imply that Wi-Fi is unavailable and
|
* STATE_PAUSED_NEED_CELLULAR_PERMISSION imply that Wi-Fi is unavailable and
|
||||||
* cellular permission will restart the service. Wi-Fi disabled means that
|
* cellular permission will restart the service. Wi-Fi disabled means that
|
||||||
* the Wi-Fi manager is returning that Wi-Fi is not enabled, while in the
|
* the Wi-Fi manager is returning that Wi-Fi is not enabled, while in the
|
||||||
* other case Wi-Fi is enabled but not available.
|
* other case Wi-Fi is enabled but not available.
|
||||||
*/
|
*/
|
||||||
static final int STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 8;
|
static final int STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 8;
|
||||||
static final int STATE_PAUSED_NEED_CELLULAR_PERMISSION = 9;
|
static final int STATE_PAUSED_NEED_CELLULAR_PERMISSION = 9;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Both STATE_PAUSED_WIFI_DISABLED and STATE_PAUSED_NEED_WIFI imply that
|
* Both STATE_PAUSED_WIFI_DISABLED and STATE_PAUSED_NEED_WIFI imply that
|
||||||
* Wi-Fi is unavailable and cellular permission will NOT restart the
|
* Wi-Fi is unavailable and cellular permission will NOT restart the
|
||||||
* service. Wi-Fi disabled means that the Wi-Fi manager is returning that
|
* service. Wi-Fi disabled means that the Wi-Fi manager is returning that
|
||||||
|
|
@ -53,27 +53,27 @@ public interface IDownloaderClient {
|
||||||
* developers with very large payloads do not allow these payloads to be
|
* developers with very large payloads do not allow these payloads to be
|
||||||
* downloaded over cellular connections.
|
* downloaded over cellular connections.
|
||||||
*/
|
*/
|
||||||
static final int STATE_PAUSED_WIFI_DISABLED = 10;
|
static final int STATE_PAUSED_WIFI_DISABLED = 10;
|
||||||
static final int STATE_PAUSED_NEED_WIFI = 11;
|
static final int STATE_PAUSED_NEED_WIFI = 11;
|
||||||
|
|
||||||
static final int STATE_PAUSED_ROAMING = 12;
|
static final int STATE_PAUSED_ROAMING = 12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scary case. We were on a network that redirected us to another website
|
* Scary case. We were on a network that redirected us to another website
|
||||||
* that delivered us the wrong file.
|
* that delivered us the wrong file.
|
||||||
*/
|
*/
|
||||||
static final int STATE_PAUSED_NETWORK_SETUP_FAILURE = 13;
|
static final int STATE_PAUSED_NETWORK_SETUP_FAILURE = 13;
|
||||||
|
|
||||||
static final int STATE_PAUSED_SDCARD_UNAVAILABLE = 14;
|
static final int STATE_PAUSED_SDCARD_UNAVAILABLE = 14;
|
||||||
|
|
||||||
static final int STATE_FAILED_UNLICENSED = 15;
|
static final int STATE_FAILED_UNLICENSED = 15;
|
||||||
static final int STATE_FAILED_FETCHING_URL = 16;
|
static final int STATE_FAILED_FETCHING_URL = 16;
|
||||||
static final int STATE_FAILED_SDCARD_FULL = 17;
|
static final int STATE_FAILED_SDCARD_FULL = 17;
|
||||||
static final int STATE_FAILED_CANCELED = 18;
|
static final int STATE_FAILED_CANCELED = 18;
|
||||||
|
|
||||||
static final int STATE_FAILED = 19;
|
static final int STATE_FAILED = 19;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called internally by the stub when the service is bound to the client.
|
* Called internally by the stub when the service is bound to the client.
|
||||||
* <p>
|
* <p>
|
||||||
* Critical implementation detail. In onServiceConnected we create the
|
* Critical implementation detail. In onServiceConnected we create the
|
||||||
|
|
@ -90,9 +90,9 @@ public interface IDownloaderClient {
|
||||||
* @param m the service Messenger. This Messenger is used to call the
|
* @param m the service Messenger. This Messenger is used to call the
|
||||||
* service API from the client.
|
* service API from the client.
|
||||||
*/
|
*/
|
||||||
void onServiceConnected(Messenger m);
|
void onServiceConnected(Messenger m);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the download state changes. Depending on the state, there may
|
* Called when the download state changes. Depending on the state, there may
|
||||||
* be user requests. The service is free to change the download state in the
|
* be user requests. The service is free to change the download state in the
|
||||||
* middle of a user request, so the client should be able to handle this.
|
* middle of a user request, so the client should be able to handle this.
|
||||||
|
|
@ -112,9 +112,9 @@ public interface IDownloaderClient {
|
||||||
*
|
*
|
||||||
* @param newState one of the STATE_* values defined in IDownloaderClient
|
* @param newState one of the STATE_* values defined in IDownloaderClient
|
||||||
*/
|
*/
|
||||||
void onDownloadStateChanged(int newState);
|
void onDownloadStateChanged(int newState);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the download progress. This is intended to be used to fill out a
|
* Shows the download progress. This is intended to be used to fill out a
|
||||||
* client UI. This progress should only be shown in a few states such as
|
* client UI. This progress should only be shown in a few states such as
|
||||||
* STATE_DOWNLOADING.
|
* STATE_DOWNLOADING.
|
||||||
|
|
@ -122,5 +122,5 @@ public interface IDownloaderClient {
|
||||||
* @param progress the DownloadProgressInfo object containing the current
|
* @param progress the DownloadProgressInfo object containing the current
|
||||||
* progress of all downloads.
|
* progress of all downloads.
|
||||||
*/
|
*/
|
||||||
void onDownloadProgress(DownloadProgressInfo progress);
|
void onDownloadProgress(DownloadProgressInfo progress);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,47 +31,47 @@ import android.os.Messenger;
|
||||||
* should immediately call {@link #onClientUpdated}.
|
* should immediately call {@link #onClientUpdated}.
|
||||||
*/
|
*/
|
||||||
public interface IDownloaderService {
|
public interface IDownloaderService {
|
||||||
/**
|
/**
|
||||||
* Set this flag in response to the
|
* Set this flag in response to the
|
||||||
* IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION state and then
|
* IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION state and then
|
||||||
* call RequestContinueDownload to resume a download
|
* call RequestContinueDownload to resume a download
|
||||||
*/
|
*/
|
||||||
public static final int FLAGS_DOWNLOAD_OVER_CELLULAR = 1;
|
public static final int FLAGS_DOWNLOAD_OVER_CELLULAR = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the service abort the current download. The service should
|
* Request that the service abort the current download. The service should
|
||||||
* respond by changing the state to {@link IDownloaderClient.STATE_ABORTED}.
|
* respond by changing the state to {@link IDownloaderClient.STATE_ABORTED}.
|
||||||
*/
|
*/
|
||||||
void requestAbortDownload();
|
void requestAbortDownload();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the service pause the current download. The service should
|
* Request that the service pause the current download. The service should
|
||||||
* respond by changing the state to
|
* respond by changing the state to
|
||||||
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
|
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
|
||||||
*/
|
*/
|
||||||
void requestPauseDownload();
|
void requestPauseDownload();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the service continue a paused download, when in any paused
|
* Request that the service continue a paused download, when in any paused
|
||||||
* or failed state, including
|
* or failed state, including
|
||||||
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
|
* {@link IDownloaderClient.STATE_PAUSED_BY_REQUEST}.
|
||||||
*/
|
*/
|
||||||
void requestContinueDownload();
|
void requestContinueDownload();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the flags for this download (e.g.
|
* Set the flags for this download (e.g.
|
||||||
* {@link DownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR}).
|
* {@link DownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR}).
|
||||||
*
|
*
|
||||||
* @param flags
|
* @param flags
|
||||||
*/
|
*/
|
||||||
void setDownloadFlags(int flags);
|
void setDownloadFlags(int flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that the download status be sent to the client.
|
* Requests that the download status be sent to the client.
|
||||||
*/
|
*/
|
||||||
void requestDownloadStatus();
|
void requestDownloadStatus();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when you get {@link
|
* Call this when you get {@link
|
||||||
* IDownloaderClient.onServiceConnected(Messenger m)} from the
|
* IDownloaderClient.onServiceConnected(Messenger m)} from the
|
||||||
* DownloaderClient to register the client with the service. It will
|
* DownloaderClient to register the client with the service. It will
|
||||||
|
|
@ -79,5 +79,5 @@ public interface IDownloaderService {
|
||||||
*
|
*
|
||||||
* @param clientMessenger
|
* @param clientMessenger
|
||||||
*/
|
*/
|
||||||
void onClientUpdated(Messenger clientMessenger);
|
void onClientUpdated(Messenger clientMessenger);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ import android.os.Messenger;
|
||||||
* {@link IDownloaderService#onClientUpdated}.
|
* {@link IDownloaderService#onClientUpdated}.
|
||||||
*/
|
*/
|
||||||
public interface IStub {
|
public interface IStub {
|
||||||
Messenger getMessenger();
|
Messenger getMessenger();
|
||||||
|
|
||||||
void connect(Context c);
|
void connect(Context c);
|
||||||
|
|
||||||
void disconnect(Context c);
|
void disconnect(Context c);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package com.google.android.vending.expansion.downloader;
|
package com.google.android.vending.expansion.downloader;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -31,96 +30,94 @@ import android.util.Log;
|
||||||
* Contains useful helper functions, typically tied to the application context.
|
* Contains useful helper functions, typically tied to the application context.
|
||||||
*/
|
*/
|
||||||
class SystemFacade {
|
class SystemFacade {
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private NotificationManager mNotificationManager;
|
private NotificationManager mNotificationManager;
|
||||||
|
|
||||||
public SystemFacade(Context context) {
|
public SystemFacade(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mNotificationManager = (NotificationManager)
|
mNotificationManager = (NotificationManager)
|
||||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long currentTimeMillis() {
|
public long currentTimeMillis() {
|
||||||
return System.currentTimeMillis();
|
return System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getActiveNetworkType() {
|
public Integer getActiveNetworkType() {
|
||||||
ConnectivityManager connectivity =
|
ConnectivityManager connectivity =
|
||||||
(ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
if (connectivity == null) {
|
if (connectivity == null) {
|
||||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
|
||||||
NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
|
if (activeInfo == null) {
|
||||||
if (activeInfo == null) {
|
if (Constants.LOGVV) {
|
||||||
if (Constants.LOGVV) {
|
Log.v(Constants.TAG, "network is not available");
|
||||||
Log.v(Constants.TAG, "network is not available");
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
return activeInfo.getType();
|
||||||
return activeInfo.getType();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNetworkRoaming() {
|
public boolean isNetworkRoaming() {
|
||||||
ConnectivityManager connectivity =
|
ConnectivityManager connectivity =
|
||||||
(ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
if (connectivity == null) {
|
if (connectivity == null) {
|
||||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
NetworkInfo info = connectivity.getActiveNetworkInfo();
|
||||||
NetworkInfo info = connectivity.getActiveNetworkInfo();
|
boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
|
||||||
boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
|
TelephonyManager tm = (TelephonyManager) mContext
|
||||||
TelephonyManager tm = (TelephonyManager)mContext
|
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
if (null == tm) {
|
||||||
if (null == tm) {
|
Log.w(Constants.TAG, "couldn't get telephony manager");
|
||||||
Log.w(Constants.TAG, "couldn't get telephony manager");
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
boolean isRoaming = isMobile && tm.isNetworkRoaming();
|
||||||
boolean isRoaming = isMobile && tm.isNetworkRoaming();
|
if (Constants.LOGVV && isRoaming) {
|
||||||
if (Constants.LOGVV && isRoaming) {
|
Log.v(Constants.TAG, "network is roaming");
|
||||||
Log.v(Constants.TAG, "network is roaming");
|
}
|
||||||
}
|
return isRoaming;
|
||||||
return isRoaming;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public Long getMaxBytesOverMobile() {
|
public Long getMaxBytesOverMobile() {
|
||||||
return (long)Integer.MAX_VALUE;
|
return (long) Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getRecommendedMaxBytesOverMobile() {
|
public Long getRecommendedMaxBytesOverMobile() {
|
||||||
return 2097152L;
|
return 2097152L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendBroadcast(Intent intent) {
|
public void sendBroadcast(Intent intent) {
|
||||||
mContext.sendBroadcast(intent);
|
mContext.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
|
public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
|
||||||
return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
|
return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postNotification(long id, Notification notification) {
|
public void postNotification(long id, Notification notification) {
|
||||||
/**
|
/**
|
||||||
* TODO: The system notification manager takes ints, not longs, as IDs,
|
* TODO: The system notification manager takes ints, not longs, as IDs,
|
||||||
* but the download manager uses IDs take straight from the database,
|
* but the download manager uses IDs take straight from the database,
|
||||||
* which are longs. This will have to be dealt with at some point.
|
* which are longs. This will have to be dealt with at some point.
|
||||||
*/
|
*/
|
||||||
mNotificationManager.notify((int)id, notification);
|
mNotificationManager.notify((int) id, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelNotification(long id) {
|
public void cancelNotification(long id) {
|
||||||
mNotificationManager.cancel((int)id);
|
mNotificationManager.cancel((int) id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelAllNotifications() {
|
public void cancelAllNotifications() {
|
||||||
mNotificationManager.cancelAll();
|
mNotificationManager.cancelAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startThread(Thread thread) {
|
public void startThread(Thread thread) {
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
131
platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java
Executable file → Normal file
131
platform/android/java/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java
Executable file → Normal file
|
|
@ -32,80 +32,81 @@ import android.util.Log;
|
||||||
* intent, it does not queue up batches of intents of the same type.
|
* intent, it does not queue up batches of intents of the same type.
|
||||||
*/
|
*/
|
||||||
public abstract class CustomIntentService extends Service {
|
public abstract class CustomIntentService extends Service {
|
||||||
private String mName;
|
private String mName;
|
||||||
private boolean mRedelivery;
|
private boolean mRedelivery;
|
||||||
private volatile ServiceHandler mServiceHandler;
|
private volatile ServiceHandler mServiceHandler;
|
||||||
private volatile Looper mServiceLooper;
|
private volatile Looper mServiceLooper;
|
||||||
private static final String LOG_TAG = "CustomIntentService";
|
private static final String LOG_TAG = "CustomIntentService";
|
||||||
private static final int WHAT_MESSAGE = -10;
|
private static final int WHAT_MESSAGE = -10;
|
||||||
|
|
||||||
public CustomIntentService(String paramString) {
|
public CustomIntentService(String paramString) {
|
||||||
this.mName = paramString;
|
this.mName = paramString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent paramIntent) {
|
public IBinder onBind(Intent paramIntent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
HandlerThread localHandlerThread = new HandlerThread("IntentService[" + this.mName + "]");
|
HandlerThread localHandlerThread = new HandlerThread("IntentService["
|
||||||
localHandlerThread.start();
|
+ this.mName + "]");
|
||||||
this.mServiceLooper = localHandlerThread.getLooper();
|
localHandlerThread.start();
|
||||||
this.mServiceHandler = new ServiceHandler(this.mServiceLooper);
|
this.mServiceLooper = localHandlerThread.getLooper();
|
||||||
}
|
this.mServiceHandler = new ServiceHandler(this.mServiceLooper);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
Thread localThread = this.mServiceLooper.getThread();
|
Thread localThread = this.mServiceLooper.getThread();
|
||||||
if ((localThread != null) && (localThread.isAlive())) {
|
if ((localThread != null) && (localThread.isAlive())) {
|
||||||
localThread.interrupt();
|
localThread.interrupt();
|
||||||
}
|
}
|
||||||
this.mServiceLooper.quit();
|
this.mServiceLooper.quit();
|
||||||
Log.d(LOG_TAG, "onDestroy");
|
Log.d(LOG_TAG, "onDestroy");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void onHandleIntent(Intent paramIntent);
|
protected abstract void onHandleIntent(Intent paramIntent);
|
||||||
|
|
||||||
protected abstract boolean shouldStop();
|
protected abstract boolean shouldStop();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart(Intent paramIntent, int startId) {
|
public void onStart(Intent paramIntent, int startId) {
|
||||||
if (!this.mServiceHandler.hasMessages(WHAT_MESSAGE)) {
|
if (!this.mServiceHandler.hasMessages(WHAT_MESSAGE)) {
|
||||||
Message localMessage = this.mServiceHandler.obtainMessage();
|
Message localMessage = this.mServiceHandler.obtainMessage();
|
||||||
localMessage.arg1 = startId;
|
localMessage.arg1 = startId;
|
||||||
localMessage.obj = paramIntent;
|
localMessage.obj = paramIntent;
|
||||||
localMessage.what = WHAT_MESSAGE;
|
localMessage.what = WHAT_MESSAGE;
|
||||||
this.mServiceHandler.sendMessage(localMessage);
|
this.mServiceHandler.sendMessage(localMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent paramIntent, int flags, int startId) {
|
public int onStartCommand(Intent paramIntent, int flags, int startId) {
|
||||||
onStart(paramIntent, startId);
|
onStart(paramIntent, startId);
|
||||||
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
|
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIntentRedelivery(boolean enabled) {
|
public void setIntentRedelivery(boolean enabled) {
|
||||||
this.mRedelivery = enabled;
|
this.mRedelivery = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private final class ServiceHandler extends Handler {
|
||||||
public ServiceHandler(Looper looper) {
|
public ServiceHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message paramMessage) {
|
public void handleMessage(Message paramMessage) {
|
||||||
CustomIntentService.this
|
CustomIntentService.this
|
||||||
.onHandleIntent((Intent)paramMessage.obj);
|
.onHandleIntent((Intent) paramMessage.obj);
|
||||||
if (shouldStop()) {
|
if (shouldStop()) {
|
||||||
Log.d(LOG_TAG, "stopSelf");
|
Log.d(LOG_TAG, "stopSelf");
|
||||||
CustomIntentService.this.stopSelf(paramMessage.arg1);
|
CustomIntentService.this.stopSelf(paramMessage.arg1);
|
||||||
Log.d(LOG_TAG, "afterStopSelf");
|
Log.d(LOG_TAG, "afterStopSelf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,68 +25,68 @@ import android.util.Log;
|
||||||
* Representation of information about an individual download from the database.
|
* Representation of information about an individual download from the database.
|
||||||
*/
|
*/
|
||||||
public class DownloadInfo {
|
public class DownloadInfo {
|
||||||
public String mUri;
|
public String mUri;
|
||||||
public final int mIndex;
|
public final int mIndex;
|
||||||
public final String mFileName;
|
public final String mFileName;
|
||||||
public String mETag;
|
public String mETag;
|
||||||
public long mTotalBytes;
|
public long mTotalBytes;
|
||||||
public long mCurrentBytes;
|
public long mCurrentBytes;
|
||||||
public long mLastMod;
|
public long mLastMod;
|
||||||
public int mStatus;
|
public int mStatus;
|
||||||
public int mControl;
|
public int mControl;
|
||||||
public int mNumFailed;
|
public int mNumFailed;
|
||||||
public int mRetryAfter;
|
public int mRetryAfter;
|
||||||
public int mRedirectCount;
|
public int mRedirectCount;
|
||||||
|
|
||||||
boolean mInitialized;
|
boolean mInitialized;
|
||||||
|
|
||||||
public int mFuzz;
|
public int mFuzz;
|
||||||
|
|
||||||
public DownloadInfo(int index, String fileName, String pkg) {
|
public DownloadInfo(int index, String fileName, String pkg) {
|
||||||
mFuzz = Helpers.sRandom.nextInt(1001);
|
mFuzz = Helpers.sRandom.nextInt(1001);
|
||||||
mFileName = fileName;
|
mFileName = fileName;
|
||||||
mIndex = index;
|
mIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetDownload() {
|
public void resetDownload() {
|
||||||
mCurrentBytes = 0;
|
mCurrentBytes = 0;
|
||||||
mETag = "";
|
mETag = "";
|
||||||
mLastMod = 0;
|
mLastMod = 0;
|
||||||
mStatus = 0;
|
mStatus = 0;
|
||||||
mControl = 0;
|
mControl = 0;
|
||||||
mNumFailed = 0;
|
mNumFailed = 0;
|
||||||
mRetryAfter = 0;
|
mRetryAfter = 0;
|
||||||
mRedirectCount = 0;
|
mRedirectCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time when a download should be restarted.
|
* Returns the time when a download should be restarted.
|
||||||
*/
|
*/
|
||||||
public long restartTime(long now) {
|
public long restartTime(long now) {
|
||||||
if (mNumFailed == 0) {
|
if (mNumFailed == 0) {
|
||||||
return now;
|
return now;
|
||||||
}
|
}
|
||||||
if (mRetryAfter > 0) {
|
if (mRetryAfter > 0) {
|
||||||
return mLastMod + mRetryAfter;
|
return mLastMod + mRetryAfter;
|
||||||
}
|
}
|
||||||
return mLastMod +
|
return mLastMod +
|
||||||
Constants.RETRY_FIRST_DELAY *
|
Constants.RETRY_FIRST_DELAY *
|
||||||
(1000 + mFuzz) * (1 << (mNumFailed - 1));
|
(1000 + mFuzz) * (1 << (mNumFailed - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logVerboseInfo() {
|
public void logVerboseInfo() {
|
||||||
Log.v(Constants.TAG, "Service adding new entry");
|
Log.v(Constants.TAG, "Service adding new entry");
|
||||||
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
||||||
Log.v(Constants.TAG, "URI : " + mUri);
|
Log.v(Constants.TAG, "URI : " + mUri);
|
||||||
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
Log.v(Constants.TAG, "FILENAME: " + mFileName);
|
||||||
Log.v(Constants.TAG, "CONTROL : " + mControl);
|
Log.v(Constants.TAG, "CONTROL : " + mControl);
|
||||||
Log.v(Constants.TAG, "STATUS : " + mStatus);
|
Log.v(Constants.TAG, "STATUS : " + mStatus);
|
||||||
Log.v(Constants.TAG, "FAILED_C: " + mNumFailed);
|
Log.v(Constants.TAG, "FAILED_C: " + mNumFailed);
|
||||||
Log.v(Constants.TAG, "RETRY_AF: " + mRetryAfter);
|
Log.v(Constants.TAG, "RETRY_AF: " + mRetryAfter);
|
||||||
Log.v(Constants.TAG, "REDIRECT: " + mRedirectCount);
|
Log.v(Constants.TAG, "REDIRECT: " + mRedirectCount);
|
||||||
Log.v(Constants.TAG, "LAST_MOD: " + mLastMod);
|
Log.v(Constants.TAG, "LAST_MOD: " + mLastMod);
|
||||||
Log.v(Constants.TAG, "TOTAL : " + mTotalBytes);
|
Log.v(Constants.TAG, "TOTAL : " + mTotalBytes);
|
||||||
Log.v(Constants.TAG, "CURRENT : " + mCurrentBytes);
|
Log.v(Constants.TAG, "CURRENT : " + mCurrentBytes);
|
||||||
Log.v(Constants.TAG, "ETAG : " + mETag);
|
Log.v(Constants.TAG, "ETAG : " + mETag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package com.google.android.vending.expansion.downloader.impl;
|
package com.google.android.vending.expansion.downloader.impl;
|
||||||
|
|
||||||
import com.godot.game.R;
|
import com.android.vending.expansion.downloader.R;
|
||||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||||
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
||||||
import com.google.android.vending.expansion.downloader.Helpers;
|
import com.google.android.vending.expansion.downloader.Helpers;
|
||||||
|
|
@ -42,183 +42,184 @@ import android.support.v4.app.NotificationCompat;
|
||||||
*/
|
*/
|
||||||
public class DownloadNotification implements IDownloaderClient {
|
public class DownloadNotification implements IDownloaderClient {
|
||||||
|
|
||||||
private int mState;
|
private int mState;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final NotificationManager mNotificationManager;
|
private final NotificationManager mNotificationManager;
|
||||||
private CharSequence mCurrentTitle;
|
private CharSequence mCurrentTitle;
|
||||||
|
|
||||||
private IDownloaderClient mClientProxy;
|
private IDownloaderClient mClientProxy;
|
||||||
private NotificationCompat.Builder mActiveDownloadBuilder;
|
private NotificationCompat.Builder mActiveDownloadBuilder;
|
||||||
private NotificationCompat.Builder mBuilder;
|
private NotificationCompat.Builder mBuilder;
|
||||||
private NotificationCompat.Builder mCurrentBuilder;
|
private NotificationCompat.Builder mCurrentBuilder;
|
||||||
private CharSequence mLabel;
|
private CharSequence mLabel;
|
||||||
private String mCurrentText;
|
private String mCurrentText;
|
||||||
private DownloadProgressInfo mProgressInfo;
|
private DownloadProgressInfo mProgressInfo;
|
||||||
private PendingIntent mContentIntent;
|
private PendingIntent mContentIntent;
|
||||||
|
|
||||||
static final String LOGTAG = "DownloadNotification";
|
static final String LOGTAG = "DownloadNotification";
|
||||||
static final int NOTIFICATION_ID = LOGTAG.hashCode();
|
static final int NOTIFICATION_ID = LOGTAG.hashCode();
|
||||||
|
|
||||||
public PendingIntent getClientIntent() {
|
public PendingIntent getClientIntent() {
|
||||||
return mContentIntent;
|
return mContentIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setClientIntent(PendingIntent clientIntent) {
|
public void setClientIntent(PendingIntent clientIntent) {
|
||||||
this.mBuilder.setContentIntent(clientIntent);
|
this.mBuilder.setContentIntent(clientIntent);
|
||||||
this.mActiveDownloadBuilder.setContentIntent(clientIntent);
|
this.mActiveDownloadBuilder.setContentIntent(clientIntent);
|
||||||
this.mContentIntent = clientIntent;
|
this.mContentIntent = clientIntent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resendState() {
|
public void resendState() {
|
||||||
if (null != mClientProxy) {
|
if (null != mClientProxy) {
|
||||||
mClientProxy.onDownloadStateChanged(mState);
|
mClientProxy.onDownloadStateChanged(mState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadStateChanged(int newState) {
|
public void onDownloadStateChanged(int newState) {
|
||||||
if (null != mClientProxy) {
|
if (null != mClientProxy) {
|
||||||
mClientProxy.onDownloadStateChanged(newState);
|
mClientProxy.onDownloadStateChanged(newState);
|
||||||
}
|
}
|
||||||
if (newState != mState) {
|
if (newState != mState) {
|
||||||
mState = newState;
|
mState = newState;
|
||||||
if (newState == IDownloaderClient.STATE_IDLE || null == mContentIntent) {
|
if (newState == IDownloaderClient.STATE_IDLE || null == mContentIntent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int stringDownloadID;
|
int stringDownloadID;
|
||||||
int iconResource;
|
int iconResource;
|
||||||
boolean ongoingEvent;
|
boolean ongoingEvent;
|
||||||
|
|
||||||
// get the new title string and paused text
|
// get the new title string and paused text
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case 0:
|
case 0:
|
||||||
iconResource = android.R.drawable.stat_sys_warning;
|
iconResource = android.R.drawable.stat_sys_warning;
|
||||||
stringDownloadID = R.string.state_unknown;
|
stringDownloadID = R.string.state_unknown;
|
||||||
ongoingEvent = false;
|
ongoingEvent = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDownloaderClient.STATE_DOWNLOADING:
|
case IDownloaderClient.STATE_DOWNLOADING:
|
||||||
iconResource = android.R.drawable.stat_sys_download;
|
iconResource = android.R.drawable.stat_sys_download;
|
||||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||||
ongoingEvent = true;
|
ongoingEvent = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDownloaderClient.STATE_FETCHING_URL:
|
case IDownloaderClient.STATE_FETCHING_URL:
|
||||||
case IDownloaderClient.STATE_CONNECTING:
|
case IDownloaderClient.STATE_CONNECTING:
|
||||||
iconResource = android.R.drawable.stat_sys_download_done;
|
iconResource = android.R.drawable.stat_sys_download_done;
|
||||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||||
ongoingEvent = true;
|
ongoingEvent = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDownloaderClient.STATE_COMPLETED:
|
case IDownloaderClient.STATE_COMPLETED:
|
||||||
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
||||||
iconResource = android.R.drawable.stat_sys_download_done;
|
iconResource = android.R.drawable.stat_sys_download_done;
|
||||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||||
ongoingEvent = false;
|
ongoingEvent = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDownloaderClient.STATE_FAILED:
|
case IDownloaderClient.STATE_FAILED:
|
||||||
case IDownloaderClient.STATE_FAILED_CANCELED:
|
case IDownloaderClient.STATE_FAILED_CANCELED:
|
||||||
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
||||||
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
||||||
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
||||||
iconResource = android.R.drawable.stat_sys_warning;
|
iconResource = android.R.drawable.stat_sys_warning;
|
||||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||||
ongoingEvent = false;
|
ongoingEvent = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
iconResource = android.R.drawable.stat_sys_warning;
|
iconResource = android.R.drawable.stat_sys_warning;
|
||||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||||
ongoingEvent = true;
|
ongoingEvent = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrentText = mContext.getString(stringDownloadID);
|
mCurrentText = mContext.getString(stringDownloadID);
|
||||||
mCurrentTitle = mLabel;
|
mCurrentTitle = mLabel;
|
||||||
mCurrentBuilder.setTicker(mLabel + ": " + mCurrentText);
|
mCurrentBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||||
mCurrentBuilder.setSmallIcon(iconResource);
|
mCurrentBuilder.setSmallIcon(iconResource);
|
||||||
mCurrentBuilder.setContentTitle(mCurrentTitle);
|
mCurrentBuilder.setContentTitle(mCurrentTitle);
|
||||||
mCurrentBuilder.setContentText(mCurrentText);
|
mCurrentBuilder.setContentText(mCurrentText);
|
||||||
if (ongoingEvent) {
|
if (ongoingEvent) {
|
||||||
mCurrentBuilder.setOngoing(true);
|
mCurrentBuilder.setOngoing(true);
|
||||||
} else {
|
} else {
|
||||||
mCurrentBuilder.setOngoing(false);
|
mCurrentBuilder.setOngoing(false);
|
||||||
mCurrentBuilder.setAutoCancel(true);
|
mCurrentBuilder.setAutoCancel(true);
|
||||||
}
|
}
|
||||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadProgress(DownloadProgressInfo progress) {
|
public void onDownloadProgress(DownloadProgressInfo progress) {
|
||||||
mProgressInfo = progress;
|
mProgressInfo = progress;
|
||||||
if (null != mClientProxy) {
|
if (null != mClientProxy) {
|
||||||
mClientProxy.onDownloadProgress(progress);
|
mClientProxy.onDownloadProgress(progress);
|
||||||
}
|
}
|
||||||
if (progress.mOverallTotal <= 0) {
|
if (progress.mOverallTotal <= 0) {
|
||||||
// we just show the text
|
// we just show the text
|
||||||
mBuilder.setTicker(mCurrentTitle);
|
mBuilder.setTicker(mCurrentTitle);
|
||||||
mBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
mBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||||
mBuilder.setContentTitle(mCurrentTitle);
|
mBuilder.setContentTitle(mCurrentTitle);
|
||||||
mBuilder.setContentText(mCurrentText);
|
mBuilder.setContentText(mCurrentText);
|
||||||
mCurrentBuilder = mBuilder;
|
mCurrentBuilder = mBuilder;
|
||||||
} else {
|
} else {
|
||||||
mActiveDownloadBuilder.setProgress((int)progress.mOverallTotal, (int)progress.mOverallProgress, false);
|
mActiveDownloadBuilder.setProgress((int) progress.mOverallTotal, (int) progress.mOverallProgress, false);
|
||||||
mActiveDownloadBuilder.setContentText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
|
mActiveDownloadBuilder.setContentText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
|
||||||
mActiveDownloadBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
mActiveDownloadBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||||
mActiveDownloadBuilder.setTicker(mLabel + ": " + mCurrentText);
|
mActiveDownloadBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||||
mActiveDownloadBuilder.setContentTitle(mLabel);
|
mActiveDownloadBuilder.setContentTitle(mLabel);
|
||||||
mActiveDownloadBuilder.setContentInfo(mContext.getString(R.string.time_remaining_notification,
|
mActiveDownloadBuilder.setContentInfo(mContext.getString(R.string.time_remaining_notification,
|
||||||
Helpers.getTimeRemaining(progress.mTimeRemaining)));
|
Helpers.getTimeRemaining(progress.mTimeRemaining)));
|
||||||
mCurrentBuilder = mActiveDownloadBuilder;
|
mCurrentBuilder = mActiveDownloadBuilder;
|
||||||
}
|
}
|
||||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called in response to onClientUpdated. Creates a new proxy and notifies
|
* Called in response to onClientUpdated. Creates a new proxy and notifies
|
||||||
* it of the current state.
|
* it of the current state.
|
||||||
*
|
*
|
||||||
* @param msg the client Messenger to notify
|
* @param msg the client Messenger to notify
|
||||||
*/
|
*/
|
||||||
public void setMessenger(Messenger msg) {
|
public void setMessenger(Messenger msg) {
|
||||||
mClientProxy = DownloaderClientMarshaller.CreateProxy(msg);
|
mClientProxy = DownloaderClientMarshaller.CreateProxy(msg);
|
||||||
if (null != mProgressInfo) {
|
if (null != mProgressInfo) {
|
||||||
mClientProxy.onDownloadProgress(mProgressInfo);
|
mClientProxy.onDownloadProgress(mProgressInfo);
|
||||||
}
|
}
|
||||||
if (mState != -1) {
|
if (mState != -1) {
|
||||||
mClientProxy.onDownloadStateChanged(mState);
|
mClientProxy.onDownloadStateChanged(mState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param ctx The context to use to obtain access to the Notification
|
* @param ctx The context to use to obtain access to the Notification
|
||||||
* Service
|
* Service
|
||||||
*/
|
*/
|
||||||
DownloadNotification(Context ctx, CharSequence applicationLabel) {
|
DownloadNotification(Context ctx, CharSequence applicationLabel) {
|
||||||
mState = -1;
|
mState = -1;
|
||||||
mContext = ctx;
|
mContext = ctx;
|
||||||
mLabel = applicationLabel;
|
mLabel = applicationLabel;
|
||||||
mNotificationManager = (NotificationManager)
|
mNotificationManager = (NotificationManager)
|
||||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
mActiveDownloadBuilder = new NotificationCompat.Builder(ctx);
|
mActiveDownloadBuilder = new NotificationCompat.Builder(ctx);
|
||||||
mBuilder = new NotificationCompat.Builder(ctx);
|
mBuilder = new NotificationCompat.Builder(ctx);
|
||||||
|
|
||||||
// Set Notification category and priorities to something that makes sense for a long
|
// Set Notification category and priorities to something that makes sense for a long
|
||||||
// lived background task.
|
// lived background task.
|
||||||
mActiveDownloadBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
mActiveDownloadBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||||
mActiveDownloadBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
|
mActiveDownloadBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
|
||||||
|
|
||||||
mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||||
mBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
|
mBuilder.setCategory(NotificationCompat.CATEGORY_PROGRESS);
|
||||||
|
|
||||||
mCurrentBuilder = mBuilder;
|
mCurrentBuilder = mBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(Messenger m) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(Messenger m) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
815
platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java
Executable file → Normal file
815
platform/android/java/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java
Executable file → Normal file
|
|
@ -27,443 +27,484 @@ import android.provider.BaseColumns;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class DownloadsDB {
|
public class DownloadsDB {
|
||||||
private static final String DATABASE_NAME = "DownloadsDB";
|
private static final String DATABASE_NAME = "DownloadsDB";
|
||||||
private static final int DATABASE_VERSION = 7;
|
private static final int DATABASE_VERSION = 7;
|
||||||
public static final String LOG_TAG = DownloadsDB.class.getName();
|
public static final String LOG_TAG = DownloadsDB.class.getName();
|
||||||
final SQLiteOpenHelper mHelper;
|
final SQLiteOpenHelper mHelper;
|
||||||
SQLiteStatement mGetDownloadByIndex;
|
SQLiteStatement mGetDownloadByIndex;
|
||||||
SQLiteStatement mUpdateCurrentBytes;
|
SQLiteStatement mUpdateCurrentBytes;
|
||||||
private static DownloadsDB mDownloadsDB;
|
private static DownloadsDB mDownloadsDB;
|
||||||
long mMetadataRowID = -1;
|
long mMetadataRowID = -1;
|
||||||
int mVersionCode = -1;
|
int mVersionCode = -1;
|
||||||
int mStatus = -1;
|
int mStatus = -1;
|
||||||
int mFlags;
|
int mFlags;
|
||||||
|
|
||||||
static public synchronized DownloadsDB getDB(Context paramContext) {
|
static public synchronized DownloadsDB getDB(Context paramContext) {
|
||||||
if (null == mDownloadsDB) {
|
if (null == mDownloadsDB) {
|
||||||
return new DownloadsDB(paramContext);
|
return new DownloadsDB(paramContext);
|
||||||
}
|
}
|
||||||
return mDownloadsDB;
|
return mDownloadsDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SQLiteStatement getDownloadByIndexStatement() {
|
private SQLiteStatement getDownloadByIndexStatement() {
|
||||||
if (null == mGetDownloadByIndex) {
|
if (null == mGetDownloadByIndex) {
|
||||||
mGetDownloadByIndex = mHelper.getReadableDatabase().compileStatement(
|
mGetDownloadByIndex = mHelper.getReadableDatabase().compileStatement(
|
||||||
"SELECT " + BaseColumns._ID + " FROM " + DownloadColumns.TABLE_NAME + " WHERE " + DownloadColumns.INDEX + " = ?");
|
"SELECT " + BaseColumns._ID + " FROM "
|
||||||
}
|
+ DownloadColumns.TABLE_NAME + " WHERE "
|
||||||
return mGetDownloadByIndex;
|
+ DownloadColumns.INDEX + " = ?");
|
||||||
}
|
}
|
||||||
|
return mGetDownloadByIndex;
|
||||||
|
}
|
||||||
|
|
||||||
private SQLiteStatement getUpdateCurrentBytesStatement() {
|
private SQLiteStatement getUpdateCurrentBytesStatement() {
|
||||||
if (null == mUpdateCurrentBytes) {
|
if (null == mUpdateCurrentBytes) {
|
||||||
mUpdateCurrentBytes = mHelper.getReadableDatabase().compileStatement(
|
mUpdateCurrentBytes = mHelper.getReadableDatabase().compileStatement(
|
||||||
"UPDATE " + DownloadColumns.TABLE_NAME + " SET " + DownloadColumns.CURRENTBYTES + " = ?"
|
"UPDATE " + DownloadColumns.TABLE_NAME + " SET " + DownloadColumns.CURRENTBYTES
|
||||||
+
|
+ " = ?" +
|
||||||
" WHERE " + DownloadColumns.INDEX + " = ?");
|
" WHERE " + DownloadColumns.INDEX + " = ?");
|
||||||
}
|
}
|
||||||
return mUpdateCurrentBytes;
|
return mUpdateCurrentBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DownloadsDB(Context paramContext) {
|
private DownloadsDB(Context paramContext) {
|
||||||
this.mHelper = new DownloadsContentDBHelper(paramContext);
|
this.mHelper = new DownloadsContentDBHelper(paramContext);
|
||||||
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
||||||
// Query for the version code, the row ID of the metadata (for future
|
// Query for the version code, the row ID of the metadata (for future
|
||||||
// updating) the status and the flags
|
// updating) the status and the flags
|
||||||
Cursor cur = sqldb.rawQuery("SELECT " +
|
Cursor cur = sqldb.rawQuery("SELECT " +
|
||||||
MetadataColumns.APKVERSION + "," +
|
MetadataColumns.APKVERSION + "," +
|
||||||
BaseColumns._ID + "," +
|
BaseColumns._ID + "," +
|
||||||
MetadataColumns.DOWNLOAD_STATUS + "," +
|
MetadataColumns.DOWNLOAD_STATUS + "," +
|
||||||
MetadataColumns.FLAGS +
|
MetadataColumns.FLAGS +
|
||||||
" FROM " + MetadataColumns.TABLE_NAME + " LIMIT 1",
|
" FROM "
|
||||||
null);
|
+ MetadataColumns.TABLE_NAME + " LIMIT 1", null);
|
||||||
if (null != cur && cur.moveToFirst()) {
|
if (null != cur && cur.moveToFirst()) {
|
||||||
mVersionCode = cur.getInt(0);
|
mVersionCode = cur.getInt(0);
|
||||||
mMetadataRowID = cur.getLong(1);
|
mMetadataRowID = cur.getLong(1);
|
||||||
mStatus = cur.getInt(2);
|
mStatus = cur.getInt(2);
|
||||||
mFlags = cur.getInt(3);
|
mFlags = cur.getInt(3);
|
||||||
cur.close();
|
cur.close();
|
||||||
}
|
}
|
||||||
mDownloadsDB = this;
|
mDownloadsDB = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DownloadInfo getDownloadInfoByFileName(String fileName) {
|
protected DownloadInfo getDownloadInfoByFileName(String fileName) {
|
||||||
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
||||||
Cursor itemcur = null;
|
Cursor itemcur = null;
|
||||||
try {
|
try {
|
||||||
itemcur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
|
itemcur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
|
||||||
DownloadColumns.FILENAME + " = ?",
|
DownloadColumns.FILENAME + " = ?",
|
||||||
new String[] {
|
new String[] {
|
||||||
fileName },
|
fileName
|
||||||
null, null, null);
|
}, null, null, null);
|
||||||
if (null != itemcur && itemcur.moveToFirst()) {
|
if (null != itemcur && itemcur.moveToFirst()) {
|
||||||
return getDownloadInfoFromCursor(itemcur);
|
return getDownloadInfoFromCursor(itemcur);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (null != itemcur)
|
if (null != itemcur)
|
||||||
itemcur.close();
|
itemcur.close();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getIDForDownloadInfo(final DownloadInfo di) {
|
public long getIDForDownloadInfo(final DownloadInfo di) {
|
||||||
return getIDByIndex(di.mIndex);
|
return getIDByIndex(di.mIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getIDByIndex(int index) {
|
public long getIDByIndex(int index) {
|
||||||
SQLiteStatement downloadByIndex = getDownloadByIndexStatement();
|
SQLiteStatement downloadByIndex = getDownloadByIndexStatement();
|
||||||
downloadByIndex.clearBindings();
|
downloadByIndex.clearBindings();
|
||||||
downloadByIndex.bindLong(1, index);
|
downloadByIndex.bindLong(1, index);
|
||||||
try {
|
try {
|
||||||
return downloadByIndex.simpleQueryForLong();
|
return downloadByIndex.simpleQueryForLong();
|
||||||
} catch (SQLiteDoneException e) {
|
} catch (SQLiteDoneException e) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDownloadCurrentBytes(final DownloadInfo di) {
|
public void updateDownloadCurrentBytes(final DownloadInfo di) {
|
||||||
SQLiteStatement downloadCurrentBytes = getUpdateCurrentBytesStatement();
|
SQLiteStatement downloadCurrentBytes = getUpdateCurrentBytesStatement();
|
||||||
downloadCurrentBytes.clearBindings();
|
downloadCurrentBytes.clearBindings();
|
||||||
downloadCurrentBytes.bindLong(1, di.mCurrentBytes);
|
downloadCurrentBytes.bindLong(1, di.mCurrentBytes);
|
||||||
downloadCurrentBytes.bindLong(2, di.mIndex);
|
downloadCurrentBytes.bindLong(2, di.mIndex);
|
||||||
downloadCurrentBytes.execute();
|
downloadCurrentBytes.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
this.mHelper.close();
|
this.mHelper.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class DownloadsContentDBHelper extends SQLiteOpenHelper {
|
protected static class DownloadsContentDBHelper extends SQLiteOpenHelper {
|
||||||
DownloadsContentDBHelper(Context paramContext) {
|
DownloadsContentDBHelper(Context paramContext) {
|
||||||
super(paramContext, DATABASE_NAME, null, DATABASE_VERSION);
|
super(paramContext, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createTableQueryFromArray(String paramString,
|
private String createTableQueryFromArray(String paramString,
|
||||||
String[][] paramArrayOfString) {
|
String[][] paramArrayOfString) {
|
||||||
StringBuilder localStringBuilder = new StringBuilder();
|
StringBuilder localStringBuilder = new StringBuilder();
|
||||||
localStringBuilder.append("CREATE TABLE ");
|
localStringBuilder.append("CREATE TABLE ");
|
||||||
localStringBuilder.append(paramString);
|
localStringBuilder.append(paramString);
|
||||||
localStringBuilder.append(" (");
|
localStringBuilder.append(" (");
|
||||||
int i = paramArrayOfString.length;
|
int i = paramArrayOfString.length;
|
||||||
for (int j = 0;; j++) {
|
for (int j = 0;; j++) {
|
||||||
if (j >= i) {
|
if (j >= i) {
|
||||||
localStringBuilder
|
localStringBuilder
|
||||||
.setLength(localStringBuilder.length() - 1);
|
.setLength(localStringBuilder.length() - 1);
|
||||||
localStringBuilder.append(");");
|
localStringBuilder.append(");");
|
||||||
return localStringBuilder.toString();
|
return localStringBuilder.toString();
|
||||||
}
|
}
|
||||||
String[] arrayOfString = paramArrayOfString[j];
|
String[] arrayOfString = paramArrayOfString[j];
|
||||||
localStringBuilder.append(' ');
|
localStringBuilder.append(' ');
|
||||||
localStringBuilder.append(arrayOfString[0]);
|
localStringBuilder.append(arrayOfString[0]);
|
||||||
localStringBuilder.append(' ');
|
localStringBuilder.append(' ');
|
||||||
localStringBuilder.append(arrayOfString[1]);
|
localStringBuilder.append(arrayOfString[1]);
|
||||||
localStringBuilder.append(',');
|
localStringBuilder.append(',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These two arrays must match and have the same order. For every Schema
|
* These two arrays must match and have the same order. For every Schema
|
||||||
* there must be a corresponding table name.
|
* there must be a corresponding table name.
|
||||||
*/
|
*/
|
||||||
static final private String[][][] sSchemas = {
|
static final private String[][][] sSchemas = {
|
||||||
DownloadColumns.SCHEMA, MetadataColumns.SCHEMA
|
DownloadColumns.SCHEMA, MetadataColumns.SCHEMA
|
||||||
};
|
};
|
||||||
|
|
||||||
static final private String[] sTables = {
|
static final private String[] sTables = {
|
||||||
DownloadColumns.TABLE_NAME, MetadataColumns.TABLE_NAME
|
DownloadColumns.TABLE_NAME, MetadataColumns.TABLE_NAME
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Goes through all of the tables in sTables and drops each table if it
|
* Goes through all of the tables in sTables and drops each table if it
|
||||||
* exists. Altered to no longer make use of reflection.
|
* exists. Altered to no longer make use of reflection.
|
||||||
*/
|
*/
|
||||||
private void dropTables(SQLiteDatabase paramSQLiteDatabase) {
|
private void dropTables(SQLiteDatabase paramSQLiteDatabase) {
|
||||||
for (String table : sTables) {
|
for (String table : sTables) {
|
||||||
try {
|
try {
|
||||||
paramSQLiteDatabase.execSQL("DROP TABLE IF EXISTS " + table);
|
paramSQLiteDatabase.execSQL("DROP TABLE IF EXISTS " + table);
|
||||||
} catch (Exception localException) {
|
} catch (Exception localException) {
|
||||||
localException.printStackTrace();
|
localException.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Goes through all of the tables in sTables and creates a database with
|
* Goes through all of the tables in sTables and creates a database with
|
||||||
* the corresponding schema described in sSchemas. Altered to no longer
|
* the corresponding schema described in sSchemas. Altered to no longer
|
||||||
* make use of reflection.
|
* make use of reflection.
|
||||||
*/
|
*/
|
||||||
public void onCreate(SQLiteDatabase paramSQLiteDatabase) {
|
public void onCreate(SQLiteDatabase paramSQLiteDatabase) {
|
||||||
int numSchemas = sSchemas.length;
|
int numSchemas = sSchemas.length;
|
||||||
for (int i = 0; i < numSchemas; i++) {
|
for (int i = 0; i < numSchemas; i++) {
|
||||||
try {
|
try {
|
||||||
String[][] schema = (String[][])sSchemas[i];
|
String[][] schema = (String[][]) sSchemas[i];
|
||||||
paramSQLiteDatabase.execSQL(createTableQueryFromArray(
|
paramSQLiteDatabase.execSQL(createTableQueryFromArray(
|
||||||
sTables[i], schema));
|
sTables[i], schema));
|
||||||
} catch (Exception localException) {
|
} catch (Exception localException) {
|
||||||
while (true)
|
while (true)
|
||||||
localException.printStackTrace();
|
localException.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpgrade(SQLiteDatabase paramSQLiteDatabase,
|
public void onUpgrade(SQLiteDatabase paramSQLiteDatabase,
|
||||||
int paramInt1, int paramInt2) {
|
int paramInt1, int paramInt2) {
|
||||||
Log.w(DownloadsContentDBHelper.class.getName(),
|
Log.w(DownloadsContentDBHelper.class.getName(),
|
||||||
"Upgrading database from version " + paramInt1 + " to " + paramInt2 + ", which will destroy all old data");
|
"Upgrading database from version " + paramInt1 + " to "
|
||||||
dropTables(paramSQLiteDatabase);
|
+ paramInt2 + ", which will destroy all old data");
|
||||||
onCreate(paramSQLiteDatabase);
|
dropTables(paramSQLiteDatabase);
|
||||||
}
|
onCreate(paramSQLiteDatabase);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class MetadataColumns implements BaseColumns {
|
public static class MetadataColumns implements BaseColumns {
|
||||||
public static final String APKVERSION = "APKVERSION";
|
public static final String APKVERSION = "APKVERSION";
|
||||||
public static final String DOWNLOAD_STATUS = "DOWNLOADSTATUS";
|
public static final String DOWNLOAD_STATUS = "DOWNLOADSTATUS";
|
||||||
public static final String FLAGS = "DOWNLOADFLAGS";
|
public static final String FLAGS = "DOWNLOADFLAGS";
|
||||||
|
|
||||||
public static final String[][] SCHEMA = {
|
public static final String[][] SCHEMA = {
|
||||||
{ BaseColumns._ID, "INTEGER PRIMARY KEY" },
|
{
|
||||||
{ APKVERSION, "INTEGER" }, { DOWNLOAD_STATUS, "INTEGER" },
|
BaseColumns._ID, "INTEGER PRIMARY KEY"
|
||||||
{ FLAGS, "INTEGER" }
|
},
|
||||||
};
|
{
|
||||||
public static final String TABLE_NAME = "MetadataColumns";
|
APKVERSION, "INTEGER"
|
||||||
public static final String _ID = "MetadataColumns._id";
|
}, {
|
||||||
}
|
DOWNLOAD_STATUS, "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FLAGS, "INTEGER"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final String TABLE_NAME = "MetadataColumns";
|
||||||
|
public static final String _ID = "MetadataColumns._id";
|
||||||
|
}
|
||||||
|
|
||||||
public static class DownloadColumns implements BaseColumns {
|
public static class DownloadColumns implements BaseColumns {
|
||||||
public static final String INDEX = "FILEIDX";
|
public static final String INDEX = "FILEIDX";
|
||||||
public static final String URI = "URI";
|
public static final String URI = "URI";
|
||||||
public static final String FILENAME = "FN";
|
public static final String FILENAME = "FN";
|
||||||
public static final String ETAG = "ETAG";
|
public static final String ETAG = "ETAG";
|
||||||
|
|
||||||
public static final String TOTALBYTES = "TOTALBYTES";
|
public static final String TOTALBYTES = "TOTALBYTES";
|
||||||
public static final String CURRENTBYTES = "CURRENTBYTES";
|
public static final String CURRENTBYTES = "CURRENTBYTES";
|
||||||
public static final String LASTMOD = "LASTMOD";
|
public static final String LASTMOD = "LASTMOD";
|
||||||
|
|
||||||
public static final String STATUS = "STATUS";
|
public static final String STATUS = "STATUS";
|
||||||
public static final String CONTROL = "CONTROL";
|
public static final String CONTROL = "CONTROL";
|
||||||
public static final String NUM_FAILED = "FAILCOUNT";
|
public static final String NUM_FAILED = "FAILCOUNT";
|
||||||
public static final String RETRY_AFTER = "RETRYAFTER";
|
public static final String RETRY_AFTER = "RETRYAFTER";
|
||||||
public static final String REDIRECT_COUNT = "REDIRECTCOUNT";
|
public static final String REDIRECT_COUNT = "REDIRECTCOUNT";
|
||||||
|
|
||||||
public static final String[][] SCHEMA = {
|
public static final String[][] SCHEMA = {
|
||||||
{ BaseColumns._ID, "INTEGER PRIMARY KEY" },
|
{
|
||||||
{ INDEX, "INTEGER UNIQUE" }, { URI, "TEXT" },
|
BaseColumns._ID, "INTEGER PRIMARY KEY"
|
||||||
{ FILENAME, "TEXT UNIQUE" }, { ETAG, "TEXT" },
|
},
|
||||||
{ TOTALBYTES, "INTEGER" }, { CURRENTBYTES, "INTEGER" },
|
{
|
||||||
{ LASTMOD, "INTEGER" }, { STATUS, "INTEGER" },
|
INDEX, "INTEGER UNIQUE"
|
||||||
{ CONTROL, "INTEGER" }, { NUM_FAILED, "INTEGER" },
|
}, {
|
||||||
{ RETRY_AFTER, "INTEGER" }, { REDIRECT_COUNT, "INTEGER" }
|
URI, "TEXT"
|
||||||
};
|
},
|
||||||
public static final String TABLE_NAME = "DownloadColumns";
|
{
|
||||||
public static final String _ID = "DownloadColumns._id";
|
FILENAME, "TEXT UNIQUE"
|
||||||
}
|
}, {
|
||||||
|
ETAG, "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TOTALBYTES, "INTEGER"
|
||||||
|
}, {
|
||||||
|
CURRENTBYTES, "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LASTMOD, "INTEGER"
|
||||||
|
}, {
|
||||||
|
STATUS, "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CONTROL, "INTEGER"
|
||||||
|
}, {
|
||||||
|
NUM_FAILED, "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RETRY_AFTER, "INTEGER"
|
||||||
|
}, {
|
||||||
|
REDIRECT_COUNT, "INTEGER"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final String TABLE_NAME = "DownloadColumns";
|
||||||
|
public static final String _ID = "DownloadColumns._id";
|
||||||
|
}
|
||||||
|
|
||||||
private static final String[] DC_PROJECTION = {
|
private static final String[] DC_PROJECTION = {
|
||||||
DownloadColumns.FILENAME,
|
DownloadColumns.FILENAME,
|
||||||
DownloadColumns.URI, DownloadColumns.ETAG,
|
DownloadColumns.URI, DownloadColumns.ETAG,
|
||||||
DownloadColumns.TOTALBYTES, DownloadColumns.CURRENTBYTES,
|
DownloadColumns.TOTALBYTES, DownloadColumns.CURRENTBYTES,
|
||||||
DownloadColumns.LASTMOD, DownloadColumns.STATUS,
|
DownloadColumns.LASTMOD, DownloadColumns.STATUS,
|
||||||
DownloadColumns.CONTROL, DownloadColumns.NUM_FAILED,
|
DownloadColumns.CONTROL, DownloadColumns.NUM_FAILED,
|
||||||
DownloadColumns.RETRY_AFTER, DownloadColumns.REDIRECT_COUNT,
|
DownloadColumns.RETRY_AFTER, DownloadColumns.REDIRECT_COUNT,
|
||||||
DownloadColumns.INDEX
|
DownloadColumns.INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int FILENAME_IDX = 0;
|
private static final int FILENAME_IDX = 0;
|
||||||
private static final int URI_IDX = 1;
|
private static final int URI_IDX = 1;
|
||||||
private static final int ETAG_IDX = 2;
|
private static final int ETAG_IDX = 2;
|
||||||
private static final int TOTALBYTES_IDX = 3;
|
private static final int TOTALBYTES_IDX = 3;
|
||||||
private static final int CURRENTBYTES_IDX = 4;
|
private static final int CURRENTBYTES_IDX = 4;
|
||||||
private static final int LASTMOD_IDX = 5;
|
private static final int LASTMOD_IDX = 5;
|
||||||
private static final int STATUS_IDX = 6;
|
private static final int STATUS_IDX = 6;
|
||||||
private static final int CONTROL_IDX = 7;
|
private static final int CONTROL_IDX = 7;
|
||||||
private static final int NUM_FAILED_IDX = 8;
|
private static final int NUM_FAILED_IDX = 8;
|
||||||
private static final int RETRY_AFTER_IDX = 9;
|
private static final int RETRY_AFTER_IDX = 9;
|
||||||
private static final int REDIRECT_COUNT_IDX = 10;
|
private static final int REDIRECT_COUNT_IDX = 10;
|
||||||
private static final int INDEX_IDX = 11;
|
private static final int INDEX_IDX = 11;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will add a new file to the database if it does not exist.
|
* This function will add a new file to the database if it does not exist.
|
||||||
*
|
*
|
||||||
* @param di DownloadInfo that we wish to store
|
* @param di DownloadInfo that we wish to store
|
||||||
* @return the row id of the record to be updated/inserted, or -1
|
* @return the row id of the record to be updated/inserted, or -1
|
||||||
*/
|
*/
|
||||||
public boolean updateDownload(DownloadInfo di) {
|
public boolean updateDownload(DownloadInfo di) {
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put(DownloadColumns.INDEX, di.mIndex);
|
cv.put(DownloadColumns.INDEX, di.mIndex);
|
||||||
cv.put(DownloadColumns.FILENAME, di.mFileName);
|
cv.put(DownloadColumns.FILENAME, di.mFileName);
|
||||||
cv.put(DownloadColumns.URI, di.mUri);
|
cv.put(DownloadColumns.URI, di.mUri);
|
||||||
cv.put(DownloadColumns.ETAG, di.mETag);
|
cv.put(DownloadColumns.ETAG, di.mETag);
|
||||||
cv.put(DownloadColumns.TOTALBYTES, di.mTotalBytes);
|
cv.put(DownloadColumns.TOTALBYTES, di.mTotalBytes);
|
||||||
cv.put(DownloadColumns.CURRENTBYTES, di.mCurrentBytes);
|
cv.put(DownloadColumns.CURRENTBYTES, di.mCurrentBytes);
|
||||||
cv.put(DownloadColumns.LASTMOD, di.mLastMod);
|
cv.put(DownloadColumns.LASTMOD, di.mLastMod);
|
||||||
cv.put(DownloadColumns.STATUS, di.mStatus);
|
cv.put(DownloadColumns.STATUS, di.mStatus);
|
||||||
cv.put(DownloadColumns.CONTROL, di.mControl);
|
cv.put(DownloadColumns.CONTROL, di.mControl);
|
||||||
cv.put(DownloadColumns.NUM_FAILED, di.mNumFailed);
|
cv.put(DownloadColumns.NUM_FAILED, di.mNumFailed);
|
||||||
cv.put(DownloadColumns.RETRY_AFTER, di.mRetryAfter);
|
cv.put(DownloadColumns.RETRY_AFTER, di.mRetryAfter);
|
||||||
cv.put(DownloadColumns.REDIRECT_COUNT, di.mRedirectCount);
|
cv.put(DownloadColumns.REDIRECT_COUNT, di.mRedirectCount);
|
||||||
return updateDownload(di, cv);
|
return updateDownload(di, cv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateDownload(DownloadInfo di, ContentValues cv) {
|
public boolean updateDownload(DownloadInfo di, ContentValues cv) {
|
||||||
long id = di == null ? -1 : getIDForDownloadInfo(di);
|
long id = di == null ? -1 : getIDForDownloadInfo(di);
|
||||||
try {
|
try {
|
||||||
final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
|
||||||
if (id != -1) {
|
if (id != -1) {
|
||||||
if (1 != sqldb.update(DownloadColumns.TABLE_NAME,
|
if (1 != sqldb.update(DownloadColumns.TABLE_NAME,
|
||||||
cv, DownloadColumns._ID + " = " + id, null)) {
|
cv, DownloadColumns._ID + " = " + id, null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return -1 != sqldb.insert(DownloadColumns.TABLE_NAME,
|
return -1 != sqldb.insert(DownloadColumns.TABLE_NAME,
|
||||||
DownloadColumns.URI, cv);
|
DownloadColumns.URI, cv);
|
||||||
}
|
}
|
||||||
} catch (android.database.sqlite.SQLiteException ex) {
|
} catch (android.database.sqlite.SQLiteException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLastCheckedVersionCode() {
|
public int getLastCheckedVersionCode() {
|
||||||
return mVersionCode;
|
return mVersionCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDownloadRequired() {
|
public boolean isDownloadRequired() {
|
||||||
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
||||||
Cursor cur = sqldb.rawQuery("SELECT Count(*) FROM " + DownloadColumns.TABLE_NAME + " WHERE " + DownloadColumns.STATUS + " <> 0", null);
|
Cursor cur = sqldb.rawQuery("SELECT Count(*) FROM "
|
||||||
try {
|
+ DownloadColumns.TABLE_NAME + " WHERE "
|
||||||
if (null != cur && cur.moveToFirst()) {
|
+ DownloadColumns.STATUS + " <> 0", null);
|
||||||
return 0 == cur.getInt(0);
|
try {
|
||||||
}
|
if (null != cur && cur.moveToFirst()) {
|
||||||
} finally {
|
return 0 == cur.getInt(0);
|
||||||
if (null != cur)
|
}
|
||||||
cur.close();
|
} finally {
|
||||||
}
|
if (null != cur)
|
||||||
return true;
|
cur.close();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public int getFlags() {
|
public int getFlags() {
|
||||||
return mFlags;
|
return mFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateFlags(int flags) {
|
public boolean updateFlags(int flags) {
|
||||||
if (mFlags != flags) {
|
if (mFlags != flags) {
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put(MetadataColumns.FLAGS, flags);
|
cv.put(MetadataColumns.FLAGS, flags);
|
||||||
if (updateMetadata(cv)) {
|
if (updateMetadata(cv)) {
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public boolean updateStatus(int status) {
|
public boolean updateStatus(int status) {
|
||||||
if (mStatus != status) {
|
if (mStatus != status) {
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put(MetadataColumns.DOWNLOAD_STATUS, status);
|
cv.put(MetadataColumns.DOWNLOAD_STATUS, status);
|
||||||
if (updateMetadata(cv)) {
|
if (updateMetadata(cv)) {
|
||||||
mStatus = status;
|
mStatus = status;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public boolean updateMetadata(ContentValues cv) {
|
public boolean updateMetadata(ContentValues cv) {
|
||||||
final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getWritableDatabase();
|
||||||
if (-1 == this.mMetadataRowID) {
|
if (-1 == this.mMetadataRowID) {
|
||||||
long newID = sqldb.insert(MetadataColumns.TABLE_NAME,
|
long newID = sqldb.insert(MetadataColumns.TABLE_NAME,
|
||||||
MetadataColumns.APKVERSION, cv);
|
MetadataColumns.APKVERSION, cv);
|
||||||
if (-1 == newID)
|
if (-1 == newID)
|
||||||
return false;
|
return false;
|
||||||
mMetadataRowID = newID;
|
mMetadataRowID = newID;
|
||||||
} else {
|
} else {
|
||||||
if (0 == sqldb.update(MetadataColumns.TABLE_NAME, cv,
|
if (0 == sqldb.update(MetadataColumns.TABLE_NAME, cv,
|
||||||
BaseColumns._ID + " = " + mMetadataRowID, null))
|
BaseColumns._ID + " = " + mMetadataRowID, null))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateMetadata(int apkVersion, int downloadStatus) {
|
public boolean updateMetadata(int apkVersion, int downloadStatus) {
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put(MetadataColumns.APKVERSION, apkVersion);
|
cv.put(MetadataColumns.APKVERSION, apkVersion);
|
||||||
cv.put(MetadataColumns.DOWNLOAD_STATUS, downloadStatus);
|
cv.put(MetadataColumns.DOWNLOAD_STATUS, downloadStatus);
|
||||||
if (updateMetadata(cv)) {
|
if (updateMetadata(cv)) {
|
||||||
mVersionCode = apkVersion;
|
mVersionCode = apkVersion;
|
||||||
mStatus = downloadStatus;
|
mStatus = downloadStatus;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public boolean updateFromDb(DownloadInfo di) {
|
public boolean updateFromDb(DownloadInfo di) {
|
||||||
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
||||||
Cursor cur = null;
|
Cursor cur = null;
|
||||||
try {
|
try {
|
||||||
cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
|
cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION,
|
||||||
DownloadColumns.FILENAME + "= ?",
|
DownloadColumns.FILENAME + "= ?",
|
||||||
new String[] {
|
new String[] {
|
||||||
di.mFileName },
|
di.mFileName
|
||||||
null, null, null);
|
}, null, null, null);
|
||||||
if (null != cur && cur.moveToFirst()) {
|
if (null != cur && cur.moveToFirst()) {
|
||||||
setDownloadInfoFromCursor(di, cur);
|
setDownloadInfoFromCursor(di, cur);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (null != cur) {
|
if (null != cur) {
|
||||||
cur.close();
|
cur.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDownloadInfoFromCursor(DownloadInfo di, Cursor cur) {
|
public void setDownloadInfoFromCursor(DownloadInfo di, Cursor cur) {
|
||||||
di.mUri = cur.getString(URI_IDX);
|
di.mUri = cur.getString(URI_IDX);
|
||||||
di.mETag = cur.getString(ETAG_IDX);
|
di.mETag = cur.getString(ETAG_IDX);
|
||||||
di.mTotalBytes = cur.getLong(TOTALBYTES_IDX);
|
di.mTotalBytes = cur.getLong(TOTALBYTES_IDX);
|
||||||
di.mCurrentBytes = cur.getLong(CURRENTBYTES_IDX);
|
di.mCurrentBytes = cur.getLong(CURRENTBYTES_IDX);
|
||||||
di.mLastMod = cur.getLong(LASTMOD_IDX);
|
di.mLastMod = cur.getLong(LASTMOD_IDX);
|
||||||
di.mStatus = cur.getInt(STATUS_IDX);
|
di.mStatus = cur.getInt(STATUS_IDX);
|
||||||
di.mControl = cur.getInt(CONTROL_IDX);
|
di.mControl = cur.getInt(CONTROL_IDX);
|
||||||
di.mNumFailed = cur.getInt(NUM_FAILED_IDX);
|
di.mNumFailed = cur.getInt(NUM_FAILED_IDX);
|
||||||
di.mRetryAfter = cur.getInt(RETRY_AFTER_IDX);
|
di.mRetryAfter = cur.getInt(RETRY_AFTER_IDX);
|
||||||
di.mRedirectCount = cur.getInt(REDIRECT_COUNT_IDX);
|
di.mRedirectCount = cur.getInt(REDIRECT_COUNT_IDX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadInfo getDownloadInfoFromCursor(Cursor cur) {
|
public DownloadInfo getDownloadInfoFromCursor(Cursor cur) {
|
||||||
DownloadInfo di = new DownloadInfo(cur.getInt(INDEX_IDX),
|
DownloadInfo di = new DownloadInfo(cur.getInt(INDEX_IDX),
|
||||||
cur.getString(FILENAME_IDX), this.getClass().getPackage().getName());
|
cur.getString(FILENAME_IDX), this.getClass().getPackage()
|
||||||
setDownloadInfoFromCursor(di, cur);
|
.getName());
|
||||||
return di;
|
setDownloadInfoFromCursor(di, cur);
|
||||||
}
|
return di;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DownloadInfo[] getDownloads() {
|
||||||
|
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
||||||
|
Cursor cur = null;
|
||||||
|
try {
|
||||||
|
cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION, null,
|
||||||
|
null, null, null, null);
|
||||||
|
if (null != cur && cur.moveToFirst()) {
|
||||||
|
DownloadInfo[] retInfos = new DownloadInfo[cur.getCount()];
|
||||||
|
int idx = 0;
|
||||||
|
do {
|
||||||
|
DownloadInfo di = getDownloadInfoFromCursor(cur);
|
||||||
|
retInfos[idx++] = di;
|
||||||
|
} while (cur.moveToNext());
|
||||||
|
return retInfos;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (null != cur) {
|
||||||
|
cur.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DownloadInfo[] getDownloads() {
|
|
||||||
final SQLiteDatabase sqldb = mHelper.getReadableDatabase();
|
|
||||||
Cursor cur = null;
|
|
||||||
try {
|
|
||||||
cur = sqldb.query(DownloadColumns.TABLE_NAME, DC_PROJECTION, null,
|
|
||||||
null, null, null, null);
|
|
||||||
if (null != cur && cur.moveToFirst()) {
|
|
||||||
DownloadInfo[] retInfos = new DownloadInfo[cur.getCount()];
|
|
||||||
int idx = 0;
|
|
||||||
do {
|
|
||||||
DownloadInfo di = getDownloadInfoFromCursor(cur);
|
|
||||||
retInfos[idx++] = di;
|
|
||||||
} while (cur.moveToNext());
|
|
||||||
return retInfos;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
if (null != cur) {
|
|
||||||
cur.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import java.util.regex.Pattern;
|
||||||
*/
|
*/
|
||||||
public final class HttpDateTime {
|
public final class HttpDateTime {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Regular expression for parsing HTTP-date. Wdy, DD Mon YYYY HH:MM:SS GMT
|
* Regular expression for parsing HTTP-date. Wdy, DD Mon YYYY HH:MM:SS GMT
|
||||||
* RFC 822, updated by RFC 1123 Weekday, DD-Mon-YY HH:MM:SS GMT RFC 850,
|
* RFC 822, updated by RFC 1123 Weekday, DD-Mon-YY HH:MM:SS GMT RFC 850,
|
||||||
* obsoleted by RFC 1036 Wdy Mon DD HH:MM:SS YYYY ANSI C's asctime() format
|
* obsoleted by RFC 1036 Wdy Mon DD HH:MM:SS YYYY ANSI C's asctime() format
|
||||||
|
|
@ -37,155 +37,164 @@ public final class HttpDateTime {
|
||||||
* (SP)D HH:MM:SS YYYY Wdy Mon DD HH:MM:SS YYYY GMT HH can be H if the first
|
* (SP)D HH:MM:SS YYYY Wdy Mon DD HH:MM:SS YYYY GMT HH can be H if the first
|
||||||
* digit is zero. Mon can be the full name of the month.
|
* digit is zero. Mon can be the full name of the month.
|
||||||
*/
|
*/
|
||||||
private static final String HTTP_DATE_RFC_REGEXP =
|
private static final String HTTP_DATE_RFC_REGEXP =
|
||||||
"([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
|
"([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
|
||||||
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
|
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
|
||||||
|
|
||||||
private static final String HTTP_DATE_ANSIC_REGEXP =
|
private static final String HTTP_DATE_ANSIC_REGEXP =
|
||||||
"[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
|
"[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
|
||||||
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
|
+ "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The compiled version of the HTTP-date regular expressions.
|
* The compiled version of the HTTP-date regular expressions.
|
||||||
*/
|
*/
|
||||||
private static final Pattern HTTP_DATE_RFC_PATTERN =
|
private static final Pattern HTTP_DATE_RFC_PATTERN =
|
||||||
Pattern.compile(HTTP_DATE_RFC_REGEXP);
|
Pattern.compile(HTTP_DATE_RFC_REGEXP);
|
||||||
private static final Pattern HTTP_DATE_ANSIC_PATTERN =
|
private static final Pattern HTTP_DATE_ANSIC_PATTERN =
|
||||||
Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
|
Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
|
||||||
|
|
||||||
private static class TimeOfDay {
|
private static class TimeOfDay {
|
||||||
TimeOfDay(int h, int m, int s) {
|
TimeOfDay(int h, int m, int s) {
|
||||||
this.hour = h;
|
this.hour = h;
|
||||||
this.minute = m;
|
this.minute = m;
|
||||||
this.second = s;
|
this.second = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hour;
|
int hour;
|
||||||
int minute;
|
int minute;
|
||||||
int second;
|
int second;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long parse(String timeString)
|
public static long parse(String timeString)
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
int date = 1;
|
int date = 1;
|
||||||
int month = Calendar.JANUARY;
|
int month = Calendar.JANUARY;
|
||||||
int year = 1970;
|
int year = 1970;
|
||||||
TimeOfDay timeOfDay;
|
TimeOfDay timeOfDay;
|
||||||
|
|
||||||
Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
|
Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
|
||||||
if (rfcMatcher.find()) {
|
if (rfcMatcher.find()) {
|
||||||
date = getDate(rfcMatcher.group(1));
|
date = getDate(rfcMatcher.group(1));
|
||||||
month = getMonth(rfcMatcher.group(2));
|
month = getMonth(rfcMatcher.group(2));
|
||||||
year = getYear(rfcMatcher.group(3));
|
year = getYear(rfcMatcher.group(3));
|
||||||
timeOfDay = getTime(rfcMatcher.group(4));
|
timeOfDay = getTime(rfcMatcher.group(4));
|
||||||
} else {
|
} else {
|
||||||
Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString);
|
Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString);
|
||||||
if (ansicMatcher.find()) {
|
if (ansicMatcher.find()) {
|
||||||
month = getMonth(ansicMatcher.group(1));
|
month = getMonth(ansicMatcher.group(1));
|
||||||
date = getDate(ansicMatcher.group(2));
|
date = getDate(ansicMatcher.group(2));
|
||||||
timeOfDay = getTime(ansicMatcher.group(3));
|
timeOfDay = getTime(ansicMatcher.group(3));
|
||||||
year = getYear(ansicMatcher.group(4));
|
year = getYear(ansicMatcher.group(4));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Y2038 BUG!
|
// FIXME: Y2038 BUG!
|
||||||
if (year >= 2038) {
|
if (year >= 2038) {
|
||||||
year = 2038;
|
year = 2038;
|
||||||
month = Calendar.JANUARY;
|
month = Calendar.JANUARY;
|
||||||
date = 1;
|
date = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time time = new Time(Time.TIMEZONE_UTC);
|
Time time = new Time(Time.TIMEZONE_UTC);
|
||||||
time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date,
|
time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date,
|
||||||
month, year);
|
month, year);
|
||||||
return time.toMillis(false /* use isDst */);
|
return time.toMillis(false /* use isDst */);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getDate(String dateString) {
|
private static int getDate(String dateString) {
|
||||||
if (dateString.length() == 2) {
|
if (dateString.length() == 2) {
|
||||||
return (dateString.charAt(0) - '0') * 10 + (dateString.charAt(1) - '0');
|
return (dateString.charAt(0) - '0') * 10
|
||||||
} else {
|
+ (dateString.charAt(1) - '0');
|
||||||
return (dateString.charAt(0) - '0');
|
} else {
|
||||||
}
|
return (dateString.charAt(0) - '0');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* jan = 9 + 0 + 13 = 22 feb = 5 + 4 + 1 = 10 mar = 12 + 0 + 17 = 29 apr = 0
|
* jan = 9 + 0 + 13 = 22 feb = 5 + 4 + 1 = 10 mar = 12 + 0 + 17 = 29 apr = 0
|
||||||
* + 15 + 17 = 32 may = 12 + 0 + 24 = 36 jun = 9 + 20 + 13 = 42 jul = 9 + 20
|
* + 15 + 17 = 32 may = 12 + 0 + 24 = 36 jun = 9 + 20 + 13 = 42 jul = 9 + 20
|
||||||
* + 11 = 40 aug = 0 + 20 + 6 = 26 sep = 18 + 4 + 15 = 37 oct = 14 + 2 + 19
|
* + 11 = 40 aug = 0 + 20 + 6 = 26 sep = 18 + 4 + 15 = 37 oct = 14 + 2 + 19
|
||||||
* = 35 nov = 13 + 14 + 21 = 48 dec = 3 + 4 + 2 = 9
|
* = 35 nov = 13 + 14 + 21 = 48 dec = 3 + 4 + 2 = 9
|
||||||
*/
|
*/
|
||||||
private static int getMonth(String monthString) {
|
private static int getMonth(String monthString) {
|
||||||
int hash = Character.toLowerCase(monthString.charAt(0)) +
|
int hash = Character.toLowerCase(monthString.charAt(0)) +
|
||||||
Character.toLowerCase(monthString.charAt(1)) +
|
Character.toLowerCase(monthString.charAt(1)) +
|
||||||
Character.toLowerCase(monthString.charAt(2)) - 3 * 'a';
|
Character.toLowerCase(monthString.charAt(2)) - 3 * 'a';
|
||||||
switch (hash) {
|
switch (hash) {
|
||||||
case 22:
|
case 22:
|
||||||
return Calendar.JANUARY;
|
return Calendar.JANUARY;
|
||||||
case 10:
|
case 10:
|
||||||
return Calendar.FEBRUARY;
|
return Calendar.FEBRUARY;
|
||||||
case 29:
|
case 29:
|
||||||
return Calendar.MARCH;
|
return Calendar.MARCH;
|
||||||
case 32:
|
case 32:
|
||||||
return Calendar.APRIL;
|
return Calendar.APRIL;
|
||||||
case 36:
|
case 36:
|
||||||
return Calendar.MAY;
|
return Calendar.MAY;
|
||||||
case 42:
|
case 42:
|
||||||
return Calendar.JUNE;
|
return Calendar.JUNE;
|
||||||
case 40:
|
case 40:
|
||||||
return Calendar.JULY;
|
return Calendar.JULY;
|
||||||
case 26:
|
case 26:
|
||||||
return Calendar.AUGUST;
|
return Calendar.AUGUST;
|
||||||
case 37:
|
case 37:
|
||||||
return Calendar.SEPTEMBER;
|
return Calendar.SEPTEMBER;
|
||||||
case 35:
|
case 35:
|
||||||
return Calendar.OCTOBER;
|
return Calendar.OCTOBER;
|
||||||
case 48:
|
case 48:
|
||||||
return Calendar.NOVEMBER;
|
return Calendar.NOVEMBER;
|
||||||
case 9:
|
case 9:
|
||||||
return Calendar.DECEMBER;
|
return Calendar.DECEMBER;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getYear(String yearString) {
|
private static int getYear(String yearString) {
|
||||||
if (yearString.length() == 2) {
|
if (yearString.length() == 2) {
|
||||||
int year = (yearString.charAt(0) - '0') * 10 + (yearString.charAt(1) - '0');
|
int year = (yearString.charAt(0) - '0') * 10
|
||||||
if (year >= 70) {
|
+ (yearString.charAt(1) - '0');
|
||||||
return year + 1900;
|
if (year >= 70) {
|
||||||
} else {
|
return year + 1900;
|
||||||
return year + 2000;
|
} else {
|
||||||
}
|
return year + 2000;
|
||||||
} else if (yearString.length() == 3) {
|
}
|
||||||
// According to RFC 2822, three digit years should be added to 1900.
|
} else if (yearString.length() == 3) {
|
||||||
int year = (yearString.charAt(0) - '0') * 100 + (yearString.charAt(1) - '0') * 10 + (yearString.charAt(2) - '0');
|
// According to RFC 2822, three digit years should be added to 1900.
|
||||||
return year + 1900;
|
int year = (yearString.charAt(0) - '0') * 100
|
||||||
} else if (yearString.length() == 4) {
|
+ (yearString.charAt(1) - '0') * 10
|
||||||
return (yearString.charAt(0) - '0') * 1000 + (yearString.charAt(1) - '0') * 100 + (yearString.charAt(2) - '0') * 10 + (yearString.charAt(3) - '0');
|
+ (yearString.charAt(2) - '0');
|
||||||
} else {
|
return year + 1900;
|
||||||
return 1970;
|
} else if (yearString.length() == 4) {
|
||||||
}
|
return (yearString.charAt(0) - '0') * 1000
|
||||||
}
|
+ (yearString.charAt(1) - '0') * 100
|
||||||
|
+ (yearString.charAt(2) - '0') * 10
|
||||||
|
+ (yearString.charAt(3) - '0');
|
||||||
|
} else {
|
||||||
|
return 1970;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static TimeOfDay getTime(String timeString) {
|
private static TimeOfDay getTime(String timeString) {
|
||||||
// HH might be H
|
// HH might be H
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int hour = timeString.charAt(i++) - '0';
|
int hour = timeString.charAt(i++) - '0';
|
||||||
if (timeString.charAt(i) != ':')
|
if (timeString.charAt(i) != ':')
|
||||||
hour = hour * 10 + (timeString.charAt(i++) - '0');
|
hour = hour * 10 + (timeString.charAt(i++) - '0');
|
||||||
// Skip ':'
|
// Skip ':'
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
int minute = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0');
|
int minute = (timeString.charAt(i++) - '0') * 10
|
||||||
// Skip ':'
|
+ (timeString.charAt(i++) - '0');
|
||||||
i++;
|
// Skip ':'
|
||||||
|
i++;
|
||||||
|
|
||||||
int second = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0');
|
int second = (timeString.charAt(i++) - '0') * 10
|
||||||
|
+ (timeString.charAt(i++) - '0');
|
||||||
|
|
||||||
return new TimeOfDay(hour, minute, second);
|
return new TimeOfDay(hour, minute, second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue