fix: reload JDK distributions when returning from terminal to onboarding activity

This commit is contained in:
Akash Yadav
2024-01-13 19:58:13 +05:30
parent 9e16f89113
commit d1fa2393d2
8 changed files with 193 additions and 74 deletions
@@ -19,6 +19,8 @@ package com.itsaky.androidide.activities
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.github.appintro.AppIntro2
@@ -32,18 +34,42 @@ import com.itsaky.androidide.fragments.onboarding.IdeSetupConfigurationFragment
import com.itsaky.androidide.fragments.onboarding.OnboardingInfoFragment
import com.itsaky.androidide.fragments.onboarding.PermissionsFragment
import com.itsaky.androidide.fragments.onboarding.StatisticsFragment
import com.itsaky.androidide.models.JdkDistribution
import com.itsaky.androidide.preferences.internal.prefManager
import com.itsaky.androidide.preferences.internal.statConsentDialogShown
import com.itsaky.androidide.tasks.launchAsyncWithProgress
import com.itsaky.androidide.ui.themes.IThemeManager
import com.itsaky.androidide.utils.Environment
import com.termux.shared.android.PackageUtils
import com.termux.shared.markdown.MarkdownUtils
import com.termux.shared.termux.TermuxConstants
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.withContext
class OnboardingActivity : AppIntro2() {
private val terminalActivityCallback = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()) {
Log.d(TAG, "TerminalActivity: resultCode=${it.resultCode}")
if (!isFinishing) {
reloadJdkDistInfo {
tryNavigateToMainIfSetupIsCompleted()
}
}
}
private val activityScope =
CoroutineScope(Dispatchers.Main + CoroutineName("OnboardingActivity"))
private var listJdkInstallationsJob: Job? = null
companion object {
private const val TAG = "OnboardingActivity"
private const val KEY_ARCHCONFIG_WARNING_IS_SHOWN = "ide.archConfig.experimentalWarning.isShown"
}
@@ -52,9 +78,8 @@ class OnboardingActivity : AppIntro2() {
super.onCreate(savedInstanceState)
if (isSetupDone()) {
openActivity(MainActivity::class.java)
finish()
if (tryNavigateToMainIfSetupIsCompleted()) {
return
}
setSwipeLock(true)
@@ -110,12 +135,16 @@ class OnboardingActivity : AppIntro2() {
override fun onResume() {
super.onResume()
if (isSetupDone()) {
openActivity(MainActivity::class.java)
finish()
reloadJdkDistInfo {
tryNavigateToMainIfSetupIsCompleted()
}
}
override fun onDestroy() {
super.onDestroy()
activityScope.cancel("Activity is being destroyed")
}
override fun onNextPressed(currentFragment: Fragment?) {
(currentFragment as? StatisticsFragment?)?.updateStatOptInStatus()
}
@@ -129,21 +158,17 @@ class OnboardingActivity : AppIntro2() {
}
if (!checkToolsIsInstalled() && currentFragment is IdeSetupConfigurationFragment) {
val intenet = Intent(this, TerminalActivity::class.java)
val intent = Intent(this, TerminalActivity::class.java)
if (currentFragment.isAutoInstall()) {
intenet.putExtra(TerminalActivity.EXTRA_ONBOARDING_RUN_IDESETUP, true)
intenet.putExtra(TerminalActivity.EXTRA_ONBOARDING_RUN_IDESETUP_ARGS,
intent.putExtra(TerminalActivity.EXTRA_ONBOARDING_RUN_IDESETUP, true)
intent.putExtra(TerminalActivity.EXTRA_ONBOARDING_RUN_IDESETUP_ARGS,
currentFragment.buildIdeSetupArguments())
}
startActivity(intenet)
terminalActivityCallback.launch(intent)
return
}
openActivity(MainActivity::class.java)
}
private fun openActivity(cls: Class<*>) {
startActivity(Intent(this, cls))
tryNavigateToMainIfSetupIsCompleted()
}
private fun checkToolsIsInstalled(): Boolean {
@@ -151,9 +176,40 @@ class OnboardingActivity : AppIntro2() {
&& Environment.ANDROID_HOME.exists()
}
private fun isSetupDone() =
(checkToolsIsInstalled() && statConsentDialogShown && PermissionsFragment.areAllPermissionsGranted(
this))
private fun isSetupCompleted(): Boolean {
return checkToolsIsInstalled()
&& statConsentDialogShown
&& PermissionsFragment.areAllPermissionsGranted(this)
}
private fun tryNavigateToMainIfSetupIsCompleted(): Boolean {
if (isSetupCompleted()) {
startActivity(Intent(this, MainActivity::class.java))
finish()
return true
}
return false
}
private inline fun reloadJdkDistInfo(crossinline distConsumer: (List<JdkDistribution>) -> Unit) {
listJdkInstallationsJob?.cancel("Reloading JDK distributions")
listJdkInstallationsJob = activityScope.launchAsyncWithProgress(Dispatchers.Default,
configureFlashbar = { builder, _ ->
builder.message(string.please_wait)
}) { _, _ ->
val distributionProvider = IJdkDistributionProvider.getInstance()
distributionProvider.loadDistributions()
withContext(Dispatchers.Main) {
distConsumer(distributionProvider.installedDistributions)
}
}.also {
it?.invokeOnCompletion {
listJdkInstallationsJob = null
}
}
}
private fun isInstalledOnSdCard(): Boolean {
// noinspection SdCardPath
@@ -36,8 +36,17 @@ class JdkDistributionProviderImpl : IJdkDistributionProvider {
private val log = ILogger.newInstance("JdkDistributionProviderImpl")
}
override val installedDistributions: List<JdkDistribution> by lazy {
JdkUtils.findJavaInstallations().also { distributions ->
private var _installedDistributions: List<JdkDistribution>? = null
override val installedDistributions: List<JdkDistribution>
get() = _installedDistributions ?: emptyList()
override fun loadDistributions() {
_installedDistributions = doLoadDistributions()
}
private fun doLoadDistributions(): List<JdkDistribution> {
return JdkUtils.findJavaInstallations().also { distributions ->
// set the default value for the 'javaHome' preference
if (javaHome.isBlank() && distributions.isNotEmpty()) {
@@ -17,6 +17,7 @@
package com.itsaky.androidide.app.configuration
import androidx.annotation.WorkerThread
import com.itsaky.androidide.models.JdkDistribution
import com.itsaky.androidide.utils.ServiceLoader
@@ -32,6 +33,13 @@ interface IJdkDistributionProvider {
*/
val installedDistributions: List<JdkDistribution>
/**
* Reloads the installed JDK distributions. This function is synchronous and should not be called
* on the UI thread.
*/
@WorkerThread
fun loadDistributions()
/**
* Get the [JdkDistribution] instance for the given java version.
*
@@ -53,7 +53,7 @@ public class ToolsManager {
CompletableFuture.runAsync(() -> {
// Load installed JDK distributions
IJdkDistributionProvider.getInstance().getInstalledDistributions();
IJdkDistributionProvider.getInstance().loadDistributions();
writeNoMediaFile();
extractAapt2();
@@ -26,12 +26,14 @@ import android.view.View
import android.widget.Button
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import com.itsaky.androidide.terminal.IdeTerminalSessionClient
import com.itsaky.androidide.terminal.IdesetupSession
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.flashError
import com.termux.R
import com.termux.app.TermuxActivity
import com.termux.app.terminal.TermuxTerminalSessionActivityClient
import com.termux.shared.termux.shell.command.runner.terminal.TermuxSession
/**
@@ -70,6 +72,10 @@ class TerminalActivity : TermuxActivity() {
KEY_TERMINAL_CAN_ADD_SESSIONS, true) ?: true
}
override fun onCreateTerminalSessionClient(): TermuxTerminalSessionActivityClient {
return IdeTerminalSessionClient(this)
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putBoolean(KEY_TERMINAL_CAN_ADD_SESSIONS, canAddNewSessions)
@@ -0,0 +1,45 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.terminal
import com.itsaky.androidide.activities.TerminalActivity
import com.termux.app.terminal.TermuxTerminalSessionActivityClient
import com.termux.terminal.TerminalSession
import com.termux.terminal.TerminalSessionClient
/**
* [TerminalSessionClient] delegate for AndroidIDE.
*
* @author Akash Yadav
*/
class IdeTerminalSessionClient(
activity: TerminalActivity
) : TermuxTerminalSessionActivityClient(activity) {
override fun onSessionFinished(finishedSession: TerminalSession) {
val termuxSession = mActivity?.termuxService?.getTermuxSessionForTerminalSession(
finishedSession)
if (termuxSession != null && termuxSession is IdesetupSession) {
// if the finished session was performing tools installation
// then set the result code for the installation process
mActivity.setResult(finishedSession.exitStatus)
}
super.onSessionFinished(finishedSession)
}
}
@@ -2,14 +2,10 @@ package com.termux.app;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -23,35 +19,36 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.autofill.AutofillManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.viewpager.widget.ViewPager;
import com.itsaky.androidide.app.BaseIDEActivity;
import com.termux.R;
import com.termux.app.activities.HelpActivity;
import com.termux.app.api.file.FileReceiverActivity;
import com.termux.app.terminal.TermuxActivityRootView;
import com.termux.app.terminal.TermuxSessionsListViewController;
import com.termux.app.terminal.TermuxTerminalSessionActivityClient;
import com.termux.app.terminal.TermuxTerminalViewClient;
import com.termux.app.terminal.io.TerminalToolbarViewPager;
import com.termux.app.terminal.io.TermuxTerminalExtraKeys;
import com.termux.shared.activities.ReportActivity;
import com.termux.shared.activity.ActivityUtils;
import com.termux.shared.activity.media.AppCompatActivityUtils;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.android.PermissionUtils;
import com.termux.shared.data.DataUtils;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
import com.termux.shared.termux.TermuxUtils;
import com.termux.shared.termux.crash.TermuxCrashUtils;
import com.termux.shared.termux.settings.preferences.TermuxAppSharedPreferences;
import com.termux.app.terminal.TermuxSessionsListViewController;
import com.termux.app.terminal.io.TerminalToolbarViewPager;
import com.termux.app.terminal.TermuxTerminalViewClient;
import com.termux.shared.termux.extrakeys.ExtraKeysView;
import com.termux.shared.termux.interact.TextInputDialogUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.termux.TermuxUtils;
import com.termux.shared.termux.settings.preferences.TermuxAppSharedPreferences;
import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties;
import com.termux.shared.termux.shell.command.runner.terminal.TermuxSession;
import com.termux.shared.termux.theme.TermuxThemeUtils;
@@ -61,13 +58,6 @@ import com.termux.terminal.TerminalSession;
import com.termux.terminal.TerminalSessionClient;
import com.termux.view.TerminalView;
import com.termux.view.TerminalViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.viewpager.widget.ViewPager;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
@@ -89,107 +79,107 @@ public class TermuxActivity extends BaseIDEActivity implements ServiceConnection
* {@link #bindService(Intent, ServiceConnection, int)}, and obtained and stored in
* {@link #onServiceConnected(ComponentName, IBinder)}.
*/
TermuxService mTermuxService;
protected TermuxService mTermuxService;
/**
* The {@link TerminalView} shown in {@link TermuxActivity} that displays the terminal.
*/
TerminalView mTerminalView;
protected TerminalView mTerminalView;
/**
* The {@link TerminalViewClient} interface implementation to allow for communication between
* {@link TerminalView} and {@link TermuxActivity}.
*/
TermuxTerminalViewClient mTermuxTerminalViewClient;
protected TermuxTerminalViewClient mTermuxTerminalViewClient;
/**
* The {@link TerminalSessionClient} interface implementation to allow for communication between
* {@link TerminalSession} and {@link TermuxActivity}.
*/
TermuxTerminalSessionActivityClient mTermuxTerminalSessionActivityClient;
protected TermuxTerminalSessionActivityClient mTermuxTerminalSessionActivityClient;
/**
* Termux app shared preferences manager.
*/
private TermuxAppSharedPreferences mPreferences;
protected TermuxAppSharedPreferences mPreferences;
/**
* Termux app SharedProperties loaded from termux.properties
*/
private TermuxAppSharedProperties mProperties;
protected TermuxAppSharedProperties mProperties;
/**
* The root view of the {@link TermuxActivity}.
*/
TermuxActivityRootView mTermuxActivityRootView;
protected TermuxActivityRootView mTermuxActivityRootView;
/**
* The space at the bottom of {@link @mTermuxActivityRootView} of the {@link TermuxActivity}.
*/
View mTermuxActivityBottomSpaceView;
protected View mTermuxActivityBottomSpaceView;
/**
* The terminal extra keys view.
*/
ExtraKeysView mExtraKeysView;
protected ExtraKeysView mExtraKeysView;
/**
* The client for the {@link #mExtraKeysView}.
*/
TermuxTerminalExtraKeys mTermuxTerminalExtraKeys;
protected TermuxTerminalExtraKeys mTermuxTerminalExtraKeys;
/**
* The termux sessions list controller.
*/
TermuxSessionsListViewController mTermuxSessionListViewController;
protected TermuxSessionsListViewController mTermuxSessionListViewController;
/**
* The last toast shown, used cancel current toast before showing new in {@link #showToast(String, boolean)}.
*/
Toast mLastToast;
protected Toast mLastToast;
/**
* If between onResume() and onStop(). Note that only one session is in the foreground of the terminal view at the
* time, so if the session causing a change is not in the foreground it should probably be treated as background.
*/
private boolean mIsVisible;
protected boolean mIsVisible;
/**
* If onResume() was called after onCreate().
*/
private boolean mIsOnResumeAfterOnCreate = false;
protected boolean mIsOnResumeAfterOnCreate = false;
/**
* If activity was restarted like due to call to {@link #recreate()} after receiving
* {@link TERMUX_ACTIVITY#ACTION_RELOAD_STYLE}, system dark night mode was changed or activity
* was killed by android.
*/
private boolean mIsActivityRecreated = false;
protected boolean mIsActivityRecreated = false;
/**
* The {@link TermuxActivity} is in an invalid state and must not be run.
*/
private boolean mIsInvalidState;
protected boolean mIsInvalidState;
private int mNavBarHeight;
protected int mNavBarHeight;
private float mTerminalToolbarDefaultHeight;
protected float mTerminalToolbarDefaultHeight;
private static final int CONTEXT_MENU_SELECT_URL_ID = 0;
private static final int CONTEXT_MENU_SHARE_TRANSCRIPT_ID = 1;
private static final int CONTEXT_MENU_SHARE_SELECTED_TEXT = 10;
private static final int CONTEXT_MENU_AUTOFILL_ID = 2;
private static final int CONTEXT_MENU_RESET_TERMINAL_ID = 3;
private static final int CONTEXT_MENU_KILL_PROCESS_ID = 4;
private static final int CONTEXT_MENU_TOGGLE_KEEP_SCREEN_ON = 6;
private static final int CONTEXT_MENU_HELP_ID = 7;
private static final int CONTEXT_MENU_REPORT_ID = 9;
protected static final int CONTEXT_MENU_SELECT_URL_ID = 0;
protected static final int CONTEXT_MENU_SHARE_TRANSCRIPT_ID = 1;
protected static final int CONTEXT_MENU_SHARE_SELECTED_TEXT = 10;
protected static final int CONTEXT_MENU_AUTOFILL_ID = 2;
protected static final int CONTEXT_MENU_RESET_TERMINAL_ID = 3;
protected static final int CONTEXT_MENU_KILL_PROCESS_ID = 4;
protected static final int CONTEXT_MENU_TOGGLE_KEEP_SCREEN_ON = 6;
protected static final int CONTEXT_MENU_HELP_ID = 7;
protected static final int CONTEXT_MENU_REPORT_ID = 9;
private static final String ARG_TERMINAL_TOOLBAR_TEXT_INPUT = "terminal_toolbar_text_input";
private static final String ARG_ACTIVITY_RECREATED = "activity_recreated";
protected static final String ARG_TERMINAL_TOOLBAR_TEXT_INPUT = "terminal_toolbar_text_input";
protected static final String ARG_ACTIVITY_RECREATED = "activity_recreated";
private static final String LOG_TAG = "TermuxActivity";
protected static final String LOG_TAG = "TermuxActivity";
@SuppressLint("InflateParams")
@NonNull
@@ -540,7 +530,7 @@ public class TermuxActivity extends BaseIDEActivity implements ServiceConnection
private void setTermuxTerminalViewAndClients() {
// Set termux terminal view and session clients
mTermuxTerminalSessionActivityClient = new TermuxTerminalSessionActivityClient(this);
mTermuxTerminalSessionActivityClient = onCreateTerminalSessionClient();
mTermuxTerminalViewClient = new TermuxTerminalViewClient(this, mTermuxTerminalSessionActivityClient);
// Set termux terminal view
@@ -554,6 +544,11 @@ public class TermuxActivity extends BaseIDEActivity implements ServiceConnection
mTermuxTerminalSessionActivityClient.onCreate();
}
@NonNull
protected TermuxTerminalSessionActivityClient onCreateTerminalSessionClient() {
return new TermuxTerminalSessionActivityClient(this);
}
private void setTermuxSessionsListView() {
ListView termuxSessionsListView = findViewById(R.id.terminal_sessions_list);
mTermuxSessionListViewController = new TermuxSessionsListViewController(this, mTermuxService.getTermuxSessions());
@@ -37,7 +37,7 @@ import java.util.Properties;
/** The {@link TerminalSessionClient} implementation that may require an {@link Activity} for its interface methods. */
public class TermuxTerminalSessionActivityClient extends TermuxTerminalSessionClientBase {
private final TermuxActivity mActivity;
protected final TermuxActivity mActivity;
private static final int MAX_SESSIONS = 8;