diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb4d1a7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,86 @@
+# Created by https://www.gitignore.io
+
+### Java ###
+*.class
+
+
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+*.iml
+
+## Directory-based project format:
+.idea/
+# if you remove the above rule, at least ignore the follwing:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+## File-based project format:
+# *.ipr
+# *.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+
+#Generated files
+build/
+
+#Build folders of Android Studio
+**/build/
+
+### Android ###
+# Built application files
+*.apk
+# *.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+# proguard/
+
+# Log Files
+*.log
+
+#Ignorando SVN!!
+**/.svn/
+.svn/
+
+app/mirror
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..1b7886d
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4762009
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Sep 10 09:54:12 BRT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/medescope/build.gradle b/medescope/build.gradle
new file mode 100644
index 0000000..52df0f8
--- /dev/null
+++ b/medescope/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.0"
+
+ defaultConfig {
+ minSdkVersion 10
+ targetSdkVersion 23
+ versionCode 2
+ versionName "1.0.1"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:23.0.0'
+ compile 'com.google.code.gson:gson:2.3'
+}
diff --git a/medescope/proguard-rules.pro b/medescope/proguard-rules.pro
new file mode 100644
index 0000000..a9ca86e
--- /dev/null
+++ b/medescope/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/bruno.costa/android-sdks/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/medescope/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java b/medescope/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java
new file mode 100644
index 0000000..497d423
--- /dev/null
+++ b/medescope/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java
@@ -0,0 +1,13 @@
+package br.com.bemobi.medescope;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/medescope/src/main/AndroidManifest.xml b/medescope/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b68e455
--- /dev/null
+++ b/medescope/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/DownloadFileUtils.java b/medescope/src/main/java/br/com/bemobi/medescope/DownloadFileUtils.java
new file mode 100644
index 0000000..bb07239
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/DownloadFileUtils.java
@@ -0,0 +1,29 @@
+package br.com.bemobi.medescope;
+
+import android.os.Environment;
+
+/**
+ * Created by bruno.costa on 10/07/15.
+ */
+public class DownloadFileUtils {
+
+ /* Checks if external storage is available for read and write */
+ public static boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ return true;
+ }
+ return false;
+ }
+
+ /* Checks if external storage is available to at least read */
+ public static boolean isExternalStorageReadable() {
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state) ||
+ Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/Medescope.java b/medescope/src/main/java/br/com/bemobi/medescope/Medescope.java
new file mode 100644
index 0000000..e048c53
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/Medescope.java
@@ -0,0 +1,219 @@
+package br.com.bemobi.medescope;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Environment;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.util.Map;
+
+import br.com.bemobi.medescope.callback.DownloadStatusCallback;
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.exception.DirectoryNotMountedException;
+import br.com.bemobi.medescope.exception.PathNotFoundException;
+import br.com.bemobi.medescope.log.Logger;
+import br.com.bemobi.medescope.model.DownloadRequest;
+import br.com.bemobi.medescope.service.impl.DownloadCommandService;
+
+/**
+ * Created by bruno.costa on 26/06/15.
+ */
+public class Medescope {
+
+
+ private final String TAG = Medescope.class.getSimpleName();
+
+ public static final String ACTION_BROADCAST_IN_PROGRESS = "br.com.bemobi.medescope.ACTION_BROADCAST_IN_PROGRESS";
+ public static final String ACTION_BROADCAST_PAUSED = "br.com.bemobi.medescope.ACTION_BROADCAST_PAUSED";
+ public static final String ACTION_BROADCAST_NOT_ENQUEUED = "br.com.bemobi.medescope.ACTION_BROADCAST_NOT_ENQUEUED";
+ public static final String ACTION_BROADCAST_CANCELLED = "br.com.bemobi.medescope.ACTION_BROADCAST_CANCELLED";
+ public static final String ACTION_BROADCAST_FINISH_WITH_SUCCESS = "br.com.bemobi.medescope.ACTION_BROADCAST_FINISH_WITH_SUCCESS";
+ public static final String ACTION_BROADCAST_FINISH_WITH_ERROR = "br.com.bemobi.medescope.ACTION_BROADCAST_FINISH_WITH_ERROR";
+
+ private Context mContext;
+
+ private static Medescope instance;
+
+ private static StatusBroadcastReceiver mReceiver;
+
+ private String applicationName;
+
+ private Medescope(Context context) {
+ this.mContext = context.getApplicationContext();
+ Logger.setContext(context.getApplicationContext());
+ this.applicationName = "";
+ }
+
+ public static Medescope getInstance(Context context) {
+ if( instance == null) {
+ instance = new Medescope(context);
+ mReceiver = new StatusBroadcastReceiver();
+ }
+ return instance;
+ }
+
+ public void setApplicationName(String applicationName){
+ this.applicationName = applicationName;
+ }
+
+ public void enqueue(String id, String uri, String fileName, String downloadName, String developerPayload) {
+ DownloadRequest downloadRequest = new DownloadRequest();
+ downloadRequest.setId(id);
+ downloadRequest.setUri(uri);
+ downloadRequest.setFileName(fileName);
+ downloadRequest.setDownloadName(downloadName);
+ downloadRequest.setDownloadDescription(this.applicationName);
+ downloadRequest.setClientPayload(developerPayload);
+ downloadRequest.setShouldDownloadOnlyInWifi(false);
+ enqueue(downloadRequest);
+ }
+
+ public void enqueue(String id, String uri, String fileName, String downloadName, String developerPayload, boolean shouldDownloadOnlyInWifi) {
+ DownloadRequest downloadRequest = new DownloadRequest();
+ downloadRequest.setId(id);
+ downloadRequest.setUri(uri);
+ downloadRequest.setFileName(fileName);
+ downloadRequest.setDownloadName(downloadName);
+ downloadRequest.setDownloadDescription(this.applicationName);
+ downloadRequest.setClientPayload(developerPayload);
+ downloadRequest.setShouldDownloadOnlyInWifi(shouldDownloadOnlyInWifi);
+ enqueue(downloadRequest);
+ }
+
+ public void enqueue(String id, String uri, String fileName, String downloadName, String developerPayload, boolean shouldDownloadOnlyInWifi, Map customHeaders) {
+ DownloadRequest downloadRequest = new DownloadRequest();
+ downloadRequest.setId(id);
+ downloadRequest.setUri(uri);
+ downloadRequest.setFileName(fileName);
+ downloadRequest.setDownloadName(downloadName);
+ downloadRequest.setDownloadDescription(this.applicationName);
+ downloadRequest.setClientPayload(developerPayload);
+ if(customHeaders != null && !customHeaders.isEmpty()) {
+ downloadRequest.setCustomHeaders(customHeaders);
+ } else {
+ Logger.error(TAG, "YOU, NASTY DEVELOPER, ARE PASSING A NULL OR EMPTY HEADER MAP");
+ }
+ downloadRequest.setShouldDownloadOnlyInWifi(shouldDownloadOnlyInWifi);
+ enqueue(downloadRequest);
+ }
+
+ public void enqueue(DownloadRequest request) {
+ if(request.isValid()){
+ DownloadCommandService.actionEnqueue(mContext, request);
+ return;
+ }
+ Logger.error(TAG, "This is not a valid Request!");
+ Logger.error(TAG, "Please fill it up with the basic data");
+ }
+
+ public void cancel(String id){
+ DownloadCommandService.actionCancel(mContext, id);
+ }
+
+ public void updateSubscriptionStatusId(Context context, String id){
+ if(mReceiver.isCallbackSet()){
+ Logger.error(TAG, "YOU HAVE NOT SET A CALLBACK YET!!");
+ Logger.error(TAG, "YOU SHOULD IMPLEMENT A BROADCAST RECEIVER BY YOURSELF");
+ }
+ DownloadCommandService.actionSubscribeStatusUpdate(context, id);
+ }
+
+ public void subscribeStatus(Activity activity, String id, DownloadStatusCallback callback){
+ mReceiver.setCallback(callback);
+ activity.registerReceiver(mReceiver, getStatusBroadcastFilter());
+ DownloadCommandService.actionSubscribeStatusUpdate(mContext, id);
+ }
+
+ public void unsubscribeStatus(Activity activity){
+ activity.unregisterReceiver(mReceiver);
+ DownloadCommandService.actionUnsubscribeStatusUpdate(mContext);
+ }
+
+ public String getDownloadDirectoryToRead(String subPath) throws DirectoryNotMountedException, PathNotFoundException {
+ if( !DownloadFileUtils.isExternalStorageReadable() ) {
+ throw new DirectoryNotMountedException("Directory is not mounted to read!");
+ }
+ return getDownloadDirectory(subPath);
+ }
+
+ public String getDownloadDirectoryToWrite(String subPath) throws DirectoryNotMountedException, PathNotFoundException {
+ if( !DownloadFileUtils.isExternalStorageWritable() ) {
+ throw new DirectoryNotMountedException("Directory is not mounted to write!");
+ }
+ return getDownloadDirectory(subPath);
+ }
+
+ private String getDownloadDirectory(String subPath) throws PathNotFoundException {
+ File file = mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
+ if(file == null) {
+ throw new PathNotFoundException("External Path DIRECTORY_DOWNLOADS was not found!!");
+ }
+ if(TextUtils.isEmpty(subPath)){
+ return file.getAbsolutePath();
+ }
+ return file.getAbsolutePath() + (subPath.startsWith("/") ? subPath : "/" + subPath);
+ }
+
+ private IntentFilter getStatusBroadcastFilter(){
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Medescope.ACTION_BROADCAST_NOT_ENQUEUED);
+ filter.addAction(Medescope.ACTION_BROADCAST_PAUSED);
+ filter.addAction(Medescope.ACTION_BROADCAST_IN_PROGRESS);
+ filter.addAction(Medescope.ACTION_BROADCAST_CANCELLED);
+ filter.addAction(Medescope.ACTION_BROADCAST_FINISH_WITH_ERROR);
+ filter.addAction(Medescope.ACTION_BROADCAST_FINISH_WITH_SUCCESS);
+ return filter;
+ }
+
+
+ private static class StatusBroadcastReceiver extends BroadcastReceiver {
+
+ private final String TAG = StatusBroadcastReceiver.class.getName();
+
+ private DownloadStatusCallback mCallback;
+
+ public StatusBroadcastReceiver() {
+ }
+
+ public void setCallback(DownloadStatusCallback callback){
+ this.mCallback = callback;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(mCallback != null) {
+ String downloadId = intent.getStringExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID);
+ String action = intent.getAction();
+ if (Medescope.ACTION_BROADCAST_IN_PROGRESS.equals(action)) {
+ int progress = intent.getIntExtra(DownloadConstants.EXTRA_INT_PROGRESS_PERCENTAGE, 0);
+ mCallback.onDownloadInProgress(downloadId, progress);
+ } else if (Medescope.ACTION_BROADCAST_PAUSED.equals(action)) {
+ int reason = intent.getIntExtra(DownloadConstants.EXTRA_INT_REASON_KEY, 0);
+ mCallback.onDownloadPaused(downloadId, reason);
+ } else if (Medescope.ACTION_BROADCAST_NOT_ENQUEUED.equals(action)) {
+ mCallback.onDownloadNotEnqueued(downloadId);
+ } else if (Medescope.ACTION_BROADCAST_CANCELLED.equals(action)){
+ mCallback.onDownloadCancelled(downloadId);
+ } else if (Medescope.ACTION_BROADCAST_FINISH_WITH_ERROR.equals(action)) {
+ int reason = intent.getIntExtra(DownloadConstants.EXTRA_INT_REASON_KEY, 0);
+ String data = intent.getStringExtra(DownloadConstants.EXTRA_STRING_JSON_DATA);
+ mCallback.onDownloadOnFinishedWithError(downloadId, reason, data);
+ } else if (Medescope.ACTION_BROADCAST_FINISH_WITH_SUCCESS.equals(action)) {
+ String filePath = intent.getStringExtra(DownloadConstants.EXTRA_STRING_FILE_PATH);
+ String data = intent.getStringExtra(DownloadConstants.EXTRA_STRING_JSON_DATA);
+ mCallback.onDownloadOnFinishedWithSuccess(downloadId, filePath, data);
+ }
+ } else {
+ Logger.error(TAG, "You forgot to set a callback!!");
+ }
+ }
+
+ public boolean isCallbackSet() {
+ return mCallback != null;
+ }
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/callback/DownloadStatusCallback.java b/medescope/src/main/java/br/com/bemobi/medescope/callback/DownloadStatusCallback.java
new file mode 100644
index 0000000..12681f1
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/callback/DownloadStatusCallback.java
@@ -0,0 +1,18 @@
+package br.com.bemobi.medescope.callback;
+
+/**
+ * Created by bruno.costa on 08/07/15.
+ */
+public interface DownloadStatusCallback {
+ void onDownloadNotEnqueued(String downloadId);
+
+ void onDownloadPaused(String downloadId, int reason);
+
+ void onDownloadInProgress(String downloadId, int progress);
+
+ void onDownloadOnFinishedWithError(String downloadId, int reason, String data);
+
+ void onDownloadOnFinishedWithSuccess(String downloadId, String filePath, String data);
+
+ void onDownloadCancelled(String downloadId);
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadConstants.java b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadConstants.java
new file mode 100644
index 0000000..a446409
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadConstants.java
@@ -0,0 +1,33 @@
+package br.com.bemobi.medescope.constant;
+
+/**
+ * Created by bruno.costa on 26/06/15.
+ */
+public class DownloadConstants {
+// public static final String EXTRA_STRING_ID = "EXTRA_STRING_ID";
+// public static final String EXTRA_STRING_URI = "EXTRA_STRING_URI";
+// public static final String EXTRA_STRING_DIRECTORY_PATH = "EXTRA_STRING_DIRECTORY_PATH";
+// public static final String EXTRA_STRING_FILE_NAME = "EXTRA_STRING_FILE_NAME";
+ public static final String EXTRA_STRING_FILE_PATH = "EXTRA_STRING_FILE_PATH";
+ public static final String EXTRA_STRING_DOWNLOAD_ID = "EXTRA_STRING_DOWNLOAD_ID";
+ public static final String EXTRA_ARRAY_STRING_DOWNLOAD_IDS = "EXTRA_ARRAY_STRING_DOWNLOAD_IDS";
+// public static final String EXTRA_STRING_TITLE = "EXTRA_STRING_TITLE";
+// public static final String EXTRA_STRING_DESCRIPTION = "EXTRA_STRING_DESCRIPTION";
+ public static final String EXTRA_STRING_JSON_DATA = "EXTRA_STRING_JSON_DATA";
+// public static final String EXTRA_MAP_STRING_CUSTOM_HEADERS = "EXTRA_MAP_STRING_CUSTOM_HEADERS";
+ public static final String EXTRA_INT_PROGRESS_PERCENTAGE = "EXTRA_INT_PROGRESS_PERCENTAGE";
+ public static final String EXTRA_INT_ERROR_REASON = "EXTRA_INT_ERROR_REASON";
+ public static final String EXTRA_DOWNLOAD_INFO = "EXTRA_DOWNLOAD_INFO";
+ public static final String EXTRA_INT_REASON_KEY = "EXTRA_INT_REASON_KEY";
+
+ public static final String DATA_MAP_PREF = "br.com.bemobi.download.DATA_MAP_PREF";
+ public static final String DM_STRING_IDS_LIB_TO_DMIDS_MAP_PREF = "br.com.bemobi.medescope.DM_STRING_IDS_LIB_TO_DMIDS_MAP_PREF";
+ public static final String DM_STRING_IDS_DMIDS_TO_LIB_MAP_PREF = "br.com.bemobi.medescope.DM_STRING_IDS_DMIDS_TO_LIB_MAP_PREF";
+
+ public static final String LOG_FEATURE_DOWNLOAD = "BEMOBI_DOWNLOAD_LIB";
+ public static final String LOG_FEATURE_SERVICE_LIFECYCLE = "SERVICE_LIFECYCLE";
+ public static final String LOG_FEATURE_DOWNLOAD_DATA_STATE = "DOWNLOAD_DATA_STATE";
+ public static final String LOG_FEATURE_DOWNLOAD_SEND_PROGRESS = "LOG_FEATURE_DOWNLOAD_SEND_PROGRESS";
+
+ public static final String PREF_SUBSCRIBED_ID = "PREF_SUBSCRIBED_ID";
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoReasonConstants.java b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoReasonConstants.java
new file mode 100644
index 0000000..956d01f
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoReasonConstants.java
@@ -0,0 +1,155 @@
+package br.com.bemobi.medescope.constant;
+
+/**
+ * Created by raphael on 7/7/15.
+ */
+public class DownloadInfoReasonConstants {
+
+ /**
+ * Unknown error. :P
+ */
+ //public final static int REASON_UNKNOWN_ERROR = 0;
+ public final static int PAUSED_GENERIC_ERROR = 2000;
+
+
+ /**
+ * Value of COLUMN_REASON when the download is paused because some network error occurred and the download manager is waiting before retrying the request.
+ */
+ //public final static int REASON_PAUSED_WAITING_TO_RETRY = 1;
+ public final static int PAUSED_WAITING_NETWORK_AFTER_ERROR = 2001;
+
+ /**
+ * Value of COLUMN_REASON when the download is waiting for network connectivity to proceed.
+ */
+ //public final static int REASON_PAUSED_WAITING_FOR_NETWORK = 2;
+ public final static int PAUSED_WAITING_NETWORK = 2002;
+
+ /**
+ * Value of COLUMN_REASON when the download exceeds a size limit for downloads over the mobile network and the download manager is waiting for a Wi-Fi connection to proceed.
+ */
+ //public final static int PAUSED_QUEUED_FOR_WIFI = 3;
+ public final static int PAUSED_QUEUED_TO_DOWNLOAD_ON_WIFI = 2003;
+
+ /**
+ * Value of COLUMN_REASON when the download is paused for some other reason.
+ */
+ //public final static int REASON_PAUSED_UNKNOWN = 4;
+ public final static int PAUSED_GENERIC_REASON = 2004;
+
+
+
+
+
+
+
+ /**
+ * Value of COLUMN_REASON when a storage issue arises which doesn't fit under any other error code.
+ */
+ //public final static int REASON_ERROR_FILE_ERROR = 1001;
+ public final static int ERROR_FILE_GENERIC_PROBLEM = 3001;
+
+ /**
+ * Value of COLUMN_REASON when an HTTP code was received that download manager can't handle.
+ */
+ //public final static int REASON_ERROR_UNHANDLED_HTTP_CODE = 1002;
+ public final static int ERROR_UNHANDLED_HTTP_CODE = 3002;
+
+ /**
+ * Value of COLUMN_REASON when an error receiving or processing data occurred at the HTTP level.
+ */
+ //public final static int REASON_ERROR_HTTP_DATA_ERROR = 1004;
+ public final static int ERROR_HTTP_DATA_ERROR = 3004;
+
+ /**
+ * Value of COLUMN_REASON when there were too many redirects.
+ */
+ //public final static int REASON_ERROR_TOO_MANY_REDIRECTS = 1005;
+ public final static int ERROR_TOO_MANY_REDIRECTS = 3005;
+
+ /**
+ * Value of COLUMN_REASON when there was insufficient storage space.
+ */
+ //public final static int REASON_ERROR_INSUFFICIENT_SPACE = 1006;
+ public final static int ERROR_INSUFFICIENT_STORAGE = 3006;
+
+ /**
+ * Value of COLUMN_REASON when no external storage device was found.
+ */
+ //public final static int REASON_ERROR_DEVICE_NOT_FOUND = 1007;
+ public final static int ERROR_STORAGE_NOT_FOUND = 3007;
+
+ /**
+ * Value of COLUMN_REASON when some possibly transient error occurred but we can't resume the download.
+ */
+ //public final static int REASON_ERROR_CANNOT_RESUME = 1008;
+ public final static int ERROR_GENERIC = 3008;
+
+ /**
+ * Value of COLUMN_REASON when the requested destination file already exists (the download manager will not overwrite an existing file).
+ */
+ //public final static int REASON_ERROR_FILE_ALREADY_EXISTS = 1009;
+ public final static int ERROR_ALREADY_EXISTING_FILE = 3009;
+
+ /**
+ * Value of COLUMN_REASON when a storage issue arises which doesn't fit under any other error code.
+ */
+ //public final static int REASON_ERROR_BLOCKED = 1010;
+ public final static int ERROR_GENERIC_STORAGE_PROBLEM = 3010;
+
+ public static String getDownloadReasonText(int reason) {
+ String reasonStr = "";
+
+ switch(reason) {
+ case DownloadInfoReasonConstants.ERROR_GENERIC: {
+ reasonStr = "ERROR_GENERIC";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_STORAGE_NOT_FOUND: {
+ reasonStr = "ERROR_STORAGE_NOT_FOUND";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_ALREADY_EXISTING_FILE: {
+ reasonStr = "ERROR_ALREADY_EXISTING_FILE";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_FILE_GENERIC_PROBLEM: {
+ reasonStr = "ERROR_FILE_GENERIC_PROBLEM";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_HTTP_DATA_ERROR: {
+ reasonStr = "ERROR_HTTP_DATA_ERROR";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_INSUFFICIENT_STORAGE: {
+ reasonStr = "ERROR_INSUFFICIENT_STORAGE";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_TOO_MANY_REDIRECTS: {
+ reasonStr = "ERROR_TOO_MANY_REDIRECTS";
+ break;
+ }
+ case DownloadInfoReasonConstants.ERROR_UNHANDLED_HTTP_CODE: {
+ reasonStr = "ERROR_UNHANDLED_HTTP_CODE";
+ break;
+ }
+ case DownloadInfoReasonConstants.PAUSED_QUEUED_TO_DOWNLOAD_ON_WIFI: {
+ reasonStr = "PAUSED_QUEUED_TO_DOWNLOAD_ON_WIFI";
+ break;
+ }
+ case DownloadInfoReasonConstants.PAUSED_GENERIC_REASON: {
+ reasonStr = "PAUSED_GENERIC_REASON";
+ break;
+ }
+ case DownloadInfoReasonConstants.PAUSED_WAITING_NETWORK: {
+ reasonStr = "PAUSED_WAITING_NETWORK";
+ break;
+ }
+ case DownloadInfoReasonConstants.PAUSED_WAITING_NETWORK_AFTER_ERROR: {
+ reasonStr = "PAUSED_WAITING_NETWORK_AFTER_ERROR";
+ break;
+ }
+ }
+
+ return reasonStr;
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoStatusConstants.java b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoStatusConstants.java
new file mode 100644
index 0000000..1cb5401
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/constant/DownloadInfoStatusConstants.java
@@ -0,0 +1,20 @@
+package br.com.bemobi.medescope.constant;
+
+/**
+ * Created by raphael on 7/7/15.
+ */
+public class DownloadInfoStatusConstants {
+ public final static int NOT_ENQUEUED_STATUS = -1;
+
+ public final static int PENDING_STATUS = 1 << 0;
+
+ public final static int IN_PROGRESS_STATUS = 1 << 1;
+
+ public final static int PAUSED_STATUS = 1 << 2;
+
+ public final static int SUCCESSFUL_STATUS = 1 << 3;
+
+ public final static int FAILED_STATUS = 1 << 4;
+
+ public final static int UNKNOWN_ERROR = 1000;
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/constant/Extras.java b/medescope/src/main/java/br/com/bemobi/medescope/constant/Extras.java
new file mode 100644
index 0000000..4d79e62
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/constant/Extras.java
@@ -0,0 +1,9 @@
+package br.com.bemobi.medescope.constant;
+
+/**
+ * Created by luis.fernandez on 7/5/15.
+ */
+public class Extras {
+
+ public static final String EXTRA_DOWNLOAD = "br.com.bemobi.medescope.EXTRA_DOWNLOAD";
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/exception/DirectoryNotMountedException.java b/medescope/src/main/java/br/com/bemobi/medescope/exception/DirectoryNotMountedException.java
new file mode 100644
index 0000000..af5f20c
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/exception/DirectoryNotMountedException.java
@@ -0,0 +1,23 @@
+package br.com.bemobi.medescope.exception;
+
+/**
+ * Created by bruno.costa on 10/07/15.
+ */
+public class DirectoryNotMountedException extends DownloadBaseException {
+
+ public DirectoryNotMountedException() {
+ }
+
+ public DirectoryNotMountedException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public DirectoryNotMountedException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public DirectoryNotMountedException(Throwable throwable) {
+ super(throwable);
+ }
+
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/exception/DownloadBaseException.java b/medescope/src/main/java/br/com/bemobi/medescope/exception/DownloadBaseException.java
new file mode 100644
index 0000000..d3079b3
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/exception/DownloadBaseException.java
@@ -0,0 +1,23 @@
+package br.com.bemobi.medescope.exception;
+
+/**
+ * Created by bruno.costa on 10/07/15.
+ */
+public class DownloadBaseException extends Exception {
+
+ public DownloadBaseException() {
+ }
+
+ public DownloadBaseException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public DownloadBaseException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public DownloadBaseException(Throwable throwable) {
+ super(throwable);
+ }
+
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/exception/PathNotFoundException.java b/medescope/src/main/java/br/com/bemobi/medescope/exception/PathNotFoundException.java
new file mode 100644
index 0000000..a3f423e
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/exception/PathNotFoundException.java
@@ -0,0 +1,22 @@
+package br.com.bemobi.medescope.exception;
+
+/**
+ * Created by bruno.costa on 10/07/15.
+ */
+public class PathNotFoundException extends DownloadBaseException {
+
+ public PathNotFoundException() {
+ }
+
+ public PathNotFoundException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public PathNotFoundException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public PathNotFoundException(Throwable throwable) {
+ super(throwable);
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/log/IntentLogger.java b/medescope/src/main/java/br/com/bemobi/medescope/log/IntentLogger.java
new file mode 100644
index 0000000..46f726e
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/log/IntentLogger.java
@@ -0,0 +1,52 @@
+package br.com.bemobi.medescope.log;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by luisfernandez on 7/3/15.
+ */
+public class IntentLogger {
+ private static final String TAG = "IntentLogger";
+
+ private String from;
+ private String feature;
+
+ public IntentLogger(String from, String feature) {
+ this.from = from;
+ this.feature = feature;
+ }
+
+ public void logIntent(Intent intent) {
+ if (intent != null) {
+ this.logBundle(intent.getExtras());
+ }
+ }
+
+ public void logBundle(Bundle bundle) {
+ Logger.debug(TAG, feature, String.format(">>>>>>>>>>>>>>>>>>>>>>>>>> DUMPING BUNDLE from: %s", from));
+
+ if (bundle != null) {
+ Set keys = bundle.keySet();
+ Iterator it = keys.iterator();
+ while (it.hasNext()) {
+ String key = it.next();
+ Object object = bundle.get(key);
+
+ if (object != null) {
+ String[] values = object.toString().split(", ");
+
+ for (int i = 0; i < values.length; i++) {
+ Logger.debug(TAG, feature, String.format(" [%s] = %s", key, values[i]));
+ }
+ }
+
+ }
+
+ Logger.debug(TAG, feature, "\n");
+ }
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/log/Logger.java b/medescope/src/main/java/br/com/bemobi/medescope/log/Logger.java
new file mode 100644
index 0000000..f77ed02
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/log/Logger.java
@@ -0,0 +1,141 @@
+package br.com.bemobi.medescope.log;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Created by brunocosta on 9/10/15.
+ */
+public class Logger {
+
+ private static final String TAG_tag = Logger.class.getSimpleName();
+
+ public static void setContext(Context mContext) {
+ LogConfig.getInstance().setContext(mContext);
+ }
+
+ private static String getString(String word) {
+ return (word == null) ? "null" : word;
+ }
+
+ public static void debug(String tag, String msg) {
+ if (LogConfig.getInstance().isDebug()) {
+ Log.d(TAG_tag, getString(tag) + ":" + getString(msg));
+ }
+ }
+
+ public static void debug(String tag, String feature, String msg) {
+ if (LogConfig.getInstance().isDebug()) {
+ if (LogConfig.getInstance().isDetailed()) {
+ Log.d(TAG_tag, getString(tag) + " [FEATURE:] " + getString(feature) + " > " + getString(msg));
+ } else {
+ Log.d(TAG_tag, getString(feature) + " > " + getString(msg));
+ }
+ }
+ }
+
+ public static void debug(String tag, String msg, Throwable throwable) {
+ if (LogConfig.getInstance().isDebug()) {
+ Log.d(TAG_tag, getString(tag) + ":" + getString(msg), throwable);
+ }
+ }
+
+ public static void error(String tag, String msg) {
+ if (LogConfig.getInstance().isError()) {
+ Log.e(TAG_tag, getString(tag) + ":" + getString(msg));
+ }
+ }
+
+ public static void error(String tag, String feature, String msg) {
+ if (LogConfig.getInstance().isDebug()) {
+ Log.e(TAG_tag, getString(tag) + " [FEATURE:] " + getString(feature) + " > " + getString(msg));
+ }
+ }
+
+ public static void error(String tag, String msg, Throwable th) {
+ if (LogConfig.getInstance().isError()) {
+ Log.e(TAG_tag, getString(tag) + ":" + getString(msg), th);
+ }
+ }
+
+ public static class LogConfig {
+ private boolean detailed = false;
+
+ private boolean debug = false;
+
+ private boolean error = false;
+
+ private int showWarnCount = 0;
+
+ private static LogConfig instance;
+
+ private Context mContext;
+
+ protected static LogConfig getInstance() {
+ if (instance == null) {
+ instance = new LogConfig();
+ }
+ return instance;
+ }
+
+ protected void setContext(Context mContext) {
+ this.mContext = mContext.getApplicationContext();
+ init();
+ }
+
+ private void init() {
+ initDetailedLog();
+ initDebugLog();
+ initErrorLog();
+ }
+
+ private void initDetailedLog() {
+ int id = mContext.getResources().getIdentifier("mcare_detailed_log", "bool", mContext.getPackageName());
+ if (id > 0) {
+ detailed = mContext.getResources().getBoolean(id);
+ }
+ }
+
+ private void initDebugLog() {
+ int id = mContext.getResources().getIdentifier("mcare_debug_log", "bool", mContext.getPackageName());
+ if (id > 0) {
+ debug = mContext.getResources().getBoolean(id);
+ }
+ }
+
+ private void initErrorLog() {
+ int id = mContext.getResources().getIdentifier("mcare_error_log", "bool", mContext.getPackageName());
+ if (id > 0) {
+ error = mContext.getResources().getBoolean(id);
+ }
+ }
+
+ protected boolean isDetailed() {
+ warningNullContext();
+ return detailed;
+ }
+
+ protected boolean isDebug() {
+ warningNullContext();
+ return debug;
+ }
+
+ protected boolean isError() {
+ warningNullContext();
+ return error;
+ }
+
+ private void warningNullContext() {
+ if (mContext == null) {
+ if (showWarnCount < 1) {
+ Log.w(">>>> MCareLogUtil",
+ "Use LogUtil.setContext(mContext) para que sua configuração no XML tenha efeito. Default: Todos os logs desligados.\n"
+ + "Exemplo de XML:\n" + "true\n"
+ + "true\n" + "true");
+ showWarnCount++;
+ }
+ }
+ }
+ }
+
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/log/MapLogger.java b/medescope/src/main/java/br/com/bemobi/medescope/log/MapLogger.java
new file mode 100644
index 0000000..28a1431
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/log/MapLogger.java
@@ -0,0 +1,37 @@
+package br.com.bemobi.medescope.log;
+
+import java.util.Map;
+
+/**
+ * Created by luis.fernandez on 7/5/15.
+ */
+public class MapLogger {
+
+ private static final String TAG = "MapLogger";
+ private String feature;
+
+ public MapLogger(String feature) {
+ this.feature = feature;
+ }
+
+ public void log(Map map) {
+ for (String key : map.keySet()) {
+ Logger.debug(TAG, feature, String.format(" [%s] = %s", key, map.get(key)));
+ }
+ Logger.debug(TAG, feature, "\n");
+ }
+
+ public void logStringLong(Map map) {
+ for (String key : map.keySet()) {
+ Logger.debug(TAG, feature, String.format(" [%s] = %s", key, map.get(key)));
+ }
+ Logger.debug(TAG, feature, "\n");
+ }
+
+ public void logLongString(Map map) {
+ for (Long key : map.keySet()) {
+ Logger.debug(TAG, feature, String.format(" [%s] = %s", key, map.get(key)));
+ }
+ Logger.debug(TAG, feature, "\n");
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadInfo.java b/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadInfo.java
new file mode 100644
index 0000000..9ba51d3
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadInfo.java
@@ -0,0 +1,194 @@
+package br.com.bemobi.medescope.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import br.com.bemobi.medescope.constant.DownloadInfoStatusConstants;
+
+/**
+ * Created by bruno.costa on 30/06/15.
+ */
+public class DownloadInfo implements Parcelable{
+
+ int status;
+
+ int reason;
+
+ String filename;
+
+ String localURI;
+
+ long lastModified;
+
+ long downloadedSoFar;
+
+ long totalSize;
+
+ int progress;
+
+ public DownloadInfo() {
+ }
+
+ public DownloadInfo(int status, int reason, String filename, String localURI, long lastModified, long downloadedSoFar, long totalSize, int progress) {
+ this.status = status;
+ this.reason = reason;
+ this.filename = filename;
+ this.localURI = localURI;
+ this.lastModified = lastModified;
+ this.downloadedSoFar = downloadedSoFar;
+ this.totalSize = totalSize;
+ this.progress = progress;
+ }
+
+ public DownloadInfo(int status, int reason) {
+ this.status = status;
+ this.reason = reason;
+ }
+
+ @Override
+ public String toString() {
+ return "DownloadInfo{" +
+ "status=" + status +
+ ", reason=" + reason +
+ ", filename='" + filename + '\'' +
+ ", localURI='" + localURI + '\'' +
+ ", lastModified=" + lastModified +
+ ", downloadedSoFar=" + downloadedSoFar +
+ ", totalSize=" + totalSize +
+ ", progress=" + progress +
+ '}';
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public void setReason(int reason) {
+ this.reason = reason;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public void setLocalURI(String localURI) {
+ this.localURI = localURI;
+ }
+
+ public void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ public void setDownloadedSoFar(long downloadedSoFar) {
+ this.downloadedSoFar = downloadedSoFar;
+ }
+
+ public void setTotalSize(long totalSize) {
+ this.totalSize = totalSize;
+ }
+
+ public void setProgress(int progress) {
+ this.progress = progress;
+ }
+
+ public int getReason() {
+ return reason;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public String getLocalURI() {
+ return localURI;
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ public long getDownloadedSoFar() {
+ return downloadedSoFar;
+ }
+
+ public long getTotalSize() {
+ return totalSize;
+ }
+
+ public int getProgress() {
+ return progress;
+ }
+
+ public boolean isPaused() {
+ return status == DownloadInfoStatusConstants.PAUSED_STATUS;
+ }
+
+ public boolean isInProgress() {
+ return status == DownloadInfoStatusConstants.IN_PROGRESS_STATUS;
+ }
+
+ public boolean hasFinishedWithSuccess() {
+ return status == DownloadInfoStatusConstants.SUCCESSFUL_STATUS;
+ }
+
+ public boolean hasFinishedWithError() {
+ return status == DownloadInfoStatusConstants.FAILED_STATUS;
+ }
+
+ public boolean hasFinished() {
+ return status == DownloadInfoStatusConstants.SUCCESSFUL_STATUS
+ || status == DownloadInfoStatusConstants.FAILED_STATUS;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DownloadInfo that = (DownloadInfo) o;
+
+ if (status != that.status) return false;
+ if (reason != that.reason) return false;
+ return (Double.compare(that.progress, progress) != 0);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(this.status);
+ out.writeInt(this.reason);
+ out.writeString(this.filename);
+ out.writeString(this.localURI);
+ out.writeLong(this.lastModified);
+ out.writeLong(this.downloadedSoFar);
+ out.writeLong(this.totalSize);
+ out.writeInt(this.progress);
+ }
+
+ private DownloadInfo(Parcel in) {
+ this.status = in.readInt();
+ this.reason = in.readInt();
+ this.filename = in.readString();
+ this.localURI = in.readString();
+ this.lastModified = in.readLong();
+ this.downloadedSoFar = in.readLong();
+ this.totalSize = in.readLong();
+ this.progress = in.readInt();
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public DownloadInfo createFromParcel(Parcel in) {
+ return new DownloadInfo(in);
+ }
+
+ public DownloadInfo[] newArray(int size) {
+ return new DownloadInfo[size];
+ }
+ };
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadRequest.java b/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadRequest.java
new file mode 100644
index 0000000..fa38917
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/model/DownloadRequest.java
@@ -0,0 +1,124 @@
+package br.com.bemobi.medescope.model;
+
+import android.text.TextUtils;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by luis.fernandez on 7/5/15.
+ */
+public class DownloadRequest implements Serializable {
+
+ String id;
+ String uri;
+ String fileName;
+ String downloadName;
+ String downloadDescription;
+ String clientPayload;
+ boolean shouldDownloadOnlyInWifi;
+ Map customHeaders;
+
+ public DownloadRequest() {
+ this.customHeaders = new HashMap<>();
+ }
+
+ public DownloadRequest(String id, String uri, String fileName, String downloadName, String downloadDescription, String clientPayload, boolean shouldDownloadOnlyInWifi, Map customHeaders) {
+ this.id = id;
+ this.uri = uri;
+ this.fileName = fileName;
+ this.downloadName = downloadName;
+ this.downloadDescription = downloadDescription;
+ this.clientPayload = clientPayload;
+ this.shouldDownloadOnlyInWifi = shouldDownloadOnlyInWifi;
+ this.customHeaders = customHeaders;
+ }
+
+ public boolean isValid() {
+ return !TextUtils.isEmpty(id) &&
+ !TextUtils.isEmpty(uri) &&
+ !TextUtils.isEmpty(fileName) &&
+ !TextUtils.isEmpty(downloadName) &&
+ !TextUtils.isEmpty(downloadDescription) &&
+ !TextUtils.isEmpty(clientPayload);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getDownloadName() {
+ return downloadName;
+ }
+
+ public void setDownloadName(String downloadName) {
+ this.downloadName = downloadName;
+ }
+
+ public String getDownloadDescription() {
+ return downloadDescription;
+ }
+
+ public void setDownloadDescription(String downloadDescription) {
+ this.downloadDescription = downloadDescription;
+ }
+
+ public String getClientPayload() {
+ return clientPayload;
+ }
+
+ public void setClientPayload(String clientPayload) {
+ this.clientPayload = clientPayload;
+ }
+
+ public Map getCustomHeaders() {
+ return customHeaders;
+ }
+
+ public void setCustomHeaders(Map customHeaders) {
+ this.customHeaders = customHeaders;
+ }
+
+ public boolean shouldDownloadOnlyInWifi() {
+ return shouldDownloadOnlyInWifi;
+ }
+
+ public void setShouldDownloadOnlyInWifi(boolean shouldDownloadOnlyInWifi) {
+ this.shouldDownloadOnlyInWifi = shouldDownloadOnlyInWifi;
+ }
+
+ @Override
+ public String toString() {
+ return "Download{" +
+ "id='" + id + '\'' +
+ ", uri='" + uri + '\'' +
+ ", fileName='" + fileName + '\'' +
+ ", downloadName='" + downloadName + '\'' +
+ ", downloadDescription='" + downloadDescription + '\'' +
+ ", clientPayload='" + clientPayload + '\'' +
+ ", shouldDownloadOnlyInWifi=" + shouldDownloadOnlyInWifi +
+ ", customHeaders=" + customHeaders +
+ '}';
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/receiver/BroadcastReceiverLogger.java b/medescope/src/main/java/br/com/bemobi/medescope/receiver/BroadcastReceiverLogger.java
new file mode 100644
index 0000000..d2c32fe
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/receiver/BroadcastReceiverLogger.java
@@ -0,0 +1,33 @@
+package br.com.bemobi.medescope.receiver;
+
+import android.content.Context;
+import android.content.Intent;
+
+import br.com.bemobi.medescope.log.IntentLogger;
+
+/**
+ * Created by luis.fernandez on 6/27/15.
+ */
+public class BroadcastReceiverLogger {
+
+ private static final String TAG = "BroadcastReceiverLogger";
+
+ private String from;
+ private String feature;
+
+// public BroadcastReceiverLogger() {
+// }
+
+ public BroadcastReceiverLogger(String from, String feature) {
+ this.from = from;
+ this.feature = feature;
+ }
+
+ public void onReceive(Context context, Intent intent) {
+// LogUtil.debug(TAG, feature, "");
+// LogUtil.debug(TAG, feature, String.format(">>>>>>>>>>>>>>>>>>>>>>>>>> DUMPING BROADCAST INTENT from: %s", from));
+// LogUtil.debug(TAG, feature, String.format(" [%s] = %s", "ACTION", intent.getAction()));
+//
+// new IntentLogger(from, feature).logIntent(intent);
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/receiver/DMDownloaderReceiver.java b/medescope/src/main/java/br/com/bemobi/medescope/receiver/DMDownloaderReceiver.java
new file mode 100644
index 0000000..7951db7
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/receiver/DMDownloaderReceiver.java
@@ -0,0 +1,46 @@
+package br.com.bemobi.medescope.receiver;
+
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import br.com.bemobi.medescope.service.impl.DMIntentService;
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD;
+
+/**
+ * Created by bruno.costa on 26/06/15.
+ */
+public class DMDownloaderReceiver extends BroadcastReceiver {
+ public static final String TAG = DMDownloaderReceiver.class.getSimpleName();
+
+ public DMDownloaderReceiver() {
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ new BroadcastReceiverLogger(TAG, LOG_FEATURE_DOWNLOAD).onReceive(context, intent);
+
+ String action = intent.getAction();
+
+ if(DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
+ Log.d(TAG, "DM DOWNLOAD COMPLETED");
+ Long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
+
+ DMIntentService.actionFinish(context, downloadId);
+ }
+ else if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) {
+ Log.d(TAG, "DM NOTIFICATION CLICKED");
+
+ long[] downloadIds = null;
+
+ if (intent.hasExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS)){
+ downloadIds = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
+ }
+
+ DMIntentService.actionNotificationClicked(context, downloadIds);
+ }
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/repository/DMRepository.java b/medescope/src/main/java/br/com/bemobi/medescope/repository/DMRepository.java
new file mode 100644
index 0000000..7176463
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/repository/DMRepository.java
@@ -0,0 +1,98 @@
+package br.com.bemobi.medescope.repository;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.log.MapLogger;
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD_DATA_STATE;
+
+/**
+ * Created by bruno.costa on 01/07/15.
+ */
+public class DMRepository {
+
+ private static final String TAG = DMRepository.class.getSimpleName();
+
+ private Context mContext;
+ private static DMRepository instance;
+
+ private Map mapIdsClientToDM;
+ private Map mapIdsDMToClient;
+
+ private DMRepository(Context context) {
+ this.mContext = context;
+ this.mapIdsClientToDM = new HashMap<>();
+ this.mapIdsDMToClient = new HashMap<>();
+ this.loadMaps();
+ }
+
+ public static DMRepository getInstance(Context context) {
+ if(instance == null){
+ instance = new DMRepository(context);
+ }
+ return instance;
+ }
+
+ public void persistIds(String downloadId, Long dmDownloadId) {
+ if( !mapIdsDMToClient.containsKey(dmDownloadId) && !mapIdsClientToDM.containsKey(downloadId)) {
+ mapIdsDMToClient.put(dmDownloadId, downloadId);
+ mapIdsClientToDM.put(downloadId, dmDownloadId);
+ persistMaps();
+ }
+
+ //TODO Should we give a feedback?
+ }
+
+ public void removeId(String downloadId){
+ Long dmDownloadId = mapIdsClientToDM.get(downloadId);
+ mapIdsDMToClient.remove(dmDownloadId);
+ mapIdsClientToDM.remove(downloadId);
+ persistMaps();
+ }
+
+ public void removeId(Long dmDownloadId){
+ String downloadId = mapIdsDMToClient.get(dmDownloadId);
+ mapIdsDMToClient.remove(dmDownloadId);
+ mapIdsClientToDM.remove(downloadId);
+ persistMaps();
+ }
+
+ public Long getDMId(String clientId){
+ return mapIdsClientToDM.get(clientId);
+ }
+
+ public String getClientId(Long dmDownloadId){
+ return mapIdsDMToClient.get(dmDownloadId);
+ }
+
+ public void loadMaps(){
+ String mapIdsLibtoDMStr = PreferencesUtils.getStringPreference(mContext.getApplicationContext(), DownloadConstants.DM_STRING_IDS_LIB_TO_DMIDS_MAP_PREF, "");
+ if(!TextUtils.isEmpty(mapIdsLibtoDMStr)) {
+ Gson gson = new Gson();
+ mapIdsClientToDM = gson.fromJson(mapIdsLibtoDMStr, new TypeToken>(){}.getType());
+ } else {
+ mapIdsClientToDM = new HashMap<>();
+ }
+
+ String mapIdsDMtoLibStr = PreferencesUtils.getStringPreference(mContext.getApplicationContext(), DownloadConstants.DM_STRING_IDS_DMIDS_TO_LIB_MAP_PREF, "");
+ if(!TextUtils.isEmpty(mapIdsDMtoLibStr)) {
+ Gson gson = new Gson();
+ mapIdsDMToClient = gson.fromJson(mapIdsDMtoLibStr, new TypeToken>(){}.getType());
+ } else {
+ mapIdsDMToClient = new HashMap<>();
+ }
+ }
+
+ public void persistMaps(){
+ PreferencesUtils.savePreference(mContext.getApplicationContext(), DownloadConstants.DM_STRING_IDS_LIB_TO_DMIDS_MAP_PREF, new Gson().toJson(mapIdsClientToDM));
+ PreferencesUtils.savePreference(mContext.getApplicationContext(), DownloadConstants.DM_STRING_IDS_DMIDS_TO_LIB_MAP_PREF, new Gson().toJson(mapIdsDMToClient));
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/repository/DownloadDataRepository.java b/medescope/src/main/java/br/com/bemobi/medescope/repository/DownloadDataRepository.java
new file mode 100644
index 0000000..1024721
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/repository/DownloadDataRepository.java
@@ -0,0 +1,29 @@
+package br.com.bemobi.medescope.repository;
+
+import java.util.Map;
+
+/**
+ * Created by bruno.costa on 02/07/15.
+ */
+public interface DownloadDataRepository {
+
+ Map getDownloadsMap();
+
+ void setDownloadsMap(Map downloadsMap);
+
+ void putDownloadData(String downloadId, String data);
+
+ String getDownloadData(String downloadId);
+
+ void removeDownloadData(String downloadId);
+
+ boolean isEmptyDownloadData();
+
+ boolean containsDownloadDataKey(String key);
+
+ void persistSubscribedId(String downloadId);
+
+ String recoverSubscribedId();
+
+ void removeSubscribedId();
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/repository/PreferencesUtils.java b/medescope/src/main/java/br/com/bemobi/medescope/repository/PreferencesUtils.java
new file mode 100644
index 0000000..d4f28d1
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/repository/PreferencesUtils.java
@@ -0,0 +1,132 @@
+package br.com.bemobi.medescope.repository;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+/**
+ * Created by brunocosta on 9/10/15.
+ */
+public class PreferencesUtils {
+ // private static Map preferences = new HashMap();
+
+ public static SharedPreferences getSharedPreferences(Context context) {
+ SharedPreferences pref
+ // = preferences.get(context.getApplicationContext())
+ ;
+ // if (pref == null) {
+ pref = context.getApplicationContext().getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
+ // preferences.put(context, pref);
+ // }
+
+ return pref;
+ }
+
+ public static void clearPreferences(Context context) {
+ SharedPreferences.Editor editor = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE).edit();
+ editor.clear();
+ editor.commit();
+ }
+
+ public static boolean getBooleanPreference(Context context, int resId, boolean defaultValue) {
+ return getSharedPreferences(context).getBoolean(context.getString(resId), defaultValue);
+ }
+
+ public static boolean getBooleanPreference(Context context, String prefKey, boolean defaultValue) {
+ return getSharedPreferences(context).getBoolean(prefKey, defaultValue);
+ }
+
+ public static int getIntPreference(Context context, int resId, int defaultValue) {
+ return getSharedPreferences(context).getInt(context.getString(resId), defaultValue);
+ }
+
+ public static int getIntPreference(Context context, String prefKey, int defaultValue) {
+ return getSharedPreferences(context).getInt(prefKey, defaultValue);
+ }
+
+ public static long getLongPreference(Context context, int resId, long defaultValue) {
+ return getSharedPreferences(context).getLong(context.getString(resId), defaultValue);
+ }
+
+ public static long getLongPreference(Context context, String prefKey, long defaultValue) {
+ return getSharedPreferences(context).getLong(prefKey, defaultValue);
+ }
+
+ public static String getStringPreference(Context context, int resId, String defaultValue) {
+ return getSharedPreferences(context).getString(context.getString(resId), defaultValue);
+ }
+
+ public static String getStringPreference(Context context, String prefKey, String defaultValue) {
+ return getSharedPreferences(context).getString(prefKey, defaultValue);
+ }
+
+ public static void savePreference(Context context, int resId, int newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putInt(context.getString(resId), newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, String prefKey, int newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putInt(prefKey, newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, int resId, long newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putLong(context.getString(resId), newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, String prefKey, long newValue){
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putLong(prefKey, newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, int resId, String newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putString(context.getString(resId), newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, String prefKey, String newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putString(prefKey, newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, int resId, Boolean newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putBoolean(context.getString(resId), newValue);
+ editor.commit();
+ }
+
+ public static void savePreference(Context context, String prefKey, Boolean newValue) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.putBoolean(prefKey, newValue);
+ editor.commit();
+ }
+
+ public static void removePreference(Context context, int resId) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.remove(context.getString(resId));
+ editor.commit();
+ }
+
+ public static void removePreference(Context context, String prefKey) {
+ SharedPreferences mPreferences = PreferencesUtils.getSharedPreferences(context);
+ Editor editor = mPreferences.edit();
+ editor.remove(prefKey);
+ editor.commit();
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/repository/impl/MapDownloadDataRepository.java b/medescope/src/main/java/br/com/bemobi/medescope/repository/impl/MapDownloadDataRepository.java
new file mode 100644
index 0000000..dcf10a0
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/repository/impl/MapDownloadDataRepository.java
@@ -0,0 +1,112 @@
+package br.com.bemobi.medescope.repository.impl;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.log.MapLogger;
+import br.com.bemobi.medescope.repository.DownloadDataRepository;
+import br.com.bemobi.medescope.repository.PreferencesUtils;
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD_DATA_STATE;
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_SERVICE_LIFECYCLE;
+
+/**
+ * Created by bruno.costa on 01/07/15.
+ */
+public class MapDownloadDataRepository implements DownloadDataRepository {
+
+ private static final String TAG = MapDownloadDataRepository.class.getSimpleName();
+
+ private static DownloadDataRepository downloadDataRepository;
+
+ private Map downloadsMap = new HashMap<>();
+
+ private Context mContext;
+
+ public static DownloadDataRepository getInstance(Context context) {
+ if(downloadDataRepository == null) {
+ downloadDataRepository = new MapDownloadDataRepository(context);
+ }
+ return downloadDataRepository;
+ }
+
+ private MapDownloadDataRepository(Context context) {
+ this.mContext = context.getApplicationContext();
+ load();
+ }
+
+ @Override
+ public Map getDownloadsMap() {
+ return downloadsMap;
+ }
+
+ @Override
+ public void setDownloadsMap(Map downloadsMap) {
+ this.downloadsMap = downloadsMap;
+ }
+
+ @Override
+ public void putDownloadData(String downloadId, String data) {
+ this.downloadsMap.put(downloadId, data);
+ persistDownloadData();
+ }
+
+ @Override
+ public String getDownloadData(String downloadId) {
+ return this.downloadsMap.get(downloadId);
+ }
+
+ @Override
+ public void removeDownloadData(String downloadId) {
+
+ this.downloadsMap.remove(downloadId);
+ persistDownloadData();
+ }
+
+ @Override
+ public boolean isEmptyDownloadData() {
+ return this.downloadsMap.isEmpty();
+ }
+
+ @Override
+ public boolean containsDownloadDataKey(String key) {
+ return this.downloadsMap.containsKey(key);
+ }
+
+ @Override
+ public void persistSubscribedId(String downloadId) {
+ PreferencesUtils.savePreference(mContext.getApplicationContext(), DownloadConstants.PREF_SUBSCRIBED_ID, downloadId);
+ }
+
+ @Override
+ public String recoverSubscribedId() {
+ return PreferencesUtils.getStringPreference(mContext.getApplicationContext(), DownloadConstants.PREF_SUBSCRIBED_ID, "");
+ }
+
+ @Override
+ public void removeSubscribedId() {
+ PreferencesUtils.removePreference(mContext.getApplicationContext(), DownloadConstants.PREF_SUBSCRIBED_ID);
+ }
+
+ private void load() {
+ String map = PreferencesUtils.getStringPreference(mContext.getApplicationContext(), DownloadConstants.DATA_MAP_PREF, "");
+ if(!TextUtils.isEmpty(map)) {
+ Gson gson = new Gson();
+ downloadsMap = gson.fromJson(map, new TypeToken>(){}.getType());
+ } else {
+ downloadsMap = new HashMap<>();
+ }
+ }
+
+ private void persistDownloadData()
+ {
+ PreferencesUtils.savePreference(mContext.getApplicationContext(), DownloadConstants.DATA_MAP_PREF, new Gson().toJson(downloadsMap));
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/CommunicationService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/CommunicationService.java
new file mode 100644
index 0000000..a787543
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/CommunicationService.java
@@ -0,0 +1,21 @@
+package br.com.bemobi.medescope.service;
+
+/**
+ * Created by bruno.costa on 02/07/15.
+ */
+public interface CommunicationService {
+
+ void sendFinishWithSuccessBroadcastData(String downloadId, String filePath, String data);
+
+ void sendFinishWithErrorBroadcastData(String downloadId, int reason, String data);
+
+ void sendDownloadStatusNotEnqueue(String downloadId);
+
+ void sendDownloadStatusPaused(String downloadId, int reason);
+
+ void sendDownloadStatusProgress(String downloadId, int progress);
+
+ void showDownloadQueue();
+
+ void sendCancelled(String downloadId);
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadCommand.java b/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadCommand.java
new file mode 100644
index 0000000..1a7bca8
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadCommand.java
@@ -0,0 +1,26 @@
+package br.com.bemobi.medescope.service;
+
+import android.os.Bundle;
+
+import br.com.bemobi.medescope.model.DownloadRequest;
+import br.com.bemobi.medescope.model.DownloadInfo;
+
+/**
+ * Created by bruno.costa on 30/06/15.
+ */
+public interface DownloadCommand {
+
+ void startCommand();
+
+ void executeCommand(String action, Bundle extras);
+
+ void enqueue(DownloadRequest downloadRequest);
+
+ void cancelAction(String downloadId);
+
+ void finishAction(String downloadId, DownloadInfo downloadInfo);
+
+ void clickNotificationAction(String[] downloadIds);
+
+ void shutdownCommand();
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadService.java
new file mode 100644
index 0000000..65e59b9
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/DownloadService.java
@@ -0,0 +1,23 @@
+package br.com.bemobi.medescope.service;
+
+import java.util.Map;
+
+import br.com.bemobi.medescope.model.DownloadInfo;
+
+/**
+ * Created by bruno.costa on 30/06/15.
+ */
+public interface DownloadService {
+
+ void shutdown();
+
+ boolean enqueue(String downloadId, String uri, String fileName, String title, String description, String data, boolean shouldDownloadOnlyInWifi, Map customHeaders);
+
+ boolean cancel(String downloadId);
+
+ void cleanupId(String downloadId);
+
+ void notificationClicked(String[] downloadIds);
+
+ DownloadInfo getDownloadInfo(String downloadId);
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/impl/BroadcastCommunicationService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/BroadcastCommunicationService.java
new file mode 100644
index 0000000..93fd365
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/BroadcastCommunicationService.java
@@ -0,0 +1,108 @@
+package br.com.bemobi.medescope.service.impl;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.content.Intent;
+
+import br.com.bemobi.medescope.Medescope;
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.log.Logger;
+import br.com.bemobi.medescope.service.CommunicationService;
+
+/**
+ * Created by bruno.costa on 02/07/15.
+ */
+public class BroadcastCommunicationService implements CommunicationService {
+
+ private static final String TAG = BroadcastCommunicationService.class.getSimpleName();
+
+ private static CommunicationService instance;
+
+ private final Context mContext;
+
+ private BroadcastCommunicationService(Context context){
+ this.mContext = context;
+ }
+
+ public static CommunicationService getInstance(Context context){
+ if(instance == null){
+ instance = new BroadcastCommunicationService(context);
+ }
+ return instance;
+ }
+
+ @Override
+ public void sendFinishWithSuccessBroadcastData(String downloadId, String filePath, String data) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendFinishWithSuccessBroadcastData with data: " + data);
+ Intent finishIntent = new Intent();
+ finishIntent.setAction(Medescope.ACTION_BROADCAST_FINISH_WITH_SUCCESS);
+ finishIntent.setPackage(mContext.getPackageName());
+ finishIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ finishIntent.putExtra(DownloadConstants.EXTRA_STRING_FILE_PATH, filePath);
+ finishIntent.putExtra(DownloadConstants.EXTRA_STRING_JSON_DATA, data);
+ mContext.sendBroadcast(finishIntent);
+ }
+
+ @Override
+ public void sendFinishWithErrorBroadcastData(String downloadId, int reason, String data) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendFinishWithErrorBroadcastData with data: " + data + " and reason: " + reason);
+ Intent finishIntent = new Intent();
+ finishIntent.setAction(Medescope.ACTION_BROADCAST_FINISH_WITH_ERROR);
+ finishIntent.setPackage(mContext.getPackageName());
+ finishIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ finishIntent.putExtra(DownloadConstants.EXTRA_INT_ERROR_REASON, reason);
+ finishIntent.putExtra(DownloadConstants.EXTRA_STRING_JSON_DATA, data);
+ mContext.sendBroadcast(finishIntent);
+ }
+
+ @Override
+ public void sendDownloadStatusNotEnqueue(String downloadId) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendDownloadStatusNotEnqueue");
+ Intent progressIntent = new Intent();
+ progressIntent.setAction(Medescope.ACTION_BROADCAST_NOT_ENQUEUED);
+ progressIntent.setPackage(mContext.getPackageName());
+ progressIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ mContext.sendBroadcast(progressIntent);
+ }
+
+ @Override
+ public void sendDownloadStatusPaused(String downloadId, int reason) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendDownloadStatusPaused");
+ Intent progressIntent = new Intent();
+ progressIntent.setAction(Medescope.ACTION_BROADCAST_PAUSED);
+ progressIntent.setPackage(mContext.getPackageName());
+ progressIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ progressIntent.putExtra(DownloadConstants.EXTRA_INT_REASON_KEY, reason);
+ mContext.sendBroadcast(progressIntent);
+ }
+
+ @Override
+ public void sendDownloadStatusProgress(String downloadId, int progress) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendDownloadStatusProgress");
+ Intent progressIntent = new Intent();
+ progressIntent.setAction(Medescope.ACTION_BROADCAST_IN_PROGRESS);
+ progressIntent.setPackage(mContext.getPackageName());
+ progressIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ progressIntent.putExtra(DownloadConstants.EXTRA_INT_PROGRESS_PERCENTAGE, progress);
+ mContext.sendBroadcast(progressIntent);
+ }
+
+ @Override
+ public void sendCancelled(String downloadId) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "sendCancelled");
+ Intent progressIntent = new Intent();
+ progressIntent.setAction(Medescope.ACTION_BROADCAST_CANCELLED);
+ progressIntent.setPackage(mContext.getPackageName());
+ progressIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ mContext.sendBroadcast(progressIntent);
+ }
+
+ @Override
+ public void showDownloadQueue() {
+ Intent i = new Intent();
+ i.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(i);
+ }
+
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMDownloadService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMDownloadService.java
new file mode 100644
index 0000000..37735c0
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMDownloadService.java
@@ -0,0 +1,206 @@
+package br.com.bemobi.medescope.service.impl;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+
+import java.util.Map;
+
+import br.com.bemobi.medescope.DownloadFileUtils;
+import br.com.bemobi.medescope.log.Logger;
+import br.com.bemobi.medescope.model.DownloadInfo;
+import br.com.bemobi.medescope.repository.DMRepository;
+import br.com.bemobi.medescope.service.DownloadService;
+import br.com.bemobi.medescope.wrapper.DownloadInfoWrapper;
+import br.com.bemobi.medescope.wrapper.impl.DMDownloadInfoWrapper;
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD;
+
+/**
+ * Created by bruno.costa on 30/06/15.
+ */
+public class DMDownloadService implements DownloadService {
+
+ public static final String TAG = DMDownloadService.class.getSimpleName();
+ private static DMDownloadService instance;
+ private static DownloadManager downloadManager;
+ private DMRepository repository;
+ private Context mContext;
+ private boolean isHidden = false;
+ private DownloadInfoWrapper downloadInfoWrapper;
+
+ private DMDownloadService(Context context) {
+ this.mContext = context;
+ this.repository = DMRepository.getInstance(context);
+ this.downloadInfoWrapper = new DMDownloadInfoWrapper();
+ }
+
+ public static DMDownloadService getInstance(Context context) {
+ if(instance == null) {
+ instance = new DMDownloadService(context);
+ }
+ return instance;
+ }
+
+ private static DownloadManager getDMInstance(Context context) {
+ if (downloadManager == null) {
+ downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+ }
+ return downloadManager;
+ }
+
+ @Override
+ public void shutdown() {
+ Log.d(TAG, "shutdown");
+ downloadManager = null;
+ }
+
+ @Override
+ public boolean enqueue(String downloadId, String uri, String fileName, String title, String description, String data, boolean shouldDownloadOnlyInWifi, Map customHeaders) {
+ if (DownloadFileUtils.isExternalStorageWritable()) {
+ DownloadManager.Request request;
+ request = new DownloadManager.Request(Uri.parse(uri));
+ request.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, fileName);
+ request.setTitle(title);
+ request.setDescription(description);
+ request.setAllowedOverRoaming(false);
+ setAllowedNetworks(shouldDownloadOnlyInWifi, request);
+
+ if (customHeaders != null && !customHeaders.isEmpty()) {
+ // TODO setar variavel no build para logar somente em debug http://toastdroid.com/2014/03/28/customizing-your-build-with-gradle/
+
+ //LogUtil.debug(TAG, LOG_FEATURE_DOWNLOAD, ">>>>>>>>>>> LOGGING CUSTOM HEADERS");
+ //new MapLogger(LOG_FEATURE_DOWNLOAD).log(customHeaders);
+
+
+ for (String key : customHeaders.keySet()) {
+ request.addRequestHeader(key, customHeaders.get(key));
+ }
+ }
+
+// request.allowScanningByMediaScanner();
+ request = setHiddenDownload(request);
+ Long dmId = getDMInstance(mContext).enqueue(request);
+ repository.persistIds(downloadId, dmId);
+ return dmId >= 0;
+ }
+
+ return false;
+ }
+
+ private void setAllowedNetworks(boolean shouldDownloadOnlyInWifi, DownloadManager.Request request) {
+ if(shouldDownloadOnlyInWifi) {
+ request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
+ } else {
+ request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
+ }
+ }
+
+ private DownloadManager.Request setHiddenDownload(DownloadManager.Request request){
+ if(isHidden) {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
+ } else {
+ request.setShowRunningNotification(false);
+ }
+ }
+ return request;
+ }
+
+ @Override
+ public boolean cancel(String downloadId) {
+ return getDMInstance(mContext).remove(repository.getDMId(downloadId)) > 0;
+ }
+
+ @Override
+ public void cleanupId(String downloadId) {
+ repository.removeId(downloadId);
+ }
+
+ @Override
+ public void notificationClicked(String[] downloadIds) {
+ DownloadCommandService.actionNotificationClicked(mContext, downloadIds);
+ }
+
+ @Override
+ public DownloadInfo getDownloadInfo(String downloadId) {
+ DownloadInfo downloadManagerInfo = null;
+ Long dmDownloadId = repository.getDMId(downloadId);
+ if (dmDownloadId != null) {
+ Cursor cursor = getDMInstance(mContext).query(new DownloadManager.Query().setFilterById(repository.getDMId(downloadId)));
+
+ if (cursor != null) {
+ int status;
+ int reason;
+ String filename;
+ String localURI;
+ long lastModified;
+ long downloadedSoFar = 0;
+ long totalSize = 1;
+ int progress = 0;
+
+ if (cursor.moveToFirst()) {
+ // TODO desligar esse log em producao
+ Logger.debug(TAG, LOG_FEATURE_DOWNLOAD, "dumpCursorToString\n" + DatabaseUtils.dumpCursorToString(cursor));
+
+
+ //column for status
+ int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ status = cursor.getInt(columnIndex);
+
+ //column for reason code if the download failed or paused
+ int columnReason = cursor.getColumnIndex(DownloadManager.COLUMN_REASON);
+ reason = cursor.getInt(columnReason);
+
+ //get the download filename
+ filename = "";
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ int filenameIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
+ filename = cursor.getString(filenameIndex);
+ } else {
+ int filenameIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
+ String cursorString = cursor.getString(filenameIndex);
+
+ if (cursorString != null) {
+ Logger.debug(TAG, LOG_FEATURE_DOWNLOAD, " [ cursor.getString(filenameIndex) ] = " + cursor.getString(filenameIndex));
+ Uri uri = Uri.parse(cursorString);
+
+ if (uri != null) {
+ Logger.debug(TAG, LOG_FEATURE_DOWNLOAD, "[ Uri.parse(cursor.getString(filenameIndex)).getPath() ] = " + Uri.parse(cursor.getString(filenameIndex)).getPath());
+ filename = uri.getPath();
+ }
+ }
+ }
+
+ int columnLocalURI = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
+ localURI = cursor.getString(columnLocalURI);
+
+ int columnLastModified = cursor.getColumnIndex(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP);
+ lastModified = cursor.getLong(columnLastModified);
+
+
+ int columnDownloadedSoFar = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
+ downloadedSoFar = cursor.getLong(columnDownloadedSoFar);
+
+ int columnTotalSize = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
+ totalSize = cursor.getLong(columnTotalSize);
+
+ if (status == DownloadManager.STATUS_RUNNING) {
+ progress = (int) Math.round(((1.0 * downloadedSoFar) / totalSize) * 100);
+ } else if (status == DownloadManager.STATUS_SUCCESSFUL) {
+ progress = 100;
+ }
+
+ downloadManagerInfo = new DownloadInfo(downloadInfoWrapper.translateStatus(status), downloadInfoWrapper.translateReason(reason), filename, localURI, lastModified, downloadedSoFar, totalSize, progress);
+ }
+ cursor.close();
+ }
+ }
+ return downloadManagerInfo;
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMIntentService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMIntentService.java
new file mode 100644
index 0000000..1c068d5
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DMIntentService.java
@@ -0,0 +1,72 @@
+package br.com.bemobi.medescope.service.impl;
+
+import android.app.DownloadManager;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+
+import br.com.bemobi.medescope.model.DownloadInfo;
+import br.com.bemobi.medescope.repository.DMRepository;
+import br.com.bemobi.medescope.service.DownloadService;
+
+/**
+ * Created by bruno.costa on 01/07/15.
+ */
+public class DMIntentService extends IntentService {
+
+ public static final String ACTION_DM_FINISH = "br.com.bemobi.medescope.ACTION_DM_FINISH";
+ public static final String ACTION_DM_NOTIFICATION_CLICKED = "br.com.bemobi.medescope.ACTION_DM_NOTIFICATION_CLICKED";
+
+ public DMIntentService() {
+ super(DMIntentService.class.getName());
+ }
+
+
+ public static void actionFinish(Context context, long downloadId) {
+ Intent intent = new Intent(context, DMIntentService.class);
+ intent.setAction(ACTION_DM_FINISH);
+ intent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
+ context.startService(intent);
+ }
+
+ public static void actionNotificationClicked(Context context, long[] downloadIds) {
+ Intent serviceIntent = new Intent(context, DMIntentService.class);
+ serviceIntent.setAction(ACTION_DM_NOTIFICATION_CLICKED);
+ if (downloadIds != null) {
+ serviceIntent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadIds);
+ }
+ context.startService(serviceIntent);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (intent != null) {
+ DownloadService downloadService = DMDownloadService.getInstance(this);
+ String action = intent.getAction();
+ if (ACTION_DM_FINISH.equals(action) ) {
+ Long dmDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
+ if(dmDownloadId >= 0) {
+ String libId = DMRepository.getInstance(getApplicationContext()).getClientId(dmDownloadId);
+
+ DownloadInfo downloadInfo = downloadService.getDownloadInfo(libId);
+
+ DownloadCommandService.actionFinishDownload(this, libId, downloadInfo);
+ }
+ } else if (ACTION_DM_NOTIFICATION_CLICKED.equals(action)) {
+ String[] downloadIds = {};
+ long[] ids = {};
+ if(intent.hasExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS)) {
+ ids = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);
+ }
+
+ if(ids != null && ids.length > 0) {
+ for (int i = 0; i < ids.length; i++) {
+ downloadIds[i] = DMRepository.getInstance(getApplicationContext()).getClientId(ids[i]);
+ }
+ }
+ downloadService.notificationClicked(downloadIds);
+ }
+ }
+
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DownloadCommandService.java b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DownloadCommandService.java
new file mode 100644
index 0000000..4b38759
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/service/impl/DownloadCommandService.java
@@ -0,0 +1,372 @@
+package br.com.bemobi.medescope.service.impl;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.text.TextUtils;
+
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.constant.DownloadInfoReasonConstants;
+import br.com.bemobi.medescope.constant.Extras;
+import br.com.bemobi.medescope.log.IntentLogger;
+import br.com.bemobi.medescope.log.Logger;
+import br.com.bemobi.medescope.model.DownloadRequest;
+import br.com.bemobi.medescope.model.DownloadInfo;
+import br.com.bemobi.medescope.repository.DownloadDataRepository;
+import br.com.bemobi.medescope.repository.impl.MapDownloadDataRepository;
+import br.com.bemobi.medescope.service.CommunicationService;
+import br.com.bemobi.medescope.service.DownloadCommand;
+import br.com.bemobi.medescope.service.DownloadService;
+
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD;
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_SERVICE_LIFECYCLE;
+
+/**
+ * Created by bruno.costa on 26/06/15.
+ */
+public class DownloadCommandService extends Service implements DownloadCommand {
+
+ public static final String TAG = DownloadCommandService.class.getSimpleName();
+
+ public static final String ACTION_ENQUEUE = "br.com.bemobi.medescope.ACTION_ENQUEUE";
+ public static final String ACTION_CANCEL = "br.com.bemobi.medescope.ACTION_CANCEL";
+ public static final String ACTION_FINISH = "br.com.bemobi.medescope.ACTION_FINISH";
+ public static final String ACTION_NOTIFICATION_CLICK = "br.com.bemobi.medescope.ACTION_NOTIFICATION_CLICK";
+ public static final String ACTION_REGISTER_FOR_STATUS = "br.com.bemobi.medescope.ACTION_REGISTER_FOR_STATUS";
+ public static final String ACTION_UNREGISTER_FOR_STATUS = "br.com.bemobi.medescope.ACTION_UNREGISTER_FOR_STATUS";
+
+ private DownloadService downloadService;
+
+ private DownloadDataRepository downloadDataRepository;
+
+ private CommunicationService communicationService;
+
+ private Looper mServiceLooper;
+
+ private ServiceHandler mServiceHandler;
+
+ private String downloadIdRegisteredToSendProgress = "";
+
+ private boolean isStartedSendProgress = false;
+
+ public static void actionEnqueue(Context context, DownloadRequest downloadRequest) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_ENQUEUE);
+ serviceIntent.putExtra(Extras.EXTRA_DOWNLOAD, downloadRequest);
+ context.startService(serviceIntent);
+ }
+
+ public static void actionSubscribeStatusUpdate(Context context, String id) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_REGISTER_FOR_STATUS);
+ serviceIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, id);
+ context.startService(serviceIntent);
+ }
+
+ public static void actionUnsubscribeStatusUpdate(Context context) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_UNREGISTER_FOR_STATUS);
+ context.startService(serviceIntent);
+ }
+
+ public static void actionCancel(Context context, String id) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_CANCEL);
+ serviceIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, id);
+ context.startService(serviceIntent);
+ }
+
+ public static void actionFinishDownload(Context context, String downloadId, DownloadInfo downloadInfo) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_FINISH);
+ serviceIntent.putExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID, downloadId);
+ serviceIntent.putExtra(DownloadConstants.EXTRA_DOWNLOAD_INFO, downloadInfo);
+ context.startService(serviceIntent);
+ }
+
+ public static void actionNotificationClicked(Context context, String[] downloadIds) {
+ Intent serviceIntent = new Intent(context, DownloadCommandService.class);
+ serviceIntent.setAction(ACTION_NOTIFICATION_CLICK);
+ serviceIntent.putExtra(DownloadConstants.EXTRA_ARRAY_STRING_DOWNLOAD_IDS, downloadIds);
+ context.startService(serviceIntent);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "onBind()");
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "onCreate()");
+ initThreadHandler();
+ startCommand();
+ }
+
+ private void initThreadHandler() {
+ HandlerThread thread = new HandlerThread("PROGRESS_SENDER", Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
+
+ @Override
+ public void startCommand() {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "startCommand()");
+ this.downloadDataRepository = MapDownloadDataRepository.getInstance(getApplicationContext());
+ this.downloadService = DMDownloadService.getInstance(getApplicationContext());
+ this.communicationService = BroadcastCommunicationService.getInstance(getApplicationContext());
+ this.isStartedSendProgress = false;
+ this.downloadIdRegisteredToSendProgress = downloadDataRepository.recoverSubscribedId();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "onStartCommand()");
+ if (intent != null) {
+ executeCommand(intent.getAction(), intent.getExtras());
+ } else {
+ startCommand();
+ if( !TextUtils.isEmpty(this.downloadIdRegisteredToSendProgress )) {
+ startProgressSender();
+ }
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public void executeCommand(String action, Bundle extras) {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, String.format(">>>>>>>>>>>>>>>>>>>>>>>>>> ACTION RECEIVED: %s", action));
+ new IntentLogger(TAG, LOG_FEATURE_DOWNLOAD).logBundle(extras);
+
+ if (ACTION_ENQUEUE.equals(action)) {
+ //TODO transformar os parametros em um objeto Parcelable
+ DownloadRequest downloadRequest = (DownloadRequest) extras.getSerializable(Extras.EXTRA_DOWNLOAD);
+ this.enqueue(downloadRequest);
+ } else if (ACTION_CANCEL.equals(action)) {
+ cancelAction(getIdStringFromBundle(extras));
+ } else if (ACTION_FINISH.equals(action)) {
+ finishAction(getDownloadIdFromBundle(extras), getDownloadInfoFromBundle(extras));
+ } else if (ACTION_NOTIFICATION_CLICK.equals(action)) {
+ clickNotificationAction(getDownloadIdsFromBundle(extras));
+ } else if (ACTION_REGISTER_FOR_STATUS.equals(action)) {
+ registerForStatus(getIdStringFromBundle(extras));
+ } else if (ACTION_UNREGISTER_FOR_STATUS.equals(action)) {
+ unregisterForStatus();
+ }
+
+ if (downloadDataRepository.isEmptyDownloadData() && !isStartedSendProgress) {
+ stopSelf();
+ shutdownCommand();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "onDestroy()");
+ shutdownCommand();
+ super.onDestroy();
+ }
+
+ @Override
+ public void shutdownCommand() {
+ downloadService.shutdown();
+ }
+
+ @Override
+ public void enqueue(DownloadRequest downloadRequest) {
+ if (downloadRequest == null || !downloadRequest.isValid()) {
+ Logger.error(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, String.format("Invalid download object param: %s", downloadRequest.toString()));
+ return;
+ }
+
+ if (downloadDataRepository.containsDownloadDataKey(downloadRequest.getId())) {
+ DownloadInfo downloadInfo = downloadService.getDownloadInfo(downloadRequest.getId());
+
+ if (downloadInfo == null) {
+ this.enqueue(downloadService, downloadRequest);
+ return;
+ }
+
+ if (downloadInfo.hasFinished()) {
+ downloadService.cleanupId(downloadRequest.getId());
+ this.enqueue(downloadService, downloadRequest);
+ } else {
+ Logger.error(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, String.format("This download is in execution with status: %s", downloadInfo.getStatus()));
+ Logger.error(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "Do you would like to subscribe to receive status update? Medescope.subscribeStatus([downloadId])");
+ }
+
+ } else {
+ this.enqueue(downloadService, downloadRequest);
+ }
+ }
+
+ private void enqueue(DownloadService downloadService, DownloadRequest downloadRequest){
+ downloadService.cleanupId(downloadRequest.getId());
+ downloadDataRepository.removeDownloadData(downloadRequest.getId());
+ boolean enqueued = downloadService.enqueue(
+ downloadRequest.getId(),
+ downloadRequest.getUri(),
+ downloadRequest.getFileName(),
+ downloadRequest.getDownloadName(),
+ downloadRequest.getDownloadDescription(),
+ downloadRequest.getClientPayload(),
+ downloadRequest.shouldDownloadOnlyInWifi(),
+ downloadRequest.getCustomHeaders()
+ );
+ downloadDataRepository.putDownloadData(downloadRequest.getId(), downloadRequest.getClientPayload());
+ if (shouldStartSendProgressOnEnqueue(downloadRequest.getId())) {
+ startProgressSender();
+ }
+
+ if (!enqueued) {
+ communicationService
+ .sendFinishWithErrorBroadcastData(
+ downloadRequest.getId(),
+ DownloadInfoReasonConstants.ERROR_GENERIC,
+ downloadDataRepository.getDownloadData(downloadRequest.getId()));
+ }
+ }
+
+ private boolean shouldStartSendProgressOnEnqueue(String enqueueDownloadId){
+ return enqueueDownloadId.equals(downloadIdRegisteredToSendProgress) && !isStartedSendProgress;
+ }
+
+ @Override
+ public void cancelAction(String downloadId) {
+ if (!TextUtils.isEmpty(downloadId) && downloadDataRepository.containsDownloadDataKey(downloadId)) {
+ downloadService.cancel(downloadId);
+ }
+ }
+
+ @Override
+ public void finishAction(String downloadId, DownloadInfo downloadInfo) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "Finish Action");
+ if (downloadInfo != null) {
+ if (downloadInfo.hasFinishedWithSuccess()) {
+ communicationService.sendFinishWithSuccessBroadcastData(downloadId, downloadInfo.getFilename(), downloadDataRepository.getDownloadData(downloadId));
+ } else if (downloadInfo.hasFinishedWithError()) {
+ communicationService.sendFinishWithErrorBroadcastData(downloadId, downloadInfo.getReason(), downloadDataRepository.getDownloadData(downloadId));
+ }
+ } else {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD, "Cancelled");
+ communicationService.sendCancelled(downloadId);
+ }
+ downloadDataRepository.removeDownloadData(downloadId);
+ downloadService.cleanupId(downloadId);
+ }
+
+ @Override
+ public void clickNotificationAction(String[] downloadIds) {
+ communicationService.showDownloadQueue();
+ }
+
+ private void registerForStatus(String downloadId) {
+ downloadIdRegisteredToSendProgress = downloadId;
+ downloadDataRepository.persistSubscribedId(downloadId);
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_SERVICE_LIFECYCLE, "isStartedSendProgress:" + isStartedSendProgress);
+
+ if (!isStartedSendProgress) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_SERVICE_LIFECYCLE, "Starting Sender Progress!");
+ startProgressSender();
+ }
+ }
+
+ private void unregisterForStatus() {
+ downloadIdRegisteredToSendProgress = "";
+ downloadDataRepository.removeSubscribedId();
+ }
+
+ //TODO this method must be atomic(?)
+ public void startProgressSender() {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "startProgressSender()");
+
+ isStartedSendProgress = true;
+ Message msg = mServiceHandler.obtainMessage();
+ mServiceHandler.sendMessage(msg);
+ }
+
+ private final class ServiceHandler extends Handler {
+
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "ServiceHandler()");
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Logger.debug(TAG, LOG_FEATURE_SERVICE_LIFECYCLE, "ServiceHandler.handleMessage()");
+ DownloadInfo lastSentDownloadInfo = null;
+
+ while (!TextUtils.isEmpty(downloadIdRegisteredToSendProgress) && isStartedSendProgress) {
+ try {
+ if (!downloadDataRepository.containsDownloadDataKey(downloadIdRegisteredToSendProgress)) {
+ communicationService.sendDownloadStatusNotEnqueue(downloadIdRegisteredToSendProgress);
+ isStartedSendProgress = false;
+ break;
+ }
+
+ DownloadInfo downloadInfo = downloadService.getDownloadInfo(downloadIdRegisteredToSendProgress);
+ if (downloadInfo == null) {
+ communicationService.sendDownloadStatusNotEnqueue(downloadIdRegisteredToSendProgress);
+ isStartedSendProgress = false;
+ break;
+ }
+
+ if (downloadInfo.equals(lastSentDownloadInfo)) {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD_SEND_PROGRESS, "I have already sent this info");
+ } else if (downloadInfo.hasFinished()) {
+ // TODO em alguns casos o download está com 100% e não está sendo enviado o evento de FINISHED. Tratar esse caso posteriormente
+ communicationService.sendDownloadStatusProgress(downloadIdRegisteredToSendProgress, downloadInfo.getProgress());
+ isStartedSendProgress = false;
+ break;
+ }else if (downloadInfo.isPaused()) {
+ communicationService.sendDownloadStatusPaused(downloadIdRegisteredToSendProgress, downloadInfo.getReason());
+ } else if (downloadInfo.isInProgress()) {
+ communicationService.sendDownloadStatusProgress(downloadIdRegisteredToSendProgress, downloadInfo.getProgress());
+ } else {
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD_SEND_PROGRESS, "BIZARRE STATUS!! PANIC ALERT!!!");
+ }
+
+ lastSentDownloadInfo = downloadInfo;
+
+ Thread.sleep(300);
+
+ } catch (InterruptedException exception) {
+ Logger.error(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD_SEND_PROGRESS, "ERROR ON THREAD STATUS SENDER!!!!");
+ isStartedSendProgress = false;
+ }
+ }
+
+ isStartedSendProgress = false;
+
+ Logger.debug(TAG, DownloadConstants.LOG_FEATURE_DOWNLOAD_SEND_PROGRESS, "Finishing Status Sender!!!!!");
+ }
+ }
+
+ private String getIdStringFromBundle(Bundle extras) {
+ return extras.getString(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID);
+ }
+
+ private String getDownloadIdFromBundle(Bundle extras) {
+ return extras.getString(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID);
+ }
+
+ private String[] getDownloadIdsFromBundle(Bundle extras) {
+ return extras.getStringArray(DownloadConstants.EXTRA_ARRAY_STRING_DOWNLOAD_IDS);
+ }
+
+ private DownloadInfo getDownloadInfoFromBundle(Bundle extras) {
+ return extras.getParcelable(DownloadConstants.EXTRA_DOWNLOAD_INFO);
+ }
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/wrapper/DownloadInfoWrapper.java b/medescope/src/main/java/br/com/bemobi/medescope/wrapper/DownloadInfoWrapper.java
new file mode 100644
index 0000000..62a3633
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/wrapper/DownloadInfoWrapper.java
@@ -0,0 +1,10 @@
+package br.com.bemobi.medescope.wrapper;
+
+/**
+ * Created by raphael on 7/7/15.
+ */
+public interface DownloadInfoWrapper {
+ int translateStatus(STATUS status);
+
+ int translateReason(REASON reason);
+}
diff --git a/medescope/src/main/java/br/com/bemobi/medescope/wrapper/impl/DMDownloadInfoWrapper.java b/medescope/src/main/java/br/com/bemobi/medescope/wrapper/impl/DMDownloadInfoWrapper.java
new file mode 100644
index 0000000..ecfd68b
--- /dev/null
+++ b/medescope/src/main/java/br/com/bemobi/medescope/wrapper/impl/DMDownloadInfoWrapper.java
@@ -0,0 +1,69 @@
+package br.com.bemobi.medescope.wrapper.impl;
+
+import android.app.DownloadManager;
+
+import br.com.bemobi.medescope.constant.DownloadInfoReasonConstants;
+import br.com.bemobi.medescope.wrapper.DownloadInfoWrapper;
+
+/**
+ * Created by raphael on 7/7/15.
+ */
+public class DMDownloadInfoWrapper implements DownloadInfoWrapper {
+
+ @Override
+ public int translateStatus(Integer status) {
+ return status;
+ }
+
+ @Override
+ public int translateReason(Integer reason) {
+ int translatedReason;
+
+ switch(reason){
+ case DownloadManager.ERROR_CANNOT_RESUME:
+ translatedReason = DownloadInfoReasonConstants.ERROR_GENERIC;
+ break;
+ case DownloadManager.ERROR_DEVICE_NOT_FOUND:
+ translatedReason = DownloadInfoReasonConstants.ERROR_STORAGE_NOT_FOUND;
+ break;
+ case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
+ translatedReason = DownloadInfoReasonConstants.ERROR_ALREADY_EXISTING_FILE;
+ break;
+ case DownloadManager.ERROR_FILE_ERROR:
+ translatedReason = DownloadInfoReasonConstants.ERROR_FILE_GENERIC_PROBLEM;
+ break;
+ case DownloadManager.ERROR_HTTP_DATA_ERROR:
+ translatedReason = DownloadInfoReasonConstants.ERROR_HTTP_DATA_ERROR;
+ break;
+ case DownloadManager.ERROR_INSUFFICIENT_SPACE:
+ translatedReason = DownloadInfoReasonConstants.ERROR_INSUFFICIENT_STORAGE;
+ break;
+ case DownloadManager.ERROR_TOO_MANY_REDIRECTS:
+ translatedReason = DownloadInfoReasonConstants.ERROR_TOO_MANY_REDIRECTS;
+ break;
+ case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:
+ translatedReason = DownloadInfoReasonConstants.ERROR_UNHANDLED_HTTP_CODE;
+ break;
+ case DownloadManager.ERROR_UNKNOWN:
+ translatedReason = DownloadInfoReasonConstants.ERROR_GENERIC;
+ break;
+ case DownloadManager.PAUSED_QUEUED_FOR_WIFI:
+ translatedReason = DownloadInfoReasonConstants.PAUSED_QUEUED_TO_DOWNLOAD_ON_WIFI;
+ break;
+ case DownloadManager.PAUSED_UNKNOWN:
+ translatedReason = DownloadInfoReasonConstants.PAUSED_GENERIC_REASON;
+ break;
+ case DownloadManager.PAUSED_WAITING_FOR_NETWORK:
+ translatedReason = DownloadInfoReasonConstants.PAUSED_WAITING_NETWORK;
+ break;
+ case DownloadManager.PAUSED_WAITING_TO_RETRY:
+ translatedReason = DownloadInfoReasonConstants.PAUSED_WAITING_NETWORK_AFTER_ERROR;
+ break;
+ default:
+ translatedReason = reason;
+ }
+
+
+ return translatedReason;
+ }
+}
\ No newline at end of file
diff --git a/medescope/src/test/java/br/com/bemobi/download/DownloadDataRepoTest.java b/medescope/src/test/java/br/com/bemobi/download/DownloadDataRepoTest.java
new file mode 100644
index 0000000..f0dc4f2
--- /dev/null
+++ b/medescope/src/test/java/br/com/bemobi/download/DownloadDataRepoTest.java
@@ -0,0 +1,45 @@
+package br.com.bemobi.medescope;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import br.com.bemobi.medescope.repository.DownloadDataRepository;
+import br.com.bemobi.medescope.repository.impl.MapDownloadDataRepository;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+/**
+ * Created by bruno.costa on 22/05/15.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(emulateSdk = 18)
+public class DownloadDataRepoTest {
+
+ @Mock
+ private DownloadDataRepository repository;
+
+ @Before
+ public void setup(){
+ //initMocks(this);
+ repository = MapDownloadDataRepository.getInstance(Robolectric.application.getApplicationContext());
+ }
+
+ @Test
+ public void shouldRufflingANumberBetweenTwoNumbers() {
+ String key = "key1";
+ String value = "value1";
+
+ repository.putDownloadData(key, value);
+
+// repository = null;
+// repository = MapDownloadDataRepository.getInstance(Robolectric.application.getApplicationContext());
+
+ assertTrue(value.equals(repository.getDownloadData(key)));
+ }
+}
diff --git a/sample/build.gradle b/sample/build.gradle
new file mode 100644
index 0000000..b7c8fab
--- /dev/null
+++ b/sample/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.0"
+
+ defaultConfig {
+ applicationId "br.com.bemobi.medescope.sample"
+ minSdkVersion 10
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:23.0.0'
+ compile 'com.facebook.stetho:stetho:1.1.1'
+ compile 'com.github.halysongoncalves:pugnotification:1.7.0'
+ compile project(':medescope')
+}
diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro
new file mode 100644
index 0000000..a9ca86e
--- /dev/null
+++ b/sample/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/bruno.costa/android-sdks/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/sample/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java b/sample/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java
new file mode 100644
index 0000000..497d423
--- /dev/null
+++ b/sample/src/androidTest/java/br/com/bemobi/medescope/ApplicationTest.java
@@ -0,0 +1,13 @@
+package br.com.bemobi.medescope;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/sample/src/androidTest/java/br/com/bemobi/medescope/sample/ApplicationTest.java b/sample/src/androidTest/java/br/com/bemobi/medescope/sample/ApplicationTest.java
new file mode 100644
index 0000000..30c78da
--- /dev/null
+++ b/sample/src/androidTest/java/br/com/bemobi/medescope/sample/ApplicationTest.java
@@ -0,0 +1,13 @@
+package br.com.bemobi.medescope.sample;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b6b20ac
--- /dev/null
+++ b/sample/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/java/br/com/bemobi/medescope/sample/AppApplication.java b/sample/src/main/java/br/com/bemobi/medescope/sample/AppApplication.java
new file mode 100644
index 0000000..b2707c2
--- /dev/null
+++ b/sample/src/main/java/br/com/bemobi/medescope/sample/AppApplication.java
@@ -0,0 +1,22 @@
+package br.com.bemobi.medescope.sample;
+
+import android.app.Application;
+
+import com.facebook.stetho.Stetho;
+
+/**
+ * Created by luisfernandez on 7/6/15.
+ */
+public class AppApplication extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Stetho.initialize(
+ Stetho.newInitializerBuilder(this)
+ .enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
+ .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this))
+ .build());
+ }
+}
diff --git a/sample/src/main/java/br/com/bemobi/medescope/sample/MainActivity.java b/sample/src/main/java/br/com/bemobi/medescope/sample/MainActivity.java
new file mode 100644
index 0000000..da90332
--- /dev/null
+++ b/sample/src/main/java/br/com/bemobi/medescope/sample/MainActivity.java
@@ -0,0 +1,199 @@
+package br.com.bemobi.medescope.sample;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ProgressBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import br.com.bemobi.medescope.Medescope;
+import br.com.bemobi.medescope.sample.model.NotificationData;
+import br.com.bemobi.medescope.callback.DownloadStatusCallback;
+import br.com.bemobi.medescope.exception.DirectoryNotMountedException;
+import br.com.bemobi.medescope.exception.PathNotFoundException;
+
+
+public class MainActivity extends ActionBarActivity {
+
+ public static final String TAG = MainActivity.class.getSimpleName();
+ public static final String FILE_PATH = "FILE.zip";
+
+ String FILE_5MB = "http://download.thinkbroadband.com/5MB.zip";
+ String FILE_10MB = "http://download.thinkbroadband.com/10MB.zip";
+ String FILE_20MB = "http://download.thinkbroadband.com/20MB.zip";
+ String FILE_50MB = "http://download.thinkbroadband.com/50MB.zip";
+ String FILE_100MB = "http://download.thinkbroadband.com/100MB.zip";
+ String [] files = {FILE_5MB, FILE_10MB, FILE_20MB, FILE_50MB, FILE_100MB};
+
+ String downloadName = FILE_5MB;
+
+ Spinner spinner;
+ ProgressBar progressBar;
+ ArrayAdapter adapter;
+
+ private TextView textStatus;
+ private TextView textAction;
+ private Medescope mMedescope;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ List spinnerArray = new ArrayList<>();
+ spinnerArray.add("5MB File");
+ spinnerArray.add("10MB File");
+ spinnerArray.add("20MB File");
+ spinnerArray.add("50MB File");
+ spinnerArray.add("100MB File");
+
+ textStatus = (TextView) findViewById(R.id.text);
+ textAction = (TextView) findViewById(R.id.textAction);
+
+ progressBar = (ProgressBar) findViewById(R.id.sample_progressBar);
+
+ adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, spinnerArray);
+
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner = (Spinner) findViewById(R.id.download_spinner);
+ spinner.setAdapter(adapter);
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView> parentView, View selectedItemView, int myPosition, long myID) {
+ downloadName = files[myPosition];
+ mMedescope.updateSubscriptionStatusId(MainActivity.this, "" + myID);
+ hideProgress();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parentView) {
+ // your code here
+ }
+
+ });
+ }
+
+ private void hideProgress() {
+ progressBar.setVisibility(View.GONE);
+ progressBar.setProgress(0);
+ progressBar.setIndeterminate(false);
+ }
+
+ private void showProgress() {
+ progressBar.setVisibility(View.VISIBLE);
+ progressBar.setProgress(0);
+ progressBar.setIndeterminate(true);
+ }
+
+ private void setPercentProgress(int progress) {
+ progressBar.setVisibility(View.VISIBLE);
+ progressBar.setProgress(progress);
+ progressBar.setIndeterminate(false);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mMedescope = Medescope.getInstance(this);
+ mMedescope.setApplicationName(getString(R.string.app_name));
+ mMedescope.subscribeStatus(this, "" + spinner.getSelectedItemId(), new DownloadStatusCallback() {
+ @Override
+ public void onDownloadNotEnqueued(String downloadId) {
+ textAction.setText("ACTION NOT ENQUEUED");
+ textStatus.setText("NOT ENQUEUED:");
+ hideProgress();
+ }
+
+ @Override
+ public void onDownloadPaused(String downloadId, int reason) {
+ textAction.setText("ACTION PAUSED");
+ textStatus.setText(String.format("NOT ENQUEUED: [reason: %s ]", reason));
+ }
+
+ @Override
+ public void onDownloadInProgress(String downloadId, int progress) {
+ textAction.setText("ACTION IN PROGRESS");
+ Log.d(TAG, "Received download progress: " + progress);
+ setPercentProgress(progress);
+ textStatus.setText(String.format("PROGRESS: [progress: %s ]", progress));
+ }
+
+ @Override
+ public void onDownloadOnFinishedWithError(String downloadId, int reason, String data) {
+ textAction.setText("ACTION FINISH WITH ERROR");
+ hideProgress();
+ }
+
+ @Override
+ public void onDownloadOnFinishedWithSuccess(String downloadId, String filePath, String data) {
+ textAction.setText("ACTION FINISH WITH SUCCESS");
+ Log.d(TAG, "Received path: " + filePath);
+ checkFilePath(filePath);
+
+ hideProgress();
+ }
+
+ @Override
+ public void onDownloadCancelled(String downloadId) {
+ textAction.setText("ACTION FINISH WITH SUCCESS");
+ hideProgress();
+ }
+ });
+ }
+
+ private void checkFilePath(String filePath) {
+ String originalFilePath = "";
+ try {
+ originalFilePath = mMedescope.getDownloadDirectoryToRead(FILE_PATH);
+ Log.d(TAG, "Original path: " + originalFilePath);
+ } catch (DirectoryNotMountedException e) {
+ e.printStackTrace();
+ } catch (PathNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ if (filePath.equals(originalFilePath)) {
+ Toast.makeText(MainActivity.this, "Path sent is the expected", Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Medescope.getInstance(this).unsubscribeStatus(this);
+ }
+
+ public void download(View view) {
+ NotificationData data = new NotificationData("" + spinner.getSelectedItemId(), downloadName, getString(R.string.app_name));
+
+ boolean shouldDownloadOnlyInWiFi = isLargeFile(downloadName);
+
+ mMedescope.enqueue(
+ data.getId(),
+ downloadName,
+ FILE_PATH,
+ (String) spinner.getSelectedItem(),
+ data.toJson(),
+ shouldDownloadOnlyInWiFi);
+ showProgress();
+ }
+
+ public void cancel(View view) {
+ mMedescope.cancel("" + spinner.getSelectedItemId());
+ hideProgress();
+ }
+
+ public boolean isLargeFile(String selected) {
+ return FILE_20MB.equals(selected) || FILE_50MB.equals(selected) || FILE_100MB.equals(selected);
+ }
+}
diff --git a/sample/src/main/java/br/com/bemobi/medescope/sample/model/NotificationData.java b/sample/src/main/java/br/com/bemobi/medescope/sample/model/NotificationData.java
new file mode 100644
index 0000000..c7b8d79
--- /dev/null
+++ b/sample/src/main/java/br/com/bemobi/medescope/sample/model/NotificationData.java
@@ -0,0 +1,56 @@
+package br.com.bemobi.medescope.sample.model;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Created by bruno.costa on 30/06/15.
+ */
+public class NotificationData {
+
+ private String id;
+
+ private String title;
+
+ private String desc;
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public NotificationData(String id) {
+ this.id = id;
+ }
+
+ public NotificationData(String id, String title, String desc) {
+ this.id = id;
+ this.title = title;
+ this.desc = desc;
+ }
+
+ public String toJson() {
+ Gson gson = new GsonBuilder().create();
+ return gson.toJson(this);
+ }
+
+}
diff --git a/sample/src/main/java/br/com/bemobi/medescope/sample/receiver/BemobiDownloadReceiver.java b/sample/src/main/java/br/com/bemobi/medescope/sample/receiver/BemobiDownloadReceiver.java
new file mode 100644
index 0000000..d28f56e
--- /dev/null
+++ b/sample/src/main/java/br/com/bemobi/medescope/sample/receiver/BemobiDownloadReceiver.java
@@ -0,0 +1,75 @@
+package br.com.bemobi.medescope.sample.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.gson.Gson;
+
+import br.com.bemobi.medescope.Medescope;
+import br.com.bemobi.medescope.constant.DownloadConstants;
+import br.com.bemobi.medescope.receiver.BroadcastReceiverLogger;
+import br.com.bemobi.medescope.sample.R;
+import br.com.bemobi.medescope.sample.model.NotificationData;
+import br.com.goncalves.pugnotification.notification.PugNotification;
+
+import static br.com.bemobi.medescope.constant.DownloadConstants.LOG_FEATURE_DOWNLOAD;
+
+/**
+ * Created by bruno.costa on 02/07/15.
+ */
+public class BemobiDownloadReceiver extends BroadcastReceiver {
+
+ private static final String TAG = BemobiDownloadReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ new BroadcastReceiverLogger(TAG, LOG_FEATURE_DOWNLOAD).onReceive(context, intent);
+
+ if (intent != null) {
+ String action = intent.getAction();
+ if (Medescope.ACTION_BROADCAST_FINISH_WITH_SUCCESS.equals(action)) {
+ String downloadId = intent.getStringExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID);
+ String filePath = intent.getStringExtra(DownloadConstants.EXTRA_STRING_FILE_PATH);
+
+ if (!TextUtils.isEmpty(downloadId)) {
+ String data = intent.getStringExtra(DownloadConstants.EXTRA_STRING_JSON_DATA);
+ NotificationData nData = new Gson().fromJson(data, NotificationData.class);
+
+ PugNotification.with(context).load()
+ .title(nData.getTitle())
+ .message(nData.getDesc())
+ .bigTextStyle(nData.getDesc())
+ .smallIcon(R.mipmap.ic_launcher)
+ .largeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.bemobi_download_ic_notif_done))
+ .autoCancel(true)
+ .simple()
+ .build();
+
+ Log.d(TAG, "File Path: " + filePath);
+ }
+ } else if (Medescope.ACTION_BROADCAST_FINISH_WITH_ERROR.equals(action)) {
+ String downloadId = intent.getStringExtra(DownloadConstants.EXTRA_STRING_DOWNLOAD_ID);
+ String errorMsg = intent.getStringExtra(DownloadConstants.EXTRA_INT_ERROR_REASON);
+
+ if (!TextUtils.isEmpty(downloadId)) {
+ String data = intent.getStringExtra(DownloadConstants.EXTRA_STRING_JSON_DATA);
+ NotificationData nData = new Gson().fromJson(data, NotificationData.class);
+
+ PugNotification.with(context).load()
+ .title(nData.getTitle())
+ .message(errorMsg + " - " + nData.getDesc())
+ .bigTextStyle(nData.getDesc())
+ .smallIcon(R.mipmap.ic_launcher)
+ .largeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.bemobi_download_ic_notif_error))
+ .autoCancel(true)
+ .simple()
+ .build();
+ }
+ }
+ }
+ }
+}
diff --git a/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..e5e334f
Binary files /dev/null and b/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..8b2e452
Binary files /dev/null and b/sample/src/main/res/drawable-hdpi-v11/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..9444af0
Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..413177a
Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..f75a60a
Binary files /dev/null and b/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..0819080
Binary files /dev/null and b/sample/src/main/res/drawable-mdpi-v11/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..b74224c
Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..07bc45e
Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..d9ad9d8
Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..84074dd
Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi-v11/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..fb26431
Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..f642485
Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..cebdcf3
Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..081368f
Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi-v11/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..420da4f
Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..d7f5ae9
Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..bdfd7a5
Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..f1718d7
Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi-v11/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_done.png b/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_done.png
new file mode 100644
index 0000000..ef699e2
Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_done.png differ
diff --git a/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_error.png b/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_error.png
new file mode 100644
index 0000000..663e2ce
Binary files /dev/null and b/sample/src/main/res/drawable-xxxhdpi/bemobi_download_ic_notif_error.png differ
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..c2a8ab5
--- /dev/null
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/sample/src/main/res/values/dimens.xml b/sample/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/sample/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
new file mode 100644
index 0000000..91fa313
--- /dev/null
+++ b/sample/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+ DownloadSample
+
+ Hello world!
+ Settings
+
+
+ true
+ true
+ true
+
diff --git a/sample/src/main/res/values/styles.xml b/sample/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/sample/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..4238e35
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':medescope', ':sample'