Initialize project in EditorActivity using Tooling API

This commit is contained in:
Akash Yadav
2022-04-29 20:07:06 +05:30
parent 53f55ed390
commit 29097cdc66
19 changed files with 1274 additions and 1001 deletions
+2
View File
@@ -86,6 +86,8 @@
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/test.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/menu/menu_editor.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/menu/menu_editor_nav.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1445" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.1445" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/common/src/main/res/layout/layout_color_picker.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/xml-inflater/src/main/res/drawable/empy_drawable.xml" value="0.147" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/xml-inflater/src/main/res/drawable/states.xml" value="0.147" />
+1
View File
@@ -109,6 +109,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Local projects here
implementation project (':build-tools:builder-api')
implementation project (':treeview')
implementation project (':emulatorview')
implementation project (':xml-inflater')
+3 -10
View File
@@ -68,6 +68,9 @@
android:configChanges="orientation|screenSize|uiMode"
android:windowSoftInputMode="adjustResize" />
<service
android:name=".services.GradleBuildService"
android:exported="false" />
<provider
android:name=".filepicker.IDEDocumentsProvider"
android:authorities="com.itsaky.androidide.documents"
@@ -87,16 +90,6 @@
<action android:name="com.itsaky.androidide.logs.APPEND_LOG" />
</intent-filter>
</receiver>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_launcher" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/secondaryColor" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/cms_channel_id_devs" />
</application>
</manifest>
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,233 @@
/*
* 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.services;
import static com.itsaky.androidide.utils.ILogger.newInstance;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.BaseApplication;
import com.itsaky.androidide.builder.BuildService;
import com.itsaky.androidide.shell.CommonProcessExecutor;
import com.itsaky.androidide.shell.ProcessStreamsHolder;
import com.itsaky.androidide.tooling.api.IToolingApiClient;
import com.itsaky.androidide.tooling.api.IToolingApiServer;
import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage;
import com.itsaky.androidide.tooling.api.messages.result.InitializeResult;
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult;
import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.InputStreamLineReader;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* A foreground service that handles interaction with the Gradle Tooling API.
*
* @author Akash Yadav
*/
public class GradleBuildService extends Service implements BuildService, IToolingApiClient {
private static final ILogger LOG = newInstance("GradleBuildService");
private static final int NOTIFICATION_ID = R.string.app_name;
private final ILogger SERVER_LOGGER = newInstance("ToolingApiServer");
private final IBinder mBinder = new GradleServiceBinder();
private boolean isToolingServerStarted = false;
private Thread toolingServerThread;
private NotificationManager notificationManager;
private IToolingApiServer server;
@Override
public void onCreate() {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
showNotification();
}
private void showNotification() {
final var ticker = getString(R.string.title_gradle_service_notification_ticker);
final var title = getString(R.string.title_gradle_service_notification);
final var message = getString(R.string.msg_gradle_service_notification);
final var intent =
PendingIntent.getActivity(this, 0, new Intent(this, EditorActivity.class), 0);
final var notification =
new Notification.Builder(this, BaseApplication.NOTIFICATION_GRADLE_BUILD_SERVICE)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker(ticker)
.setWhen(System.currentTimeMillis())
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent)
.build();
LOG.info("Showing notification to user...");
startForeground(NOTIFICATION_ID, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// No point in restarting the service if it really gets killed.
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
LOG.info("Service is being destroyed.", "Dismissing the shown notification...");
notificationManager.cancel(NOTIFICATION_ID);
if (toolingServerThread != null) {
toolingServerThread.interrupt();
}
isToolingServerStarted = false;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@NonNull
@Override
public CompletableFuture<InitializeResult> initializeProject(@NonNull File rootDir) {
checkServerStarted();
return server.initialize(new InitializeProjectMessage(rootDir));
}
@NonNull
@Override
public CompletableFuture<TaskExecutionResult> executeTasks(@NonNull String... tasks) {
checkServerStarted();
return null;
}
@NonNull
@Override
public CompletableFuture<TaskExecutionResult> executeProjectTasks(
@NonNull String projectPath, @NonNull String... tasks) {
checkServerStarted();
return null;
}
private void checkServerStarted() {
if (!isToolingServerStarted) {
throw new IllegalStateException("Tooling API server has not been started");
}
}
public void startToolingServer(@Nullable OnServerStartListener listener) {
if (toolingServerThread != null && toolingServerThread.isAlive()) {
throw new IllegalStateException("Tooling API server is already started!");
}
toolingServerThread = new Thread(new ToolingServerRunner(listener));
toolingServerThread.start();
}
protected void onServerStarted() {}
protected void onServerExited(int exitCode) {
LOG.warn("Tooling API process terminated with exit code:", exitCode);
stopForeground(true);
}
private void startServerOutputReader(InputStream err) {
new Thread(new InputStreamLineReader(err, this::onServerOutput)).start();
}
protected void onServerOutput(String line) {
SERVER_LOGGER.warn(line);
}
public class GradleServiceBinder extends Binder {
public GradleBuildService getService() {
return GradleBuildService.this;
}
}
private class ToolingServerRunner implements Runnable {
@Nullable private final OnServerStartListener listener;
public ToolingServerRunner(@Nullable OnServerStartListener listener) {
this.listener = listener;
}
@Override
public void run() {
try {
LOG.info("Starting tooling API server...");
final var serverStreams = new ProcessStreamsHolder();
final var executor = new CommonProcessExecutor();
executor.execAsync(
serverStreams,
GradleBuildService.this::onServerExited,
false,
Environment.JAVA.getAbsolutePath(),
"-jar",
Environment.TOOLING_API_JAR.getAbsolutePath());
final var launcher =
ToolingApiLauncher.createClientLauncher(
GradleBuildService.this, serverStreams.in, serverStreams.out);
final var future = launcher.startListening();
GradleBuildService.this.startServerOutputReader(serverStreams.err);
GradleBuildService.this.server = launcher.getRemoteProxy();
GradleBuildService.this.isToolingServerStarted = true;
if (listener != null) {
listener.onServerStarted();
}
// Wait(block) until the process terminates
try {
future.get();
} catch (ExecutionException | InterruptedException e) {
LOG.error(
"An error occurred while waiting for tooling API server to terminate");
}
} catch (Throwable e) {
LOG.error("Unable to start tooling API server", e);
}
}
}
/** Callback to listen for Tooling API server start event. */
public interface OnServerStartListener {
/** Called when the tooling API server has been successfully started. */
void onServerStarted();
}
}
@@ -1,8 +1,6 @@
/************************************************************************************
/*
* 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
@@ -16,14 +14,21 @@
* 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.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher;
public class JSONUtility {
public static final Gson gson = new Gson();
public static final Gson toolingGson;
public static final Gson prettyPrinter = new GsonBuilder().setPrettyPrinting().create();
static {
final var builder = new GsonBuilder();
ToolingApiLauncher.configureGson(builder);
toolingGson = builder.create();
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

+4
View File
@@ -357,6 +357,10 @@
<string name="msg_editor_find_case_sensitive">Case sensitive</string>
<string name="msg_editor_find_regex">Regex</string>
<string name="msg_replacing">Replacing...</string>
<string name="title_gradle_service_notification_ticker">Gradle build service started</string>
<string name="title_gradle_service_notification">Gradle build service</string>
<string name="msg_gradle_service_notification">AndroidIDE\'s Gradle build service is running in the background.</string>
</resources>
@@ -29,7 +29,7 @@ import java.util.concurrent.*
* @author Akash Yadav
*/
interface BuildService {
/**
* Initialize the project.
*
@@ -60,7 +60,6 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.gradle:gradle-tooling-api:7.4.2'
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
testImplementation tests.junit
testImplementation tests.google_truth
+2 -2
View File
@@ -27,14 +27,14 @@ java {
dependencies {
implementation project (path: ':build-tools:logger')
implementation project (path: ':build-tools:tooling-api-model')
api project (path: ':build-tools:tooling-api-model')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
testImplementation tests.junit
testImplementation tests.google_truth
api "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
api google.gson
}
@@ -49,8 +49,7 @@ import java.util.Arrays;
public abstract class BaseApplication extends MultiDexApplication {
public static final String NOTIFICATION_ID_UPDATE = "17571";
public static final String NOTIFICATION_ID_DEVS = "17572";
public static final String NOTIFICATION_GRADLE_BUILD_SERVICE = "17571";
public static final String TELEGRAM_GROUP_URL = "https://t.me/androidide_discussions";
public static final String GITHUB_URL = "https://github.com/itsaky/AndroidIDE";
public static final String WEBSITE = "https://androidide.com";
@@ -103,44 +102,18 @@ public abstract class BaseApplication extends MultiDexApplication {
createNotificationChannels();
}
@SuppressLint("NewApi")
private void createNotificationChannels() {
NotificationChannel updateChannel =
new NotificationChannel(
NOTIFICATION_ID_UPDATE,
getNotificationChannelNameForId(NOTIFICATION_ID_UPDATE),
NOTIFICATION_GRADLE_BUILD_SERVICE,
getString(R.string.title_gradle_service_notification_channel),
NotificationManager.IMPORTANCE_HIGH);
updateChannel.enableLights(true);
updateChannel.enableVibration(true);
updateChannel.setLightColor(Color.RED);
updateChannel.setVibrationPattern(new long[] {10, 50, 10});
NotificationManagerCompat.from(this).createNotificationChannel(updateChannel);
NotificationChannel devsChannels =
new NotificationChannel(
NOTIFICATION_ID_DEVS,
getNotificationChannelNameForId(NOTIFICATION_ID_DEVS),
NotificationManager.IMPORTANCE_MIN);
devsChannels.enableLights(true);
devsChannels.enableVibration(true);
devsChannels.setLightColor(Color.BLUE);
devsChannels.setVibrationPattern(new long[] {10, 50, 10});
NotificationManagerCompat.from(this).createNotificationChannel(devsChannels);
}
public String getNotificationChannelNameForId(@NonNull String id) {
if (id.equals(NOTIFICATION_ID_UPDATE)) return getUpdateNotificationChannelName();
else if (id.equals(NOTIFICATION_ID_DEVS)) return getDevNotificationChannelName();
else return "AndroidIDE Notifications";
}
private String getUpdateNotificationChannelName() {
return getString(R.string.cms_channel_id_update);
}
private String getDevNotificationChannelName() {
return getString(R.string.cms_channel_id_devs);
}
public ShellServer newShell(ShellServer.Callback callback) {
@@ -159,12 +132,6 @@ public abstract class BaseApplication extends MultiDexApplication {
return shellServer;
}
public void writeException(Throwable th) {
FileUtil.writeFile(
new File(FileUtil.getExternalStorageDir(), "idelog.txt").getAbsolutePath(),
ThrowableUtils.getFullStackTrace(th));
}
public File getRootDir() {
return new File(getIDEDataDir(), "framework");
}
@@ -174,6 +141,12 @@ public abstract class BaseApplication extends MultiDexApplication {
return Environment.mkdirIfNotExits(new File("/data/data/com.itsaky.androidide/files"));
}
public void writeException(Throwable th) {
FileUtil.writeFile(
new File(FileUtil.getExternalStorageDir(), "idelog.txt").getAbsolutePath(),
ThrowableUtils.getFullStackTrace(th));
}
@NonNull
@Contract(" -> new")
public final File getLogSenderDir() {
@@ -192,26 +165,22 @@ public abstract class BaseApplication extends MultiDexApplication {
return mPrefsManager;
}
public File getProjectsDir() {
return Environment.PROJECTS_DIR;
}
public File[] listProjects() {
return getProjectsDir().listFiles(File::isDirectory);
}
public StudioUtils getUtils() {
return mUtils == null ? mUtils = new StudioUtils(this) : mUtils;
}
public void toast(String msg, Toaster.Type type) {
getUtils().toast(msg, type);
public File getProjectsDir() {
return Environment.PROJECTS_DIR;
}
public void toast(int msg, Toaster.Type type) {
getUtils().toast(msg, type);
}
public StudioUtils getUtils() {
return mUtils == null ? mUtils = new StudioUtils(this) : mUtils;
}
public void toastLong(String msg, Toaster.Type type) {
getUtils().toastLong(msg, type);
}
@@ -241,6 +210,10 @@ public abstract class BaseApplication extends MultiDexApplication {
}
}
public void toast(String msg, Toaster.Type type) {
getUtils().toast(msg, type);
}
public void openGitHub() {
try {
Intent open = new Intent();
+1 -2
View File
@@ -1,6 +1,5 @@
<resources>
<string name="cms_channel_id_devs">Notifications from Developers</string>
<string name="cms_channel_id_update">App Update Notifications</string>
<string name="title_gradle_service_notification_channel">AndroidIDE Gradle build service</string>
<string name="msg_hex_color_code">Hex color code</string>
<string name="msg_color_picker_pick">Pick</string>
<string name="msg_chmod_exec">Setting executable permissions…</string>