public class AssetsPageAdapter extends FragmentStatePagerAdapter {

	private static final int PAGES_COUNT = 4;

	private Context context;

	public AssetsPageAdapter(Context context, FragmentManager fm) {
		super(fm);
		this.context = context.getApplicationContext();
	}

	private String getTitleForAssetsType(AssetsType assetsType) {
		return context.getString(assetsType.getTitleRes());
	}

	@Override
	public Fragment getItem(int position) {
		switch (position) {
			case 0:
				return AssetsListFragment.newInstance(AssetsType.FONTS);
			case 1:
				return AssetsListFragment.newInstance(AssetsType.OVERLAYS);
			case 2:
				return AssetsListFragment.newInstance(AssetsType.AUDIO);
			case 3:
				return AssetsListFragment.newInstance(AssetsType.BOOT_ANIMATIONS);
			default:
				throw new RuntimeException("Unknown fragment position " + position);

		}
	}

	@Override
	public int getCount() {
		return PAGES_COUNT;
	}

	@Override
	public CharSequence getPageTitle(int position) {
		switch (position) {
			case 0:
				return getTitleForAssetsType(AssetsType.FONTS);
			case 1:
				return getTitleForAssetsType(AssetsType.OVERLAYS);
			case 2:
				return getTitleForAssetsType(AssetsType.AUDIO);
			case 3:
				return getTitleForAssetsType(AssetsType.BOOT_ANIMATIONS);
			default:
				throw new RuntimeException("Unknown fragment position " + position);

		}
	}
} @EBean(scope = EBean.Scope.Singleton)
public class EventBusBean {

	private EventBus bus;

	@AfterInject
	protected void prepareBean() {
		if(bus == null) {
			bus = EventBus.getDefault();
		}
	}

	public EventBus getEventBus() {
		return bus;
	}

	public void post(Object o) {
		getEventBus().post(o);
	}

	public void register(Object o) {
		getEventBus().register(o);
	}

	public void unregister(Object o) {
		getEventBus().unregister(o);
	}

} @EBean(scope = EBean.Scope.Singleton)
public class ManifestProcessorBean {

	private static final String TAG = ManifestProcessorBean.class.getSimpleName();

	private static final String OTG_SUBS_META_DATA_FLAG = "OTGSubs_Supported";

	@RootContext
	protected Context context;

	@Bean
	ApkHelper apkHelper;

	public List<ApkInfo> getSupportedThemes() {
		return StreamSupport.stream(apkHelper.getInstalledApks()).map(apkInfo -> {
			try {
				ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(apkInfo.getPackageName(), PackageManager.GET_META_DATA);
				if(applicationInfo == null) {
					return null;
				}

				Bundle bundle = applicationInfo.metaData;
				if(bundle == null) {
					return null;
				}

				boolean supported = bundle.getBoolean(OTG_SUBS_META_DATA_FLAG, false);
				if(supported) {
					Log.d(TAG, "Found OTGSubs supported apk: " + apkInfo.getPackageName());
					return apkInfo;
				} else {
					return null;
				}
			} catch (PackageManager.NameNotFoundException e) {
				//ignore
				return null;
			}
		}).filter(Objects::nonNull).collect(Collectors.toList());
	}

	public boolean isPackageSupported(String packageName) {
		if(packageName == null || packageName.isEmpty()) {
			return false;
		}
		return StreamSupport.stream(getSupportedThemes()).anyMatch(apkInfo -> apkInfo.getPackageName().equals(packageName));
	}

	public ApkInfo getApkInfoForPackage(String packageName) {
		if(!isPackageSupported(packageName)) {
			return null;
		}
		return StreamSupport.stream(getSupportedThemes()).filter(apkInfo -> apkInfo.getPackageName().equals(packageName)).findAny().orElse(null);
	}

	public Context getRootContext() {
		return context;
	}

} @EBean
public class NavigationBean {

	@RootContext
	protected Context context;

