package com.malcom.library.android.module.config; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Locale; import java.util.concurrent.TimeUnit; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import com.malcom.library.android.MCMDefines; import com.malcom.library.android.exceptions.ApplicationConfigurationNotFoundException; import com.malcom.library.android.exceptions.ConfigModuleNotInitializedException; import com.malcom.library.android.exceptions.ConfigurationException; import com.malcom.library.android.module.core.MCMCoreAdapter; import com.malcom.library.android.utils.MCMUtils; import com.malcom.library.android.utils.ToolBox; import com.malcom.library.android.utils.ToolBox.HTTP_METHOD; /** * Class for configuration module * * @author Malcom Ventures, S.L. * @since 2012 */ public class MCMConfigManager { /** MALCOM API endpoint */ private final static String GLOBAL_CONF = "v1/globalconf/"; /** MALCOM library remote configuration file name */ private final static String CONFIG_FILE_NAME = "config.json"; /** MALCOM library basic bundled configuration. This name is used when saving the remote configuration data. */ private final static String BUNDLE_CONFIG_FILE_NAME = "mcmconfiginfo.json"; /** MALCOM splash image file name used when store the remote file in disk */ private final static String CONFIG_SPLASH_IMAGE_NAME = "splash.img"; /** MALCOM off-line splash image file name used when there is no network and no splash has never been downloaded. */ private final static String CONFIG_SPLASH_ASSETS_IMAGE_NAME = "splash.img"; /** Value returned by Malcom when an application does not have a configuration specified. */ private final static String NO_CONFIGURATION_DATA = "{}\n"; /** Library shared preferences "interstitial times shown" parameter */ private final static String LIB_PREFENCES_INTERSTITIAL_TIMES_SHOWN = "insterstitialTimesShown"; private final static String LIB_PREFENCES_INTERSTITIAL_TIMES_TO_SHOW = "insterstitialTimesToShow"; private static MCMConfigManager instance = null; /** Malcom application configuration */ private Configuration configuration = null; private static int configLayoutResId = 0; private LinearLayout splash_layout; private ImageView splashImageView; private LinearLayout splash_progress_zone; private LinearLayout interstitialLayout; private WebView interstitial; private Button interstitialClose; private LinearLayout interstitial_progress_zone; private Activity activity; private ProgressDialog dialog; private boolean isLoading = false; private String originalTitle; private boolean executeAfterLoad; private static boolean configShown; protected MCMConfigManager() { // Exists only to defeat instantiation. } // Singleton public static MCMConfigManager getInstance() { if (instance == null) { instance = new MCMConfigManager(); } return instance; } // PUBLIC FUNCTIONS /** * Loads the configuration for the application. First it tries to get from the server * if the device is on-line, if not, it will try to load previously downloaded one and, * if there is no previous one, it will load the bundled default (configurable) * configuration stored in the assets directory. * * @param activity Activity in which to display the information. * @param appPackage package of the application */ public void createConfig(final Activity activity, String appPackage) { this.activity = activity; executeAfterLoad = true; // Gets the resources ids configLayoutResId = activity.getResources().getIdentifier("config_layout", "id", appPackage); int resSplashID = activity.getResources().getIdentifier("image_view", "id", appPackage); int resInterstitialID = activity.getResources().getIdentifier("webview", "id", appPackage); int resInterstitialCloseButtonID = activity.getResources().getIdentifier("web_view_close", "id", appPackage); int resInterstitialLayoutID = activity.getResources().getIdentifier("webview_layout", "id", appPackage); int resInterstitialProgressZoneID = activity.getResources().getIdentifier("progresszone", "id", appPackage); int resSplashProgressZoneID = activity.getResources().getIdentifier("splash_progresszone", "id", appPackage); int resSplashLayoutID = activity.getResources().getIdentifier("splash_layout", "id", appPackage); this.splashImageView = (ImageView) activity.findViewById(resSplashID); // Initializes the layout components initializeLayoutComponents(resSplashID, resInterstitialLayoutID, resInterstitialID, resInterstitialCloseButtonID, resInterstitialProgressZoneID, resSplashProgressZoneID, resSplashLayoutID); // Loads the remote configuration from server loadConfiguration(); } private void initializeLayoutComponents(int resSplashID, int resInterstitialLayoutID, int resInterstitialID, int resInterstitialCloseButtonID, int resInterstitialProgressZoneID, int resSplashProgressZoneID, int resSplashLayoutID){ if (resSplashID != 0) { this.splashImageView = (ImageView) activity.findViewById(resSplashID); } if (resInterstitialLayoutID != 0) { this.interstitialLayout = (LinearLayout) activity.findViewById(resInterstitialLayoutID); } if (resInterstitialCloseButtonID != 0) { this.interstitialClose = (Button) activity.findViewById(resInterstitialCloseButtonID); } this.originalTitle = this.activity.getTitle().toString(); if (resInterstitialProgressZoneID != 0) { this.interstitial_progress_zone = (LinearLayout) activity.findViewById(resInterstitialProgressZoneID); } if (resSplashProgressZoneID != 0) { this.splash_progress_zone = (LinearLayout) activity.findViewById(resSplashProgressZoneID); } if (resSplashLayoutID != 0) { this.splash_layout = (LinearLayout) activity.findViewById(resSplashLayoutID); } if (resInterstitialID != 0) { this.interstitial = (WebView) activity.findViewById(resInterstitialID); if (this.interstitial != null) { this.interstitial.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { //It seems that Android is opening the url in the system //default navigator instead in the webview. We avoid this //with the code below. view.loadUrl(url); return true; } }); this.interstitial.setWebChromeClient(new WebChromeClient(){ @Override public void onProgressChanged(WebView view, int newProgress) { //We show the loading icon while the page is being loaded. if(newProgress < 100 && interstitial_progress_zone.getVisibility() == ProgressBar.GONE){ interstitial_progress_zone.setVisibility(ProgressBar.VISIBLE); } if(newProgress == 100) { //...and hide the loading once is loaded. interstitial_progress_zone.setVisibility(ProgressBar.GONE); } } }); } } } public void getProperty(String key, MCMCoreAdapter.ConfigListener listener) throws ConfigModuleNotInitializedException { if(isConfigurationLoaded()){ listener.onReceivedParameter(key, (String) configuration.getProperty(key)); }else{ //TODO: Pedro - Cargamos el fichero de configuración y cuando termina llamamos al callback con el(acentuado) executeAfterLoad = false; loadConfiguration(); if(isLoading) throw new ConfigModuleNotInitializedException("Config module is being initialized, wait until is fully initialized before call this method."); else throw new ConfigModuleNotInitializedException("Config module has not been initialized, use useConfigModule()."); } } /** * Get a value for key * * @param key Key string for value * @return value for key or null if the property key does not exist or if the key is null. */ public String getKeyValue(String key) { if (isConfigurationLoaded()) { if (!TextUtils.isEmpty(key)) { if(configuration!=null){ return configuration.getProperty(key)!=null?(String)configuration.getProperty(key):null; }else{ Log.e(MCMDefines.LOG_TAG,"CONFIG_GET_KEY_VALUE. There is no config available (not even the default one)!. " + "Please do not forget to include in your project the file '"+ BUNDLE_CONFIG_FILE_NAME +"' file."); throw new ConfigModuleNotInitializedException("Config module has not been initialized, use useConfigModule()."); } }else{ return null; } } else { if(isLoading) throw new ConfigModuleNotInitializedException("Config module is being initialized, wait until is fully initialized."); else throw new ConfigModuleNotInitializedException("Config module has not been initialized, use useConfigModule()."); } } // AUXILIAR METHODS // AUXILIAR FUNCTIONS private void showProgressDialog(){ if(!isLoading){ isLoading = true; dialog = new ProgressDialog(activity); //dialog.setMessage("Loading application..."); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.setCancelable(false); dialog.show(); } } private void closeProgressDialog(){ isLoading = false; dialog.dismiss(); } // -- Configuration loading options. Local and Remote. private void loadConfiguration(){ configShown = true; if(ToolBox.network_haveNetworkConnection(activity)){ Log.d(MCMDefines.LOG_TAG, "READ_CONGIF_DOWNLOAD. On-Line. Downloading configuration."); new DownloadConfigurationFile().execute(); }else{ loadLocalConfiguration(); } } private void loadLocalConfiguration(){ if(activity.getFileStreamPath(CONFIG_FILE_NAME).exists()){ config_useDownloaded(); } else { config_useBundled(); } if(executeAfterLoad){ executeConfig(false); } } // -- End Configuration loading options. Local and Remote. // -- Configuration load options methods. private void config_useDownloaded(){ Log.d(MCMDefines.LOG_TAG, "READ_CONGIF_DOWNLOADED. Off-line. The configuration was already downloaded, using this configuration."); try{ //showProgressDialog(); FileInputStream fIn = activity.openFileInput(CONFIG_FILE_NAME); InputStreamReader isr = new InputStreamReader(fIn); char[] data = new char[fIn.available()]; isr.read(data); loadConfigurationFileFromDisk(new String(data)); }catch(Exception e){ Log.e(MCMDefines.LOG_TAG, "READ_CONGIF_DOWNLOADED_ERROR. Problems obtaining the configuration from '"+ CONFIG_FILE_NAME +"':"+e.getMessage(),e); } finally { //closeProgressDialog(); } } private void config_useBundled(){ Log.d(MCMDefines.LOG_TAG, "READ_CONGIF_BUNDLED. Off-line. The configuration was not previously downloaded. using the default bundled one."); try{ //showProgressDialog(); InputStream assetIn = activity.getAssets().open(BUNDLE_CONFIG_FILE_NAME); InputStreamReader isr = new InputStreamReader(assetIn); char[] assetData = new char[assetIn.available()]; isr.read(assetData); loadConfigurationFileFromDisk(new String(assetData)); }catch(Exception e){ Log.e(MCMDefines.LOG_TAG, "READ_CONGIF_BUNDLED_ERROR. Problems obtaining the default configuration from '"+ BUNDLE_CONFIG_FILE_NAME +"':"+e.getMessage(),e); } finally { //closeProgressDialog(); } } // -- End configuration load options methods. private void executeConfig(boolean isNetworkAccess) { //((RelativeLayout)activity.findViewById(configLayoutResId)).setVisibility(View.VISIBLE); Log.d(MCMDefines.LOG_TAG, "Execute config with configuration: "+configuration); if(configuration!=null){ if(configuration.isSplash()){ showSplash(isNetworkAccess); } if(configuration.isInterstitial()) { showInterstitial(isNetworkAccess); } if(configuration.isAlert()) { //((RelativeLayout)activity.findViewById(configLayoutResId)).setVisibility(View.GONE); showAlert(); }/*else { ((RelativeLayout)activity.findViewById(configLayoutResId)).setVisibility(View.GONE); //There is configuration but none of the possibilities are active. activity.startActivity(principalActivity); activity.finish(); }*/ }else{ Log.e(MCMDefines.LOG_TAG, "EXECUTE_CONFIG_ERROR. There is no configuration available to execute!"); } } private boolean loadConfigurationFile(Activity context) { boolean configLoaded = false; try{ URL configPath = obtainConfigurationURLPath(); configuration = new Configuration(getJSONfromURL(configPath.toString()),Locale.getDefault().getLanguage()); Log.i(MCMDefines.LOG_TAG, "CONFIG_LANGUAGE." + Locale.getDefault().getLanguage()); configLoaded = true; }catch(ApplicationConfigurationNotFoundException e){ Log.i(MCMDefines.LOG_TAG, "CONFIGURATION_GET_FROM_SERVER. Application does not have a configuration."); }catch(ConfigurationException e){ Log.e(MCMDefines.LOG_TAG, "LOAD_CONFIG_FILE_FROM_SERVER_ERROR." + e.getMessage()); } return configLoaded; } private void loadConfigurationFileFromDisk(String configData) { try { JSONObject jObject = new JSONObject(configData); configuration = new Configuration(jObject,Locale.getDefault().getLanguage()); } catch( Exception e){ Log.e(MCMDefines.LOG_TAG, "LOAD_CONFIG_FROM_DISK_ERROR. "+e.getMessage()); } } private URL obtainConfigurationURLPath() throws ConfigurationException{ try{ String deviceId = MCMUtils.getEncodedUDID(ToolBox.device_getId(activity)); String path = MCMCoreAdapter.getInstance().coreGetProperty(MCMCoreAdapter.PROPERTIES_MALCOM_BASEURL) + GLOBAL_CONF + MCMCoreAdapter.getInstance().coreGetProperty(MCMCoreAdapter.PROPERTIES_MALCOM_APPID) + "/" + deviceId + "/" + CONFIG_FILE_NAME; URL url = new URL(path); Log.d(MCMDefines.LOG_TAG, "CONFIG. " + path); return url; }catch(MalformedURLException e){ Log.e(MCMDefines.LOG_TAG, "CONFIG_GET_SERVER_PATH." + e.getMessage()); throw new ConfigurationException("Bad server configuration path!",ConfigurationException.CONFIGURATION_EXCEPTION_BAD_SERVER_CONFIG_PATH); } } private JSONObject getJSONfromURL(String url) throws ApplicationConfigurationNotFoundException{ String result = ""; JSONObject jObject = null; try{ result = ToolBox.net_httpclient_doAction(HTTP_METHOD.GET, url, null, null); //Check for non set configuration data for the application. if(result.equalsIgnoreCase(NO_CONFIGURATION_DATA)) throw new ApplicationConfigurationNotFoundException(); //try parse the string to a JSON object jObject = new JSONObject(result); }catch(JSONException e){ Log.e(MCMDefines.LOG_TAG, "Error getting the configuration from http connection "+e.toString()); }catch(IOException e){ Log.e(MCMDefines.LOG_TAG, "Error getting the configuration from http connection "+e.toString()); } return jObject; } private synchronized void saveConfigurationFile() { FileOutputStream fos; try { if(configuration!=null){ fos = activity.openFileOutput(CONFIG_FILE_NAME, Context.MODE_PRIVATE); fos.write(configuration.getConfigDataRaw().getBytes()); fos.close(); } } catch (Exception e) { Log.e(MCMDefines.LOG_TAG, "CONFIGURATION_SAVE_ERROR."+e.getMessage()); } } // -- Show/Remove Splash, interstitial and alert functions private void showSplash(final boolean isNetworkAccess){ //Get the splash image. Log.d(MCMDefines.LOG_TAG, "Show splash"); if (splash_layout != null) { this.splash_layout.setVisibility(View.VISIBLE); this.splash_progress_zone.setVisibility(View.VISIBLE); PrepareAndShowSplashImage prepareSplashImage = new PrepareAndShowSplashImage(isNetworkAccess); prepareSplashImage.execute(); } } private void removeSplash(boolean isNetworkAccess) { Log.d(MCMDefines.LOG_TAG, "Remove splash"); //((RelativeLayout)activity.findViewById(configLayoutResId)).setVisibility(View.GONE); this.splashImageView.postInvalidate(); this.splashImageView.setImageBitmap(null); this.splashImageView.setVisibility(View.GONE); this.splash_layout.setVisibility(View.GONE); updateFullscreenStatus(false, true); } private void showInterstitial(boolean isNetworkAccess){ Log.d(MCMDefines.LOG_TAG, "Show Intersitial"); if(isNetworkAccess && interstitial != null && interstitial_checkTimesShown()){ interstitial.getSettings().setJavaScriptEnabled(true); interstitial.loadUrl(configuration.getInterstitialWeb()); interstitial.setVisibility(View.VISIBLE); interstitialClose.setText("Close"); interstitialClose.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { removeInterstitial(); } }); updateFullscreenStatus(true, false); interstitialClose.setVisibility(View.VISIBLE); interstitialLayout.setVisibility(View.VISIBLE); }else{ removeInterstitial(); } } private boolean interstitial_checkTimesShown(){ boolean show = true; //Check if there are still times to be shown SharedPreferences librarySettings = activity.getSharedPreferences(MCMCoreAdapter.MALCOM_LIBRARY_PREFERENCES_FILE_NAME, 0); SharedPreferences.Editor editor = librarySettings.edit(); if(configuration.getInterstitialTimesToShow()!=null){ //1º Look for changes. int timesToShow = librarySettings.getInt(LIB_PREFENCES_INTERSTITIAL_TIMES_TO_SHOW, -1); if(timesToShow!=-1){ //If the value is different than the previous one we save it and reset the counter. if(timesToShow!=configuration.getInterstitialTimesToShow().intValue()){ timesToShow = configuration.getInterstitialTimesToShow().intValue(); editor.putInt(LIB_PREFENCES_INTERSTITIAL_TIMES_TO_SHOW, timesToShow); editor.putInt(LIB_PREFENCES_INTERSTITIAL_TIMES_SHOWN, 0); editor.commit(); } }else{ //The variable did not exist, we save it. timesToShow = configuration.getInterstitialTimesToShow().intValue(); editor.putInt(LIB_PREFENCES_INTERSTITIAL_TIMES_TO_SHOW, timesToShow); editor.commit(); } //2º See if there are remaining times to show. int timesShown = librarySettings.getInt(LIB_PREFENCES_INTERSTITIAL_TIMES_SHOWN, 0); if(timesShown==timesToShow){ show=false; }else{ timesShown++; editor.putInt(LIB_PREFENCES_INTERSTITIAL_TIMES_SHOWN, timesShown); editor.commit(); } }else{ editor.remove(LIB_PREFENCES_INTERSTITIAL_TIMES_SHOWN); editor.commit(); } return show; } private void removeInterstitial(){ Log.d(MCMDefines.LOG_TAG, "Remove intersitial"); if (configLayoutResId != 0) { if (interstitial != null) { //((RelativeLayout)activity.findViewById(configLayoutResId)).setVisibility(View.GONE); interstitial.postInvalidate(); interstitial.setVisibility(View.GONE); interstitialClose.postInvalidate(); interstitialClose.setVisibility(View.GONE); interstitialLayout.setVisibility(View.GONE); updateFullscreenStatus(false, false); } } } private void showAlert() { Log.d(MCMDefines.LOG_TAG, "Show alert"); boolean showAlert = true; String alertAppVersion = configuration.getAlertAppStoreVersion(); String appVersionName = getVersionName(); Log.d(MCMDefines.LOG_TAG, "App Version: "+alertAppVersion); Log.d(MCMDefines.LOG_TAG, "Version Name: "+appVersionName); if (alertAppVersion != null) { alertAppVersion= normalisedVersion(alertAppVersion); appVersionName = normalisedVersion(appVersionName); showAlert = showAlertIfVersion(configuration.getAlertVersionCondition(), appVersionName, alertAppVersion); } if (showAlert) { AlertDialog alertDialog = ConfigurationUtils.createAlertDialog(activity, configuration); if(alertDialog != null) alertDialog.show(); } } private String getVersionName() { try { // http://stackoverflow.com/questions/4616095/how-to-get-the-build-version-number-of-your-android-application final String versionName = activity.getPackageManager() .getPackageInfo(activity.getPackageName(), 0).versionName; if (versionName == null) { throw new RuntimeException("Your manifest must include android:versionName to check the app version"); } return versionName; } catch (NameNotFoundException e) { throw new RuntimeException("The android:versionName could not be retrieved", e); } } // -- End Show/Remove Splash, interstitial and alert functions private void updateFullscreenStatus(boolean useFullscreen, boolean isSplash) { if(useFullscreen) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); } else { activity.setTitle(originalTitle); activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } if(isSplash){ splashImageView.requestLayout(); }else{ interstitialLayout.requestLayout(); } } private boolean showAlertIfVersion(String compareString, String appVersion, String alertAppVersion) { if (!compareString.equals("NONE")) { int cmpVersions = appVersion.compareTo(alertAppVersion); return (compareString.equals("GREATER_EQUAL") && cmpVersions >= 0 ) || (compareString.equals("GREATER") && cmpVersions > 0 ) || (compareString.equals("LESS") && cmpVersions < 0 ) || (compareString.equals("LESS_EQUAL") && cmpVersions <= 0 ) || (compareString.equals("EQUAL") && cmpVersions == 0 ); } else { return true; } } private String normalisedVersion(String version) { return ConfigurationUtils.normalisedVersion(version, ".", 4); } // -- Configuration status functions. public boolean isConfigurationLoaded(){ return (configuration!=null && !isLoading); } public boolean isConfigurationLoading(){ return isLoading; } // -- End Configuration status functions. /** Background tasks */ /* * This thread class downloads the configuration and execute it. * * @author Malcom Ventures S.L * @since 2012 * */ private class DownloadConfigurationFile extends AsyncTask<Void, Float, Integer>{ protected void onPreExecute() { } protected Integer doInBackground(Void ...valores) { if(!loadConfigurationFile(activity)){ //If there is a failure downloading the config, we try from our local store. if(activity.getFileStreamPath(CONFIG_FILE_NAME).exists()){ config_useDownloaded(); } else { config_useBundled(); } } return 0; } protected void onPostExecute(Integer bytes) { saveConfigurationFile(); if(executeAfterLoad) executeConfig(true); } } /** * This task gets and displays the splash image. * * First, it downloads the splash image if possible. Then, it tries to retrieve the downloaded image * or, if it is not found, it tries to get the image from the 'assets' folder. * * When the image Bitmap is loaded, it publishes progress with that Bitmap so the splash is displayed. * After that, it sleeps for a while (that depends on the configuration) and then it publishes progress * again but this time with no Bitmap, which means the splash must be removed from the display. */ private class PrepareAndShowSplashImage extends AsyncTask<Boolean, Bitmap, Void> { private boolean isNetworkAccess; public PrepareAndShowSplashImage(boolean isNetworkAccess) { this.isNetworkAccess = isNetworkAccess; } @Override protected Void doInBackground(Boolean... params) { // Obtain the splash image if (isNetworkAccess) { //We try to get the image and update the stored one. downloadSplashFile(configuration.getSplashImageUrl()); } try { final Bitmap splashBitmap = loadSplashBitmap(); // Show splash for a while publishProgress(splashBitmap); sleepSeconds(configuration.getSplashAnimationDelay()); } catch (Exception e) { Log.e(MCMDefines.LOG_TAG, "Error loading splash image BitMap", e); } // Hide splash publishProgress(); return null; } @Override protected void onProgressUpdate(Bitmap... splashBitmap) { // The progress zone is hidden in both cases because it might happen // that the splash image is never loaded splash_progress_zone.setVisibility(View.GONE); if (splashBitmap.length == 1) // Show splash { //Set the image in the view. splashImageView.setImageBitmap(splashBitmap[0]); updateFullscreenStatus(true, true); splashImageView.setVisibility(View.VISIBLE); } else if (splashBitmap.length == 0) // Hide splash { removeSplash(isNetworkAccess); } } /* * Downloads the image of the http address saving * it to the application internal private storage. * * @param imageHttpAddress */ private synchronized void downloadSplashFile(String imageHttpAddress) { URL imageUrl = null; Log.d(MCMDefines.LOG_TAG, "DOWNLOAD_SPLASH_IMAGE: " + imageHttpAddress); try { imageUrl = new URL(imageHttpAddress); HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection(); conn.setUseCaches(true); conn.connect(); //We save the image for off-line later usage. File f = ToolBox.storage_getAppInternalStorageFilePath(activity, CONFIG_SPLASH_IMAGE_NAME); FileOutputStream fOut = new FileOutputStream(f); ToolBox.storage_copyStream(conn.getInputStream(), fOut, 1024); fOut.close(); } catch (Exception e) { Log.e(MCMDefines.LOG_TAG, "DOWNLOAD_SPLASH_IMAGE_ERROR: ("+ imageUrl.toString()+") : " + e.getMessage()); } } private Bitmap loadSplashBitmap() throws Exception { final Bitmap splashBitmap; if (ToolBox.storage_checkIfFileExistsInInternalStorage(activity, CONFIG_SPLASH_IMAGE_NAME)) { // Load splash image from disk splashBitmap = ToolBox.media_loadBitmapFromInternalStorage(activity, CONFIG_SPLASH_IMAGE_NAME); } else { // No previously downloaded splash image so we use the provided one in the assets folder. splashBitmap = loadSplashFromAssetsFolder(); } return splashBitmap; } private void sleepSeconds(int seconds) { try { Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); } catch (InterruptedException e) { Log.e(MCMDefines.LOG_TAG, "Unexpected interruption", e); } } /* * Loads the splash image provided in the assets folder. * * @return */ private Bitmap loadSplashFromAssetsFolder() throws IOException { InputStream is= activity.getAssets().open(CONFIG_SPLASH_ASSETS_IMAGE_NAME); return BitmapFactory.decodeStream(is); } } }