mirror of
https://github.com/QuasarApp/QtAndroidTools.git
synced 2025-05-18 06:49:33 +00:00
Changed java libraries for apk expensions file download with non official better-apk-expansion version 5.0.3 and better-licensing version 1.5.0
This commit is contained in:
parent
12e61b761b
commit
b926f059f4
@ -1,29 +1,51 @@
|
||||
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Fabio Falsini <falsinsoft@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <QGuiApplication>
|
||||
#include "QtAndroidApkExpansionFiles.h"
|
||||
#include "QAndroidApkExpansionFiles.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_com_google_android_vending_expansion_downloader_Helpers_getDownloaderStringResourceFromState(JNIEnv *env, jobject thiz, jint state)
|
||||
JNIEXPORT jstring JNICALL Java_com_falsinsoft_qtandroidtools_ApkExpansionDownloader_getString(JNIEnv *env, jobject thiz, jint stringID)
|
||||
{
|
||||
QtAndroidApkExpansionFiles *pInstance = QtAndroidApkExpansionFiles::instance();
|
||||
QString TextInfo;
|
||||
QAndroidApkExpansionFiles *pInstance = QAndroidApkExpansionFiles::instance();
|
||||
QString TextString;
|
||||
|
||||
Q_UNUSED(thiz)
|
||||
|
||||
if(pInstance != nullptr)
|
||||
{
|
||||
TextInfo = pInstance->stateTextInfo(state);
|
||||
TextString = pInstance->getString(stringID);
|
||||
}
|
||||
|
||||
return env->NewString(TextInfo.utf16(), TextInfo.length());
|
||||
return env->NewString(TextString.utf16(), TextString.length());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_falsinsoft_QtAndroidTools_ApkExpansionDownloader_downloadStateChanged(JNIEnv *env, jobject thiz, jint newState)
|
||||
JNIEXPORT void JNICALL Java_com_falsinsoft_qtandroidtools_ApkExpansionDownloader_downloadStateChanged(JNIEnv *env, jobject thiz, jint newState)
|
||||
{
|
||||
QtAndroidApkExpansionFiles *pInstance = QtAndroidApkExpansionFiles::instance();
|
||||
QAndroidApkExpansionFiles *pInstance = QAndroidApkExpansionFiles::instance();
|
||||
|
||||
Q_UNUSED(env)
|
||||
Q_UNUSED(thiz)
|
||||
@ -34,9 +56,9 @@ JNIEXPORT void JNICALL Java_com_falsinsoft_QtAndroidTools_ApkExpansionDownloader
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_falsinsoft_QtAndroidTools_ApkExpansionDownloader_downloadProgress(JNIEnv *env, jobject thiz, jlong overallTotal, jlong overallProgress, jlong timeRemaining, jfloat currentSpeed)
|
||||
JNIEXPORT void JNICALL Java_com_falsinsoft_qtandroidtools_ApkExpansionDownloader_downloadProgress(JNIEnv *env, jobject thiz, jlong overallTotal, jlong overallProgress, jlong timeRemaining, jfloat currentSpeed)
|
||||
{
|
||||
QtAndroidApkExpansionFiles *pInstance = QtAndroidApkExpansionFiles::instance();
|
||||
QAndroidApkExpansionFiles *pInstance = QAndroidApkExpansionFiles::instance();
|
||||
|
||||
Q_UNUSED(env)
|
||||
Q_UNUSED(thiz)
|
||||
@ -51,52 +73,62 @@ JNIEXPORT void JNICALL Java_com_falsinsoft_QtAndroidTools_ApkExpansionDownloader
|
||||
}
|
||||
#endif
|
||||
|
||||
QtAndroidApkExpansionFiles *QtAndroidApkExpansionFiles::m_pInstance = nullptr;
|
||||
QAndroidApkExpansionFiles *QAndroidApkExpansionFiles::m_pInstance = nullptr;
|
||||
|
||||
QtAndroidApkExpansionFiles::QtAndroidApkExpansionFiles() : m_JavaApkExpansionDownloader("com/falsinsoft/QtAndroidTools/ApkExpansionDownloader",
|
||||
QAndroidApkExpansionFiles::QAndroidApkExpansionFiles() : m_JavaApkExpansionDownloader("com/falsinsoft/qtandroidtools/ApkExpansionDownloader",
|
||||
"(Landroid/app/Activity;)V",
|
||||
QtAndroid::androidActivity().object<jobject>())
|
||||
{
|
||||
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QtAndroidApkExpansionFiles::ApplicationStateChanged);
|
||||
qRegisterMetaType<ExpansionFileInfo>();
|
||||
m_pInstance = this;
|
||||
|
||||
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidApkExpansionFiles::ApplicationStateChanged);
|
||||
qRegisterMetaType<ExpansionFileInfo>();
|
||||
SetNewAppState(APP_STATE_CREATE);
|
||||
}
|
||||
|
||||
QObject* QtAndroidApkExpansionFiles::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
QAndroidApkExpansionFiles::~QAndroidApkExpansionFiles()
|
||||
{
|
||||
SetNewAppState(APP_STATE_DESTROY);
|
||||
}
|
||||
|
||||
QObject* QAndroidApkExpansionFiles::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine);
|
||||
Q_UNUSED(scriptEngine);
|
||||
|
||||
return new QtAndroidApkExpansionFiles();
|
||||
return new QAndroidApkExpansionFiles();
|
||||
}
|
||||
|
||||
QtAndroidApkExpansionFiles* QtAndroidApkExpansionFiles::instance()
|
||||
QAndroidApkExpansionFiles* QAndroidApkExpansionFiles::instance()
|
||||
{
|
||||
return m_pInstance;
|
||||
}
|
||||
|
||||
void QtAndroidApkExpansionFiles::ApplicationStateChanged(Qt::ApplicationState State)
|
||||
void QAndroidApkExpansionFiles::ApplicationStateChanged(Qt::ApplicationState State)
|
||||
{
|
||||
const bool StubConnect = (State == Qt::ApplicationActive) ? true : false;
|
||||
SetNewAppState((State == Qt::ApplicationActive) ? APP_STATE_START : APP_STATE_STOP);
|
||||
}
|
||||
|
||||
void QAndroidApkExpansionFiles::SetNewAppState(APP_STATE NewState)
|
||||
{
|
||||
if(m_JavaApkExpansionDownloader.isValid())
|
||||
{
|
||||
m_JavaApkExpansionDownloader.callMethod<void>("enableClientStubConnection",
|
||||
"(Z)V",
|
||||
StubConnect
|
||||
m_JavaApkExpansionDownloader.callMethod<void>("appStateChanged",
|
||||
"(I)V",
|
||||
NewState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QtAndroidApkExpansionFiles::APKEF_STATE QtAndroidApkExpansionFiles::startDownloadFiles()
|
||||
QAndroidApkExpansionFiles::APKEF_STATE QAndroidApkExpansionFiles::startDownloadFiles()
|
||||
{
|
||||
if(m_JavaApkExpansionDownloader.isValid() == false) return APKEF_INVALID_JAVA_CLASS;
|
||||
if(m_Base64PublicKey.count() == 0) return APKEF_INVALID_BASE64_PUBLIC_KEY;
|
||||
if(m_SALT.count() != 20) return APKEF_INVALID_SALT;
|
||||
if(QtAndroid::androidSdkVersion() >= 23)
|
||||
{
|
||||
if(QtAndroid::checkPermission("Manifest.permission.READ_EXTERNAL_STORAGE") != QtAndroid::PermissionResult::Granted) return APKEF_STORAGE_READ_PERMISSION_REQUIRED;
|
||||
if(QtAndroid::checkPermission("Manifest.permission.WRITE_EXTERNAL_STORAGE") != QtAndroid::PermissionResult::Granted) return APKEF_STORAGE_WRITE_PERMISSION_REQUIRED;
|
||||
if(QtAndroid::checkPermission("android.permission.READ_EXTERNAL_STORAGE") != QtAndroid::PermissionResult::Granted) return APKEF_STORAGE_READ_PERMISSION_REQUIRED;
|
||||
if(QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE") != QtAndroid::PermissionResult::Granted) return APKEF_STORAGE_WRITE_PERMISSION_REQUIRED;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
@ -147,7 +179,7 @@ QtAndroidApkExpansionFiles::APKEF_STATE QtAndroidApkExpansionFiles::startDownloa
|
||||
return APKEF_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
QString QtAndroidApkExpansionFiles::mainFileName()
|
||||
QString QAndroidApkExpansionFiles::mainFileName()
|
||||
{
|
||||
QString FileName;
|
||||
|
||||
@ -164,7 +196,7 @@ QString QtAndroidApkExpansionFiles::mainFileName()
|
||||
return FileName;
|
||||
}
|
||||
|
||||
QString QtAndroidApkExpansionFiles::patchFileName()
|
||||
QString QAndroidApkExpansionFiles::patchFileName()
|
||||
{
|
||||
QString FileName;
|
||||
|
||||
@ -181,111 +213,128 @@ QString QtAndroidApkExpansionFiles::patchFileName()
|
||||
return FileName;
|
||||
}
|
||||
|
||||
const QString& QtAndroidApkExpansionFiles::getBase64PublicKey() const
|
||||
void QAndroidApkExpansionFiles::sendRequest(REQUEST_ID requestID)
|
||||
{
|
||||
if(m_JavaApkExpansionDownloader.isValid())
|
||||
{
|
||||
m_JavaApkExpansionDownloader.callMethod<void>("sendRequest",
|
||||
"(I)V",
|
||||
requestID
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const QString& QAndroidApkExpansionFiles::getBase64PublicKey() const
|
||||
{
|
||||
return m_Base64PublicKey;
|
||||
}
|
||||
|
||||
void QtAndroidApkExpansionFiles::setBase64PublicKey(const QString &Base64PublicKey)
|
||||
void QAndroidApkExpansionFiles::setBase64PublicKey(const QString &Base64PublicKey)
|
||||
{
|
||||
m_Base64PublicKey = Base64PublicKey;
|
||||
}
|
||||
|
||||
const QVector<int>& QtAndroidApkExpansionFiles::getSALT() const
|
||||
const QVector<int>& QAndroidApkExpansionFiles::getSALT() const
|
||||
{
|
||||
return m_SALT;
|
||||
}
|
||||
|
||||
void QtAndroidApkExpansionFiles::setSALT(const QVector<int> &SALT)
|
||||
void QAndroidApkExpansionFiles::setSALT(const QVector<int> &SALT)
|
||||
{
|
||||
m_SALT = SALT;
|
||||
}
|
||||
|
||||
const ExpansionFileInfo& QtAndroidApkExpansionFiles::getMainExpansionFileInfo() const
|
||||
const ExpansionFileInfo& QAndroidApkExpansionFiles::getMainExpansionFileInfo() const
|
||||
{
|
||||
return m_ExpansionsFileInfo[0];
|
||||
}
|
||||
|
||||
void QtAndroidApkExpansionFiles::setMainExpansionFileInfo(const ExpansionFileInfo &MainExpansionFileInfo)
|
||||
void QAndroidApkExpansionFiles::setMainExpansionFileInfo(const ExpansionFileInfo &MainExpansionFileInfo)
|
||||
{
|
||||
m_ExpansionsFileInfo[0] = MainExpansionFileInfo;
|
||||
}
|
||||
|
||||
const ExpansionFileInfo& QtAndroidApkExpansionFiles::getPatchExpansionFileInfo() const
|
||||
const ExpansionFileInfo& QAndroidApkExpansionFiles::getPatchExpansionFileInfo() const
|
||||
{
|
||||
return m_ExpansionsFileInfo[1];
|
||||
}
|
||||
|
||||
void QtAndroidApkExpansionFiles::setPatchExpansionFileInfo(const ExpansionFileInfo &PatchExpansionFileInfo)
|
||||
void QAndroidApkExpansionFiles::setPatchExpansionFileInfo(const ExpansionFileInfo &PatchExpansionFileInfo)
|
||||
{
|
||||
m_ExpansionsFileInfo[1] = PatchExpansionFileInfo;
|
||||
}
|
||||
|
||||
QString QtAndroidApkExpansionFiles::stateTextInfo(int state)
|
||||
QString QAndroidApkExpansionFiles::getString(int stringID)
|
||||
{
|
||||
QString TextInfo;
|
||||
QString TextString;
|
||||
|
||||
switch(state)
|
||||
switch(stringID)
|
||||
{
|
||||
case STATE_IDLE:
|
||||
TextInfo = tr("Waiting for download to start");
|
||||
case STRING_IDLE:
|
||||
TextString = tr("Waiting for download to start");
|
||||
break;
|
||||
case STATE_FETCHING_URL:
|
||||
TextInfo = tr("Looking for resources to download");
|
||||
case STRING_FETCHING_URL:
|
||||
TextString = tr("Looking for resources to download");
|
||||
break;
|
||||
case STATE_CONNECTING:
|
||||
TextInfo = tr("Connecting to the download server");
|
||||
case STRING_CONNECTING:
|
||||
TextString = tr("Connecting to the download server");
|
||||
break;
|
||||
case STATE_DOWNLOADING:
|
||||
TextInfo = tr("Downloading resources");
|
||||
case STRING_DOWNLOADING:
|
||||
TextString = tr("Downloading resources");
|
||||
break;
|
||||
case STATE_COMPLETED:
|
||||
TextInfo = tr("Download finished");
|
||||
case STRING_COMPLETED:
|
||||
TextString = tr("Download finished");
|
||||
break;
|
||||
case STATE_PAUSED_NETWORK_UNAVAILABLE:
|
||||
TextInfo = tr("Download paused because no network is available");
|
||||
case STRING_PAUSED_NETWORK_UNAVAILABLE:
|
||||
TextString = tr("Download paused because no network is available");
|
||||
break;
|
||||
case STATE_PAUSED_BY_REQUEST:
|
||||
TextInfo = tr("Download paused");
|
||||
case STRING_PAUSED_BY_REQUEST:
|
||||
TextString = tr("Download paused");
|
||||
break;
|
||||
case STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
|
||||
TextInfo = tr("Download paused because wifi is disabled");
|
||||
case STRING_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
|
||||
TextString = tr("Download paused because wifi is disabled");
|
||||
break;
|
||||
case STATE_PAUSED_NEED_CELLULAR_PERMISSION:
|
||||
case STATE_PAUSED_NEED_WIFI:
|
||||
TextInfo = tr("Download paused because wifi is unavailable");
|
||||
case STRING_PAUSED_NEED_CELLULAR_PERMISSION:
|
||||
case STRING_PAUSED_NEED_WIFI:
|
||||
TextString = tr("Download paused because wifi is unavailable");
|
||||
break;
|
||||
case STATE_PAUSED_WIFI_DISABLED:
|
||||
TextInfo = tr("Download paused because wifi is disabled");
|
||||
case STRING_PAUSED_WIFI_DISABLED:
|
||||
TextString = tr("Download paused because wifi is disabled");
|
||||
break;
|
||||
case STATE_PAUSED_ROAMING:
|
||||
TextInfo = tr("Download paused because you are roaming");
|
||||
case STRING_PAUSED_ROAMING:
|
||||
TextString = tr("Download paused because you are roaming");
|
||||
break;
|
||||
case STATE_PAUSED_NETWORK_SETUP_FAILURE:
|
||||
TextInfo = tr("Download paused. Test a website in browser");
|
||||
case STRING_PAUSED_NETWORK_SETUP_FAILURE:
|
||||
TextString = tr("Download paused. Test a website in browser");
|
||||
break;
|
||||
case STATE_PAUSED_SDCARD_UNAVAILABLE:
|
||||
TextInfo = tr("Download paused because the external storage is unavailable");
|
||||
case STRING_PAUSED_SDCARD_UNAVAILABLE:
|
||||
TextString = tr("Download paused because the external storage is unavailable");
|
||||
break;
|
||||
case STATE_FAILED_UNLICENSED:
|
||||
TextInfo = tr("Download failed because you may not have purchased this app");
|
||||
case STRING_FAILED_UNLICENSED:
|
||||
TextString = tr("Download failed because you may not have purchased this app");
|
||||
break;
|
||||
case STATE_FAILED_FETCHING_URL:
|
||||
TextInfo = tr("Download failed because the resources could not be found");
|
||||
case STRING_FAILED_FETCHING_URL:
|
||||
TextString = tr("Download failed because the resources could not be found");
|
||||
break;
|
||||
case STATE_FAILED_SDCARD_FULL:
|
||||
TextInfo = tr("Download failed because the external storage is full");
|
||||
case STRING_FAILED_SDCARD_FULL:
|
||||
TextString = tr("Download failed because the external storage is full");
|
||||
break;
|
||||
case STATE_FAILED_CANCELED:
|
||||
TextInfo = tr("Download cancelled");
|
||||
case STRING_FAILED_CANCELED:
|
||||
TextString = tr("Download cancelled");
|
||||
break;
|
||||
case STATE_UNKNOWN:
|
||||
TextInfo = tr("Starting...");
|
||||
case STRING_FAILED:
|
||||
TextString = tr("Download failed");
|
||||
break;
|
||||
case STATE_DOWNLOADING_TIME_LEFT:
|
||||
TextInfo = tr("Time left");
|
||||
case STRING_UNKNOWN:
|
||||
TextString = tr("Unknown error");
|
||||
break;
|
||||
case STRING_TIME_LEFT:
|
||||
TextString = tr("Time left");
|
||||
break;
|
||||
case STRING_NOTIFICATION_CHANNEL_NAME:
|
||||
TextString = tr("App data download");
|
||||
break;
|
||||
}
|
||||
|
||||
return TextInfo;
|
||||
return TextString;
|
||||
}
|
||||
|
@ -1,3 +1,26 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Fabio Falsini <falsinsoft@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtAndroidExtras>
|
||||
@ -15,43 +38,45 @@ public:
|
||||
};
|
||||
Q_DECLARE_METATYPE(ExpansionFileInfo)
|
||||
|
||||
class QtAndroidApkExpansionFiles : public QObject
|
||||
class QAndroidApkExpansionFiles : public QObject
|
||||
{
|
||||
Q_PROPERTY(ExpansionFileInfo main READ getMainExpansionFileInfo WRITE setMainExpansionFileInfo)
|
||||
Q_PROPERTY(ExpansionFileInfo patch READ getPatchExpansionFileInfo WRITE setPatchExpansionFileInfo)
|
||||
Q_PROPERTY(QString base64PublicKey READ getBase64PublicKey WRITE setBase64PublicKey)
|
||||
Q_PROPERTY(QVector<int> salt READ getSALT WRITE setSALT)
|
||||
Q_DISABLE_COPY(QtAndroidApkExpansionFiles)
|
||||
Q_DISABLE_COPY(QAndroidApkExpansionFiles)
|
||||
Q_ENUMS(DOWNLOAD_STATE)
|
||||
Q_ENUMS(APKEF_STATE)
|
||||
Q_ENUMS(REQUEST_ID)
|
||||
Q_ENUMS(STRING_ID)
|
||||
Q_OBJECT
|
||||
|
||||
QtAndroidApkExpansionFiles();
|
||||
QAndroidApkExpansionFiles();
|
||||
|
||||
public:
|
||||
~QAndroidApkExpansionFiles();
|
||||
|
||||
enum DOWNLOAD_STATE
|
||||
{
|
||||
STATE_IDLE = 1,
|
||||
STATE_FETCHING_URL = 2,
|
||||
STATE_CONNECTING = 3,
|
||||
STATE_DOWNLOADING = 4,
|
||||
STATE_COMPLETED = 5,
|
||||
STATE_PAUSED_NETWORK_UNAVAILABLE = 6,
|
||||
STATE_PAUSED_BY_REQUEST = 7,
|
||||
STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 8,
|
||||
STATE_PAUSED_NEED_CELLULAR_PERMISSION = 9,
|
||||
STATE_PAUSED_WIFI_DISABLED = 10,
|
||||
STATE_PAUSED_NEED_WIFI = 11,
|
||||
STATE_PAUSED_ROAMING = 12,
|
||||
STATE_PAUSED_NETWORK_SETUP_FAILURE = 13,
|
||||
STATE_PAUSED_SDCARD_UNAVAILABLE = 14,
|
||||
STATE_FAILED_UNLICENSED = 15,
|
||||
STATE_FAILED_FETCHING_URL = 16,
|
||||
STATE_FAILED_SDCARD_FULL = 17,
|
||||
STATE_FAILED_CANCELED = 18,
|
||||
STATE_FAILED = 19,
|
||||
STATE_UNKNOWN = 20,
|
||||
STATE_DOWNLOADING_TIME_LEFT = 21
|
||||
STATE_FETCHING_URL,
|
||||
STATE_CONNECTING,
|
||||
STATE_DOWNLOADING,
|
||||
STATE_COMPLETED,
|
||||
STATE_PAUSED_NETWORK_UNAVAILABLE,
|
||||
STATE_PAUSED_BY_REQUEST,
|
||||
STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION,
|
||||
STATE_PAUSED_NEED_CELLULAR_PERMISSION,
|
||||
STATE_PAUSED_WIFI_DISABLED,
|
||||
STATE_PAUSED_NEED_WIFI,
|
||||
STATE_PAUSED_ROAMING,
|
||||
STATE_PAUSED_NETWORK_SETUP_FAILURE,
|
||||
STATE_PAUSED_SDCARD_UNAVAILABLE,
|
||||
STATE_FAILED_UNLICENSED,
|
||||
STATE_FAILED_FETCHING_URL,
|
||||
STATE_FAILED_SDCARD_FULL,
|
||||
STATE_FAILED_CANCELED,
|
||||
STATE_FAILED
|
||||
};
|
||||
enum APKEF_STATE
|
||||
{
|
||||
@ -65,14 +90,47 @@ public:
|
||||
APKEF_INVALID_SALT,
|
||||
APKEF_UNKNOWN_ERROR
|
||||
};
|
||||
enum REQUEST_ID
|
||||
{
|
||||
REQUEST_ABORT_DOWNLOAD = 0,
|
||||
REQUEST_PAUSE_DOWNLOAD,
|
||||
REQUEST_CONTINUE_DOWNLOAD,
|
||||
REQUEST_DOWNLOAD_STATUS
|
||||
};
|
||||
enum STRING_ID
|
||||
{
|
||||
STRING_IDLE = 0,
|
||||
STRING_FETCHING_URL,
|
||||
STRING_CONNECTING,
|
||||
STRING_DOWNLOADING,
|
||||
STRING_COMPLETED,
|
||||
STRING_PAUSED_NETWORK_UNAVAILABLE,
|
||||
STRING_PAUSED_BY_REQUEST,
|
||||
STRING_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION,
|
||||
STRING_PAUSED_NEED_CELLULAR_PERMISSION,
|
||||
STRING_PAUSED_WIFI_DISABLED,
|
||||
STRING_PAUSED_NEED_WIFI,
|
||||
STRING_PAUSED_ROAMING,
|
||||
STRING_PAUSED_NETWORK_SETUP_FAILURE,
|
||||
STRING_PAUSED_SDCARD_UNAVAILABLE,
|
||||
STRING_FAILED_UNLICENSED,
|
||||
STRING_FAILED_FETCHING_URL,
|
||||
STRING_FAILED_SDCARD_FULL,
|
||||
STRING_FAILED_CANCELED,
|
||||
STRING_FAILED,
|
||||
STRING_UNKNOWN,
|
||||
STRING_TIME_LEFT,
|
||||
STRING_NOTIFICATION_CHANNEL_NAME
|
||||
};
|
||||
|
||||
static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
static QtAndroidApkExpansionFiles* instance();
|
||||
static QAndroidApkExpansionFiles* instance();
|
||||
|
||||
Q_INVOKABLE QString mainFileName();
|
||||
Q_INVOKABLE QString patchFileName();
|
||||
Q_INVOKABLE APKEF_STATE startDownloadFiles();
|
||||
Q_INVOKABLE QString stateTextInfo(int state);
|
||||
Q_INVOKABLE QString getString(int stringID);
|
||||
Q_INVOKABLE void sendRequest(REQUEST_ID requestID);
|
||||
|
||||
const QString& getBase64PublicKey() const;
|
||||
void setBase64PublicKey(const QString &Base64PublicKey);
|
||||
@ -92,8 +150,17 @@ private slots:
|
||||
|
||||
private:
|
||||
const QAndroidJniObject m_JavaApkExpansionDownloader;
|
||||
static QtAndroidApkExpansionFiles *m_pInstance;
|
||||
static QAndroidApkExpansionFiles *m_pInstance;
|
||||
ExpansionFileInfo m_ExpansionsFileInfo[2];
|
||||
QString m_Base64PublicKey;
|
||||
QVector<int> m_SALT;
|
||||
|
||||
enum APP_STATE
|
||||
{
|
||||
APP_STATE_CREATE = 0,
|
||||
APP_STATE_START = 1,
|
||||
APP_STATE_STOP = 2,
|
||||
APP_STATE_DESTROY = 3
|
||||
};
|
||||
void SetNewAppState(APP_STATE NewState);
|
||||
};
|
||||
|
@ -1,41 +1,85 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Fabio Falsini <falsinsoft@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "QAndroidAppPermissions.h"
|
||||
|
||||
#include "QtAndroidAppPermissions.h"
|
||||
|
||||
QtAndroidAppPermissions::QtAndroidAppPermissions()
|
||||
QAndroidAppPermissions::QAndroidAppPermissions()
|
||||
{
|
||||
}
|
||||
|
||||
QObject* QtAndroidAppPermissions::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
QObject* QAndroidAppPermissions::qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
|
||||
{
|
||||
Q_UNUSED(engine);
|
||||
Q_UNUSED(scriptEngine);
|
||||
|
||||
return new QtAndroidAppPermissions();
|
||||
return new QAndroidAppPermissions();
|
||||
}
|
||||
|
||||
void QtAndroidAppPermissions::requestPermissions(const QStringList &permissionsNameList)
|
||||
{
|
||||
for(int i = 0; i < permissionsNameList.count(); i++)
|
||||
{
|
||||
requestPermission(permissionsNameList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void QtAndroidAppPermissions::requestPermission(const QString &permissionName)
|
||||
void QAndroidAppPermissions::requestPermissions(const QStringList &permissionsNameList)
|
||||
{
|
||||
if(QtAndroid::androidSdkVersion() >= 23)
|
||||
{
|
||||
if(QtAndroid::checkPermission(permissionName) != QtAndroid::PermissionResult::Granted)
|
||||
QStringList PermissionsNotGrantedList;
|
||||
|
||||
for(int i = 0; i < permissionsNameList.count(); i++)
|
||||
{
|
||||
QtAndroid::requestPermissions(QStringList() << permissionName, std::bind(&QtAndroidAppPermissions::RequestPermissionResults, this, std::placeholders::_1));
|
||||
return;
|
||||
if(QtAndroid::checkPermission(permissionsNameList[i]) != QtAndroid::PermissionResult::Granted)
|
||||
{
|
||||
PermissionsNotGrantedList << permissionsNameList[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
emit requestPermissionsResults(permissionsNameList[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
if(PermissionsNotGrantedList.count() > 0)
|
||||
{
|
||||
QtAndroid::requestPermissions(PermissionsNotGrantedList, std::bind(&QAndroidAppPermissions::RequestPermissionResults, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < permissionsNameList.count(); i++)
|
||||
{
|
||||
emit requestPermissionsResults(permissionsNameList[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
emit requestPermissionsResults(permissionName, true);
|
||||
}
|
||||
|
||||
bool QtAndroidAppPermissions::shouldShowRequestPermissionInfo(const QString &permissionName)
|
||||
void QAndroidAppPermissions::requestPermission(const QString &permissionName)
|
||||
{
|
||||
if(QtAndroid::androidSdkVersion() >= 23 && QtAndroid::checkPermission(permissionName) != QtAndroid::PermissionResult::Granted)
|
||||
{
|
||||
QtAndroid::requestPermissions(QStringList() << permissionName, std::bind(&QAndroidAppPermissions::RequestPermissionResults, this, std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
{
|
||||
emit requestPermissionsResults(permissionName, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool QAndroidAppPermissions::shouldShowRequestPermissionInfo(const QString &permissionName)
|
||||
{
|
||||
if(QtAndroid::androidSdkVersion() >= 23)
|
||||
{
|
||||
@ -45,7 +89,7 @@ bool QtAndroidAppPermissions::shouldShowRequestPermissionInfo(const QString &per
|
||||
return false;
|
||||
}
|
||||
|
||||
void QtAndroidAppPermissions::RequestPermissionResults(const QtAndroid::PermissionResultMap &ResultMap)
|
||||
void QAndroidAppPermissions::RequestPermissionResults(const QtAndroid::PermissionResultMap &ResultMap)
|
||||
{
|
||||
if(ResultMap.count() > 0)
|
||||
{
|
||||
|
@ -1,13 +1,36 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Fabio Falsini <falsinsoft@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtAndroidExtras>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class QtAndroidAppPermissions : public QObject
|
||||
class QAndroidAppPermissions : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QtAndroidAppPermissions();
|
||||
QAndroidAppPermissions();
|
||||
|
||||
public:
|
||||
static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine);
|
||||
|
@ -1,14 +1,31 @@
|
||||
package com.falsinsoft.QtAndroidTools;
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Fabio Falsini <falsinsoft@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
||||
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderService;
|
||||
import com.google.android.vending.expansion.downloader.IStub;
|
||||
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
|
||||
package com.falsinsoft.qtandroidtools;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.*;
|
||||
import com.google.android.vending.expansion.downloader.impl.*;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@ -20,87 +37,124 @@ import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.os.Build;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ApkExpansionDownloader implements IDownloaderClient
|
||||
public class ApkExpansionDownloader
|
||||
{
|
||||
private final Activity m_ActivityInstance;
|
||||
private IDownloaderService m_RemoteService;
|
||||
private IStub m_DownloaderClientStub;
|
||||
private final String NOTIFICATION_CHANNEL_ID;
|
||||
private final DownloaderClient mDownloaderClient;
|
||||
private final DownloaderProxy mDownloaderProxy;
|
||||
private final Activity mActivityInstance;
|
||||
|
||||
public ApkExpansionDownloader(Activity ActivityInstance)
|
||||
{
|
||||
final IDownloaderClient Client = this;
|
||||
ActivityInstance.runOnUiThread(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
m_DownloaderClientStub = DownloaderClientMarshaller.CreateStub(Client, ApkExpansionDownloaderService.class);
|
||||
}
|
||||
});
|
||||
m_ActivityInstance = ActivityInstance;
|
||||
NOTIFICATION_CHANNEL_ID = ActivityInstance.getClass().getName();
|
||||
mDownloaderClient = new DownloaderClient();
|
||||
mDownloaderProxy = new DownloaderProxy(ActivityInstance);
|
||||
mActivityInstance = ActivityInstance;
|
||||
}
|
||||
|
||||
public boolean isAPKFileDelivered(boolean IsMain, int FileVersion, int FileSize)
|
||||
{
|
||||
final String FileName = Helpers.getExpansionAPKFileName(m_ActivityInstance, IsMain, FileVersion);
|
||||
return Helpers.doesFileExist(m_ActivityInstance, FileName, FileSize, false);
|
||||
final String FileName = Helpers.getExpansionAPKFileName(mActivityInstance, IsMain, FileVersion);
|
||||
return Helpers.doesFileExist(mActivityInstance, FileName, FileSize, false);
|
||||
}
|
||||
|
||||
public String getExpansionAPKFileName(boolean IsMain, int FileVersion)
|
||||
{
|
||||
final String FileName = Helpers.getExpansionAPKFileName(m_ActivityInstance, IsMain, FileVersion);
|
||||
return Helpers.generateSaveFileName(m_ActivityInstance, FileName);
|
||||
final String FileName = Helpers.getExpansionAPKFileName(mActivityInstance, IsMain, FileVersion);
|
||||
return Helpers.generateSaveFileName(mActivityInstance, FileName);
|
||||
}
|
||||
|
||||
public void enableClientStubConnection(boolean ConnectionEnabled)
|
||||
public void sendRequest(int requestID)
|
||||
{
|
||||
if(ConnectionEnabled == true)
|
||||
m_DownloaderClientStub.connect(m_ActivityInstance);
|
||||
else
|
||||
m_DownloaderClientStub.disconnect(m_ActivityInstance);
|
||||
mDownloaderProxy.connect();
|
||||
|
||||
switch(requestID)
|
||||
{
|
||||
case REQUEST_ABORT_DOWNLOAD:
|
||||
mDownloaderProxy.requestAbortDownload();
|
||||
break;
|
||||
case REQUEST_PAUSE_DOWNLOAD:
|
||||
mDownloaderProxy.requestPauseDownload();
|
||||
break;
|
||||
case REQUEST_CONTINUE_DOWNLOAD:
|
||||
mDownloaderProxy.requestContinueDownload();
|
||||
break;
|
||||
case REQUEST_DOWNLOAD_STATUS:
|
||||
mDownloaderProxy.requestDownloadStatus();
|
||||
break;
|
||||
}
|
||||
|
||||
mDownloaderProxy.disconnect();
|
||||
}
|
||||
|
||||
public void appStateChanged(int newState)
|
||||
{
|
||||
switch(newState)
|
||||
{
|
||||
case APP_STATE_CREATE:
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
NotificationManager Manager = mActivityInstance.getSystemService(NotificationManager.class);
|
||||
NotificationChannel Channel;
|
||||
|
||||
Channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
|
||||
getString(STRING_NOTIFICATION_CHANNEL_NAME),
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
|
||||
Manager.createNotificationChannel(Channel);
|
||||
}
|
||||
mDownloaderClient.register(mActivityInstance);
|
||||
break;
|
||||
case APP_STATE_START:
|
||||
mDownloaderClient.register(mActivityInstance);
|
||||
break;
|
||||
case APP_STATE_STOP:
|
||||
mDownloaderClient.unregister(mActivityInstance);
|
||||
break;
|
||||
case APP_STATE_DESTROY:
|
||||
/*
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
NotificationManager Manager = mActivityInstance.getSystemService(NotificationManager.class);
|
||||
Manager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
*/
|
||||
mDownloaderClient.unregister(mActivityInstance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int startDownloader(String BASE64_PUBLIC_KEY, byte[] SALT)
|
||||
{
|
||||
int DownloadResult = -1;
|
||||
|
||||
ApkExpansionDownloaderService.BASE64_PUBLIC_KEY = BASE64_PUBLIC_KEY;
|
||||
ApkExpansionDownloaderService.SALT = SALT;
|
||||
|
||||
try
|
||||
{
|
||||
Intent LaunchIntent, IntentToLaunchThisActivityFromNotification;
|
||||
Intent IntentToLaunchThisActivityFromNotification;
|
||||
PendingIntent PendingActivity;
|
||||
|
||||
LaunchIntent = m_ActivityInstance.getIntent();
|
||||
|
||||
IntentToLaunchThisActivityFromNotification = new Intent(m_ActivityInstance, m_ActivityInstance.getClass());
|
||||
IntentToLaunchThisActivityFromNotification = new Intent(mActivityInstance, mActivityInstance.getClass());
|
||||
IntentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
IntentToLaunchThisActivityFromNotification.setAction(LaunchIntent.getAction());
|
||||
|
||||
|
||||
if(LaunchIntent.getCategories() != null)
|
||||
{
|
||||
for(String Category : LaunchIntent.getCategories())
|
||||
{
|
||||
IntentToLaunchThisActivityFromNotification.addCategory(Category);
|
||||
}
|
||||
}
|
||||
|
||||
PendingActivity = PendingIntent.getActivity(m_ActivityInstance,
|
||||
PendingActivity = PendingIntent.getActivity(mActivityInstance,
|
||||
0,
|
||||
IntentToLaunchThisActivityFromNotification,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
DownloadResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(m_ActivityInstance, PendingActivity, ApkExpansionDownloaderService.class);
|
||||
|
||||
if(DownloadResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED)
|
||||
{
|
||||
enableClientStubConnection(true);
|
||||
}
|
||||
DownloadResult = DownloaderService.startDownloadServiceIfRequired(mActivityInstance,
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
PendingActivity,
|
||||
SALT,
|
||||
BASE64_PUBLIC_KEY
|
||||
);
|
||||
}
|
||||
catch(NameNotFoundException e)
|
||||
{
|
||||
@ -110,26 +164,56 @@ public class ApkExpansionDownloader implements IDownloaderClient
|
||||
return DownloadResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(Messenger m)
|
||||
private class DownloaderClient extends BroadcastDownloaderClient
|
||||
{
|
||||
m_RemoteService = DownloaderServiceMarshaller.CreateProxy(m);
|
||||
m_RemoteService.onClientUpdated(m_DownloaderClientStub.getMessenger());
|
||||
}
|
||||
@Override
|
||||
public void onDownloadStateChanged(int newState)
|
||||
{
|
||||
downloadStateChanged(newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStateChanged(int newState)
|
||||
{
|
||||
downloadStateChanged(newState);
|
||||
@Override
|
||||
public void onDownloadProgress(DownloadProgressInfo progress)
|
||||
{
|
||||
downloadProgress(progress.mOverallTotal, progress.mOverallProgress, progress.mTimeRemaining, progress.mCurrentSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int STRING_IDLE = 0;
|
||||
public static final int STRING_FETCHING_URL = 1;
|
||||
public static final int STRING_CONNECTING = 2;
|
||||
public static final int STRING_DOWNLOADING = 3;
|
||||
public static final int STRING_COMPLETED = 4;
|
||||
public static final int STRING_PAUSED_NETWORK_UNAVAILABLE = 5;
|
||||
public static final int STRING_PAUSED_BY_REQUEST = 6;
|
||||
public static final int STRING_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION = 7;
|
||||
public static final int STRING_PAUSED_NEED_CELLULAR_PERMISSION = 8;
|
||||
public static final int STRING_PAUSED_WIFI_DISABLED = 9;
|
||||
public static final int STRING_PAUSED_NEED_WIFI = 10;
|
||||
public static final int STRING_PAUSED_ROAMING = 11;
|
||||
public static final int STRING_PAUSED_NETWORK_SETUP_FAILURE = 12;
|
||||
public static final int STRING_PAUSED_SDCARD_UNAVAILABLE = 13;
|
||||
public static final int STRING_FAILED_UNLICENSED = 14;
|
||||
public static final int STRING_FAILED_FETCHING_URL = 15;
|
||||
public static final int STRING_FAILED_SDCARD_FULL = 16;
|
||||
public static final int STRING_FAILED_CANCELED = 17;
|
||||
public static final int STRING_FAILED = 18;
|
||||
public static final int STRING_UNKNOWN = 19;
|
||||
public static final int STRING_TIME_LEFT = 20;
|
||||
public static final int STRING_NOTIFICATION_CHANNEL_NAME = 21;
|
||||
|
||||
@Override
|
||||
public void onDownloadProgress(DownloadProgressInfo progress)
|
||||
{
|
||||
downloadProgress(progress.mOverallTotal, progress.mOverallProgress, progress.mTimeRemaining, progress.mCurrentSpeed);
|
||||
}
|
||||
private static final int APP_STATE_CREATE = 0;
|
||||
private static final int APP_STATE_START = 1;
|
||||
private static final int APP_STATE_STOP = 2;
|
||||
private static final int APP_STATE_DESTROY = 3;
|
||||
|
||||
private static final int REQUEST_ABORT_DOWNLOAD = 0;
|
||||
private static final int REQUEST_PAUSE_DOWNLOAD = 1;
|
||||
private static final int REQUEST_CONTINUE_DOWNLOAD = 2;
|
||||
private static final int REQUEST_DOWNLOAD_STATUS = 3;
|
||||
|
||||
private static native void downloadStateChanged(int newState);
|
||||
private static native void downloadProgress(long overallTotal, long overallProgress, long timeRemaining, float currentSpeed);
|
||||
public static native String getString(int stringID);
|
||||
}
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
package com.falsinsoft.QtAndroidTools;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
||||
public class ApkExpansionDownloaderAlarmReceiver extends BroadcastReceiver
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
try
|
||||
{
|
||||
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, ApkExpansionDownloaderService.class);
|
||||
}
|
||||
catch (NameNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.falsinsoft.QtAndroidTools;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
|
||||
|
||||
public class ApkExpansionDownloaderService extends DownloaderService
|
||||
{
|
||||
public static String BASE64_PUBLIC_KEY = null;
|
||||
public static byte[] SALT = null;
|
||||
|
||||
@Override
|
||||
public String getPublicKey()
|
||||
{
|
||||
return BASE64_PUBLIC_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSALT()
|
||||
{
|
||||
return SALT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlarmReceiverClassName()
|
||||
{
|
||||
return ApkExpansionDownloaderAlarmReceiver.class.getName();
|
||||
}
|
||||
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class binds the service API to your application client. It contains the IDownloaderClient proxy,
|
||||
* which is used to call functions in your client as well as the Stub, which is used to call functions
|
||||
* in the client implementation of IDownloaderClient.
|
||||
*
|
||||
* <p>The IPC is implemented using an Android Messenger and a service Binder. The connect method
|
||||
* should be called whenever the client wants to bind to the service. It opens up a service connection
|
||||
* that ends up calling the onServiceConnected client API that passes the service messenger
|
||||
* in. If the client wants to be notified by the service, it is responsible for then passing its
|
||||
* messenger to the service in a separate call.
|
||||
*
|
||||
* <p>Critical methods are {@link #startDownloadServiceIfRequired} and {@link #CreateStub}.
|
||||
*
|
||||
* <p>When your application first starts, you should first check whether your app's expansion files are
|
||||
* already on the device. If not, you should then call {@link #startDownloadServiceIfRequired}, which
|
||||
* starts your {@link impl.DownloaderService} to download the expansion files if necessary. The method
|
||||
* returns a value indicating whether download is required or not.
|
||||
*
|
||||
* <p>If a download is required, {@link #startDownloadServiceIfRequired} begins the download through
|
||||
* the specified service and you should then call {@link #CreateStub} to instantiate a member {@link
|
||||
* IStub} object that you need in order to receive calls through your {@link IDownloaderClient}
|
||||
* interface.
|
||||
*/
|
||||
public class DownloaderClientMarshaller {
|
||||
public static final int MSG_ONDOWNLOADSTATE_CHANGED = 10;
|
||||
public static final int MSG_ONDOWNLOADPROGRESS = 11;
|
||||
public static final int MSG_ONSERVICECONNECTED = 12;
|
||||
|
||||
public static final String PARAM_NEW_STATE = "newState";
|
||||
public static final String PARAM_PROGRESS = "progress";
|
||||
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 LVL_CHECK_REQUIRED = DownloaderService.LVL_CHECK_REQUIRED;
|
||||
public static final int DOWNLOAD_REQUIRED = DownloaderService.DOWNLOAD_REQUIRED;
|
||||
|
||||
private static class Proxy implements IDownloaderClient {
|
||||
private Messenger mServiceMessenger;
|
||||
|
||||
@Override
|
||||
public void onDownloadStateChanged(int newState) {
|
||||
Bundle params = new Bundle(1);
|
||||
params.putInt(PARAM_NEW_STATE, newState);
|
||||
send(MSG_ONDOWNLOADSTATE_CHANGED, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadProgress(DownloadProgressInfo progress) {
|
||||
Bundle params = new Bundle(1);
|
||||
params.putParcelable(PARAM_PROGRESS, progress);
|
||||
send(MSG_ONDOWNLOADPROGRESS, params);
|
||||
}
|
||||
|
||||
private void send(int method, Bundle params) {
|
||||
Message m = Message.obtain(null, method);
|
||||
m.setData(params);
|
||||
try {
|
||||
mServiceMessenger.send(m);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Proxy(Messenger msg) {
|
||||
mServiceMessenger = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(Messenger m) {
|
||||
/**
|
||||
* This is never called through the proxy.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
private static class Stub implements IStub {
|
||||
private IDownloaderClient mItf = null;
|
||||
private Class<?> mDownloaderServiceClass;
|
||||
private boolean mBound;
|
||||
private Messenger mServiceMessenger;
|
||||
private Context mContext;
|
||||
/**
|
||||
* Target we publish for clients to send messages to IncomingHandler.
|
||||
*/
|
||||
final Messenger mMessenger = new Messenger(new Handler() {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
|
||||
mItf = itf;
|
||||
mDownloaderServiceClass = downloaderService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for interacting with the main interface of the service.
|
||||
*/
|
||||
private ServiceConnection mConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
// This is called when the connection with the service has been
|
||||
// established, giving us the object we can use to
|
||||
// interact with the service. We are communicating with the
|
||||
// service using a Messenger, so here we get a client-side
|
||||
// representation of that from the raw IBinder object.
|
||||
mServiceMessenger = new Messenger(service);
|
||||
mItf.onServiceConnected(
|
||||
mServiceMessenger);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected -- that is, its process crashed.
|
||||
mServiceMessenger = null;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void connect(Context c) {
|
||||
mContext = c;
|
||||
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
|
||||
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
|
||||
if ( !c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND) ) {
|
||||
if ( Constants.LOGVV ) {
|
||||
Log.d(Constants.TAG, "Service Unbound");
|
||||
}
|
||||
} else {
|
||||
mBound = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(Context c) {
|
||||
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
|
||||
*
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static IDownloaderClient CreateProxy(Messenger msg) {
|
||||
return new Proxy(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stub object that, when connected, will listen for marshaled
|
||||
* {@link IDownloaderClient} methods and translate them into calls to the supplied
|
||||
* interface.
|
||||
*
|
||||
* @param itf An implementation of IDownloaderClient that will be called
|
||||
* when remote method calls are unmarshaled.
|
||||
* @param downloaderService The class for your implementation of {@link
|
||||
* impl.DownloaderService}.
|
||||
* @return The {@link IStub} that allows you to connect to the service such that
|
||||
* your {@link IDownloaderClient} receives status updates.
|
||||
*/
|
||||
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
|
||||
return new Stub(itf, downloaderService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the metadata database updated 2) If the APK version does not match,
|
||||
* checks the new LVL status to see if a new download is required 3) If the
|
||||
* APK version does match, then checks to see if the download(s) have been
|
||||
* completed 4) If the downloads have been completed, returns
|
||||
* NO_DOWNLOAD_REQUIRED The idea is that this can be called during the
|
||||
* startup of an application to quickly ascertain if the application needs
|
||||
* to wait to hear about any updated APK expansion files. Note that this does
|
||||
* mean that the application MUST be run for the first time with a network
|
||||
* connection, even if Market delivers all of the files.
|
||||
*
|
||||
* @param context Your application Context.
|
||||
* @param notificationClient A PendingIntent to start the Activity in your application
|
||||
* that shows the download progress and which will also start the application when download
|
||||
* completes.
|
||||
* @param serviceClass the class of your {@link imp.DownloaderService} implementation
|
||||
* @return whether the service was started and the reason for starting the service.
|
||||
* Either {@link #NO_DOWNLOAD_REQUIRED}, {@link #LVL_CHECK_REQUIRED}, or {@link
|
||||
* #DOWNLOAD_REQUIRED}.
|
||||
* @throws NameNotFoundException
|
||||
*/
|
||||
public static int startDownloadServiceIfRequired(Context context, PendingIntent notificationClient,
|
||||
Class<?> serviceClass)
|
||||
throws NameNotFoundException {
|
||||
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
||||
serviceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* This version assumes that the intent contains the pending intent as a parameter. This
|
||||
* is used for responding to alarms.
|
||||
* <p>The pending intent must be in an extra with the key {@link
|
||||
* impl.DownloaderService#EXTRA_PENDING_INTENT}.
|
||||
*
|
||||
* @param context
|
||||
* @param notificationClient
|
||||
* @param serviceClass the class of the service to start
|
||||
* @return
|
||||
* @throws NameNotFoundException
|
||||
*/
|
||||
public static int startDownloadServiceIfRequired(Context context, Intent notificationClient,
|
||||
Class<?> serviceClass)
|
||||
throws NameNotFoundException {
|
||||
return DownloaderService.startDownloadServiceIfRequired(context, notificationClient,
|
||||
serviceClass);
|
||||
}
|
||||
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is used by the client activity to proxy requests to the Downloader
|
||||
* Service.
|
||||
*
|
||||
* Most importantly, you must call {@link #CreateProxy} during the {@link
|
||||
* IDownloaderClient#onServiceConnected} callback in your activity in order to instantiate
|
||||
* an {@link IDownloaderService} object that you can then use to issue commands to the {@link
|
||||
* DownloaderService} (such as to pause and resume downloads).
|
||||
*/
|
||||
public class DownloaderServiceMarshaller {
|
||||
|
||||
public static final int MSG_REQUEST_ABORT_DOWNLOAD =
|
||||
1;
|
||||
public static final int MSG_REQUEST_PAUSE_DOWNLOAD =
|
||||
2;
|
||||
public static final int MSG_SET_DOWNLOAD_FLAGS =
|
||||
3;
|
||||
public static final int MSG_REQUEST_CONTINUE_DOWNLOAD =
|
||||
4;
|
||||
public static final int MSG_REQUEST_DOWNLOAD_STATE =
|
||||
5;
|
||||
public static final int MSG_REQUEST_CLIENT_UPDATE =
|
||||
6;
|
||||
|
||||
public static final String PARAMS_FLAGS = "flags";
|
||||
public static final String PARAM_MESSENGER = DownloaderService.EXTRA_MESSAGE_HANDLER;
|
||||
|
||||
private static class Proxy implements IDownloaderService {
|
||||
private Messenger mMsg;
|
||||
|
||||
private void send(int method, Bundle params) {
|
||||
Message m = Message.obtain(null, method);
|
||||
m.setData(params);
|
||||
try {
|
||||
mMsg.send(m);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Proxy(Messenger msg) {
|
||||
mMsg = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestAbortDownload() {
|
||||
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPauseDownload() {
|
||||
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownloadFlags(int flags) {
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(PARAMS_FLAGS, flags);
|
||||
send(MSG_SET_DOWNLOAD_FLAGS, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestContinueDownload() {
|
||||
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDownloadStatus() {
|
||||
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientUpdated(Messenger clientMessenger) {
|
||||
Bundle bundle = new Bundle(1);
|
||||
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
|
||||
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Stub implements IStub {
|
||||
private IDownloaderService mItf = null;
|
||||
final Messenger mMessenger = new Messenger(new Handler() {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
public Stub(IDownloaderService itf) {
|
||||
mItf = itf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Messenger getMessenger() {
|
||||
return mMessenger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(Context c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(Context c) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy that will marshall calls to IDownloaderService methods
|
||||
*
|
||||
* @param ctx
|
||||
* @return
|
||||
*/
|
||||
public static IDownloaderService CreateProxy(Messenger msg) {
|
||||
return new Proxy(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stub object that, when connected, will listen for marshalled
|
||||
* IDownloaderService methods and translate them into calls to the supplied
|
||||
* interface.
|
||||
*
|
||||
* @param itf An implementation of IDownloaderService that will be called
|
||||
* when remote method calls are unmarshalled.
|
||||
* @return
|
||||
*/
|
||||
public static IStub CreateStub(IDownloaderService itf) {
|
||||
return new Stub(itf);
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,12 @@ import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
|
||||
//import com.android.vending.expansion.downloader.R;
|
||||
import com.falsinsoft.qtandroidtools.ApkExpansionDownloader;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
@ -266,7 +270,7 @@ public class Helpers {
|
||||
*
|
||||
* @param c the app/activity/service context
|
||||
* @param fileName the name (sans path) of the file to query
|
||||
* @return true if it does exist, false otherwise
|
||||
* @return value representing whether the file exists and is readable
|
||||
*/
|
||||
static public int getFileStatus(Context c, String fileName) {
|
||||
// the file may have been delivered by Play --- let's make sure
|
||||
@ -312,5 +316,48 @@ public class Helpers {
|
||||
* @param state One of the STATE_* constants from {@link IDownloaderClient}.
|
||||
* @return string resource ID for the corresponding string.
|
||||
*/
|
||||
public static native String getDownloaderStringResourceFromState(int state);
|
||||
@StringRes
|
||||
static public int getDownloaderStringResourceIDFromState(int state) {
|
||||
switch (state) {
|
||||
case IDownloaderClient.STATE_IDLE:
|
||||
return ApkExpansionDownloader.STRING_IDLE;
|
||||
case IDownloaderClient.STATE_FETCHING_URL:
|
||||
return ApkExpansionDownloader.STRING_FETCHING_URL;
|
||||
case IDownloaderClient.STATE_CONNECTING:
|
||||
return ApkExpansionDownloader.STRING_CONNECTING;
|
||||
case IDownloaderClient.STATE_DOWNLOADING:
|
||||
return ApkExpansionDownloader.STRING_DOWNLOADING;
|
||||
case IDownloaderClient.STATE_COMPLETED:
|
||||
return ApkExpansionDownloader.STRING_COMPLETED;
|
||||
case IDownloaderClient.STATE_PAUSED_NETWORK_UNAVAILABLE:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_NETWORK_UNAVAILABLE;
|
||||
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_BY_REQUEST;
|
||||
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION;
|
||||
case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_NEED_CELLULAR_PERMISSION;
|
||||
case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_WIFI_DISABLED;
|
||||
case IDownloaderClient.STATE_PAUSED_NEED_WIFI:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_NEED_WIFI;
|
||||
case IDownloaderClient.STATE_PAUSED_ROAMING:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_ROAMING;
|
||||
case IDownloaderClient.STATE_PAUSED_NETWORK_SETUP_FAILURE:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_NETWORK_SETUP_FAILURE;
|
||||
case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
|
||||
return ApkExpansionDownloader.STRING_PAUSED_SDCARD_UNAVAILABLE;
|
||||
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
||||
return ApkExpansionDownloader.STRING_FAILED_UNLICENSED;
|
||||
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
||||
return ApkExpansionDownloader.STRING_FAILED_FETCHING_URL;
|
||||
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
||||
return ApkExpansionDownloader.STRING_FAILED_SDCARD_FULL;
|
||||
case IDownloaderClient.STATE_FAILED_CANCELED:
|
||||
return ApkExpansionDownloader.STRING_FAILED_CANCELED;
|
||||
default:
|
||||
return ApkExpansionDownloader.STRING_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import android.os.Messenger;
|
||||
|
||||
/**
|
||||
* This interface should be implemented by the client activity for the
|
||||
* downloader. It is used to pass status from the service to the client.
|
||||
@ -72,28 +70,6 @@ public interface IDownloaderClient {
|
||||
static final int STATE_FAILED_CANCELED = 18;
|
||||
|
||||
static final int STATE_FAILED = 19;
|
||||
|
||||
static final int STATE_UNKNOWN = 20;
|
||||
static final int STATE_DOWNLOADING_TIME_LEFT = 21;
|
||||
|
||||
/**
|
||||
* Called internally by the stub when the service is bound to the client.
|
||||
* <p>
|
||||
* Critical implementation detail. In onServiceConnected we create the
|
||||
* remote service and marshaler. This is how we pass the client information
|
||||
* back to the service so the client can be properly notified of changes. We
|
||||
* must do this every time we reconnect to the service.
|
||||
* <p>
|
||||
* That is, when you receive this callback, you should call
|
||||
* {@link DownloaderServiceMarshaller#CreateProxy} to instantiate a member
|
||||
* instance of {@link IDownloaderService}, then call
|
||||
* {@link IDownloaderService#onClientUpdated} with the Messenger retrieved
|
||||
* from your {@link IStub} proxy object.
|
||||
*
|
||||
* @param m the service Messenger. This Messenger is used to call the
|
||||
* service API from the client.
|
||||
*/
|
||||
void onServiceConnected(Messenger m);
|
||||
|
||||
/**
|
||||
* Called when the download state changes. Depending on the state, there may
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.impl.DownloaderService;
|
||||
import android.os.Messenger;
|
||||
|
||||
/**
|
||||
* This interface is implemented by the DownloaderService and by the
|
||||
@ -70,14 +69,4 @@ public interface IDownloaderService {
|
||||
* Requests that the download status be sent to the client.
|
||||
*/
|
||||
void requestDownloadStatus();
|
||||
|
||||
/**
|
||||
* Call this when you get {@link
|
||||
* IDownloaderClient.onServiceConnected(Messenger m)} from the
|
||||
* DownloaderClient to register the client with the service. It will
|
||||
* automatically send the current status to the client.
|
||||
*
|
||||
* @param clientMessenger
|
||||
*/
|
||||
void onClientUpdated(Messenger clientMessenger);
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Messenger;
|
||||
|
||||
/**
|
||||
* This is the interface that is used to connect/disconnect from the downloader
|
||||
* service.
|
||||
* <p>
|
||||
* You should get a proxy object that implements this interface by calling
|
||||
* {@link DownloaderClientMarshaller#CreateStub} in your activity when the
|
||||
* downloader service starts. Then, call {@link #connect} during your activity's
|
||||
* onResume() and call {@link #disconnect} during onStop().
|
||||
* <p>
|
||||
* Then during the {@link IDownloaderClient#onServiceConnected} callback, you
|
||||
* should call {@link #getMessenger} to pass the stub's Messenger object to
|
||||
* {@link IDownloaderService#onClientUpdated}.
|
||||
*/
|
||||
public interface IStub {
|
||||
Messenger getMessenger();
|
||||
|
||||
void connect(Context c);
|
||||
|
||||
void disconnect(Context c);
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.vending.expansion.downloader;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Contains useful helper functions, typically tied to the application context.
|
||||
*/
|
||||
class SystemFacade {
|
||||
private Context mContext;
|
||||
private NotificationManager mNotificationManager;
|
||||
|
||||
public SystemFacade(Context context) {
|
||||
mContext = context;
|
||||
mNotificationManager = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
public long currentTimeMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Integer getActiveNetworkType() {
|
||||
ConnectivityManager connectivity =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity == null) {
|
||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||
return null;
|
||||
}
|
||||
|
||||
NetworkInfo activeInfo = connectivity.getActiveNetworkInfo();
|
||||
if (activeInfo == null) {
|
||||
if (Constants.LOGVV) {
|
||||
Log.v(Constants.TAG, "network is not available");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return activeInfo.getType();
|
||||
}
|
||||
|
||||
public boolean isNetworkRoaming() {
|
||||
ConnectivityManager connectivity =
|
||||
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity == null) {
|
||||
Log.w(Constants.TAG, "couldn't get connectivity manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkInfo info = connectivity.getActiveNetworkInfo();
|
||||
boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
|
||||
TelephonyManager tm = (TelephonyManager) mContext
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (null == tm) {
|
||||
Log.w(Constants.TAG, "couldn't get telephony manager");
|
||||
return false;
|
||||
}
|
||||
boolean isRoaming = isMobile && tm.isNetworkRoaming();
|
||||
if (Constants.LOGVV && isRoaming) {
|
||||
Log.v(Constants.TAG, "network is roaming");
|
||||
}
|
||||
return isRoaming;
|
||||
}
|
||||
|
||||
public Long getMaxBytesOverMobile() {
|
||||
return (long) Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public Long getRecommendedMaxBytesOverMobile() {
|
||||
return 2097152L;
|
||||
}
|
||||
|
||||
public void sendBroadcast(Intent intent) {
|
||||
mContext.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
|
||||
return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
|
||||
}
|
||||
|
||||
public void postNotification(long id, Notification notification) {
|
||||
/**
|
||||
* TODO: The system notification manager takes ints, not longs, as IDs,
|
||||
* but the download manager uses IDs take straight from the database,
|
||||
* which are longs. This will have to be dealt with at some point.
|
||||
*/
|
||||
mNotificationManager.notify((int) id, notification);
|
||||
}
|
||||
|
||||
public void cancelNotification(long id) {
|
||||
mNotificationManager.cancel((int) id);
|
||||
}
|
||||
|
||||
public void cancelAllNotifications() {
|
||||
mNotificationManager.cancelAll();
|
||||
}
|
||||
|
||||
public void startThread(Thread thread) {
|
||||
thread.start();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderService;
|
||||
|
||||
/**
|
||||
* This class is used by client to receive download events. The current
|
||||
* implementation uses {@link LocalBroadcastManager}, so client should
|
||||
* subscribe for the updates within the application context.
|
||||
*
|
||||
* <br /> <br />
|
||||
*
|
||||
* <b>Note:</b> You should preferably register the receiver through the
|
||||
* methods {@link #register(Context)} and {@link #unregister(Context)}.
|
||||
* The broadcast is not sticky, so client should use
|
||||
* {@link IDownloaderService#requestDownloadStatus()} to receive the
|
||||
* current download status immediately (see {@link DownloaderProxy}).
|
||||
*
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public abstract class BroadcastDownloaderClient extends BroadcastReceiver implements IDownloaderClient {
|
||||
|
||||
public static final String ACTION_STATE_CHANGED = "com.google.android.vending.expansion.downloader.ACTION_STATE_CHANGED";
|
||||
public static final String ACTION_PROGRESS = "com.google.android.vending.expansion.downloader.ACTION_PROGRESS";
|
||||
|
||||
public static final String EXTRA_NEW_STATE = "newState";
|
||||
public static final String EXTRA_PROGRESS = "progress";
|
||||
|
||||
public void register(Context context) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_STATE_CHANGED);
|
||||
filter.addAction(ACTION_PROGRESS);
|
||||
LocalBroadcastManager.getInstance(context).registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
public void unregister(Context context) {
|
||||
LocalBroadcastManager.getInstance(context).unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override public void onReceive(Context context, Intent intent) {
|
||||
switch (intent.getAction()) {
|
||||
case ACTION_STATE_CHANGED:
|
||||
int state = intent.getIntExtra(EXTRA_NEW_STATE, -1);
|
||||
onDownloadStateChanged(state);
|
||||
break;
|
||||
case ACTION_PROGRESS:
|
||||
DownloadProgressInfo info = intent.getParcelableExtra(EXTRA_PROGRESS);
|
||||
onDownloadProgress(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
|
||||
/**
|
||||
* Internal class used for communication with client. In current
|
||||
* implementation the communication is done via {@link LocalBroadcastManager}.
|
||||
* Commands are received by the {@link BroadcastDownloaderClient}.
|
||||
*/
|
||||
class ClientProxy implements IDownloaderClient {
|
||||
|
||||
private LocalBroadcastManager mBroadcastManager;
|
||||
|
||||
ClientProxy(Context ctx) {
|
||||
mBroadcastManager = LocalBroadcastManager.getInstance(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStateChanged(int newState) {
|
||||
Bundle params = new Bundle(1);
|
||||
params.putInt(BroadcastDownloaderClient.EXTRA_NEW_STATE, newState);
|
||||
send(BroadcastDownloaderClient.ACTION_STATE_CHANGED, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadProgress(DownloadProgressInfo progress) {
|
||||
Bundle params = new Bundle(1);
|
||||
params.putParcelable(BroadcastDownloaderClient.EXTRA_PROGRESS, progress);
|
||||
send(BroadcastDownloaderClient.ACTION_PROGRESS, params);
|
||||
}
|
||||
|
||||
private void send(String action, Bundle params) {
|
||||
Intent intent = new Intent(action);
|
||||
intent.putExtras(params);
|
||||
mBroadcastManager.sendBroadcast(intent);
|
||||
}
|
||||
}
|
@ -18,20 +18,16 @@ package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.*;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This service differs from IntentService in a few minor ways/ It will not
|
||||
* auto-stop itself after the intent is handled unless the target returns "true"
|
||||
* in should stop. Since the goal of this service is to handle a single kind of
|
||||
* in shouldStop(). Since the goal of this service is to handle a single kind of
|
||||
* intent, it does not queue up batches of intents of the same type.
|
||||
*/
|
||||
public abstract class CustomIntentService extends Service {
|
||||
abstract class CustomIntentService extends Service {
|
||||
private String mName;
|
||||
private boolean mRedelivery;
|
||||
private volatile ServiceHandler mServiceHandler;
|
||||
|
@ -16,15 +16,14 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.util.Log;
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Representation of information about an individual download from the database.
|
||||
*/
|
||||
public class DownloadInfo {
|
||||
class DownloadInfo {
|
||||
public String mUri;
|
||||
public final int mIndex;
|
||||
public final String mFileName;
|
||||
|
@ -16,18 +16,19 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Messenger;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
//import com.android.vending.expansion.downloader.R;
|
||||
import com.falsinsoft.qtandroidtools.ApkExpansionDownloader;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
|
||||
/**
|
||||
* This class handles displaying the notification associated with the download
|
||||
* queue going on in the download manager. It handles multiple status types;
|
||||
@ -39,155 +40,24 @@ import android.support.v4.app.NotificationCompat;
|
||||
* The application interface for the downloader also needs to understand and
|
||||
* handle these transient states.
|
||||
*/
|
||||
public class DownloadNotification implements IDownloaderClient {
|
||||
class DownloadNotification {
|
||||
|
||||
private int mState;
|
||||
private final Context mContext;
|
||||
private final NotificationManager mNotificationManager;
|
||||
private CharSequence mCurrentTitle;
|
||||
private final IDownloaderClient mClientProxy;
|
||||
|
||||
private IDownloaderClient mClientProxy;
|
||||
private CharSequence mCurrentTitle;
|
||||
private NotificationCompat.Builder mActiveDownloadBuilder;
|
||||
private NotificationCompat.Builder mBuilder;
|
||||
private NotificationCompat.Builder mCurrentBuilder;
|
||||
private CharSequence mLabel;
|
||||
private String mCurrentText;
|
||||
private DownloadProgressInfo mProgressInfo;
|
||||
private PendingIntent mContentIntent;
|
||||
|
||||
static final String LOGTAG = "DownloadNotification";
|
||||
static final int NOTIFICATION_ID = LOGTAG.hashCode();
|
||||
|
||||
public PendingIntent getClientIntent() {
|
||||
return mContentIntent;
|
||||
}
|
||||
|
||||
public void setClientIntent(PendingIntent clientIntent) {
|
||||
this.mBuilder.setContentIntent(clientIntent);
|
||||
this.mActiveDownloadBuilder.setContentIntent(clientIntent);
|
||||
this.mContentIntent = clientIntent;
|
||||
}
|
||||
|
||||
public void resendState() {
|
||||
if (null != mClientProxy) {
|
||||
mClientProxy.onDownloadStateChanged(mState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStateChanged(int newState) {
|
||||
if (null != mClientProxy) {
|
||||
mClientProxy.onDownloadStateChanged(newState);
|
||||
}
|
||||
if (newState != mState) {
|
||||
mState = newState;
|
||||
if (newState == IDownloaderClient.STATE_IDLE || null == mContentIntent) {
|
||||
return;
|
||||
}
|
||||
int iconResource;
|
||||
boolean ongoingEvent;
|
||||
|
||||
// get the new title string and paused text
|
||||
switch (newState) {
|
||||
case 0:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(IDownloaderClient.STATE_UNKNOWN);
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_DOWNLOADING:
|
||||
iconResource = android.R.drawable.stat_sys_download;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_FETCHING_URL:
|
||||
case IDownloaderClient.STATE_CONNECTING:
|
||||
iconResource = android.R.drawable.stat_sys_download_done;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_COMPLETED:
|
||||
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
||||
iconResource = android.R.drawable.stat_sys_download_done;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(newState);
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_FAILED:
|
||||
case IDownloaderClient.STATE_FAILED_CANCELED:
|
||||
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
||||
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
||||
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(newState);
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
mCurrentText = Helpers.getDownloaderStringResourceFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mCurrentTitle = mLabel;
|
||||
mCurrentBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||
mCurrentBuilder.setSmallIcon(iconResource);
|
||||
mCurrentBuilder.setContentTitle(mCurrentTitle);
|
||||
mCurrentBuilder.setContentText(mCurrentText);
|
||||
if (ongoingEvent) {
|
||||
mCurrentBuilder.setOngoing(true);
|
||||
} else {
|
||||
mCurrentBuilder.setOngoing(false);
|
||||
mCurrentBuilder.setAutoCancel(true);
|
||||
}
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadProgress(DownloadProgressInfo progress) {
|
||||
mProgressInfo = progress;
|
||||
if (null != mClientProxy) {
|
||||
mClientProxy.onDownloadProgress(progress);
|
||||
}
|
||||
if (progress.mOverallTotal <= 0) {
|
||||
// we just show the text
|
||||
mBuilder.setTicker(mCurrentTitle);
|
||||
mBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
mBuilder.setContentTitle(mCurrentTitle);
|
||||
mBuilder.setContentText(mCurrentText);
|
||||
mCurrentBuilder = mBuilder;
|
||||
} else {
|
||||
mActiveDownloadBuilder.setProgress((int) progress.mOverallTotal, (int) progress.mOverallProgress, false);
|
||||
mActiveDownloadBuilder.setContentText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
|
||||
mActiveDownloadBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
mActiveDownloadBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||
mActiveDownloadBuilder.setContentTitle(mLabel);
|
||||
mActiveDownloadBuilder.setContentInfo(Helpers.getDownloaderStringResourceFromState(IDownloaderClient.STATE_DOWNLOADING_TIME_LEFT) + ": " + Helpers.getTimeRemaining(progress.mTimeRemaining));
|
||||
mCurrentBuilder = mActiveDownloadBuilder;
|
||||
}
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in response to onClientUpdated. Creates a new proxy and notifies
|
||||
* it of the current state.
|
||||
*
|
||||
* @param msg the client Messenger to notify
|
||||
*/
|
||||
public void setMessenger(Messenger msg) {
|
||||
mClientProxy = DownloaderClientMarshaller.CreateProxy(msg);
|
||||
if (null != mProgressInfo) {
|
||||
mClientProxy.onDownloadProgress(mProgressInfo);
|
||||
}
|
||||
if (mState != -1) {
|
||||
mClientProxy.onDownloadStateChanged(mState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -200,6 +70,7 @@ public class DownloadNotification implements IDownloaderClient {
|
||||
mLabel = applicationLabel;
|
||||
mNotificationManager = (NotificationManager)
|
||||
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mClientProxy = new ClientProxy(ctx);
|
||||
mActiveDownloadBuilder = new NotificationCompat.Builder(ctx);
|
||||
mBuilder = new NotificationCompat.Builder(ctx);
|
||||
|
||||
@ -214,8 +85,125 @@ public class DownloadNotification implements IDownloaderClient {
|
||||
mCurrentBuilder = mBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(Messenger m) {
|
||||
public PendingIntent getClientIntent() {
|
||||
return mContentIntent;
|
||||
}
|
||||
|
||||
public void setClientIntent(PendingIntent clientIntent) {
|
||||
this.mBuilder.setContentIntent(clientIntent);
|
||||
this.mActiveDownloadBuilder.setContentIntent(clientIntent);
|
||||
this.mContentIntent = clientIntent;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.mBuilder.setChannelId(channelId);
|
||||
this.mActiveDownloadBuilder.setChannelId(channelId);
|
||||
}
|
||||
|
||||
public void resendState() {
|
||||
mClientProxy.onDownloadStateChanged(mState);
|
||||
}
|
||||
|
||||
void onDownloadStateChanged(int newState) {
|
||||
mClientProxy.onDownloadStateChanged(newState);
|
||||
if (newState != mState) {
|
||||
mState = newState;
|
||||
if (newState == IDownloaderClient.STATE_IDLE || null == mContentIntent) {
|
||||
return;
|
||||
}
|
||||
@StringRes int stringDownloadID;
|
||||
int iconResource;
|
||||
boolean ongoingEvent;
|
||||
|
||||
mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
|
||||
// get the new title string and paused text
|
||||
switch (newState) {
|
||||
case 0:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
stringDownloadID = ApkExpansionDownloader.STRING_UNKNOWN;
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_DOWNLOADING:
|
||||
iconResource = android.R.drawable.stat_sys_download;
|
||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_FETCHING_URL:
|
||||
case IDownloaderClient.STATE_CONNECTING:
|
||||
iconResource = android.R.drawable.stat_sys_download_done;
|
||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_COMPLETED:
|
||||
// show notification without progress
|
||||
mCurrentBuilder = mBuilder;
|
||||
mBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
|
||||
case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
|
||||
iconResource = android.R.drawable.stat_sys_download_done;
|
||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
case IDownloaderClient.STATE_FAILED:
|
||||
case IDownloaderClient.STATE_FAILED_CANCELED:
|
||||
case IDownloaderClient.STATE_FAILED_FETCHING_URL:
|
||||
case IDownloaderClient.STATE_FAILED_SDCARD_FULL:
|
||||
case IDownloaderClient.STATE_FAILED_UNLICENSED:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||
ongoingEvent = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
iconResource = android.R.drawable.stat_sys_warning;
|
||||
stringDownloadID = Helpers.getDownloaderStringResourceIDFromState(newState);
|
||||
ongoingEvent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mCurrentText = ApkExpansionDownloader.getString(stringDownloadID);
|
||||
mCurrentTitle = mLabel;
|
||||
mCurrentBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||
mCurrentBuilder.setSmallIcon(iconResource);
|
||||
mCurrentBuilder.setContentTitle(mCurrentTitle);
|
||||
mCurrentBuilder.setContentText(mCurrentText);
|
||||
if (ongoingEvent) {
|
||||
mCurrentBuilder.setOngoing(true);
|
||||
mCurrentBuilder.setOnlyAlertOnce(true);
|
||||
} else {
|
||||
mCurrentBuilder.setOnlyAlertOnce(false);
|
||||
mCurrentBuilder.setOngoing(false);
|
||||
mCurrentBuilder.setAutoCancel(true);
|
||||
}
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
void onDownloadProgress(DownloadProgressInfo progress) {
|
||||
mClientProxy.onDownloadProgress(progress);
|
||||
if (progress.mOverallTotal <= 0) {
|
||||
// we just show the text
|
||||
mBuilder.setTicker(mCurrentTitle);
|
||||
mBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
mBuilder.setContentTitle(mCurrentTitle);
|
||||
mBuilder.setContentText(mCurrentText);
|
||||
mCurrentBuilder = mBuilder;
|
||||
} else {
|
||||
mActiveDownloadBuilder.setProgress((int) progress.mOverallTotal, (int) progress.mOverallProgress, false);
|
||||
mActiveDownloadBuilder.setContentText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal));
|
||||
mActiveDownloadBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
|
||||
mActiveDownloadBuilder.setTicker(mLabel + ": " + mCurrentText);
|
||||
mActiveDownloadBuilder.setContentTitle(mLabel);
|
||||
mActiveDownloadBuilder.setContentInfo(ApkExpansionDownloader.getString(ApkExpansionDownloader.STRING_TIME_LEFT) + ": " + Helpers.getTimeRemaining(progress.mTimeRemaining));
|
||||
mActiveDownloadBuilder.setOngoing(true);
|
||||
mActiveDownloadBuilder.setOnlyAlertOnce(true);
|
||||
mCurrentBuilder = mActiveDownloadBuilder;
|
||||
}
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mCurrentBuilder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,21 +16,15 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.SyncFailedException;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
@ -38,7 +32,7 @@ import java.util.Locale;
|
||||
/**
|
||||
* Runs an actual download
|
||||
*/
|
||||
public class DownloadThread {
|
||||
class DownloadThread {
|
||||
|
||||
private Context mContext;
|
||||
private DownloadInfo mInfo;
|
||||
@ -47,7 +41,7 @@ public class DownloadThread {
|
||||
private final DownloadNotification mNotification;
|
||||
private String mUserAgent;
|
||||
|
||||
public DownloadThread(DownloadInfo info, DownloaderService service,
|
||||
DownloadThread(DownloadInfo info, DownloaderService service,
|
||||
DownloadNotification notification) {
|
||||
mContext = service;
|
||||
mInfo = info;
|
||||
@ -135,7 +129,7 @@ public class DownloadThread {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the download in a separate thread
|
||||
* Executes the download
|
||||
*/
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
@ -0,0 +1,125 @@
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.*;
|
||||
import android.util.Log;
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderService;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import static com.google.android.vending.expansion.downloader.impl
|
||||
.ServiceHandler.*;
|
||||
|
||||
/**
|
||||
* This class is used by the client to issue commands to the {@link
|
||||
* DownloaderService} (such as to pause and resume downloads).
|
||||
* <p>
|
||||
* Most importantly, you must first call {@link #connect()} method to
|
||||
* establish the connection with the service. All the calls to {@link
|
||||
* IDownloaderService} that happen when connection is being
|
||||
* established will be queued and delivered when connection is ready.
|
||||
*/
|
||||
public final class DownloaderProxy implements IDownloaderService,
|
||||
ServiceConnection {
|
||||
|
||||
private final Context mContext;
|
||||
private Messenger mMessenger;
|
||||
private boolean mConnected;
|
||||
private boolean connectCalled;
|
||||
private final Queue<Message> mMessages = new LinkedList<>();
|
||||
|
||||
public DownloaderProxy(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mMessenger = new Messenger(service);
|
||||
mConnected = true;
|
||||
drainMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onServiceDisconnected(ComponentName name) {
|
||||
mMessenger = null;
|
||||
mConnected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestAbortDownload() {
|
||||
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPauseDownload() {
|
||||
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownloadFlags(int flags) {
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(PARAMS_FLAGS, flags);
|
||||
send(MSG_SET_DOWNLOAD_FLAGS, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestContinueDownload() {
|
||||
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDownloadStatus() {
|
||||
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
|
||||
}
|
||||
|
||||
private void drainMessages() {
|
||||
while (mMessenger != null && !mMessages.isEmpty()) {
|
||||
try {
|
||||
mMessenger.send(mMessages.peek());
|
||||
mMessages.remove();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(Constants.TAG, "send: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void send(int method, Bundle params) {
|
||||
|
||||
if (!connectCalled) {
|
||||
throw new IllegalStateException("connect() method was not called");
|
||||
}
|
||||
|
||||
Message m = Message.obtain(null, method);
|
||||
m.setData(params);
|
||||
|
||||
mMessages.add(m);
|
||||
|
||||
if (mConnected) {
|
||||
drainMessages();
|
||||
}
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
Intent bindIntent = new Intent(mContext.getApplicationContext(),
|
||||
DownloaderService.class);
|
||||
if (!mContext.getApplicationContext().bindService(bindIntent, this,
|
||||
Context.BIND_DEBUG_UNBIND)) {
|
||||
Log.w(Constants.TAG, "Service not bound. Check Manifest.xml " +
|
||||
"declaration");
|
||||
} else {
|
||||
connectCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (mConnected) {
|
||||
mContext.getApplicationContext().unbindService(this);
|
||||
connectCalled = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,19 +16,6 @@
|
||||
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import com.google.android.vending.expansion.downloader.Constants;
|
||||
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
|
||||
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
|
||||
import com.google.android.vending.expansion.downloader.Helpers;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderClient;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderService;
|
||||
import com.google.android.vending.expansion.downloader.IStub;
|
||||
import com.google.android.vending.licensing.AESObfuscator;
|
||||
import com.google.android.vending.licensing.APKExpansionPolicy;
|
||||
import com.google.android.vending.licensing.LicenseChecker;
|
||||
import com.google.android.vending.licensing.LicenseCheckerCallback;
|
||||
import com.google.android.vending.licensing.Policy;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
@ -38,17 +25,17 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Messenger;
|
||||
import android.os.SystemClock;
|
||||
import android.os.*;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import com.google.android.vending.expansion.downloader.*;
|
||||
import com.google.android.vending.licensing.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@ -59,7 +46,7 @@ import java.io.File;
|
||||
* Note that Android by default will kill off any process that has an open file
|
||||
* handle on the shared (SD Card) partition if the partition is unmounted.
|
||||
*/
|
||||
public abstract class DownloaderService extends CustomIntentService implements IDownloaderService {
|
||||
public class DownloaderService extends CustomIntentService implements IDownloaderService {
|
||||
|
||||
public DownloaderService() {
|
||||
super("LVLDownloadService");
|
||||
@ -446,9 +433,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
* Our binding to the network state broadcasts
|
||||
*/
|
||||
private BroadcastReceiver mConnReceiver;
|
||||
final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
|
||||
final private Messenger mServiceMessenger = mServiceStub.getMessenger();
|
||||
private Messenger mClientMessenger;
|
||||
final private Messenger mServiceMessenger = new Messenger(new ServiceHandler(this));
|
||||
private DownloadNotification mNotification;
|
||||
private PendingIntent mPendingIntent;
|
||||
private PendingIntent mAlarmIntent;
|
||||
@ -588,9 +573,10 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
public static final int LVL_CHECK_REQUIRED = 1;
|
||||
public static final int DOWNLOAD_REQUIRED = 2;
|
||||
|
||||
public static final String EXTRA_PACKAGE_NAME = "EPN";
|
||||
public static final String EXTRA_PENDING_INTENT = "EPI";
|
||||
public static final String EXTRA_MESSAGE_HANDLER = "EMH";
|
||||
public static final String EXTRA_CHANNEL_ID = "ECI";
|
||||
public static final String EXTRA_SALT = "ESALT";
|
||||
public static final String EXTRA_PUBLIC_KEY = "EPK";
|
||||
|
||||
/**
|
||||
* Returns true if the LVL check is required
|
||||
@ -621,25 +607,6 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
sIsRunning = isRunning;
|
||||
}
|
||||
|
||||
public static int startDownloadServiceIfRequired(Context context,
|
||||
Intent intent, Class<?> serviceClass) throws NameNotFoundException {
|
||||
final PendingIntent pendingIntent = (PendingIntent) intent
|
||||
.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||
return startDownloadServiceIfRequired(context, pendingIntent,
|
||||
serviceClass);
|
||||
}
|
||||
|
||||
public static int startDownloadServiceIfRequired(Context context,
|
||||
PendingIntent pendingIntent, Class<?> serviceClass)
|
||||
throws NameNotFoundException
|
||||
{
|
||||
String packageName = context.getPackageName();
|
||||
String className = serviceClass.getName();
|
||||
|
||||
return startDownloadServiceIfRequired(context, pendingIntent,
|
||||
packageName, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the
|
||||
@ -654,14 +621,18 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
* network connection, even if Market delivers all of the files.
|
||||
*
|
||||
* @param context
|
||||
* @param channelId The Channel ID to use for download progress
|
||||
* notifications on Android O+
|
||||
* @param pendingIntent
|
||||
* @return true if the app should wait for more guidance from the
|
||||
* downloader, false if the app can continue
|
||||
* @throws NameNotFoundException
|
||||
*/
|
||||
public static int startDownloadServiceIfRequired(Context context,
|
||||
PendingIntent pendingIntent, String classPackage, String className)
|
||||
String channelId,
|
||||
PendingIntent pendingIntent, byte[] salt, String publicKey)
|
||||
throws NameNotFoundException {
|
||||
|
||||
// first: do we need to do an LVL update?
|
||||
// we begin by getting our APK version from the package manager
|
||||
final PackageInfo pi = context.getPackageManager().getPackageInfo(
|
||||
@ -696,10 +667,12 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
switch (status) {
|
||||
case DOWNLOAD_REQUIRED:
|
||||
case LVL_CHECK_REQUIRED:
|
||||
Intent fileIntent = new Intent();
|
||||
fileIntent.setClassName(classPackage, className);
|
||||
fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
|
||||
context.startService(fileIntent);
|
||||
Intent downloadIntent = new Intent(context, DownloaderService.class);
|
||||
downloadIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
|
||||
downloadIntent.putExtra(EXTRA_CHANNEL_ID, channelId);
|
||||
downloadIntent.putExtra(EXTRA_SALT, salt);
|
||||
downloadIntent.putExtra(EXTRA_PUBLIC_KEY, publicKey);
|
||||
context.startService(downloadIntent);
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
@ -732,19 +705,19 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
this.startService(fileIntent);
|
||||
}
|
||||
|
||||
public abstract String getPublicKey();
|
||||
|
||||
public abstract byte[] getSALT();
|
||||
|
||||
public abstract String getAlarmReceiverClassName();
|
||||
|
||||
private class LVLRunnable implements Runnable {
|
||||
LVLRunnable(Context context, PendingIntent intent) {
|
||||
mContext = context;
|
||||
mPendingIntent = intent;
|
||||
}
|
||||
|
||||
final Context mContext;
|
||||
private final String mChannelId;
|
||||
private final byte[] mSalt;
|
||||
private final String mPublicKey;
|
||||
|
||||
LVLRunnable(Context context, String channelId, byte[] salt, String publicKey) {
|
||||
mContext = context;
|
||||
mChannelId = channelId;
|
||||
mSalt = salt;
|
||||
mPublicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
@ -754,7 +727,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
Secure.ANDROID_ID);
|
||||
|
||||
final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
|
||||
new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
|
||||
new AESObfuscator(mSalt, mContext.getPackageName(), deviceId));
|
||||
|
||||
// reset our policy back to the start of the world to force a
|
||||
// re-check
|
||||
@ -763,7 +736,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
// let's try and get the OBB file from LVL first
|
||||
// Construct the LicenseChecker with a Policy.
|
||||
final LicenseChecker checker = new LicenseChecker(mContext, aep,
|
||||
getPublicKey() // Your public licensing key.
|
||||
mPublicKey // Your public licensing key.
|
||||
);
|
||||
checker.checkAccess(new LicenseCheckerCallback() {
|
||||
|
||||
@ -829,9 +802,8 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
pi = mContext.getPackageManager().getPackageInfo(
|
||||
mContext.getPackageName(), 0);
|
||||
db.updateMetadata(pi.versionCode, status);
|
||||
Class<?> serviceClass = DownloaderService.this.getClass();
|
||||
switch (startDownloadServiceIfRequired(mContext, mPendingIntent,
|
||||
serviceClass)) {
|
||||
Class<? extends DownloaderService> serviceClass = DownloaderService.this.getClass();
|
||||
switch (startDownloadServiceIfRequired(mContext, mChannelId, mPendingIntent, mSalt, mPublicKey)) {
|
||||
case NO_DOWNLOAD_REQUIRED:
|
||||
mNotification
|
||||
.onDownloadStateChanged(IDownloaderClient.STATE_COMPLETED);
|
||||
@ -900,10 +872,10 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public void updateLVL(final Context context) {
|
||||
public void updateLVL(final Context context, String channelId, byte[] salt, String publicKey) {
|
||||
Context c = context.getApplicationContext();
|
||||
Handler h = new Handler(c.getMainLooper());
|
||||
h.post(new LVLRunnable(c, mPendingIntent));
|
||||
h.post(new LVLRunnable(c, channelId, salt, publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -939,7 +911,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
return !Helpers.doesFileExist(this, filename, fileSize, true);
|
||||
}
|
||||
|
||||
private void scheduleAlarm(long wakeUp) {
|
||||
private void scheduleAlarm(long wakeUp, boolean repeated, Bundle callerExtras) {
|
||||
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
if (alarms == null) {
|
||||
Log.e(Constants.TAG, "couldn't get alarm manager");
|
||||
@ -950,17 +922,22 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
Log.v(Constants.TAG, "scheduling retry in " + wakeUp + "ms");
|
||||
}
|
||||
|
||||
String className = getAlarmReceiverClassName();
|
||||
Intent intent = new Intent(Constants.ACTION_RETRY);
|
||||
intent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent);
|
||||
intent.setClassName(this.getPackageName(),
|
||||
className);
|
||||
// put original extras to the wake up intent
|
||||
Intent intent = new Intent(this, AlarmReceiver.class);
|
||||
intent.setAction(Constants.ACTION_RETRY);
|
||||
intent.putExtras(callerExtras);
|
||||
|
||||
mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
|
||||
PendingIntent.FLAG_ONE_SHOT);
|
||||
alarms.set(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
System.currentTimeMillis() + wakeUp, mAlarmIntent
|
||||
);
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
if (repeated) {
|
||||
alarms.setRepeating(AlarmManager.RTC_WAKEUP,
|
||||
System.currentTimeMillis() + wakeUp, wakeUp, mAlarmIntent);
|
||||
return;
|
||||
}
|
||||
|
||||
alarms.set(AlarmManager.RTC_WAKEUP,
|
||||
System.currentTimeMillis() + wakeUp, mAlarmIntent);
|
||||
}
|
||||
|
||||
private void cancelAlarms() {
|
||||
@ -998,7 +975,31 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
context.startService(fileIntent);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to handle wake up calls from service watch dogs.
|
||||
*/
|
||||
public static class AlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
final PendingIntent pendingIntent = (PendingIntent) intent
|
||||
.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||
|
||||
startDownloadServiceIfRequired(
|
||||
context,
|
||||
intent.getStringExtra(EXTRA_CHANNEL_ID),
|
||||
pendingIntent,
|
||||
intent.getByteArrayExtra(EXTRA_SALT),
|
||||
intent.getStringExtra(EXTRA_PUBLIC_KEY)
|
||||
);
|
||||
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(getClass().getSimpleName(), "onReceive: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main thread for the Downloader. This thread is responsible
|
||||
@ -1011,11 +1012,14 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
// the database automatically reads the metadata for version code
|
||||
// and download status when the instance is created
|
||||
DownloadsDB db = DownloadsDB.getDB(this);
|
||||
final PendingIntent pendingIntent = (PendingIntent) intent
|
||||
.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||
final PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||
final String channelId = intent.getStringExtra(EXTRA_CHANNEL_ID);
|
||||
final byte[] salt = intent.getByteArrayExtra(EXTRA_SALT);
|
||||
final String publicKey = intent.getStringExtra(EXTRA_PUBLIC_KEY);
|
||||
|
||||
if (null != pendingIntent)
|
||||
{
|
||||
mNotification.setChannelId(channelId);
|
||||
|
||||
if (null != pendingIntent) {
|
||||
mNotification.setClientIntent(pendingIntent);
|
||||
mPendingIntent = pendingIntent;
|
||||
} else if (null != mPendingIntent) {
|
||||
@ -1028,7 +1032,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
// when the LVL check completes, a successful response will update
|
||||
// the service
|
||||
if (isLVLCheckRequired(db, mPackageInfo)) {
|
||||
updateLVL(this);
|
||||
updateLVL(this, channelId, salt, publicKey);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1074,7 +1078,8 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
if (info.mStatus != STATUS_SUCCESS) {
|
||||
DownloadThread dt = new DownloadThread(info, this, mNotification);
|
||||
cancelAlarms();
|
||||
scheduleAlarm(Constants.ACTIVE_THREAD_WATCHDOG);
|
||||
// schedule repeated alarm to check if process is alive
|
||||
scheduleAlarm(Constants.ACTIVE_THREAD_WATCHDOG, true, intent.getExtras());
|
||||
dt.run();
|
||||
cancelAlarms();
|
||||
}
|
||||
@ -1084,7 +1089,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
switch (info.mStatus) {
|
||||
case STATUS_FORBIDDEN:
|
||||
// the URL is out of date
|
||||
updateLVL(this);
|
||||
updateLVL(this, channelId, salt, publicKey);
|
||||
return;
|
||||
case STATUS_SUCCESS:
|
||||
mBytesSoFar += info.mCurrentBytes - startingCount;
|
||||
@ -1139,7 +1144,7 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
break;
|
||||
}
|
||||
if (setWakeWatchdog) {
|
||||
scheduleAlarm(Constants.WATCHDOG_WAKE_TIMER);
|
||||
scheduleAlarm(Constants.WATCHDOG_WAKE_TIMER, false, intent.getExtras());
|
||||
} else {
|
||||
cancelAlarms();
|
||||
}
|
||||
@ -1161,7 +1166,6 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
unregisterReceiver(mConnReceiver);
|
||||
mConnReceiver = null;
|
||||
}
|
||||
mServiceStub.disconnect(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@ -1331,11 +1335,4 @@ public abstract class DownloaderService extends CustomIntentService implements I
|
||||
public void requestDownloadStatus() {
|
||||
mNotification.resendState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientUpdated(Messenger clientMessenger) {
|
||||
this.mClientMessenger = clientMessenger;
|
||||
mNotification.setMessenger(mClientMessenger);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package com.google.android.vending.expansion.downloader.impl;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import com.google.android.vending.expansion.downloader.IDownloaderService;
|
||||
|
||||
class ServiceHandler extends Handler {
|
||||
|
||||
public static final String PARAMS_FLAGS = "flags";
|
||||
|
||||
public static final int MSG_REQUEST_ABORT_DOWNLOAD = 1;
|
||||
public static final int MSG_REQUEST_PAUSE_DOWNLOAD = 2;
|
||||
public static final int MSG_SET_DOWNLOAD_FLAGS = 3;
|
||||
public static final int MSG_REQUEST_CONTINUE_DOWNLOAD = 4;
|
||||
public static final int MSG_REQUEST_DOWNLOAD_STATE = 5;
|
||||
|
||||
private final IDownloaderService service;
|
||||
|
||||
ServiceHandler(IDownloaderService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_REQUEST_ABORT_DOWNLOAD:
|
||||
service.requestAbortDownload();
|
||||
break;
|
||||
case MSG_REQUEST_CONTINUE_DOWNLOAD:
|
||||
service.requestContinueDownload();
|
||||
break;
|
||||
case MSG_REQUEST_PAUSE_DOWNLOAD:
|
||||
service.requestPauseDownload();
|
||||
break;
|
||||
case MSG_SET_DOWNLOAD_FLAGS:
|
||||
service.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
|
||||
break;
|
||||
case MSG_REQUEST_DOWNLOAD_STATE:
|
||||
service.requestDownloadStatus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -33,8 +33,8 @@ import java.util.Vector;
|
||||
/**
|
||||
* Default policy. All policy decisions are based off of response data received
|
||||
* from the licensing service. Specifically, the licensing server sends the
|
||||
* following information: response validity period, error retry period,
|
||||
* error retry count and a URL for restoring app access in unlicensed cases.
|
||||
* following information: response validity period, error retry period, and
|
||||
* error retry count.
|
||||
* <p>
|
||||
* These values will vary based on the the way the application is configured in
|
||||
* the Google Play publishing console, such as whether the application is
|
||||
@ -53,7 +53,6 @@ public class APKExpansionPolicy implements Policy {
|
||||
private static final String PREF_RETRY_UNTIL = "retryUntil";
|
||||
private static final String PREF_MAX_RETRIES = "maxRetries";
|
||||
private static final String PREF_RETRY_COUNT = "retryCount";
|
||||
private static final String PREF_LICENSING_URL = "licensingUrl";
|
||||
private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
|
||||
private static final String DEFAULT_RETRY_UNTIL = "0";
|
||||
private static final String DEFAULT_MAX_RETRIES = "0";
|
||||
@ -67,7 +66,6 @@ public class APKExpansionPolicy implements Policy {
|
||||
private long mRetryCount;
|
||||
private long mLastResponseTime = 0;
|
||||
private int mLastResponse;
|
||||
private String mLicensingUrl;
|
||||
private PreferenceObfuscator mPreferences;
|
||||
private Vector<String> mExpansionURLs = new Vector<String>();
|
||||
private Vector<String> mExpansionFileNames = new Vector<String>();
|
||||
@ -96,7 +94,6 @@ public class APKExpansionPolicy implements Policy {
|
||||
mRetryUntil = Long.parseLong(mPreferences.getString(PREF_RETRY_UNTIL, DEFAULT_RETRY_UNTIL));
|
||||
mMaxRetries = Long.parseLong(mPreferences.getString(PREF_MAX_RETRIES, DEFAULT_MAX_RETRIES));
|
||||
mRetryCount = Long.parseLong(mPreferences.getString(PREF_RETRY_COUNT, DEFAULT_RETRY_COUNT));
|
||||
mLicensingUrl = mPreferences.getString(PREF_LICENSING_URL, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,8 +119,6 @@ public class APKExpansionPolicy implements Policy {
|
||||
* until
|
||||
* <li>GT: the timestamp that the client should ignore retry errors until
|
||||
* <li>GR: the number of retry errors that the client should ignore
|
||||
* <li>LU: a deep link URL that can enable access for unlicensed apps (e.g.
|
||||
* buy app on the Play Store)
|
||||
* </ul>
|
||||
*
|
||||
* @param response the result from validating the server response
|
||||
@ -139,12 +134,10 @@ public class APKExpansionPolicy implements Policy {
|
||||
setRetryCount(mRetryCount + 1);
|
||||
}
|
||||
|
||||
// Update server policy data
|
||||
Map<String, String> extras = decodeExtras(rawData);
|
||||
if (response == Policy.LICENSED) {
|
||||
// Update server policy data
|
||||
Map<String, String> extras = decodeExtras(rawData.extra);
|
||||
mLastResponse = response;
|
||||
// Reset the licensing URL since it is only applicable for NOT_LICENSED responses.
|
||||
setLicensingUrl(null);
|
||||
setValidityTimestamp(Long.toString(System.currentTimeMillis() + MILLIS_PER_MINUTE));
|
||||
Set<String> keys = extras.keySet();
|
||||
for (String key : keys) {
|
||||
@ -166,12 +159,10 @@ public class APKExpansionPolicy implements Policy {
|
||||
}
|
||||
}
|
||||
} else if (response == Policy.NOT_LICENSED) {
|
||||
// Clear out stale retry params
|
||||
// Clear out stale policy data
|
||||
setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
|
||||
setRetryUntil(DEFAULT_RETRY_UNTIL);
|
||||
setMaxRetries(DEFAULT_MAX_RETRIES);
|
||||
// Update the licensing URL
|
||||
setLicensingUrl(extras.get("LU"));
|
||||
}
|
||||
|
||||
setLastResponse(response);
|
||||
@ -284,20 +275,6 @@ public class APKExpansionPolicy implements Policy {
|
||||
return mMaxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the licensing URL that displays a Play Store UI for the user to regain app access.
|
||||
*
|
||||
* @param url the LU string received
|
||||
*/
|
||||
private void setLicensingUrl(String url) {
|
||||
mLicensingUrl = url;
|
||||
mPreferences.putString(PREF_LICENSING_URL, url);
|
||||
}
|
||||
|
||||
public String getLicensingUrl() {
|
||||
return mLicensingUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of expansion URLs. Since expansionURLs are not committed
|
||||
* to preferences, this will return zero if there has been no LVL fetch
|
||||
@ -395,15 +372,10 @@ public class APKExpansionPolicy implements Policy {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Map<String, String> decodeExtras(
|
||||
com.google.android.vending.licensing.ResponseData rawData) {
|
||||
private Map<String, String> decodeExtras(String extras) {
|
||||
Map<String, String> results = new HashMap<String, String>();
|
||||
if (rawData == null) {
|
||||
return results;
|
||||
}
|
||||
|
||||
try {
|
||||
URI rawExtras = new URI("?" + rawData.extra);
|
||||
URI rawExtras = new URI("?" + extras);
|
||||
URIQueryDecoder.DecodeQuery(rawExtras, results);
|
||||
} catch (URISyntaxException e) {
|
||||
Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
|
||||
|
@ -21,7 +21,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
@ -196,20 +195,6 @@ public class LicenseChecker implements ServiceConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the last deep link licensing URL returned from the server, which redirects users to a
|
||||
* page which enables them to gain access to the app. If no such URL is returned by the server, it
|
||||
* will go to the details page of the app in the Play Store.
|
||||
*/
|
||||
public void followLastLicensingUrl(Context context) {
|
||||
String licensingUrl = mPolicy.getLicensingUrl();
|
||||
if (licensingUrl == null) {
|
||||
licensingUrl = "https://play.google.com/store/apps/details?id=" + context.getPackageName();
|
||||
}
|
||||
Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(licensingUrl));
|
||||
context.startActivity(marketIntent);
|
||||
}
|
||||
|
||||
private void runChecks() {
|
||||
LicenseValidator validator;
|
||||
while ((validator = mPendingChecks.poll()) != null) {
|
||||
|
@ -39,9 +39,9 @@ public interface Obfuscator {
|
||||
/**
|
||||
* Undo the transformation applied to data by the obfuscate() method.
|
||||
*
|
||||
* @param obfuscated The data that is to be un-obfuscated.
|
||||
* @param key The key for the data that is to be un-obfuscated.
|
||||
* @return The original data transformed by the obfuscate() method.
|
||||
* @param original The data that is to be obfuscated.
|
||||
* @param key The key for the data that is to be obfuscated.
|
||||
* @return A transformed version of the original data.
|
||||
* @throws ValidationException Optionally thrown if a data integrity check fails.
|
||||
*/
|
||||
String unobfuscate(String obfuscated, String key) throws ValidationException;
|
||||
|
@ -56,10 +56,4 @@ public interface Policy {
|
||||
* Check if the user should be allowed access to the application.
|
||||
*/
|
||||
boolean allowAccess();
|
||||
|
||||
/**
|
||||
* Gets the licensing URL returned by the server that can enable access for unlicensed apps (e.g.
|
||||
* buy app on the Play Store).
|
||||
*/
|
||||
String getLicensingUrl();
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ import com.google.android.vending.licensing.util.URIQueryDecoder;
|
||||
/**
|
||||
* Default policy. All policy decisions are based off of response data received
|
||||
* from the licensing service. Specifically, the licensing server sends the
|
||||
* following information: response validity period, error retry period,
|
||||
* error retry count and a URL for restoring app access in unlicensed cases.
|
||||
* following information: response validity period, error retry period, and
|
||||
* error retry count.
|
||||
* <p>
|
||||
* These values will vary based on the the way the application is configured in
|
||||
* the Google Play publishing console, such as whether the application is
|
||||
@ -50,7 +50,6 @@ public class ServerManagedPolicy implements Policy {
|
||||
private static final String PREF_RETRY_UNTIL = "retryUntil";
|
||||
private static final String PREF_MAX_RETRIES = "maxRetries";
|
||||
private static final String PREF_RETRY_COUNT = "retryCount";
|
||||
private static final String PREF_LICENSING_URL = "licensingUrl";
|
||||
private static final String DEFAULT_VALIDITY_TIMESTAMP = "0";
|
||||
private static final String DEFAULT_RETRY_UNTIL = "0";
|
||||
private static final String DEFAULT_MAX_RETRIES = "0";
|
||||
@ -64,7 +63,6 @@ public class ServerManagedPolicy implements Policy {
|
||||
private long mRetryCount;
|
||||
private long mLastResponseTime = 0;
|
||||
private int mLastResponse;
|
||||
private String mLicensingUrl;
|
||||
private PreferenceObfuscator mPreferences;
|
||||
|
||||
/**
|
||||
@ -82,7 +80,6 @@ public class ServerManagedPolicy implements Policy {
|
||||
mRetryUntil = Long.parseLong(mPreferences.getString(PREF_RETRY_UNTIL, DEFAULT_RETRY_UNTIL));
|
||||
mMaxRetries = Long.parseLong(mPreferences.getString(PREF_MAX_RETRIES, DEFAULT_MAX_RETRIES));
|
||||
mRetryCount = Long.parseLong(mPreferences.getString(PREF_RETRY_COUNT, DEFAULT_RETRY_COUNT));
|
||||
mLicensingUrl = mPreferences.getString(PREF_LICENSING_URL, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,12 +88,10 @@ public class ServerManagedPolicy implements Policy {
|
||||
* This data will be used for computing future policy decisions. The
|
||||
* following parameters are processed:
|
||||
* <ul>
|
||||
* <li>VT: the timestamp that the client should consider the response valid
|
||||
* until
|
||||
* <li>VT: the timestamp that the client should consider the response
|
||||
* valid until
|
||||
* <li>GT: the timestamp that the client should ignore retry errors until
|
||||
* <li>GR: the number of retry errors that the client should ignore
|
||||
* <li>LU: a deep link URL that can enable access for unlicensed apps (e.g.
|
||||
* buy app on the Play Store)
|
||||
* </ul>
|
||||
*
|
||||
* @param response the result from validating the server response
|
||||
@ -111,22 +106,18 @@ public class ServerManagedPolicy implements Policy {
|
||||
setRetryCount(mRetryCount + 1);
|
||||
}
|
||||
|
||||
// Update server policy data
|
||||
Map<String, String> extras = decodeExtras(rawData);
|
||||
if (response == Policy.LICENSED) {
|
||||
// Update server policy data
|
||||
Map<String, String> extras = decodeExtras(rawData.extra);
|
||||
mLastResponse = response;
|
||||
// Reset the licensing URL since it is only applicable for NOT_LICENSED responses.
|
||||
setLicensingUrl(null);
|
||||
setValidityTimestamp(extras.get("VT"));
|
||||
setRetryUntil(extras.get("GT"));
|
||||
setMaxRetries(extras.get("GR"));
|
||||
} else if (response == Policy.NOT_LICENSED) {
|
||||
// Clear out stale retry params
|
||||
// Clear out stale policy data
|
||||
setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
|
||||
setRetryUntil(DEFAULT_RETRY_UNTIL);
|
||||
setMaxRetries(DEFAULT_MAX_RETRIES);
|
||||
// Update the licensing URL
|
||||
setLicensingUrl(extras.get("LU"));
|
||||
}
|
||||
|
||||
setLastResponse(response);
|
||||
@ -239,21 +230,6 @@ public class ServerManagedPolicy implements Policy {
|
||||
return mMaxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the license URL value (LU) as received from the server and add to preferences. You must
|
||||
* manually call PreferenceObfuscator.commit() to commit these changes to disk.
|
||||
*
|
||||
* @param url the LU string received
|
||||
*/
|
||||
private void setLicensingUrl(String url) {
|
||||
mLicensingUrl = url;
|
||||
mPreferences.putString(PREF_LICENSING_URL, url);
|
||||
}
|
||||
|
||||
public String getLicensingUrl() {
|
||||
return mLicensingUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -281,15 +257,10 @@ public class ServerManagedPolicy implements Policy {
|
||||
return false;
|
||||
}
|
||||
|
||||
private Map<String, String> decodeExtras(
|
||||
com.google.android.vending.licensing.ResponseData rawData) {
|
||||
private Map<String, String> decodeExtras(String extras) {
|
||||
Map<String, String> results = new HashMap<String, String>();
|
||||
if (rawData == null) {
|
||||
return results;
|
||||
}
|
||||
|
||||
try {
|
||||
URI rawExtras = new URI("?" + rawData.extra);
|
||||
URI rawExtras = new URI("?" + extras);
|
||||
URIQueryDecoder.DecodeQuery(rawExtras, results);
|
||||
} catch (URISyntaxException e) {
|
||||
Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
|
||||
|
@ -16,13 +16,6 @@
|
||||
|
||||
package com.google.android.vending.licensing;
|
||||
|
||||
import android.util.Log;
|
||||
import com.google.android.vending.licensing.util.URIQueryDecoder;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Non-caching policy. All requests will be sent to the licensing service,
|
||||
* and no local caching is performed.
|
||||
@ -33,38 +26,28 @@ import java.util.Map;
|
||||
* weigh the risks of using this Policy over one which implements caching,
|
||||
* such as ServerManagedPolicy.
|
||||
* <p>
|
||||
* Access to the application is only allowed if a LICENSED response is.
|
||||
* Access to the application is only allowed if a LICESNED response is.
|
||||
* received. All other responses (including RETRY) will deny access.
|
||||
*/
|
||||
public class StrictPolicy implements Policy {
|
||||
|
||||
private static final String TAG = "StrictPolicy";
|
||||
|
||||
private int mLastResponse;
|
||||
private String mLicensingUrl;
|
||||
|
||||
public StrictPolicy() {
|
||||
// Set default policy. This will force the application to check the policy on launch.
|
||||
mLastResponse = Policy.RETRY;
|
||||
mLicensingUrl = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a new response from the license server. Since we aren't
|
||||
* performing any caching, this equates to reading the LicenseResponse.
|
||||
* Any cache-related ResponseData is ignored, but the licensing URL
|
||||
* extra is still extracted in cases where the app is unlicensed.
|
||||
* Any ResponseData provided is ignored.
|
||||
*
|
||||
* @param response the result from validating the server response
|
||||
* @param rawData the raw server response data
|
||||
*/
|
||||
public void processServerResponse(int response, ResponseData rawData) {
|
||||
mLastResponse = response;
|
||||
|
||||
if (response == Policy.NOT_LICENSED) {
|
||||
Map<String, String> extras = decodeExtras(rawData);
|
||||
mLicensingUrl = extras.get("LU");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,24 +60,4 @@ public class StrictPolicy implements Policy {
|
||||
return (mLastResponse == Policy.LICENSED);
|
||||
}
|
||||
|
||||
public String getLicensingUrl() {
|
||||
return mLicensingUrl;
|
||||
}
|
||||
|
||||
private Map<String, String> decodeExtras(
|
||||
com.google.android.vending.licensing.ResponseData rawData) {
|
||||
Map<String, String> results = new HashMap<String, String>();
|
||||
if (rawData == null) {
|
||||
return results;
|
||||
}
|
||||
|
||||
try {
|
||||
URI rawExtras = new URI("?" + rawData.extra);
|
||||
URIQueryDecoder.DecodeQuery(rawExtras, results);
|
||||
} catch (URISyntaxException e) {
|
||||
Log.w(TAG, "Invalid syntax error while decoding extras data from server.");
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,8 +64,8 @@
|
||||
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
|
||||
<service android:name="com.falsinsoft.QtAndroidTools.ApkExpansionDownloaderService"/>
|
||||
<receiver android:name="com.falsinsoft.QtAndroidTools.ApkExpansionDownloaderAlarmReceiver"/>
|
||||
<service android:name="com.google.android.vending.expansion.downloader.impl.DownloaderService" android:enabled="true"/>
|
||||
<receiver android:name="com.google.android.vending.expansion.downloader.impl.DownloaderService$AlarmReceiver" android:enabled="true"/>
|
||||
|
||||
</application>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user