	public void openAboutScreen() {
		String aboutAppName = context.getString(R.string.app_name);
		String aboutAppDesc = context.getString(R.string.about_app_desc);
		String activityTitle = context.getString(R.string.about_app_title);

		new LibsBuilder().withAboutAppName(aboutAppName)
						 .withAboutIconShown(true)
						 .withAboutDescription(aboutAppDesc)
						 .withAboutVersionShown(true)
						 .withAboutVersionShownName(true)
						 .withAboutVersionShownCode(true)
						 .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
						 .withActivityTitle(activityTitle)
						 .withAutoDetect(true)
						 .start(context);
	}

	public void openSettingsScreen(int settingsRequestCode) {
		SettingsActivity.open(context, settingsRequestCode);
	}

	public void openGitHubPage() {
		openWebUrl("https://github.com/PDDStudio/OTGSubs");
	}

	public void openWebUrl(String url) {
		Intent intent = new Intent(Intent.ACTION_VIEW);
		intent.setData(Uri.parse(url));
		context.startActivity(intent);
	}

} @EBean(scope = EBean.Scope.Singleton)
public class PackageInfoBean {

	private final List<AssetFileInfo> fontInformation;
	private final List<AssetFileInfo> overlayInformation;
	private final List<AssetFileInfo> audioInformation;
	private final List<AssetFileInfo> bootAnimationInformation;

	@Bean
	EventBusBean eventBusBean;

	public PackageInfoBean() {
		this.fontInformation = new ArrayList<>();
		this.overlayInformation = new ArrayList<>();
		this.audioInformation = new ArrayList<>();
		this.bootAnimationInformation = new ArrayList<>();
	}

	private void redirectEvent(AssetTypeAddedEvent event) {
		event.setFragmentIgnore(false);
		eventBusBean.post(event);
	}

	private void storeAssetFileInfo(@NonNull AssetFileInfo assetFileInfo) {
		switch (assetFileInfo.getType()) {
			case AUDIO:
				addItemIfNotPresent(audioInformation, assetFileInfo);
				break;
			case BOOT_ANIMATIONS:
				addItemIfNotPresent(bootAnimationInformation, assetFileInfo);
				break;
			case FONTS:
				addItemIfNotPresent(fontInformation, assetFileInfo);
				break;
			case OVERLAYS:
				addItemIfNotPresent(overlayInformation, assetFileInfo);
				break;
		}
	}

	private void addItemIfNotPresent(List<AssetFileInfo> fileInformation, AssetFileInfo assetFileInfo) {
		boolean itemExist = StreamSupport.stream(fileInformation).filter(assetFileInfo1 ->  assetFileInfo1.getFileLocation().equals(assetFileInfo.getFileLocation())).findAny().isPresent();
		if(!itemExist) {
			fileInformation.add(assetFileInfo);
		}
	}

	private void removeItemIfPresent(AssetFileInfo assetFileInfo) {
		StreamSupport.stream(getExistingInformation(assetFileInfo.getType())).forEach(assetFileInfo1 -> {
			if(assetFileInfo1.getFileLocation().equals(assetFileInfo.getFileLocation())) {
				getExistingInformation(assetFileInfo.getType()).remove(assetFileInfo1);
			}
		});
	}

	private void renameItemIfPresent(AssetFileInfo assetFileInfo, String newFileName) {
		StreamSupport.stream(getExistingInformation(assetFileInfo.getType())).filter(info -> info.getFileLocation().equals(assetFileInfo.getFileLocation())).findAny().ifPresent(oldInfo -> {
			if(oldInfo.getRelativeAssetsDestinationLocation() != null) {
				String parent = new File(oldInfo.getRelativeAssetsDestinationLocation()).getParent();
				File newFile = new File(parent, newFileName);
				oldInfo.setRelativeAssetsDestinationLocation(newFile.getAbsolutePath());
			} else {
				oldInfo.setFileName(newFileName);
			}
		});
	}

	public void register() {
		eventBusBean.register(this);
	}

