package com.njlabs.showjava.processor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import com.crashlytics.android.Crashlytics; import com.njlabs.showjava.utils.SourceInfo; import net.dongliu.apk.parser.ApkParser; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import jadx.api.JadxDecompiler; /** * Created by Niranjan on 30-05-2015. */ public class ResourcesExtractor extends ProcessServiceHelper { private final ApkParser apkParser; public ResourcesExtractor(ProcessService processService) { this.processService = processService; this.UIHandler = processService.UIHandler; this.packageFilePath = processService.packageFilePath; this.packageName = processService.packageName; this.exceptionHandler = processService.exceptionHandler; this.apkParser = processService.apkParser; this.sourceOutputDir = processService.sourceOutputDir; this.javaSourceOutputDir = processService.javaSourceOutputDir; } public void extract() { broadcastStatus("res"); if(processService.decompilerToUse.equals("jadx")){ extractResourcesWithJadx(); } else { extractResourcesWithParser(); } } private void extractResourcesWithJadx(){ ThreadGroup group = new ThreadGroup("XML Extraction Group"); Thread xmlExtractionThread = new Thread(group, new Runnable() { @Override public void run() { try { File resDir = new File(sourceOutputDir); JadxDecompiler jadx = new JadxDecompiler(); jadx.setOutputDir(resDir); jadx.loadFile(new File(packageFilePath)); jadx.saveResources(); ZipFile zipFile = new ZipFile(packageFilePath); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); if (!zipEntry.isDirectory() && (FilenameUtils.getExtension(zipEntry.getName()).equals("png") || FilenameUtils.getExtension(zipEntry.getName()).equals("jpg"))) { broadcastStatus("progress_stream", zipEntry.getName()); writeFile(zipFile.getInputStream(zipEntry), zipEntry.getName()); } } zipFile.close(); saveIcon(); allDone(); } catch (Exception | StackOverflowError e) { processService.publishProgress("start_activity_with_error"); } } }, "XML Extraction Thread", processService.STACK_SIZE); xmlExtractionThread.setPriority(Thread.MAX_PRIORITY); xmlExtractionThread.setUncaughtExceptionHandler(exceptionHandler); xmlExtractionThread.start(); } private void extractResourcesWithParser(){ ThreadGroup group = new ThreadGroup("XML Extraction Group"); Thread xmlExtractionThread = new Thread(group, new Runnable() { @Override public void run() { try { ZipFile zipFile = new ZipFile(packageFilePath); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); if (!zipEntry.isDirectory() && !zipEntry.getName().equals("AndroidManifest.xml") && FilenameUtils.getExtension(zipEntry.getName()).equals("xml")) { broadcastStatus("progress_stream", zipEntry.getName()); writeXML(zipEntry.getName()); } else if (!zipEntry.isDirectory() && (FilenameUtils.getExtension(zipEntry.getName()).equals("png") || FilenameUtils.getExtension(zipEntry.getName()).equals("jpg"))) { broadcastStatus("progress_stream", zipEntry.getName()); writeFile(zipFile.getInputStream(zipEntry), zipEntry.getName()); } } zipFile.close(); writeManifest(); saveIcon(); allDone(); } catch (Exception | StackOverflowError e) { processService.publishProgress("start_activity_with_error"); } } }, "XML Extraction Thread", processService.STACK_SIZE); xmlExtractionThread.setPriority(Thread.MAX_PRIORITY); xmlExtractionThread.setUncaughtExceptionHandler(exceptionHandler); xmlExtractionThread.start(); } private void writeFile(InputStream fileStream, String path) { FileOutputStream outputStream = null; try { String fileFolderPath = sourceOutputDir + "/" + path.replace(FilenameUtils.getName(path), ""); File fileFolder = new File(fileFolderPath); if (!fileFolder.exists() || !fileFolder.isDirectory()) { fileFolder.mkdirs(); } outputStream = new FileOutputStream(new File(fileFolderPath + FilenameUtils.getName(path))); int read; byte[] bytes = new byte[1024]; while ((read = fileStream.read(bytes)) != -1) { outputStream.write(bytes, 0, read); } } catch (IOException e) { Crashlytics.logException(e); } finally { if (fileStream != null) { try { fileStream.close(); } catch (IOException e) { Crashlytics.logException(e); } } if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { Crashlytics.logException(e); } } } } private void writeXML(String path) { try { String xml = apkParser.transBinaryXml(path); String fileFolderPath = sourceOutputDir + "/" + path.replace(FilenameUtils.getName(path), ""); File fileFolder = new File(fileFolderPath); if (!fileFolder.exists() || !fileFolder.isDirectory()) { fileFolder.mkdirs(); } FileUtils.writeStringToFile(new File(fileFolderPath + FilenameUtils.getName(path)), xml); } catch (IOException e) { Crashlytics.logException(e); } } private void allDone() { SourceInfo.setXmlSourceStatus(processService, true); processService.publishProgress("start_activity"); } private void writeManifest() { try { String manifestXml = apkParser.getManifestXml(); FileUtils.writeStringToFile(new File(sourceOutputDir + "/AndroidManifest.xml"), manifestXml); } catch (IOException e) { e.printStackTrace(); } } private void saveIcon() { try { byte[] icon = apkParser.getIconFile().getData(); Bitmap bitmap = BitmapFactory.decodeByteArray(icon, 0, icon.length); FileOutputStream out = null; try { out = new FileOutputStream(sourceOutputDir + "/icon.png"); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); } catch (Exception e) { Crashlytics.logException(e); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { Crashlytics.logException(e); } } } catch (IOException e) { Crashlytics.logException(e); } } }