mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Initialize project in EditorActivity using Tooling API
This commit is contained in:
Generated
+2
@@ -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" />
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -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,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>
|
||||
|
||||
Reference in New Issue
Block a user