	public void unregister() {
		eventBusBean.unregister(this);
	}

	@Subscribe
	public void onDialogResultReceived(AssetTypeAddedEvent event) {
		if(event != null && event.isIgnore()) {
			AssetFileInfo info = event.getAssetFileInfo();
			storeAssetFileInfo(info);
			redirectEvent(event);
		}
	}

	@Subscribe(threadMode = ThreadMode.ASYNC)
	public void onImportEventReceived(ImportAssetsFromApkEvent event) {
		if(event != null) {
			StreamSupport.stream(event.getAssets()).forEach(this::storeAssetFileInfo);
			eventBusBean.post(new RefreshItemListEvent());
		}
	}

	@Subscribe
	public void onExistingAssetsModifiedEventReceived(ExistingAssetsItemEvent event) {
		if(event != null) {
			switch (event.getModificationType()) {
				case DELETE:
					removeItemIfPresent(event.getAssetFileInfo());
					break;
				case RENAME:
					renameItemIfPresent(event.getAssetFileInfo(), event.getNewName());
					break;
			}
			eventBusBean.post(new RefreshItemListEvent());
		}
	}

	public List<AssetFileInfo> getExistingInformation(AssetsType assetsType) {
		switch (assetsType) {
			case AUDIO:
				return audioInformation;
			case BOOT_ANIMATIONS:
				return bootAnimationInformation;
			case FONTS:
				return fontInformation;
			case OVERLAYS:
				return overlayInformation;
			default:
				return new ArrayList<>();
		}
	}

} @EBean
public class InfoDialog {

	@RootContext
	Context context;

	private MaterialDialog infoDialog;

	public void show(@StringRes int title, @StringRes int content) {
		infoDialog = new MaterialDialog.Builder(context).title(title).content(content).positiveText(android.R.string.ok).show();
	}

	public void dismiss() {
		if(infoDialog != null && infoDialog.isShowing()) {
			infoDialog.dismiss();
		}
	}

} public class AssetTypeAddedEvent {

	private final AssetsType    assetsType;
	private final AssetFileInfo assetFileInfo;
	private boolean fragmentIgnore = true;

	public AssetTypeAddedEvent(AssetsType assetsType, AssetFileInfo assetFileInfo) {
		this.assetsType = assetsType;
		this.assetFileInfo = assetFileInfo;
	}

	public AssetsType getAssetsType() {
		return assetsType;
	}

	public AssetFileInfo getAssetFileInfo() {
		return assetFileInfo;
	}

	public boolean isIgnore() {
		return fragmentIgnore;
	}

	public void setFragmentIgnore(boolean fragmentIgnore) {
		this.fragmentIgnore = fragmentIgnore;
	}
} public class FileChooserDialogEvent {

	private String resultLocation;
	private boolean openRequest;
	private FileChooserType fileChooserType;

	public FileChooserDialogEvent(boolean openRequest, FileChooserType fileChooserType) {
		this.openRequest = openRequest;
		this.fileChooserType = fileChooserType;
	}

	public void setResult(String resultLocation) {
		this.resultLocation = resultLocation;
	}

	public String getResultLocation() {
		return resultLocation;
	}

	public boolean isOpenRequest() {
		return openRequest;
	}

	public FileChooserType getFileChooserType() {
		return fileChooserType;
	}

} public class RefreshItemListEvent {} public enum FileChooserType implements Serializable {
	DIRECTORY,
	ZIP_FILE,
	IGNORE
} @EIntentService
public class ImportApkService extends AbstractIntentService {

	private static final String TAG = ImportApkService.class.getSimpleName();

	private static final String ASSETS_DIR         = "assets";
	private static final String OVERLAYS_DIR       = "overlays";
	private static final String AUDIO_DIR          = "audio";
	private static final String FONTS_DIR          = "fonts";
	private static final String BOOT_ANIMATION_DIR = "bootanimation";

