package com.itsaky.androidide.handlers; import android.view.View; import com.itsaky.androidide.R; import com.itsaky.androidide.managers.PreferenceManager; import com.itsaky.androidide.models.AndroidProject; import com.itsaky.androidide.models.project.IDEModule; import com.itsaky.androidide.models.project.IDEProject; import com.itsaky.androidide.services.IDEService; import com.itsaky.androidide.tasks.GradleTask; import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask; import com.itsaky.androidide.utils.Environment; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.Collection; public class BuildServiceHandler extends IDEHandler implements IDEService.BuildListener { private IDEService service; public BuildServiceHandler(Provider provider) { super(provider); } public IDEService getService() { return service; } @Override public void start() { if(this.activity() == null) throwNPE(); AndroidProject project = provider.provideAndroidProject(); if(project == null) throwNPE(); if(project.getProjectPath() == null) throwNPE(); this.service = new IDEService(new File(project.getProjectPath())); this.service.setListener(this); } @Override public void stop() { this.service.stopAllDaemons(); this.service.exit(); } @Override public void onBuildModified() { // Does nothing currently } @Override public void onProjectLoaded(IDEProject project, Optional appModule) { project.iconPath = provider.provideAndroidProject().getIconPath(); activity().setIDEProject(project); activity().createProjectInfoSheet(); // Recreate sheet, even if already created. Just to update its contents if(appModule.isPresent()) { IDEModule app = appModule.get(); activity().setStatus(activity().getString(R.string.msg_starting_completion)); final List paths = new ArrayList(app.dependencies); paths.stream().filter(path -> isClasspathValid(path)).collect(Collectors.toList()); provider.provideAndroidProject().setClassPaths(paths); File androidJar = new File(Environment.ANDROID_HOME, String.format("platforms/%s/android.jar", app.compileSdkVersion)); if(androidJar.exists()) { Environment.setBootClasspath(androidJar); provider.provideAndroidProject().addClasspath(androidJar.getAbsolutePath()); activity().createServices(); } else activity().setStatus("android.jar not found!"); } else activity().setStatus("Cannot get :app module..."); } @Override public void onStartingGradleDaemon(GradleTask task) { activity().setStatus(activity().getString(R.string.msg_starting_daemon)); activity().getApp().setStopGradleDaemon(false); } @Override public void onRunTask(GradleTask task, String name) { if(task != null && task.canOutput()) { activity().setStatus(service.typeString(task.getTaskID()) + " " + name); } } @Override public void onBuildSuccessful(GradleTask task, String msg) { if(task == null || activity() == null) return; if(task.canOutput()) activity().setStatus(msg); // If this task generates APK, ask user if he/she wants to install them if (task instanceof ApkGeneratingTask) { final IDEProject project = activity().provideIDEProject(); final Optional app = project.getModuleByPath(":app"); // TODO Handle multiple application modules if(app.isPresent()) { final String path = app.get().projectDir; if(path != null) { final File buildDir = new File (path, "build"); installApks (((ApkGeneratingTask) task).getApks(buildDir)); } } } activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); activity().invalidateOptionsMenu(); activity().getBinding().buildProgressIndicator.setVisibility(View.GONE); } @Override public void onBuildFailed(GradleTask task, String msg) { if(task == null) return; if(task.canOutput()) { activity().setStatus(msg); } activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); activity().invalidateOptionsMenu(); activity().getBinding().buildProgressIndicator.setVisibility(View.GONE); } @Override public void saveFiles() { activity().saveAll(false, false); } @Override public void appendOutput(GradleTask task, CharSequence text) { if(text == null) return; if(task != null && !task.canOutput()) { return; } activity().appendBuildOut(text.toString()); } @Override public void prepareBuild() { boolean isFirstBuild = activity().getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true); activity().setStatus(activity().getString(isFirstBuild ? R.string.preparing_first : R.string.preparing)); if(isFirstBuild) { activity().showFirstBuildNotice(); } activity().getBinding().buildProgressIndicator.setVisibility(View.VISIBLE); } private boolean isClasspathValid(String path) { if(path == null || path.trim().length() <= 0 || path.trim().equals("/")) return false; File file = new File(path); return file.isFile() && file.getName().endsWith(".jar") /** * Release R.jar shouldn't be included in classpath */ && !file.getAbsolutePath().endsWith("/release/R.jar"); } private void installApks (final Set apks) { if (activity () == null || apks == null || apks.isEmpty()) { return; } if(apks.size() == 1) { activity().install(apks.iterator().next()); } else { final List files = new ArrayList (apks); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog); builder.setTitle(activity().getString(R.string.title_install_apks)); builder.setItems(getNames(files), (d, w) -> { d.dismiss(); activity().install(files.get(w)); }); builder.show(); } } private CharSequence[] getNames (final Collection apks) { final String[] names = new String[apks.size()]; int i = 0; for (File apk : apks) { names[i] = apk.getName(); ++i; } return names; } }