package com.aptoide.amethyst.downloadmanager; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.support.v4.app.NotificationCompat; import android.util.DisplayMetrics; import android.util.Log; import com.aptoide.amethyst.Aptoide; import com.aptoide.amethyst.R; import com.aptoide.amethyst.downloadmanager.model.FinishedApk; import com.aptoide.amethyst.utils.AptoideUtils; import com.aptoide.amethyst.utils.Base64; import com.aptoide.amethyst.utils.Logger; import com.nostra13.universalimageloader.core.ImageLoader; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Locale; /** * Created by hsousa on 07-07-2015. */ public class DownloadUtils { public static String formatBytes(long bytes) { int unit = 1024; if (bytes < unit) return bytes + " B"; int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = ("KMGTPE").charAt(exp - 1) + ""; return String.format(Locale.ENGLISH, "%.1f %sB", bytes / Math.pow(unit, exp), pre); } public static boolean canRunRootCommands() { boolean retval; Process suProcess; try { suProcess = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(suProcess.getOutputStream()); DataInputStream osRes = new DataInputStream(suProcess.getInputStream()); // Getting the id of the current user to check if this is root os.writeBytes("id\n"); os.flush(); String currUid = osRes.readLine(); boolean exitSu; if (null == currUid) { retval = false; exitSu = false; Log.d("ROOT", "Can't get root access or denied by user"); } else if (currUid.contains("uid=0")) { retval = true; exitSu = true; Log.d("ROOT", "Root access granted"); } else { retval = false; exitSu = true; Log.d("ROOT", "Root access rejected: " + currUid); } if (exitSu) { os.writeBytes("exit\n"); os.flush(); } } catch (Exception e) { // Can't get root ! // Probably broken pipe exception on trying to write to output stream (os) after su failed, meaning that the device is not rooted retval = false; Log.d("ROOT", "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage()); } return retval; } public static int dpToPixels(Context context, int dpi) { DisplayMetrics dm = context.getResources().getDisplayMetrics(); return (int) (dpi * dm.density); } public static void installWithRoot(final FinishedApk apk) { try { final Context context = Aptoide.getContext(); final NotificationManager managerNotification = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); final Process p; DataOutputStream os; byte[] arrayOfByte = Base64.decode("cG0gaW5zdGFsbCAtciA=", 0); String install = new String(arrayOfByte, "UTF-8"); if (!Aptoide.IS_SYSTEM) { p = Runtime.getRuntime().exec("su"); os = new DataOutputStream(p.getOutputStream()); // Execute commands that require root access os.writeBytes(install + "\"" + apk.getPath() + "\"\n"); os.flush(); } else { p = Runtime.getRuntime().exec(install + apk.getPath()); os = new DataOutputStream(p.getOutputStream()); } NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context); Intent onClick = new Intent(Intent.ACTION_VIEW); onClick.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); onClick.setDataAndType(Uri.fromFile(new File(apk.getPath())), "application/vnd.android.package-archive"); // The PendingIntent to launch our activity if the user selects this notification PendingIntent onClickAction = PendingIntent.getActivity(context, 0, onClick, 0); mBuilder.setContentTitle(Aptoide.getConfiguration().getMarketName()) .setContentText(AptoideUtils.StringUtils.getFormattedString(context, R.string.installing, apk.getName())); int size = dpToPixels(context, 36); Bitmap bitmap = decodeSampledBitmapFromResource(ImageLoader.getInstance().getDiscCache().get(apk.getIconPath()).getAbsolutePath(), size, size); if (bitmap != null) mBuilder.setLargeIcon(bitmap); mBuilder.setSmallIcon(android.R.drawable.stat_sys_download); mBuilder.setContentIntent(onClickAction); mBuilder.setAutoCancel(true); managerNotification.notify((int) apk.getAppHashId(), mBuilder.build()); new Thread(new Runnable() { @Override public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream())); int read; char[] buffer = new char[4096]; StringBuilder output = new StringBuilder(); while ((read = reader.read(buffer)) > 0) { output.append(buffer, 0, read); } reader.close(); p.waitFor(); String failure = output.toString(); if (p.exitValue() != 255 && !failure.toLowerCase(Locale.ENGLISH).contains("failure") && !failure.toLowerCase(Locale.ENGLISH).contains("segmentation")) { // Sucess :-) Logger.d("Aptoide", String.valueOf(p.exitValue() + " " + failure)); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context); Intent onClick = context.getPackageManager().getLaunchIntentForPackage(apk.getApkid()); if (onClick == null) { // onClick = new Intent(Intent.ACTION_VIEW); // onClick.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // onClick.setDataAndType(Uri.fromFile(new File(path)),"application/vnd.android.package-archive"); managerNotification.cancel((int) apk.getAppHashId()); onClick = new Intent(); try { context.getPackageManager().getPackageInfo(apk.getApkid(), 0); } catch (PackageManager.NameNotFoundException e) { Logger.d("Aptoide", "Package not found"); Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 14) install.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.getPackageName()); install.setDataAndType(Uri.fromFile(new File(apk.getPath())), "application/vnd.android.package-archive"); Logger.d("Aptoide", "Installing app: " + apk.getPath()); context.startActivity(install); return; } } // The PendingIntent to launch our activity if the user selects this notification PendingIntent onClickAction = PendingIntent.getActivity(context, 0, onClick, 0); mBuilder.setContentTitle(Aptoide.getConfiguration().getMarketName()) .setContentText(AptoideUtils.StringUtils.getFormattedString(context, R.string.finished_install, apk.getName())); int size = dpToPixels(context, 36); mBuilder.setLargeIcon(decodeSampledBitmapFromResource(ImageLoader.getInstance().getDiscCache().get(apk.getIconPath()).getAbsolutePath(), size, size)); mBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done); mBuilder.setContentIntent(onClickAction); mBuilder.setAutoCancel(true); managerNotification.notify((int) apk.getAppHashId(), mBuilder.build()); if (Build.VERSION.SDK_INT >= 11) context.getPackageManager().setInstallerPackageName(apk.getApkid(), context.getPackageName()); } else { managerNotification.cancel((int) apk.getAppHashId()); Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 14) install.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.getPackageName()); install.setDataAndType(Uri.fromFile(new File(apk.getPath())), "application/vnd.android.package-archive"); Log.d("Aptoide", "Installing app: " + apk.getPath()); context.startActivity(install); } } catch (Exception e) { e.printStackTrace(); managerNotification.cancel((int) apk.getAppHashId()); Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 14) install.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.getPackageName()); install.setDataAndType(Uri.fromFile(new File(apk.getPath())), "application/vnd.android.package-archive"); Log.d("Aptoide", "Installing app: " + apk.getPath()); context.startActivity(install); } } }).start(); if (!Aptoide.IS_SYSTEM) { os.writeBytes("exit\n"); os.flush(); } } catch (Exception e) { Logger.printException(e); } } public static Bitmap decodeSampledBitmapFromResource(String file, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(file, options); } public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public static String formatEta(long eta, String left) { if (eta > 0) { long days = eta / (1000 * 60 * 60 * 24); eta -= days * 1000 * 60 * 60 * 24; long hours = eta / (1000 * 60 * 60); eta -= hours * 1000 * 60 * 60; long minutes = eta / (1000 * 60); eta -= minutes * 1000 * 60; long seconds = eta / 1000; String etaString = ""; if (days > 0) { etaString += days + "d "; } if (hours > 0) { etaString += hours + "h "; } if (minutes > 0) { etaString += minutes + "m "; } if (seconds > 0) { etaString += seconds + "s"; } return etaString + " " + left; } return ""; } }