	public static void processImportRequest(@NonNull Context context, ApkInfo apkInfo) {
		ImportApkService_.intent(context).processApkImportRequest(apkInfo).start();
	}

	@Bean
	protected ApkExtractor apkExtractor;

	@Bean
	protected EventBusBean eventBus;

	public ImportApkService() {
		super(TAG);
	}

	@ServiceAction
	protected void processApkImportRequest(ApkInfo apkInfo) {
		File targetCacheDir = new File(getCacheDir(), apkInfo.getPackageName());
		try {
			File cachedApkFile = apkExtractor.copyApkToCache(targetCacheDir, apkInfo);
			apkExtractor.extractAssetsFromApk(cachedApkFile, targetCacheDir);
			FileUtils.forceDelete(cachedApkFile);
		} catch (IOException io) {
			io.printStackTrace();
		}
		sendEvent(new File(targetCacheDir, ASSETS_DIR));
	}

	private void sendEvent(File cachedAssetsFile) {
		File overlayDir = new File(cachedAssetsFile, OVERLAYS_DIR);
		if(!overlayDir.exists()) {
			overlayDir.mkdirs();
		}

		File fontsDir = new File(cachedAssetsFile, FONTS_DIR);
		if(!fontsDir.exists()) {
			fontsDir.mkdirs();
		}

		File audioDir = new File(cachedAssetsFile, AUDIO_DIR);
		if(!audioDir.exists()) {
			audioDir.mkdirs();
		}

		File bootAnimationDir = new File(cachedAssetsFile, BOOT_ANIMATION_DIR);
		if(!bootAnimationDir.exists()) {
			bootAnimationDir.mkdirs();
		}

		Collection<File> overlays = FileUtils.listFiles(overlayDir, null, true);
		Collection<File> fonts = FileUtils.listFiles(fontsDir, null, true);
		Collection<File> audio = FileUtils.listFiles(audioDir, null, true);
		Collection<File> bootAnimation = FileUtils.listFiles(bootAnimationDir, null, true);

		ImportAssetsFromApkEvent event = new ImportAssetsFromApkEvent();
		event.withList(convertCollectionToAssetsFileInfo(overlays, AssetsType.OVERLAYS));
		event.withList(convertCollectionToAssetsFileInfo(fonts, AssetsType.FONTS));
		event.withList(convertCollectionToAssetsFileInfo(audio, AssetsType.AUDIO));
		event.withList(convertCollectionToAssetsFileInfo(bootAnimation, AssetsType.BOOT_ANIMATIONS));
		eventBus.post(event);
	}

	private List<AssetFileInfo> convertCollectionToAssetsFileInfo(Collection<File> files, AssetsType assetsType) {
		return StreamSupport.stream(files).map(file -> {
			if (file != null && file.exists()) {
				AssetFileInfo assetFileInfo = new AssetFileInfo(assetsType, file.getAbsolutePath(), file.getName());
				String[] splits = file.getAbsolutePath().split(ASSETS_DIR);
				File file1 = new File(splits[1]);
				if(file1.isDirectory()) {
					assetFileInfo.setRelativeAssetsDestinationLocation(file1.getAbsolutePath());
				} else {
					assetFileInfo.setRelativeAssetsDestinationLocation(file1.getParent());
				}
				return assetFileInfo;
			} else {
				return null;
			}
		}).filter(assetFileInfo -> assetFileInfo != null).collect(Collectors.toList());
	}

} @EIntentService
public class PackageService extends AbstractIntentService implements PackageCallback {

	public static final String TAG = PackageService.class.getSimpleName();

	public static final String ACTION_PACKAGING_DONE = "com.pddstudio.otgsubs.services.ACTION_PACKAGING_DONE";
	public static final String EXTRA_PACKAGING_DONE_STATUS = "com.pddstudio.otgsubs.services.EXTRA_PACKAGING_DONE_STATUS";
	public static final String EXTRA_PACKAGING_DONE_FILE = "com.pddstudio.otgsubs.services.EXTRA_PACKAGING_DONE_FILE";

