diff --git a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java index 7ca65411b..d6356bacb 100755 --- a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java +++ b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java @@ -1,35 +1,34 @@ -/************************************************************************************ +/* * This file is part of AndroidIDE. * - * - * * AndroidIDE is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * AndroidIDE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . * -**************************************************************************************/ - - + */ package com.itsaky.androidide; import abhishekti7.unicorn.filepicker.UnicornFilePicker; + import android.content.Intent; import android.content.res.ColorStateList; import android.os.Bundle; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; + import androidx.core.content.ContextCompat; import androidx.transition.TransitionManager; + import com.blankj.utilcode.util.FileIOUtils; import com.blankj.utilcode.util.SizeUtils; import com.blankj.utilcode.util.ThreadUtils; @@ -49,175 +48,173 @@ import com.itsaky.androidide.utils.Environment; import com.itsaky.androidide.utils.FileUtil; import com.itsaky.androidide.utils.InputStreamLineReader; import com.itsaky.toaster.Toaster; + import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; public class DownloadActivity extends StudioActivity { - - private ActivityDownloadBinding binding; - private ProgressSheet progressSheet; - private File choosenDir = null; - - @Override - protected View bindLayout() { - binding = ActivityDownloadBinding.inflate(getLayoutInflater()); - return binding.getRoot(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - OnClickListener clickListener = v -> handleClick(v); - binding.dismiss.setOnClickListener(clickListener); - binding.choose.setOnClickListener(clickListener); - binding.install.setOnClickListener(clickListener); - - binding.install.setEnabled(false); - } - - private void handleClick(View v) { - final int id = v.getId(); - if(id == binding.dismiss.getId()) { - TransitionManager.beginDelayedTransition(binding.getRoot()); - binding.card.setVisibility(View.GONE); - } else if(id == binding.choose.getId()) { - try { - binding.install.setEnabled(false); - UnicornFilePicker.from(this) - .addConfigBuilder() - .addItemDivider(false) - .selectMultipleFiles(false) - .setRootDirectory(FileUtil.getExternalStorageDir()) - .showHiddenFiles(true) - .showOnlyDirectory(true) - .theme(R.style.AppTheme_FilePicker) - .build() - .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE); - } catch (Throwable th){ - LOG.error(getString (R.string.err_cannot_start_file_picker), th); - } - } else if(id == binding.install.getId()) { - if(choosenDir == null) { - getApp().toast(R.string.msg_pick_files, Toaster.Type.ERROR); - return; - } - - if(!choosenDir.exists()) { - getApp().toast(R.string.msg_file_doesnt_exist, Toaster.Type.ERROR); - return; - } - - installAll(); - } - } - - private final String DONE = "DONE"; - private void installAll() { - showProgress(); - getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true); - - try { - final File script = createExtractScript(); - final ProcessStreamsHolder holder = new ProcessStreamsHolder (); - final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor(); + private ActivityDownloadBinding binding; + private ProgressSheet progressSheet; + private File choosenDir = null; + + @Override + protected View bindLayout () { + binding = ActivityDownloadBinding.inflate (getLayoutInflater ()); + return binding.getRoot (); + } + + @Override + protected void onCreate (Bundle savedInstanceState) { + super.onCreate (savedInstanceState); + final OnClickListener clickListener = this::handleClick; + binding.dismiss.setOnClickListener (clickListener); + binding.choose.setOnClickListener (clickListener); + binding.install.setOnClickListener (clickListener); + + binding.install.setEnabled (false); + } + + private void handleClick (View v) { + final int id = v.getId (); + if (id == binding.dismiss.getId ()) { + TransitionManager.beginDelayedTransition (binding.getRoot ()); + binding.card.setVisibility (View.GONE); + } else if (id == binding.choose.getId ()) { + try { + binding.install.setEnabled (false); + UnicornFilePicker.from (this) + .addConfigBuilder () + .addItemDivider (false) + .selectMultipleFiles (false) + .setRootDirectory (FileUtil.getExternalStorageDir ()) + .showHiddenFiles (true) + .showOnlyDirectory (true) + .theme (R.style.AppTheme_FilePicker) + .build () + .forResult (abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE); + } catch (Throwable th) { + LOG.error (getString (R.string.err_cannot_start_file_picker), th); + } + } else if (id == binding.install.getId ()) { + if (choosenDir == null) { + getApp ().toast (R.string.msg_pick_files, Toaster.Type.ERROR); + return; + } - executor.execAsync(holder, - code -> onInstallProcessExit (code), - true, - Environment.BUSYBOX.getAbsolutePath(), "sh",// We use busybox's sh, because Environment.SHELL is not installed yet... - script.getAbsolutePath() + if (!choosenDir.exists ()) { + getApp ().toast (R.string.msg_file_doesnt_exist, Toaster.Type.ERROR); + return; + } + + installAll (); + } + } + + private void installAll () { + showProgress (); + getApp ().getPrefManager ().putBoolean (PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true); + + try { + final File script = createExtractScript (); + final ProcessStreamsHolder holder = new ProcessStreamsHolder (); + final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor (); + + executor.execAsync (holder, + this::onInstallProcessExit, + true, + Environment.BUSYBOX.getAbsolutePath (), "sh",// We use busybox's sh, because Environment.SHELL is not installed yet... + script.getAbsolutePath () ); - final InputStreamLineReader reader = new InputStreamLineReader (holder.in, line -> onInstallationOutput (line)); - new Thread (reader).start(); + final InputStreamLineReader reader = new InputStreamLineReader (holder.in, this::onInstallationOutput); + new Thread (reader).start (); } catch (DownloadActivity.InstallationException e) { - LOG.error(getString(R.string.err_installation), e); - onInstallationFailed(e.exitCode); + LOG.error (getString (R.string.err_installation), e); + onInstallationFailed (e.exitCode); } catch (IOException e) { - LOG.error(getString(R.string.err_installation), e); - onInstallationFailed(5); // Exit code 5 : I/O Error + LOG.error (getString (R.string.err_installation), e); + onInstallationFailed (5); // Exit code 5 : I/O Error } - } + } private void onInstallationOutput (final String line) { - ThreadUtils.runOnUiThread(() -> { - getProgressSheet().setSubMessage(line); - }); + ThreadUtils.runOnUiThread (() -> getProgressSheet ().setSubMessage (line)); } private void onInstallProcessExit (final int code) { - ThreadUtils.runOnUiThread(() -> { + ThreadUtils.runOnUiThread (() -> { if (code == 0) { // 0 = normal execution - getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true); - showRestartNeeded(); - if(getProgressSheet().isShowing()) { - getProgressSheet().dismiss(); + getApp ().getPrefManager ().putBoolean (PreferenceManager.KEY_FRAMEWORK_INSTALLED, true); + showRestartNeeded (); + if (getProgressSheet ().isShowing ()) { + getProgressSheet ().dismiss (); } } else { onInstallationFailed (code); } }); } - - private void onInstallationFailed(int code) { + + private void onInstallationFailed (int code) { // TODO Can this be improved by adding some animations? - if(getProgressSheet().isShowing()) { - getProgressSheet().dismiss(); + if (getProgressSheet ().isShowing ()) { + getProgressSheet ().dismiss (); } final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (this); - builder.setTitle(R.string.title_installation_failed); - builder.setMessage(getString(R.string.msg_installation_failed, code)); - builder.setPositiveButton(android.R.string.ok, (d, w) -> { - d.dismiss(); - finishAffinity(); + builder.setTitle (R.string.title_installation_failed); + builder.setMessage (getString (R.string.msg_installation_failed, code)); + builder.setPositiveButton (android.R.string.ok, (d, w) -> { + d.dismiss (); + finishAffinity (); }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); + builder.setNegativeButton (android.R.string.cancel, null); + builder.show (); } - - private File createExtractScript() throws DownloadActivity.InstallationException { + + private File createExtractScript () throws DownloadActivity.InstallationException { final StringBuilder sb = new StringBuilder (); - sb.append("cd"); - joiner(sb); - sb.append("echo 'Installing...'"); - joiner(sb); + sb.append ("cd"); + joiner (sb); + sb.append ("echo 'Installing...'"); + joiner (sb); - File[] files = choosenDir.listFiles(ARCHIVE_FILTER); + File[] files = choosenDir.listFiles (ARCHIVE_FILTER); if (files == null || files.length <= 0) { throw new InstallationException (2); } for (File f : files) { - if(f.getName().endsWith(".tar.xz")) { - if(f.getName().startsWith("androidide-sysroot")) { - sb.append("cd $SYSROOT/.."); - joiner(sb); + if (f.getName ().endsWith (".tar.xz")) { + if (f.getName ().startsWith ("androidide-sysroot")) { + sb.append ("cd $SYSROOT/.."); + joiner (sb); } - sb.append("$BUSYBOX tar xvJf '" + f.getAbsolutePath() + "'"); - joiner(sb); - sb.append("cd $HOME"); - joiner(sb); - } else if(f.getName().endsWith(".zip")) { - sb.append("$BUSYBOX unzip '" + f.getAbsolutePath() + "'"); - joiner(sb); + sb.append ("$BUSYBOX tar xvJf '").append (f.getAbsolutePath ()).append ("'"); + joiner (sb); + sb.append ("cd $HOME"); + joiner (sb); + } else if (f.getName ().endsWith (".zip")) { + sb.append ("$BUSYBOX unzip '").append (f.getAbsolutePath ()).append ("'"); + joiner (sb); } } - sb.append("echo 'Cleaning unsupported flags in binaries...'"); - joiner(sb); - sb.append("$BUSYBOX find $JAVA_HOME -type f -exec androidide-cleaner {} \\;"); - joiner(sb); - sb.append("echo " + DONE); + sb.append ("echo 'Cleaning unsupported flags in binaries...'"); + joiner (sb); + sb.append ("$BUSYBOX find $JAVA_HOME -type f -exec androidide-cleaner {} \\;"); + joiner (sb); + String DONE = "DONE"; + sb.append ("echo ").append (DONE); final File script = new File (Environment.TMP_DIR, "extract_tools.sh"); - if (!FileIOUtils.writeFileFromString(script, sb.toString())) { + if (!FileIOUtils.writeFileFromString (script, sb.toString ())) { throw new InstallationException (2); } @@ -225,85 +222,77 @@ public class DownloadActivity extends StudioActivity { } private void joiner (StringBuilder sb) { - sb.append(" && "); + sb.append (" && "); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if(requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE && resultCode == RESULT_OK) { - ArrayList files = data.getStringArrayListExtra("filePaths"); - if(files != null) { - if(files.size() == 1) { - File choosenDir = new File(files.get(0)); - if(choosenDir.exists() && choosenDir.isDirectory()) { - this.choosenDir = choosenDir; - setDone(binding.choose); - binding.install.setEnabled(true); - new TaskExecutor().executeAsync(new ListDirectoryCallable(choosenDir), __ -> { - binding.filesText.setText(getString(R.string.msg_installable_files, __)); - }); - } else { - getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR); - choosenDir = null; - } - } else { - getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR); - } - } - } - } - - private void setDone(MaterialButton button) { - final int dp8 = SizeUtils.dp2px(8); - final int dp4 = SizeUtils.dp2px(4); - final int dp24 = SizeUtils.dp2px(24); - button.setText(""); - button.setIconResource(R.drawable.ic_ok); - button.setIconTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.bg_green))); - button.setBackgroundColor(android.graphics.Color.TRANSPARENT); - button.setPaddingRelative(dp8, dp4, dp8, dp4); - button.setStrokeWidth(0); - button.setGravity(Gravity.CENTER); - button.setIconPadding(0); - button.setInsetTop(0); - button.setInsetBottom(0); - button.setPaddingRelative(dp24, dp4, dp24, dp4); - } - - private void showProgress() { - getProgressSheet().setCancelable(false); - getProgressSheet().setShowShadow(false); - getProgressSheet().setSubMessageEnabled(true) - .setWelcomeTextEnabled(true) - .show(getSupportFragmentManager(), "progress_sheet"); - } - - private void showRestartNeeded() { - setDone(binding.install); - final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (this); - builder.setTitle(R.string.title_restart); - builder.setMessage(R.string.msg_restart); - builder.setCancelable(false); - builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity()); - builder.create().show(); - } - - private ProgressSheet getProgressSheet() { - return progressSheet == null ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait)) : progressSheet; - } - - private final FileFilter ARCHIVE_FILTER = new FileFilter(){ - - @Override - public boolean accept(File p1) { - return p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip")); - } - }; - private class InstallationException extends Exception { + @Override + protected void onActivityResult (int requestCode, int resultCode, Intent data) { + super.onActivityResult (requestCode, resultCode, data); + if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE && resultCode == RESULT_OK) { + ArrayList files = data.getStringArrayListExtra ("filePaths"); + if (files != null) { + if (files.size () == 1) { + File choosenDir = new File (files.get (0)); + if (choosenDir.exists () && choosenDir.isDirectory ()) { + this.choosenDir = choosenDir; + setDone (binding.choose); + binding.install.setEnabled (true); + new TaskExecutor ().executeAsync (new ListDirectoryCallable (choosenDir), __ -> binding.filesText.setText (getString (R.string.msg_installable_files, __))); + } else { + getApp ().toast (R.string.msg_picked_isnt_dir, Toaster.Type.ERROR); + } + } else { + getApp ().toast (R.string.msg_pick_single_file, Toaster.Type.ERROR); + } + } + } + } + + private void setDone (MaterialButton button) { + final int dp8 = SizeUtils.dp2px (8); + final int dp4 = SizeUtils.dp2px (4); + final int dp24 = SizeUtils.dp2px (24); + button.setText (""); + button.setIconResource (R.drawable.ic_ok); + button.setIconTint (ColorStateList.valueOf (ContextCompat.getColor (this, R.color.bg_green))); + button.setBackgroundColor (android.graphics.Color.TRANSPARENT); + button.setPaddingRelative (dp8, dp4, dp8, dp4); + button.setStrokeWidth (0); + button.setGravity (Gravity.CENTER); + button.setIconPadding (0); + button.setInsetTop (0); + button.setInsetBottom (0); + button.setPaddingRelative (dp24, dp4, dp24, dp4); + } + + private void showProgress () { + getProgressSheet ().setCancelable (false); + getProgressSheet ().setShowShadow (false); + getProgressSheet ().setSubMessageEnabled (true) + .setWelcomeTextEnabled (true) + .show (getSupportFragmentManager (), "progress_sheet"); + } + + private void showRestartNeeded () { + setDone (binding.install); + final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (this); + builder.setTitle (R.string.title_restart); + builder.setMessage (R.string.msg_restart); + builder.setCancelable (false); + builder.setPositiveButton (android.R.string.ok, (p1, p2) -> finishAffinity ()); + builder.create ().show (); + } + + private ProgressSheet getProgressSheet () { + return progressSheet == null ? progressSheet = new ProgressSheet ().setMessage (getString (R.string.please_wait)) : progressSheet; + } + + private final FileFilter ARCHIVE_FILTER = p1 -> p1.isFile () && (p1.getName ().endsWith (".tar.xz") || p1.getName ().endsWith (".zip")); + + private static class InstallationException extends Exception { private final int exitCode; - public InstallationException(int exitCode) { + + public InstallationException (int exitCode) { this.exitCode = exitCode; } } diff --git a/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java b/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java index 0daa45d06..8df2da9f9 100755 --- a/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java +++ b/app/src/main/java/com/itsaky/androidide/services/builder/IDEService.java @@ -353,7 +353,7 @@ public class IDEService { final ProcessStreamsHolder holder = new ProcessStreamsHolder(); listener.saveFiles(); - Environment.mkdirIfNotExits(Environment.TMP_DIR); + currentTask = task; listener.appendOutput(task, getString(R.string.msg_task_begin, currentTime(), task.getName())); try { diff --git a/common/src/main/java/com/itsaky/androidide/utils/BootstrapInstaller.java b/common/src/main/java/com/itsaky/androidide/utils/BootstrapInstaller.java index 7997d41a6..b995110dd 100644 --- a/common/src/main/java/com/itsaky/androidide/utils/BootstrapInstaller.java +++ b/common/src/main/java/com/itsaky/androidide/utils/BootstrapInstaller.java @@ -69,32 +69,32 @@ public class BootstrapInstaller { LOG.error ("Cannot delete staging directory"); throw new CompletionException (new InstallationException (context.getString (R.string.msg_delete_sysroot_staging))); } - + if (SYSROOT.exists () && !FileUtils.delete (SYSROOT)) { LOG.error ("Cannot delete sysroot directory."); throw new CompletionException (new InstallationException (context.getString (R.string.msg_delete_sysroot))); } - + if (stagingDir.exists () && !FileUtils.createOrExistsDir (stagingDir)) { LOG.error ("Cannot create staging directory"); throw new CompletionException (new InstallationException (context.getString (R.string.msg_create_sysroot_staging))); } - + if (SYSROOT.exists () && !FileUtils.createOrExistsDir (SYSROOT)) { LOG.error ("Cannot create sysroot directory"); throw new CompletionException (new InstallationException (context.getString (R.string.msg_create_sysroot))); } - + final var buffer = new byte[8096]; final var symlinks = new ArrayList> (50); - + ZipEntry entry; while ((entry = zip.getNextEntry ()) != null) { msg = "Reading zip entry " + entry.getName (); notify (listener, msg); LOG.verbose (msg); if (entry.getName ().equals ("SYMLINKS.txt")) { - + msg = "Reading SYMLINKS.txt..."; notify (listener, msg); LOG.debug (msg); @@ -127,39 +127,39 @@ public class BootstrapInstaller { LOG.error ("Cannot create target file parent directory"); throw new CompletionException (new InstallationException (context.getString (R.string.msg_mkdir_failed, ___.getAbsolutePath ()))); } - + if (!isDirectory) { try (final var outStream = new FileOutputStream (targetFile)) { int readBytes; while ((readBytes = zip.read (buffer)) != -1) outStream.write (buffer, 0, readBytes); } - + if (zipEntryName.startsWith ("bin/") || zipEntryName.startsWith ("libexec") || zipEntryName.startsWith ("lib/apt/apt-helper") || zipEntryName.startsWith ("lib/apt/methods")) { - notify (listener, context.getString(R.string.msg_chmod_exec)); + notify (listener, context.getString (R.string.msg_chmod_exec)); LOG.verbose ("chmod 0700", zipEntryName); - + //noinspection OctalInteger Os.chmod (targetFile.getAbsolutePath (), 0700); } } } } - - if (symlinks.isEmpty()) { + + if (symlinks.isEmpty ()) { LOG.error ("No SYMLINKS.txt file encountered..."); throw new CompletionException (new InstallationException ("No SYMLINKS.txt encountered")); } - + for (Pair symlink : symlinks) { notify (listener, context.getString (R.string.msg_linking, symlink.second, symlink.first)); - Os.symlink(symlink.first, symlink.second); + Os.symlink (symlink.first, symlink.second); } LOG.info ("Renaming 'sysroot-staging' directory to 'sysroot'"); - + if (!FileUtils.rename (stagingDir, SYSROOT.getName ())) { LOG.error ("Cannot rename 'sysroot-staging' to 'sysroot'"); throw new CompletionException (new InstallationException ("Failed to rename staging sysroot directory.")); @@ -167,9 +167,9 @@ public class BootstrapInstaller { notify (listener, context.getString (R.string.msg_extracting_hooks)); ToolsManager.extractLibHooks (); - + LOG.info ("Bootstrap packages installed successfully."); - } catch (IOException| ErrnoException e) { + } catch (IOException | ErrnoException e) { LOG.error ("Failed to extract bootstrap", e); throw new RuntimeException (e); }