package org.exobel.routerkeygen; import android.annotation.TargetApi; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Environment; import android.preference.PreferenceManager; import android.widget.Toast; import org.exobel.routerkeygen.ui.Preferences; import org.exobel.routerkeygen.utils.HashUtils; import java.io.DataInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; public class DictionaryDownloadService extends IntentService { public final static String URL_DOWNLOAD = "org.exobel.routerkeygen.DictionaryDownloadService.URL_DOWNLOAD"; private final static String DEFAULT_DIC_NAME = "RouterKeygen.dic"; private static final long MIN_TIME_BETWWEN_UPDATES = 500; private static final byte[] DICTIONARY_HASH = {(byte) 0x8c, (byte) 0xcf, 0x2c, (byte) 0xb2, (byte) 0xe8, (byte) 0xda, (byte) 0x13, (byte) 0xc2, (byte) 0xd8, (byte) 0xc7, (byte) 0xbb, (byte) 0x08, 0x2c, (byte) 0xc2, (byte) 0x1f, (byte) 0xe6}; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. private final int UNIQUE_ID = R.string.app_name + DictionaryDownloadService.class.getName().hashCode(); private NotificationManager mNotificationManager; private boolean cancelNotification = true; private boolean stopRequested = false; public DictionaryDownloadService() { super("DictionaryDownloadService"); } @Override public void onCreate() { super.onCreate(); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } public void onDestroy() { super.onDestroy(); stopRequested = true; if (cancelNotification) mNotificationManager.cancel(UNIQUE_ID); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override protected void onHandleIntent(Intent intent) { File myDicFile; HttpURLConnection con = null; DataInputStream dis = null; FileOutputStream fos = null; int myProgress; int byteRead; byte[] buf; if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_nosdcard)).build()); cancelNotification = false; return; } final String dicTemp = Environment.getExternalStorageDirectory() .getPath() + File.separator + "DicTemp" + System.currentTimeMillis(); try { final String urlDownload = intent.getStringExtra(URL_DOWNLOAD); con = (HttpURLConnection) new URL(urlDownload).openConnection(); myProgress = 0; dis = new DataInputStream(con.getInputStream()); int fileLen = con.getContentLength(); if (noSpaceLeft(fileLen)) { mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_nomemoryonsdcard)) .build()); dis.close(); con.disconnect(); cancelNotification = false; return; } String dicFile = PreferenceManager.getDefaultSharedPreferences( getBaseContext()).getString(Preferences.dicLocalPref, null); if (dicFile == null) { dicFile = Environment.getExternalStorageDirectory().getPath() + File.separator + DEFAULT_DIC_NAME; final SharedPreferences.Editor editor = PreferenceManager .getDefaultSharedPreferences(getBaseContext()).edit(); editor.putString(Preferences.dicLocalPref, dicFile); editor.apply(); } // Testing if we can write to the file if (canNotWrite(dicFile) || canNotWrite(dicTemp)) { mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_no_write_permissions)) .build()); dis.close(); con.disconnect(); cancelNotification = false; return; } myDicFile = new File(dicTemp); fos = new FileOutputStream(myDicFile, false); final Intent i = new Intent(getApplicationContext(), CancelOperationActivity.class) .putExtra(CancelOperationActivity.SERVICE_TO_TERMINATE, DictionaryDownloadService.class.getName()) .putExtra( CancelOperationActivity.MESSAGE, getApplicationContext().getString( R.string.cancel_download)) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); Notification update = NotificationUtils.createProgressBar(this, getString(R.string.msg_dl_dlingdic), "", fileLen, myProgress, false, PendingIntent.getActivity( getApplicationContext(), 0, i, PendingIntent.FLAG_UPDATE_CURRENT)); mNotificationManager.notify(UNIQUE_ID, update); long lastNotificationTime = System.currentTimeMillis(); buf = new byte[1024 * 512]; while (myProgress < fileLen) { if (stopRequested) { mNotificationManager.cancel(UNIQUE_ID); dis.close(); fos.close(); con.disconnect(); myDicFile.delete(); return; } if ((byteRead = dis.read(buf)) != -1) { fos.write(buf, 0, byteRead); myProgress += byteRead; } else { dis.close(); fos.close(); con.disconnect(); myProgress = fileLen; } if ((System.currentTimeMillis() - lastNotificationTime) > MIN_TIME_BETWWEN_UPDATES) { mNotificationManager.notify(UNIQUE_ID, NotificationUtils .updateProgressBar(update, fileLen, myProgress )); lastNotificationTime = System.currentTimeMillis(); } } mNotificationManager .notify(UNIQUE_ID, NotificationUtils .createProgressBar( this, getString(R.string.msg_dl_dlingdic), getString(R.string.msg_wait), 0, 0, true, NotificationUtils .getDefaultPendingIntent(getApplicationContext()))); if (!HashUtils.checkDicMD5(dicTemp, DICTIONARY_HASH)) { new File(dicTemp).delete(); mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_err_unkown)).build()); cancelNotification = false; return; } if (renameFile(dicTemp, dicFile, true)) { new File(dicTemp).delete(); mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.pref_msg_err_rename_dic)) .build()); cancelNotification = false; return; } mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.app_name), getString(R.string.msg_dic_updated_finished)) .build()); cancelNotification = false; } catch (FileNotFoundException e) { new File(dicTemp).delete(); mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_nosdcard)).build()); cancelNotification = false; e.printStackTrace(); } catch (Exception e) { new File(dicTemp).delete(); mNotificationManager.notify( UNIQUE_ID, NotificationUtils.getSimple(this, getString(R.string.msg_error), getString(R.string.msg_err_unkown)).build()); cancelNotification = false; e.printStackTrace(); } finally { if (fos != null) try { fos.close(); } catch (IOException e) { e.printStackTrace(); } if (dis != null) try { dis.close(); } catch (IOException e) { e.printStackTrace(); } if (con != null) con.disconnect(); } } private boolean canNotWrite(String filename) { File file; while ((file = new File(filename)).exists()) { filename += "1"; } try { file.createNewFile(); boolean ret = file.canWrite(); file.delete(); return !ret; } catch (IOException e) { e.printStackTrace(); } return true; } private boolean renameFile(String file, String toFile, boolean saveOld) { File toBeRenamed = new File(file); File newFile = new File(toFile); if (!toBeRenamed.exists() || toBeRenamed.isDirectory() || newFile.isDirectory()) return true; if (newFile.exists() && saveOld) { if (renameFile(toFile, toFile + "_backup", true)) Toast.makeText(getBaseContext(), R.string.pref_msg_err_backup_dic, Toast.LENGTH_SHORT) .show(); } // Rename return !toBeRenamed.renameTo(newFile); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @SuppressWarnings("deprecation") private boolean noSpaceLeft(int fileLen) { // Checking if external storage has enough memory ... android.os.StatFs stat = new android.os.StatFs(Environment .getExternalStorageDirectory().getPath()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return stat.getBlockSizeLong() == 0 || stat.getAvailableBlocksLong() < ((long) fileLen / stat.getBlockSizeLong()); } else { return stat.getBlockSize() == 0 || stat.getAvailableBlocks() < (fileLen / stat.getBlockSize()); } } }