	public static void packageApplication(@NonNull Context context, @Nullable List<String> assetsList) {
		PackageService_.intent(context).doPackage(assetsList).start();
	}

	public static void packageApplication(@NonNull Context context, @Nullable File assetsDir) {
		PackageService_.intent(context).packageWithAssets(assetsDir).start();
	}

	public static void createApkFromRequestedAssets(@NonNull Context context) {
		PackageService_.intent(context).createPatchedPackage().start();
	}

	@Bean
	protected SubstratumPackager packager;

	@Bean
	PackageInfoBean packageBean;

	public PackageService() {
		super(PackageService.class.getSimpleName());
	}

	private void sendPackagingResultBroadcast(boolean packageSuccess, @Nullable File result) {
		Intent intent = new Intent(ACTION_PACKAGING_DONE);
		intent.putExtra(EXTRA_PACKAGING_DONE_STATUS, packageSuccess);
		if(result != null) {
			intent.putExtra(EXTRA_PACKAGING_DONE_FILE, result.getAbsolutePath());
		}
		LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
	}

	@ServiceAction
	void doPackage(@Nullable List<String> fileList) {
		SubstratumPackager.Builder builder = new SubstratumPackager.Builder(this);
		if(fileList != null && !fileList.isEmpty()) {
			StreamSupport.stream(fileList)
						 .map(File::new)
						 .peek(file -> Log.d(TAG, "adding directory: " + file.getAbsolutePath()))
						 .forEach(builder::addAssetsDir);
		}
		packager = builder.build();
		packager.doWork(this);
	}

	@ServiceAction
	void packageWithAssets(@Nullable File assetsDir) {
		List<String> fileList = StreamSupport.stream(org.apache.commons.io.FileUtils.listFiles(assetsDir, null, true)).filter(File::isDirectory).map(File::getAbsolutePath).collect(Collectors.toList());
		SubstratumPackager.Builder builder = new SubstratumPackager.Builder(this);
		if(fileList != null && !fileList.isEmpty()) {
			StreamSupport.stream(fileList)
						 .map(File::new)
						 .peek(file -> Log.d(TAG, "adding directory: " + file.getAbsolutePath()))
						 .forEach(builder::addAssetsDir);
		}
		packager = builder.build();
		packager.doWork(this);
	}

	@ServiceAction
	protected void createPatchedPackage() {
		packager = new SubstratumPackager.Builder(this).build();
		SubstratumPackager.PackageRequest packageRequest = new SubstratumPackager.PackageRequest();
		packageRequest.setFontSources(packageBean.getExistingInformation(AssetsType.FONTS));
		packageRequest.setAudioSources(packageBean.getExistingInformation(AssetsType.AUDIO));
		packageRequest.setBootAnimationSources(packageBean.getExistingInformation(AssetsType.BOOT_ANIMATIONS));
		packageRequest.setOverlaySources(packageBean.getExistingInformation(AssetsType.OVERLAYS));

		packager.processPackageRequest(packageRequest, this);
	}

	@Override
	public void onPackagingFailed(int errorCode) {
		sendPackagingResultBroadcast(false, null);
	}

	@Override
	public void onPackagingSucceeded(File signedApk) {
		if(signedApk != null && signedApk.exists()) {
			try {
				File resultApk = new File(Environment.getExternalStorageDirectory(), "otg.subs." + System.nanoTime() + ".apk");
				FileUtils.copyFile(signedApk, resultApk);
				if(resultApk.exists()) {
					sendPackagingResultBroadcast(true, resultApk);
				} else {
					throw new IOException("Couldn't copy file from " + signedApk + " to " + resultApk);
				}
			} catch (IOException e) {
				e.printStackTrace();
				sendPackagingResultBroadcast(true, signedApk);
			}
		}
		packager.cleanCache();
	}

} @EBean(scope = EBean.Scope.Singleton)
public class DeviceUtils {

	@RootContext
	Context context;

	@Pref
	Preferences_ preferences;

	public boolean isLollipopOrHigher() {
		return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
	}

	public Context getRootContext() {
		return context;
	}

	@ColorInt
	private int getColorForTheme(Activity activity) {
		TypedValue typedValue = new TypedValue();

		TypedArray a = activity.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimary});
		int color = a.getColor(0, 0);

		a.recycle();

		return color;
	}

	public void setNavigationBarColor(Activity activity) {
		if (isLollipopOrHigher()) {
			if (preferences.tintNavigationBar().get()) {
				activity.getWindow().setNavigationBarColor(getColorForTheme(activity));
			} else {
				activity.getWindow().setNavigationBarColor(context.getResources().getColor(android.R.color.black));
			}
		}
	}

	public void reloadCrashReportConfig() {
		Log.d("DeviceUtils", "reloadCrashReportConfig() enabled => " + preferences.crashReportEnabled().get());
		FirebaseAnalytics.getInstance(context).setAnalyticsCollectionEnabled(preferences.crashReportEnabled().get());
	}

	public void setCrashReportsEnabled(boolean enabled) {
		Log.d("DeviceUtils", "setCrashReportsEnabled() changed to => " + enabled);
		FirebaseAnalytics.getInstance(context).setAnalyticsCollectionEnabled(enabled);
	}

} @EBean
public class DrawerUtils {

	DrawerUtils() {}

	public PrimaryDrawerItem createPrimaryDrawerItem(Activity activity, IIcon itemIcon, @StringRes int name, @StringRes int desc, long id, boolean selectable) {
		int primaryColor = ThemeUtils.getPrimaryColorForTheme(activity);
		PrimaryDrawerItem drawerItem = new PrimaryDrawerItem().withName(name)
															  .withIcon(itemIcon)
															  .withIdentifier(id)
															  .withSelectable(selectable)
															  .withSelectedTextColor(primaryColor)
															  .withSelectedIconColor(primaryColor);
		if(desc != 0) {
			return drawerItem.withDescription(desc);
		} else {
			return drawerItem;
		}
	}

	public SectionDrawerItem createSectionHeaderDrawerItem(@StringRes int nameStringRes, boolean divider) {
		return new SectionDrawerItem().withName(nameStringRes).withDivider(divider);
	}

	public AccountHeader createAccountHeader(Activity activity) {
		return new AccountHeaderBuilder().withActivity(activity)
										 .addProfiles(new ProfileDrawerItem().withName(activity.getString(R.string.drawer_header)).withEmail(activity.getString(R.string.drawer_header_subtitle)))
										 .withHeaderBackground(ThemeUtils.getDrawerHeaderColorForSelectedTheme(activity))
										 .withProfileImagesVisible(false)
										 .withSelectionListEnabled(false)
										 .withSelectionListEnabledForSingleProfile(false)
										 .withAlternativeProfileHeaderSwitching(false)
										 .withOnlyMainProfileImageVisible(false)
										 .build();
	}

	public Drawer createDrawer(Activity activity, Toolbar toolbar, int selectedItemId, Drawer.OnDrawerItemClickListener onDrawerItemClickListener, List<IDrawerItem> drawerItems) {
		return new DrawerBuilder().withActivity(activity)
								  .withToolbar(toolbar)
								  .withAccountHeader(createAccountHeader(activity))
								  .withActionBarDrawerToggle(true)
								  .withActionBarDrawerToggleAnimated(true)
								  .withCloseOnClick(true)
								  .withDrawerItems(drawerItems)
								  .withOnDrawerItemClickListener(onDrawerItemClickListener)
								  .withCloseOnClick(true)
								  .withDelayOnDrawerClose(150)
								  .withSelectedItem(selectedItemId)
								  .withFireOnInitialOnClick(true)
								  .build();
	}

} public class ThemeUtils {

	public static final String PREFERENCE_NAME = Preferences.class.getSimpleName();

	public static void applySelectedTheme(Activity activity) {
		SharedPreferences preferences = activity.getSharedPreferences(PREFERENCE_NAME, 0);
		String themePreference = activity.getString(R.string.pref_item_app_theme_key);
		switch (preferences.getString(themePreference, "0")) {
			case "0":
				activity.setTheme(R.style.AppTheme_Blue);
				break;
			case "1":
				activity.setTheme(R.style.AppTheme_Red);
				break;
			case "2":
				activity.setTheme(R.style.AppTheme_Green);
				break;
			case "3":
				activity.setTheme(R.style.AppTheme_DarkOrange);
				break;
		}
	}

	public static int getDrawerHeaderColorForSelectedTheme(Activity activity) {
		SharedPreferences preferences = activity.getSharedPreferences(PREFERENCE_NAME, 0);
		String themePreference = activity.getString(R.string.pref_item_app_theme_key);
		switch (preferences.getString(themePreference, "0")) {
			case "0":
			default:
				return R.color.colorPrimary;
			case "1":
				return R.color.colorPrimaryRed;
			case "2":
				return R.color.colorPrimaryGreen;
			case "3":
				return R.color.colorPrimaryDarkOrange;
		}
	}

	public static void restartActivity(Activity activity) {
		activity.finish();
		final Intent intent = activity.getIntent();
		activity.startActivity(intent);
	}

	@ColorInt
	public static int getPrimaryColorForTheme(Activity activity) {
		TypedValue typedValue = new TypedValue();

		TypedArray a = activity.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimary});
		int color = a.getColor(0, 0);

		a.recycle();

		return color;
	}

	@ColorInt
	public static int getAccentColorForTheme(Activity activity) {
		TypedValue typedValue = new TypedValue();

		TypedArray a = activity.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorAccent});
		int color = a.getColor(0, 0);

		a.recycle();

		return color;
	}

} @EBean(scope = EBean.Scope.Singleton)
public class ApkExtractor {

	private static final String TAG = ApkExtractor.class.getSimpleName();

	private List<ApkInfo> apkInfos;

	@Bean
	protected ApkHelper apkHelper;

	@AfterInject
	protected void loadApks() {
		apkInfos = apkHelper.getInstalledApks();
		StreamSupport.stream(apkInfos).forEach(apkInfo -> Log.d(TAG, "APK INFO: " + apkInfo));
	}

	public File copyApkToCache(File cacheDir, ApkInfo apkInfo) throws IOException {
		File apkSource = new File(apkInfo.getSource());
		File outputLoc = new File(cacheDir, apkSource.getName());
		FileUtils.copyFile(apkSource, outputLoc);
		return outputLoc;
	}

	public List<ApkInfo> getApkInfoList() {
		return apkInfos;
	}

	public void extractAssetsFromApk(File cachedApkFile, File destDir) {
		ZipUtil.unpack(cachedApkFile, destDir, name -> name.startsWith("assets/") ? name : null);
	}

} public class ApkInfo implements Serializable {

	private String appName;
	private String apk;
	private String version;
	private String source;
	private String data;

	public ApkInfo(String appName, String apk, String version, String source, String data) {
		this.appName = appName;
		this.apk = apk;
		this.version = version;
		this.source = source;
		this.data = data;
	}

	public String getAppName() {
		return appName;
	}

	public void setAppName(String appName) {
		this.appName = appName;
	}

	public String getPackageName() {
		return apk;
	}

	public void setApk(String apk) {
		this.apk = apk;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public String getSource() {
		return source;
	}

	public void setSource(String source) {
		this.source = source;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	@Override
	public String toString() {
		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		return gson.toJson(this);
	}
} public class AssetFileInfo implements Serializable {

	private AssetsType type;
	private String     fileLocation;
	private String     fileName;
	private String relativeAssetsDestinationLocation;

	public AssetFileInfo(AssetsType type, String fileLocation, String fileName) {
		this.type = type;
		this.fileLocation = fileLocation;
		this.fileName = fileName;
	}

	public AssetsType getType() {
		return type;
	}

	public String getFileLocation() {
		return fileLocation;
	}

	public String getFileName() {
		return fileName;
	}

	public String getRelativeAssetsDestinationLocation() {
		return relativeAssetsDestinationLocation;
	}

	public void setRelativeAssetsDestinationLocation(String relativeAssetsDestinationLocation) {
		this.relativeAssetsDestinationLocation = relativeAssetsDestinationLocation;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
} public enum AssetsType implements Serializable {
	FONTS(R.string.fragment_title_fonts),
	OVERLAYS(R.string.fragment_title_overlays),
	AUDIO(R.string.fragment_title_audio),
	BOOT_ANIMATIONS(R.string.fragment_title_boot_animations);

	private final int titleName;

	AssetsType(int title) {
		this.titleName = title;
	}

	public int getTitleRes() {
		return titleName;
	}

}
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.folderselector.FileChooserDialog;
import com.afollestad.materialdialogs.folderselector.FolderChooserDialog;
import com.afollestad.materialdialogs.simplelist.MaterialSimpleListAdapter;
import com.afollestad.materialdialogs.simplelist.MaterialSimpleListItem;
import com.appcam.sdk.AppCam;
import com.github.clans.fab.FloatingActionButton;
import com.github.clans.fab.FloatingActionMenu;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.pddstudio.otgsubs.adapters.AssetsPageAdapter;
import com.pddstudio.otgsubs.beans.EventBusBean;
import com.pddstudio.otgsubs.beans.ManifestProcessorBean;
import com.pddstudio.otgsubs.beans.NavigationBean;
import com.pddstudio.otgsubs.beans.PackageInfoBean;
import com.pddstudio.otgsubs.dialogs.InfoDialog;
import com.pddstudio.otgsubs.events.AssetTypeAddedEvent;
import com.pddstudio.otgsubs.events.FileChooserDialogEvent;
import com.pddstudio.otgsubs.events.RefreshItemListEvent;
import com.pddstudio.otgsubs.models.FileChooserType;
import com.pddstudio.otgsubs.services.ImportApkService;
import com.pddstudio.otgsubs.services.PackageService;
import com.pddstudio.otgsubs.utils.DeviceUtils;
import com.pddstudio.otgsubs.utils.DrawerUtils;
import com.pddstudio.otgsubs.utils.Preferences_;
import com.pddstudio.otgsubs.utils.ThemeUtils;
import com.pddstudio.substratum.packager.ApkExtractor;
import com.pddstudio.substratum.packager.models.ApkInfo;
import com.pddstudio.substratum.packager.models.AssetFileInfo;
import com.pddstudio.substratum.packager.models.AssetsType;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OnActivityResult;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.Receiver;
import org.androidannotations.annotations.ViewById;
import org.androidannotations.annotations.sharedpreferences.Pref;
import org.greenrobot.eventbus.Subscribe;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java8.util.stream.Collectors;
import java8.util.stream.StreamSupport;
package com.pddstudio.otgsubs;






@EActivity(R.layout.activity_main)
@OptionsMenu(R.menu.menu_main)
public class MainActivity extends AppCompatActivity
		implements FolderChooserDialog.FolderCallback, FileChooserDialog.FileCallback, MaterialSimpleListAdapter.Callback, Drawer.OnDrawerItemClickListener {

	private static final int      REQUEST_PERMISSIONS   = 42;
	private static final int      SETTINGS_REQUEST_CODE = 69;
	private static final String[] REQUIRED_PERMISSIONS  = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};

	private static final int THEME_PATCHER_ITEM = 87;
	private static final int THEME_PACKAGER_ITEM = 88;
	private static final int GITHUB_ITEM         = 89;
	private static final int SETTINGS_ITEM       = 90;
	private static final int ABOUT_ITEM          = 91;

	@Bean
	EventBusBean eventBus;

	@Bean
	PackageInfoBean packageInfoBean;

	@Bean
	ApkExtractor apkExtractor;