mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Fixed merge conflict
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
name: Format Code
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: axel-op/googlejavaformat-action@v3
|
||||
with:
|
||||
commitMessage: Format code
|
||||
args: "--aosp --replace"
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -36,110 +36,169 @@ import java.util.List;
|
||||
public class AboutActivity extends StudioActivity {
|
||||
|
||||
private ActivityAboutBinding binding;
|
||||
|
||||
|
||||
public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android";
|
||||
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivityAboutBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
||||
binding.items.footerText.setText(getFooter());
|
||||
|
||||
setupTranslations ();
|
||||
|
||||
setupTranslations();
|
||||
setupLicenses();
|
||||
|
||||
|
||||
LayoutAboutItemsBinding items = binding.items;
|
||||
TooltipCompat.setTooltipText(items.discuss, getString(R.string.discussions_on_telegram));
|
||||
TooltipCompat.setTooltipText(items.github, getString(R.string.title_github));
|
||||
TooltipCompat.setTooltipText(items.email, getString(R.string.about_option_email));
|
||||
TooltipCompat.setTooltipText(items.website, getString(R.string.about_option_website));
|
||||
items.github.setOnClickListener(v -> {
|
||||
getApp().openGitHub ();
|
||||
});
|
||||
items.discuss.setOnClickListener(v -> {
|
||||
getApp().openTelegramGroup();
|
||||
});
|
||||
items.email.setOnClickListener(v -> {
|
||||
getApp().emailUs();
|
||||
});
|
||||
items.website.setOnClickListener(v -> {
|
||||
getApp().openWebsite();
|
||||
});
|
||||
items.github.setOnClickListener(
|
||||
v -> {
|
||||
getApp().openGitHub();
|
||||
});
|
||||
items.discuss.setOnClickListener(
|
||||
v -> {
|
||||
getApp().openTelegramGroup();
|
||||
});
|
||||
items.email.setOnClickListener(
|
||||
v -> {
|
||||
getApp().emailUs();
|
||||
});
|
||||
items.website.setOnClickListener(
|
||||
v -> {
|
||||
getApp().openWebsite();
|
||||
});
|
||||
}
|
||||
|
||||
private void setupTranslations () {
|
||||
final var translations = createTranslationsList ();
|
||||
final var sb = new StringBuilder ();
|
||||
|
||||
|
||||
private void setupTranslations() {
|
||||
final var translations = createTranslationsList();
|
||||
final var sb = new StringBuilder();
|
||||
|
||||
for (var translation : translations) {
|
||||
final var author = translation.first;
|
||||
final var language = translation.second;
|
||||
|
||||
sb.append (author);
|
||||
sb.append (" - ");
|
||||
sb.append (language);
|
||||
sb.append ("\n");
|
||||
|
||||
sb.append(author);
|
||||
sb.append(" - ");
|
||||
sb.append(language);
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
binding.items.translations.setText (sb);
|
||||
|
||||
binding.items.translations.setText(sb);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the list of translations.
|
||||
* The list items are pairs.
|
||||
* The first item of the pair is the name of the language.
|
||||
* The second item is the name of the person who made the translations.
|
||||
* Create the list of translations. The list items are pairs. The first item of the pair is the
|
||||
* name of the language. The second item is the name of the person who made the translations.
|
||||
*
|
||||
* If you're adding translations, make sure you add an item to this list.
|
||||
* You can choose to display the language in a localized version. For example :
|
||||
* Language: हिन्दी - < name of author >
|
||||
* <p>If you're adding translations, make sure you add an item to this list. You can choose to
|
||||
* display the language in a localized version. For example : Language: हिन्दी - < name of
|
||||
* author >
|
||||
*
|
||||
* Prefer using unicode escapes for language names. For example, हिन्दी can be written as :
|
||||
* <p>Prefer using unicode escapes for language names. For example, हिन्दी can be written as :
|
||||
* Language: \u0939\u093f\u0928\u094d\u0926\u0940 - < name of author >
|
||||
*
|
||||
* @return The translations list.
|
||||
*/
|
||||
private List<Pair<String, String>> createTranslationsList () {
|
||||
final var list = new ArrayList<Pair<String, String>> ();
|
||||
|
||||
list.add (Pair.create ("Bahasa Indonesia", "Fitrah Nuno Syahbani"));
|
||||
list.add (Pair.create ("\u4e2d\u6587", "Rosemoe")); // 中文
|
||||
list.add (Pair.create ("Deutsch", "Marvin Stelter"));
|
||||
list.add (Pair.create ("\u0939\u093f\u0928\u094d\u0926\u0940", "Premjit Chowdhury")); // हिन्दी
|
||||
list.add (Pair.create ("Russian", "Smooth-E"));
|
||||
list.add (Pair.create ("French", "Se-Lyan"));
|
||||
|
||||
private List<Pair<String, String>> createTranslationsList() {
|
||||
final var list = new ArrayList<Pair<String, String>>();
|
||||
|
||||
list.add(Pair.create("Bahasa Indonesia", "Fitrah Nuno Syahbani"));
|
||||
list.add(Pair.create("\u4e2d\u6587", "Rosemoe")); // 中文
|
||||
list.add(Pair.create("Deutsch", "Marvin Stelter"));
|
||||
list.add(
|
||||
Pair.create("\u0939\u093f\u0928\u094d\u0926\u0940", "Premjit Chowdhury")); // हिन्दी
|
||||
list.add(Pair.create("Russian", "Smooth-E"));
|
||||
list.add(Pair.create("French", "Se-Lyan"));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private void setupLicenses() {
|
||||
final var licenses = new ArrayList<License>();
|
||||
licenses.add(new License("AndroidX Libraries", "Apache License 2.0", "https://github.com/androidx/androidx"));
|
||||
licenses.add(new License("Gradle Build Tool", "Apache License 2.0", "https://github.com/gradle/gradle"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"AndroidX Libraries",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/androidx/androidx"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Gradle Build Tool",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/gradle/gradle"));
|
||||
licenses.add(new License("Gson", "Apache License 2.0", "https://github.com/google/gson"));
|
||||
licenses.add(new License("Material Components for Android", "Apache License 2.0", "https://github.com/material-components/material-components-android"));
|
||||
licenses.add(new License("ANTLR4 Runtime", "BSD 3-clause License", "https://github.com/antlr/antlr4"));
|
||||
licenses.add(new License("Glide", "BSD, part MIT and Apache 2.0", "https://github.com/bumptech/glide"));
|
||||
licenses.add(new License("Android UtilCode", "Apache License 2.0", "https://github.com/Blankj/AndroidUtilCode"));
|
||||
licenses.add(new License("UnicornFilePicker", "Apache License 2.0", "https://github.com/abhishekti7/UnicornFilePicker"));
|
||||
licenses.add(new License("QuickAction", "Apache License 2.0", "https://github.com/piruin/quickaction"));
|
||||
licenses.add(new License("XmlToJson", "Apache License 2.0", "https://github.com/smart-fun/XmlToJson"));
|
||||
licenses.add(new License("AndroidTreeView", "Apache License 2.0", "https://github.com/bmelnychuk/AndroidTreeView"));
|
||||
licenses.add(new License("CodeEditor v0.5.2", "Apache License 2.0", "https://github.com/Rosemoe/CodeEditor"));
|
||||
licenses.add(new License("Guava-Android", "Apache License 2.0", "https://github.com/google/guava"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Material Components for Android",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/material-components/material-components-android"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"ANTLR4 Runtime",
|
||||
"BSD 3-clause License",
|
||||
"https://github.com/antlr/antlr4"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Glide",
|
||||
"BSD, part MIT and Apache 2.0",
|
||||
"https://github.com/bumptech/glide"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Android UtilCode",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/Blankj/AndroidUtilCode"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"UnicornFilePicker",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/abhishekti7/UnicornFilePicker"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"QuickAction",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/piruin/quickaction"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"XmlToJson",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/smart-fun/XmlToJson"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"AndroidTreeView",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/bmelnychuk/AndroidTreeView"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"CodeEditor v0.5.2",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/Rosemoe/CodeEditor"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Guava-Android", "Apache License 2.0", "https://github.com/google/guava"));
|
||||
licenses.add(new License("Jsoup", "MIT License", "https://github.com/jhy/jsoup"));
|
||||
licenses.add(new License("Termux [Terminal Emulator]", "GPL 3", "https://github.com/termux/termux-app"));
|
||||
licenses.add(new License("JavaPoet", "Apache License 2.0", "https://github.com/square/JavaPoet"));
|
||||
licenses.add(new License("Zip4j", "Apache License 2.0", "https://github.com/srikanth-lingala/zip4j"));
|
||||
|
||||
licenses.add(
|
||||
new License(
|
||||
"Termux [Terminal Emulator]",
|
||||
"GPL 3",
|
||||
"https://github.com/termux/termux-app"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"JavaPoet", "Apache License 2.0", "https://github.com/square/JavaPoet"));
|
||||
licenses.add(
|
||||
new License(
|
||||
"Zip4j",
|
||||
"Apache License 2.0",
|
||||
"https://github.com/srikanth-lingala/zip4j"));
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(License license : licenses) {
|
||||
for (License license : licenses) {
|
||||
sb.append("\u2022 ");
|
||||
sb.append(license.name);
|
||||
sb.append("<br>");
|
||||
@@ -158,11 +217,12 @@ public class AboutActivity extends StudioActivity {
|
||||
binding.items.licenses.setText(Html.fromHtml(sb.toString()));
|
||||
binding.items.licenses.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
|
||||
private String getFooter() {
|
||||
final String arch = android.os.Build.SUPPORTED_ABIS[0];
|
||||
try {
|
||||
final String version = getPackageManager().getPackageInfo("com.itsaky.androidide", 0).versionName;
|
||||
final String version =
|
||||
getPackageManager().getPackageInfo("com.itsaky.androidide", 0).versionName;
|
||||
return getString(R.string.about_footer, version, arch);
|
||||
} catch (Throwable th) {
|
||||
return getString(R.string.about_footer_alternate, arch);
|
||||
|
||||
@@ -17,53 +17,43 @@
|
||||
|
||||
package com.itsaky.androidide;
|
||||
|
||||
import static com.blankj.utilcode.util.AppUtils.getAppVersionCode;
|
||||
import static com.blankj.utilcode.util.AppUtils.getAppVersionName;
|
||||
import static com.blankj.utilcode.util.DeviceUtils.getManufacturer;
|
||||
import static com.blankj.utilcode.util.DeviceUtils.getModel;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.ClipboardUtils;
|
||||
import com.itsaky.androidide.app.BaseApplication;
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
import com.itsaky.androidide.databinding.ActivityCrashHandlerBinding;
|
||||
import com.itsaky.androidide.fragments.CrashReportFragment;
|
||||
|
||||
public class CrashHandlerActivity extends StudioActivity {
|
||||
|
||||
|
||||
private ActivityCrashHandlerBinding binding;
|
||||
|
||||
|
||||
public static final String REPORT_ACTION = "com.itsaky.androidide.REPORT_CRASH";
|
||||
public static final String TRACE_KEY = "crash_trace";
|
||||
|
||||
|
||||
@Override
|
||||
protected View bindLayout () {
|
||||
binding = ActivityCrashHandlerBinding.inflate (getLayoutInflater ());
|
||||
return binding.getRoot ();
|
||||
protected View bindLayout() {
|
||||
binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
final var extra = getIntent ().getExtras ();
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final var extra = getIntent().getExtras();
|
||||
if (extra == null) {
|
||||
finishAffinity ();
|
||||
finishAffinity();
|
||||
return;
|
||||
}
|
||||
|
||||
final var report = extra.getString (TRACE_KEY, "Unable to get logs.");
|
||||
final var fragment = CrashReportFragment.newInstance (report);
|
||||
|
||||
getSupportFragmentManager ().beginTransaction ()
|
||||
.replace (binding.getRoot ().getId (), fragment, "crash_report_fragment")
|
||||
.addToBackStack (null)
|
||||
.commit ();
|
||||
|
||||
final var report = extra.getString(TRACE_KEY, "Unable to get logs.");
|
||||
final var fragment = CrashReportFragment.newInstance(report);
|
||||
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(binding.getRoot().getId(), fragment, "crash_report_fragment")
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,335 +80,399 @@ import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class DesignerActivity extends StudioActivity implements WidgetItemAdapter.OnDragStartListener {
|
||||
|
||||
public class DesignerActivity extends StudioActivity
|
||||
implements WidgetItemAdapter.OnDragStartListener {
|
||||
|
||||
public static final String KEY_LAYOUT_PATH = "designer_layoutPath";
|
||||
public static final String KEY_GENERATED_CODE = "designer_xmlCode";
|
||||
public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET";
|
||||
public static final String DRAGGING_WIDGET_MIME = "application/ide_widget";
|
||||
private static final Logger LOG = Logger.instance ("DesignerActivity");
|
||||
private final boolean isTablet = DeviceUtils.isTablet ();
|
||||
private final List<UIWidgetGroup> widgetGroups = new ArrayList<> ();
|
||||
private static final Logger LOG = Logger.instance("DesignerActivity");
|
||||
private final boolean isTablet = DeviceUtils.isTablet();
|
||||
private final List<UIWidgetGroup> widgetGroups = new ArrayList<>();
|
||||
private ActivityDesignerBinding mBinding;
|
||||
private UIWidgetGroup checkedWidgetCategory;
|
||||
private IViewGroup inflatedRoot;
|
||||
private File layout;
|
||||
private boolean inflationFailed = false;
|
||||
private AttrEditorSheet mEditorSheet;
|
||||
// This will make sure to apply listeners, background and data to view that are inflated from XML.
|
||||
private final IInflateListener mInflateListener = new IInflateListener () {
|
||||
|
||||
@Override
|
||||
public void onBeginInflate () {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInflateView (IView view, IViewGroup parent) {
|
||||
if (view instanceof IViewGroup) {
|
||||
View v = view.asView ();
|
||||
v.setBackgroundResource (R.drawable.bg_design_container_background);
|
||||
v.setOnDragListener (getOnDragListener ((IViewGroup) view));
|
||||
}
|
||||
|
||||
setupInflatedView (view);
|
||||
setDragDataToInflatedView (view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplyAttribute (IAttribute attr, IView view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate (IView rootView) {
|
||||
if (rootView instanceof IViewGroup) {
|
||||
inflatedRoot = (IViewGroup) rootView;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected View bindLayout () {
|
||||
this.mBinding = ActivityDesignerBinding.inflate (getLayoutInflater ());
|
||||
return mBinding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
setSupportActionBar (mBinding.toolbar);
|
||||
getSupportActionBar ().setDisplayHomeAsUpEnabled (true);
|
||||
|
||||
final var toggle = new ActionBarDrawerToggle (this, mBinding.getRoot (), mBinding.toolbar, R.string.app_name, R.string.app_name) {
|
||||
|
||||
@Override
|
||||
public void onDrawerSlide (View drawerView, float slideOffset) {
|
||||
super.onDrawerSlide (drawerView, slideOffset);
|
||||
if (isTablet) {
|
||||
mBinding.mainContainer.setTranslationX (drawerView.getWidth () * slideOffset);
|
||||
// This will make sure to apply listeners, background and data to view that are inflated from
|
||||
// XML.
|
||||
private final IInflateListener mInflateListener =
|
||||
new IInflateListener() {
|
||||
|
||||
@Override
|
||||
public void onBeginInflate() {}
|
||||
|
||||
@Override
|
||||
public void onInflateView(IView view, IViewGroup parent) {
|
||||
if (view instanceof IViewGroup) {
|
||||
View v = view.asView();
|
||||
v.setBackgroundResource(R.drawable.bg_design_container_background);
|
||||
v.setOnDragListener(getOnDragListener((IViewGroup) view));
|
||||
}
|
||||
|
||||
setupInflatedView(view);
|
||||
setDragDataToInflatedView(view);
|
||||
}
|
||||
}
|
||||
};
|
||||
mBinding.getRoot ().addDrawerListener (toggle);
|
||||
toggle.syncState ();
|
||||
|
||||
final var extras = getIntent ().getExtras ();
|
||||
final var path = extras.getString (KEY_LAYOUT_PATH, null);
|
||||
final var name = path.substring (path.lastIndexOf (File.separator) + 1);
|
||||
getSupportActionBar ().setTitle (name);
|
||||
|
||||
|
||||
@Override
|
||||
public void onApplyAttribute(IAttribute attr, IView view) {}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate(IView rootView) {
|
||||
if (rootView instanceof IViewGroup) {
|
||||
inflatedRoot = (IViewGroup) rootView;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater());
|
||||
return mBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setSupportActionBar(mBinding.toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
final var toggle =
|
||||
new ActionBarDrawerToggle(
|
||||
this,
|
||||
mBinding.getRoot(),
|
||||
mBinding.toolbar,
|
||||
R.string.app_name,
|
||||
R.string.app_name) {
|
||||
|
||||
@Override
|
||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||
super.onDrawerSlide(drawerView, slideOffset);
|
||||
if (isTablet) {
|
||||
mBinding.mainContainer.setTranslationX(
|
||||
drawerView.getWidth() * slideOffset);
|
||||
}
|
||||
}
|
||||
};
|
||||
mBinding.getRoot().addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
final var extras = getIntent().getExtras();
|
||||
final var path = extras.getString(KEY_LAYOUT_PATH, null);
|
||||
final var name = path.substring(path.lastIndexOf(File.separator) + 1);
|
||||
getSupportActionBar().setTitle(name);
|
||||
|
||||
try {
|
||||
this.layout = new File (path);
|
||||
|
||||
final ILayoutInflater inflater = getApp ().getLayoutInflater ();
|
||||
inflater.resetContextProvider (newContextProvider ());
|
||||
inflater.registerInflateListener (this.mInflateListener);
|
||||
final IView view = inflater.inflatePath (path, mBinding.layoutContainer);
|
||||
|
||||
this.layout = new File(path);
|
||||
|
||||
final ILayoutInflater inflater = getApp().getLayoutInflater();
|
||||
inflater.resetContextProvider(newContextProvider());
|
||||
inflater.registerInflateListener(this.mInflateListener);
|
||||
final IView view = inflater.inflatePath(path, mBinding.layoutContainer);
|
||||
|
||||
if (this.inflatedRoot != null) {
|
||||
this.inflatedRoot.asView ().setOnDragListener (getOnDragListener (this.inflatedRoot));
|
||||
this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot));
|
||||
}
|
||||
|
||||
mBinding.layoutContainer.addView (view.asView ());
|
||||
mBinding.layoutContainer.setOnClickListener (v -> mBinding.getRoot ().openDrawer (GravityCompat.START));
|
||||
|
||||
mBinding.layoutContainer.addView(view.asView());
|
||||
mBinding.layoutContainer.setOnClickListener(
|
||||
v -> mBinding.getRoot().openDrawer(GravityCompat.START));
|
||||
} catch (Throwable th) {
|
||||
mBinding.layoutContainer.removeAllViews ();
|
||||
mBinding.layoutContainer.addView (createErrorText (th));
|
||||
LOG.error (getString (R.string.err_cannot_inflate_layout), th);
|
||||
mBinding.layoutContainer.removeAllViews();
|
||||
mBinding.layoutContainer.addView(createErrorText(th));
|
||||
LOG.error(getString(R.string.err_cannot_inflate_layout), th);
|
||||
inflationFailed = true;
|
||||
}
|
||||
|
||||
setupWidgets ();
|
||||
|
||||
|
||||
setupWidgets();
|
||||
|
||||
// Always open the drawer if this device is a tablet.
|
||||
if (isTablet) {
|
||||
mBinding.getRoot ().setScrimColor (Color.TRANSPARENT);
|
||||
mBinding.getRoot ().openDrawer (GravityCompat.START);
|
||||
mBinding.getRoot ().setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_OPEN);
|
||||
mBinding.getRoot().setScrimColor(Color.TRANSPARENT);
|
||||
mBinding.getRoot().openDrawer(GravityCompat.START);
|
||||
mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed () {
|
||||
|
||||
public void onBackPressed() {
|
||||
|
||||
if (inflationFailed) {
|
||||
super.onBackPressed ();
|
||||
super.onBackPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// When the user presses the back button, set the activity result and finish this activity
|
||||
|
||||
final var progress = new ProgressSheet ();
|
||||
progress.setSubMessageEnabled (true);
|
||||
progress.setShowTitle (false);
|
||||
progress.setMessage (getString (R.string.title_generating_xml));
|
||||
progress.setSubMessage (getString (R.string.please_wait));
|
||||
progress.show (getSupportFragmentManager (), "generate_code_progress");
|
||||
|
||||
final var future = CompletableFuture.supplyAsync (() -> BaseView.XML_HEADER.concat ("\n").concat (inflatedRoot.generateCode ()));
|
||||
future.whenComplete ((s, throwable) -> {
|
||||
|
||||
progress.dismiss ();
|
||||
|
||||
if (throwable != null) {
|
||||
notifyXmlGenerationFailed (throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
finishWithResult (future.get ());
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
notifyXmlGenerationFailed (e);
|
||||
}
|
||||
});
|
||||
|
||||
final var progress = new ProgressSheet();
|
||||
progress.setSubMessageEnabled(true);
|
||||
progress.setShowTitle(false);
|
||||
progress.setMessage(getString(R.string.title_generating_xml));
|
||||
progress.setSubMessage(getString(R.string.please_wait));
|
||||
progress.show(getSupportFragmentManager(), "generate_code_progress");
|
||||
|
||||
final var future =
|
||||
CompletableFuture.supplyAsync(
|
||||
() -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode()));
|
||||
future.whenComplete(
|
||||
(s, throwable) -> {
|
||||
progress.dismiss();
|
||||
|
||||
if (throwable != null) {
|
||||
notifyXmlGenerationFailed(throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
finishWithResult(future.get());
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
notifyXmlGenerationFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyXmlGenerationFailed (Throwable error) {
|
||||
final var errorMessage = getString (R.string.msg_generate_xml_failed);
|
||||
LOG.error (errorMessage, error);
|
||||
|
||||
DialogUtils.newYesNoDialog (this,
|
||||
getString (R.string.title_code_generation_failed),
|
||||
getString (R.string.msg_code_generation_failed),
|
||||
(dialog1, which) -> finishWithError (),
|
||||
(dialog1, which) -> dialog1.dismiss ())
|
||||
|
||||
.show ();
|
||||
|
||||
private void notifyXmlGenerationFailed(Throwable error) {
|
||||
final var errorMessage = getString(R.string.msg_generate_xml_failed);
|
||||
LOG.error(errorMessage, error);
|
||||
|
||||
DialogUtils.newYesNoDialog(
|
||||
this,
|
||||
getString(R.string.title_code_generation_failed),
|
||||
getString(R.string.msg_code_generation_failed),
|
||||
(dialog1, which) -> finishWithError(),
|
||||
(dialog1, which) -> dialog1.dismiss())
|
||||
.show();
|
||||
}
|
||||
|
||||
private void finishWithResult (String result) {
|
||||
final var intent = new Intent ();
|
||||
intent.putExtra (KEY_GENERATED_CODE, result);
|
||||
setResult (RESULT_OK, intent);
|
||||
finish ();
|
||||
|
||||
private void finishWithResult(String result) {
|
||||
final var intent = new Intent();
|
||||
intent.putExtra(KEY_GENERATED_CODE, result);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void finishWithError () {
|
||||
final var intent = new Intent ();
|
||||
setResult (-123, intent);
|
||||
finish ();
|
||||
|
||||
private void finishWithError() {
|
||||
final var intent = new Intent();
|
||||
setResult(-123, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy () {
|
||||
|
||||
if (isAttrEditorShowing ()) {
|
||||
getAttrEditorSheet ().dismiss ();
|
||||
protected void onDestroy() {
|
||||
|
||||
if (isAttrEditorShowing()) {
|
||||
getAttrEditorSheet().dismiss();
|
||||
}
|
||||
|
||||
|
||||
this.mEditorSheet = null;
|
||||
|
||||
|
||||
// Release the reference to inflate listener and context from the layout inflater
|
||||
// Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment manager
|
||||
// Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment
|
||||
// manager
|
||||
try {
|
||||
final var layoutInflater = getApp ().getLayoutInflater ();
|
||||
layoutInflater.unregisterListener (mInflateListener);
|
||||
layoutInflater.resetContextProvider (null);
|
||||
final var layoutInflater = getApp().getLayoutInflater();
|
||||
layoutInflater.unregisterListener(mInflateListener);
|
||||
layoutInflater.resetContextProvider(null);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace ();
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
super.onDestroy ();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void setupInflatedView (@NonNull IView view) {
|
||||
final var listener = new WidgetTouchListener (
|
||||
view,
|
||||
this::onLayoutViewClick,
|
||||
this::onLayoutViewLongClick
|
||||
);
|
||||
view.asView ().setOnTouchListener (listener);
|
||||
|
||||
setDragDataToInflatedView (view);
|
||||
|
||||
private void setupInflatedView(@NonNull IView view) {
|
||||
final var listener =
|
||||
new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick);
|
||||
view.asView().setOnTouchListener(listener);
|
||||
|
||||
setDragDataToInflatedView(view);
|
||||
}
|
||||
|
||||
private boolean onLayoutViewLongClick (@NonNull IView view) {
|
||||
final var shadow = new WidgetDragShadowBuilder (view.asView ());
|
||||
final var item = new ClipData.Item (DesignerActivity.DRAGGING_WIDGET_TAG);
|
||||
final var clip = new ClipData (DesignerActivity.DRAGGING_WIDGET_TAG, new String[]{DesignerActivity.DRAGGING_WIDGET_MIME}, item);
|
||||
final var data = view.getExtraData ();
|
||||
|
||||
view.asView ().startDragAndDrop (clip, shadow, data, 0);
|
||||
view.removeFromParent ();
|
||||
|
||||
|
||||
private boolean onLayoutViewLongClick(@NonNull IView view) {
|
||||
final var shadow = new WidgetDragShadowBuilder(view.asView());
|
||||
final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
|
||||
final var clip =
|
||||
new ClipData(
|
||||
DesignerActivity.DRAGGING_WIDGET_TAG,
|
||||
new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
|
||||
item);
|
||||
final var data = view.getExtraData();
|
||||
|
||||
view.asView().startDragAndDrop(clip, shadow, data, 0);
|
||||
view.removeFromParent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onLayoutViewClick (@NonNull IView view) {
|
||||
if (isAttrEditorShowing ()) {
|
||||
getAttrEditorSheet ().dismiss ();
|
||||
|
||||
private void onLayoutViewClick(@NonNull IView view) {
|
||||
if (isAttrEditorShowing()) {
|
||||
getAttrEditorSheet().dismiss();
|
||||
}
|
||||
|
||||
getAttrEditorSheet ().setSelectedView (view);
|
||||
|
||||
if (!getSupportFragmentManager ().isDestroyed ()) {
|
||||
getAttrEditorSheet ().show (getSupportFragmentManager (), "attribute_editor_dialog");
|
||||
|
||||
getAttrEditorSheet().setSelectedView(view);
|
||||
|
||||
if (!getSupportFragmentManager().isDestroyed()) {
|
||||
getAttrEditorSheet().show(getSupportFragmentManager(), "attribute_editor_dialog");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAttrEditorShowing () {
|
||||
return getAttrEditorSheet ().getDialog () != null && getAttrEditorSheet ().getDialog ().isShowing ();
|
||||
|
||||
private boolean isAttrEditorShowing() {
|
||||
return getAttrEditorSheet().getDialog() != null
|
||||
&& getAttrEditorSheet().getDialog().isShowing();
|
||||
}
|
||||
|
||||
private void setupWidgets () {
|
||||
final var android = new UIWidgetGroup (getString (R.string.widget_group_android));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_button), R.drawable.ic_widget_button, Button.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_checkbox), R.drawable.ic_widget_checkbox, CheckBox.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_checked_textview), R.drawable.ic_widget_checked_textview, CheckedTextView.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_edittext), R.drawable.ic_widget_edittext, EditText.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_image_button), R.drawable.ic_widget_image, ImageButton.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_image_view), R.drawable.ic_widget_image, ImageView.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_progressbar), R.drawable.ic_widget_progress, ProgressBar.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_radio_button), R.drawable.ic_widget_radio_button, RadioButton.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_seekbar), R.drawable.ic_widget_seekbar, SeekBar.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_spinner), R.drawable.ic_widget_spinner, Spinner.class));
|
||||
android.addChild (new UIWidget (getString (R.string.widget_textview), R.drawable.ic_widget_textview, TextView.class));
|
||||
|
||||
|
||||
private void setupWidgets() {
|
||||
final var android = new UIWidgetGroup(getString(R.string.widget_group_android));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_button),
|
||||
R.drawable.ic_widget_button,
|
||||
Button.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_checkbox),
|
||||
R.drawable.ic_widget_checkbox,
|
||||
CheckBox.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_checked_textview),
|
||||
R.drawable.ic_widget_checked_textview,
|
||||
CheckedTextView.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_edittext),
|
||||
R.drawable.ic_widget_edittext,
|
||||
EditText.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_image_button),
|
||||
R.drawable.ic_widget_image,
|
||||
ImageButton.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_image_view),
|
||||
R.drawable.ic_widget_image,
|
||||
ImageView.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_progressbar),
|
||||
R.drawable.ic_widget_progress,
|
||||
ProgressBar.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_radio_button),
|
||||
R.drawable.ic_widget_radio_button,
|
||||
RadioButton.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_seekbar),
|
||||
R.drawable.ic_widget_seekbar,
|
||||
SeekBar.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_spinner),
|
||||
R.drawable.ic_widget_spinner,
|
||||
Spinner.class));
|
||||
android.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.widget_textview),
|
||||
R.drawable.ic_widget_textview,
|
||||
TextView.class));
|
||||
|
||||
// IMPORTANT: Do not select any other groups here
|
||||
android.setSelected (true);
|
||||
android.setSelected(true);
|
||||
this.checkedWidgetCategory = android;
|
||||
widgetGroups.add (android);
|
||||
|
||||
final var layouts = new UIWidgetGroup (getString (R.string.widget_group_layouts));
|
||||
layouts.addChild (new UIWidget (getString (R.string.layout_linear), R.drawable.ic_widget_linear, LinearLayout.class));
|
||||
layouts.addChild (new UIWidget (getString (R.string.layout_relative), R.drawable.ic_widget_relative, RelativeLayout.class));
|
||||
|
||||
widgetGroups.add (layouts);
|
||||
|
||||
final var adapter = new WidgetGroupItemAdapter (widgetGroups, this::showWidgetGroup);
|
||||
mBinding.groupItems.setAdapter (adapter);
|
||||
|
||||
showWidgetGroup (android);
|
||||
widgetGroups.add(android);
|
||||
|
||||
final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts));
|
||||
layouts.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.layout_linear),
|
||||
R.drawable.ic_widget_linear,
|
||||
LinearLayout.class));
|
||||
layouts.addChild(
|
||||
new UIWidget(
|
||||
getString(R.string.layout_relative),
|
||||
R.drawable.ic_widget_relative,
|
||||
RelativeLayout.class));
|
||||
|
||||
widgetGroups.add(layouts);
|
||||
|
||||
final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup);
|
||||
mBinding.groupItems.setAdapter(adapter);
|
||||
|
||||
showWidgetGroup(android);
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private void showWidgetGroup (@NonNull UIWidgetGroup group) {
|
||||
final var children = group.getChildren ();
|
||||
this.mBinding.widgetItems.setAdapter (new WidgetItemAdapter (children, this));
|
||||
this.checkedWidgetCategory.setSelected (false);
|
||||
private void showWidgetGroup(@NonNull UIWidgetGroup group) {
|
||||
final var children = group.getChildren();
|
||||
this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this));
|
||||
this.checkedWidgetCategory.setSelected(false);
|
||||
this.checkedWidgetCategory = group;
|
||||
this.checkedWidgetCategory.setSelected (true);
|
||||
|
||||
TransitionManager.beginDelayedTransition (mBinding.navigation,
|
||||
new TransitionSet ()
|
||||
.addTransition (new Slide (Gravity.END))
|
||||
.addTransition (new Fade ())
|
||||
);
|
||||
|
||||
this.checkedWidgetCategory.setSelected(true);
|
||||
|
||||
TransitionManager.beginDelayedTransition(
|
||||
mBinding.navigation,
|
||||
new TransitionSet()
|
||||
.addTransition(new Slide(Gravity.END))
|
||||
.addTransition(new Fade()));
|
||||
|
||||
//noinspection ConstantConditions
|
||||
this.mBinding.groupItems.getAdapter ().notifyDataSetChanged ();
|
||||
this.mBinding.groupItems.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Contract(pure = true)
|
||||
private ILayoutInflater.ContextProvider newContextProvider () {
|
||||
private ILayoutInflater.ContextProvider newContextProvider() {
|
||||
return () -> DesignerActivity.this;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private TextView createErrorText (Throwable th) {
|
||||
final TextView error = new TextView (this);
|
||||
error.setText (ThrowableUtils.getFullStackTrace (th));
|
||||
error.setLayoutParams (new ViewGroup.LayoutParams (-2, -2));
|
||||
error.setTextColor (ContextCompat.getColor (this, android.R.color.black));
|
||||
error.setBackgroundColor (ContextCompat.getColor (this, android.R.color.white));
|
||||
private TextView createErrorText(Throwable th) {
|
||||
final TextView error = new TextView(this);
|
||||
error.setText(ThrowableUtils.getFullStackTrace(th));
|
||||
error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2));
|
||||
error.setTextColor(ContextCompat.getColor(this, android.R.color.black));
|
||||
error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDragStarted (View view) {
|
||||
mBinding.getRoot ().closeDrawer (GravityCompat.START);
|
||||
public void onDragStarted(View view) {
|
||||
mBinding.getRoot().closeDrawer(GravityCompat.START);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
private View.OnDragListener getOnDragListener (IViewGroup group) {
|
||||
private View.OnDragListener getOnDragListener(IViewGroup group) {
|
||||
// WidgetDragListener cannot be reused
|
||||
// This is because they keep a reference to the view group in which dragged view will be added
|
||||
|
||||
return new WidgetDragListener (
|
||||
this,
|
||||
group,
|
||||
this::setupInflatedView);
|
||||
// This is because they keep a reference to the view group in which dragged view will be
|
||||
// added
|
||||
|
||||
return new WidgetDragListener(this, group, this::setupInflatedView);
|
||||
}
|
||||
|
||||
private void setDragDataToInflatedView (@NonNull IView view) {
|
||||
view.setExtraData (new WidgetDragData (true, view, null));
|
||||
|
||||
private void setDragDataToInflatedView(@NonNull IView view) {
|
||||
view.setExtraData(new WidgetDragData(true, view, null));
|
||||
}
|
||||
|
||||
private AttrEditorSheet getAttrEditorSheet () {
|
||||
|
||||
private AttrEditorSheet getAttrEditorSheet() {
|
||||
return this.mEditorSheet == null
|
||||
? mEditorSheet = new AttrEditorSheet ()
|
||||
.setDeletionFailedListener (this::onViewDeletionFailed)
|
||||
.setLayout (this.layout)
|
||||
? mEditorSheet =
|
||||
new AttrEditorSheet()
|
||||
.setDeletionFailedListener(this::onViewDeletionFailed)
|
||||
.setLayout(this.layout)
|
||||
: mEditorSheet;
|
||||
}
|
||||
|
||||
private boolean onViewDeletionFailed (@NonNull IView view) {
|
||||
final var v = view.asView ();
|
||||
if (mBinding.layoutContainer.indexOfChild (v) >= 0) {
|
||||
mBinding.layoutContainer.removeView (v);
|
||||
|
||||
private boolean onViewDeletionFailed(@NonNull IView view) {
|
||||
final var v = view.asView();
|
||||
if (mBinding.layoutContainer.indexOfChild(v) >= 0) {
|
||||
mBinding.layoutContainer.removeView(v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
package com.itsaky.androidide;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
@@ -29,6 +27,8 @@ import android.view.View.OnClickListener;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
@@ -55,252 +55,274 @@ 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 ();
|
||||
protected View bindLayout() {
|
||||
binding = ActivityDownloadBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
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);
|
||||
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 ()) {
|
||||
|
||||
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);
|
||||
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);
|
||||
LOG.error(getString(R.string.err_cannot_start_file_picker), th);
|
||||
}
|
||||
} else if (id == binding.install.getId ()) {
|
||||
} else if (id == binding.install.getId()) {
|
||||
if (choosenDir == null) {
|
||||
getApp ().toast (R.string.msg_pick_files, Toaster.Type.ERROR);
|
||||
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);
|
||||
|
||||
if (!choosenDir.exists()) {
|
||||
getApp().toast(R.string.msg_file_doesnt_exist, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
installAll ();
|
||||
|
||||
installAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void installAll () {
|
||||
showProgress ();
|
||||
getApp ().getPrefManager ().putBoolean (PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
|
||||
|
||||
|
||||
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,
|
||||
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 ()
|
||||
);
|
||||
|
||||
this.output = new StringBuilder ();
|
||||
final InputStreamLineReader reader = new InputStreamLineReader (holder.in, this::onInstallationOutput);
|
||||
new Thread (reader).start ();
|
||||
|
||||
Environment.BUSYBOX.getAbsolutePath(),
|
||||
"sh", // We use busybox's sh, because Environment.SHELL is not installed yet...
|
||||
script.getAbsolutePath());
|
||||
|
||||
this.output = new StringBuilder();
|
||||
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 (() -> this.appendOut (line));
|
||||
|
||||
private void onInstallationOutput(final String line) {
|
||||
ThreadUtils.runOnUiThread(() -> this.appendOut(line));
|
||||
}
|
||||
|
||||
private void onInstallProcessExit (final int code) {
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
if (code == 0) { // 0 = normal execution
|
||||
getApp ().getPrefManager ().putBoolean (PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
|
||||
showRestartNeeded ();
|
||||
if (getProgressSheet ().isShowing ()) {
|
||||
getProgressSheet ().dismiss ();
|
||||
}
|
||||
} else {
|
||||
onInstallationFailed (code);
|
||||
}
|
||||
});
|
||||
|
||||
private void onInstallProcessExit(final int code) {
|
||||
ThreadUtils.runOnUiThread(
|
||||
() -> {
|
||||
if (code == 0) { // 0 = normal execution
|
||||
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, this.output.toString ()));
|
||||
builder.setPositiveButton (android.R.string.ok, (d, w) -> {
|
||||
d.dismiss ();
|
||||
finishAffinity ();
|
||||
});
|
||||
builder.setNegativeButton (android.R.string.cancel, null);
|
||||
builder.show ();
|
||||
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_installation_failed);
|
||||
builder.setMessage(
|
||||
getString(R.string.msg_installation_failed, code, this.output.toString()));
|
||||
builder.setPositiveButton(
|
||||
android.R.string.ok,
|
||||
(d, w) -> {
|
||||
d.dismiss();
|
||||
finishAffinity();
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private File createExtractScript () throws DownloadActivity.InstallationException {
|
||||
final StringBuilder sb = new StringBuilder ();
|
||||
sb.append ("cd");
|
||||
joiner (sb);
|
||||
sb.append ("echo 'Installing...'");
|
||||
joiner (sb);
|
||||
|
||||
File[] files = choosenDir.listFiles (ARCHIVE_FILTER);
|
||||
|
||||
|
||||
private File createExtractScript() throws DownloadActivity.InstallationException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("cd");
|
||||
joiner(sb);
|
||||
sb.append("echo 'Installing...'");
|
||||
joiner(sb);
|
||||
|
||||
File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
|
||||
|
||||
if (files == null || files.length <= 0) {
|
||||
throw new InstallationException (2);
|
||||
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 '").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("$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("echo 'Cleaning unsupported flags in binaries...'");
|
||||
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 ())) {
|
||||
throw new InstallationException (2);
|
||||
sb.append("echo ").append(DONE);
|
||||
|
||||
final File script = new File(Environment.TMP_DIR, "extract_tools.sh");
|
||||
if (!FileIOUtils.writeFileFromString(script, sb.toString())) {
|
||||
throw new InstallationException(2);
|
||||
}
|
||||
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
private void joiner (StringBuilder sb) {
|
||||
sb.append (" && ");
|
||||
|
||||
private void joiner(StringBuilder sb) {
|
||||
sb.append(" && ");
|
||||
}
|
||||
|
||||
private StringBuilder output = new StringBuilder ();
|
||||
private void appendOut (String line) {
|
||||
LOG.debug ("Installation:", line);
|
||||
output.append(line.trim ());
|
||||
output.append ("\n");
|
||||
|
||||
getProgressSheet ().setSubMessage (line);
|
||||
|
||||
private StringBuilder output = new StringBuilder();
|
||||
|
||||
private void appendOut(String line) {
|
||||
LOG.debug("Installation:", line);
|
||||
output.append(line.trim());
|
||||
output.append("\n");
|
||||
|
||||
getProgressSheet().setSubMessage(line);
|
||||
}
|
||||
|
||||
|
||||
@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<String> files = data.getStringArrayListExtra ("filePaths");
|
||||
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<String> files = data.getStringArrayListExtra("filePaths");
|
||||
if (files != null) {
|
||||
if (files.size () == 1) {
|
||||
File choosenDir = new File (files.get (0));
|
||||
if (choosenDir.exists () && choosenDir.isDirectory ()) {
|
||||
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, __)));
|
||||
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);
|
||||
getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
|
||||
}
|
||||
} else {
|
||||
getApp ().toast (R.string.msg_pick_single_file, Toaster.Type.ERROR);
|
||||
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 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 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 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 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@ import android.view.View;
|
||||
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.transition.MaterialContainerTransform;
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
@@ -52,363 +54,403 @@ import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
public class MainActivity extends StudioActivity
|
||||
implements View.OnClickListener, ProjectWriterCallback {
|
||||
|
||||
public class MainActivity extends StudioActivity implements View.OnClickListener, ProjectWriterCallback {
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
private LayoutCreateProjectBinding createBinding;
|
||||
private LayoutCreateProjectContentBinding createLayoutBinding;
|
||||
|
||||
|
||||
private ProgressSheet mProgressSheet;
|
||||
|
||||
private ArrayList<ProjectTemplate> mTemplates = new ArrayList<> ();
|
||||
|
||||
|
||||
private ArrayList<ProjectTemplate> mTemplates = new ArrayList<>();
|
||||
|
||||
private int currentTemplateIndex = 0;
|
||||
|
||||
|
||||
@Override
|
||||
protected View bindLayout () {
|
||||
binding = ActivityMainBinding.inflate (getLayoutInflater ());
|
||||
protected View bindLayout() {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
createBinding = binding.createLayout;
|
||||
createLayoutBinding = createBinding.createProjectContent;
|
||||
return binding.getRoot ();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
binding.createProject.setOnClickListener (this);
|
||||
binding.openProject.setOnClickListener (this);
|
||||
binding.openTerminal.setOnClickListener (this);
|
||||
createBinding.createprojectClose.setOnClickListener (this);
|
||||
createLayoutBinding.createprojectCreate.setOnClickListener (this);
|
||||
createLayoutBinding.previousCard.setOnClickListener (this);
|
||||
createLayoutBinding.nextCard.setOnClickListener (this);
|
||||
binding.createNewCard.setVisibility (View.GONE);
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding.createProject.setOnClickListener(this);
|
||||
binding.openProject.setOnClickListener(this);
|
||||
binding.openTerminal.setOnClickListener(this);
|
||||
createBinding.createprojectClose.setOnClickListener(this);
|
||||
createLayoutBinding.createprojectCreate.setOnClickListener(this);
|
||||
createLayoutBinding.previousCard.setOnClickListener(this);
|
||||
createLayoutBinding.nextCard.setOnClickListener(this);
|
||||
binding.createNewCard.setVisibility(View.GONE);
|
||||
|
||||
currentTemplateIndex = 0;
|
||||
createTemplates ();
|
||||
createProgressSheet ();
|
||||
showTemplate (currentTemplateIndex);
|
||||
|
||||
binding.gotoPreferences.setOnClickListener (this);
|
||||
|
||||
binding.getRoot ().post (() -> {
|
||||
PreferenceManager manager = getApp ().getPrefManager ();
|
||||
if (manager.autoOpenProject () && manager.wasProjectOpened () && ConstantsBridge.SPLASH_TO_MAIN) {
|
||||
String path = manager.getOpenedProject ();
|
||||
if (path == null || path.trim ().isEmpty ()) {
|
||||
getApp ().toast (R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
|
||||
} else {
|
||||
File root = new File (manager.getOpenedProject ());
|
||||
if (!root.exists ()) {
|
||||
getApp ().toast (R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
|
||||
} else {
|
||||
if (manager.confirmProjectOpen ()) {
|
||||
askProjectOpenPermission (root);
|
||||
} else {
|
||||
openProject (root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConstantsBridge.SPLASH_TO_MAIN = false;
|
||||
});
|
||||
createTemplates();
|
||||
createProgressSheet();
|
||||
showTemplate(currentTemplateIndex);
|
||||
|
||||
binding.gotoPreferences.setOnClickListener(this);
|
||||
|
||||
binding.getRoot()
|
||||
.post(
|
||||
() -> {
|
||||
PreferenceManager manager = getApp().getPrefManager();
|
||||
if (manager.autoOpenProject()
|
||||
&& manager.wasProjectOpened()
|
||||
&& ConstantsBridge.SPLASH_TO_MAIN) {
|
||||
String path = manager.getOpenedProject();
|
||||
if (path == null || path.trim().isEmpty()) {
|
||||
getApp().toast(
|
||||
R.string.msg_opened_project_does_not_exist,
|
||||
Toaster.Type.INFO);
|
||||
} else {
|
||||
File root = new File(manager.getOpenedProject());
|
||||
if (!root.exists()) {
|
||||
getApp().toast(
|
||||
R.string.msg_opened_project_does_not_exist,
|
||||
Toaster.Type.INFO);
|
||||
} else {
|
||||
if (manager.confirmProjectOpen()) {
|
||||
askProjectOpenPermission(root);
|
||||
} else {
|
||||
openProject(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConstantsBridge.SPLASH_TO_MAIN = false;
|
||||
});
|
||||
}
|
||||
|
||||
private void askProjectOpenPermission (File root) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (this);
|
||||
builder.setTitle (R.string.title_confirm_open_project);
|
||||
builder.setMessage (getString (R.string.msg_confirm_open_project, root.getAbsolutePath ()));
|
||||
builder.setCancelable (false);
|
||||
builder.setPositiveButton (R.string.yes, (d, w) -> openProject (root));
|
||||
builder.setNegativeButton (R.string.no, null);
|
||||
builder.show ();
|
||||
|
||||
private void askProjectOpenPermission(File root) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_confirm_open_project);
|
||||
builder.setMessage(getString(R.string.msg_confirm_open_project, root.getAbsolutePath()));
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.yes, (d, w) -> openProject(root));
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void gotoSettings () {
|
||||
startActivity (new Intent (this, PreferencesActivity.class));
|
||||
|
||||
private void gotoSettings() {
|
||||
startActivity(new Intent(this, PreferencesActivity.class));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStorageGranted () {
|
||||
}
|
||||
|
||||
protected void onStorageGranted() {}
|
||||
|
||||
@Override
|
||||
protected void onStorageDenied () {
|
||||
getApp ().toast (R.string.msg_storage_denied, Toaster.Type.ERROR);
|
||||
finishAffinity ();
|
||||
protected void onStorageDenied() {
|
||||
getApp().toast(R.string.msg_storage_denied, Toaster.Type.ERROR);
|
||||
finishAffinity();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick (View p1) {
|
||||
if (p1.getId () == binding.createProject.getId ()) {
|
||||
showCreateProject ();
|
||||
} else if (p1.getId () == createBinding.createprojectClose.getId ()) {
|
||||
hideCreateProject ();
|
||||
} else if (p1.getId () == createLayoutBinding.nextCard.getId ()) {
|
||||
public void onClick(View p1) {
|
||||
if (p1.getId() == binding.createProject.getId()) {
|
||||
showCreateProject();
|
||||
} else if (p1.getId() == createBinding.createprojectClose.getId()) {
|
||||
hideCreateProject();
|
||||
} else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
|
||||
currentTemplateIndex++;
|
||||
if (currentTemplateIndex >= mTemplates.size ()) {
|
||||
if (currentTemplateIndex >= mTemplates.size()) {
|
||||
currentTemplateIndex = 0;
|
||||
}
|
||||
showTemplate (currentTemplateIndex);
|
||||
} else if (p1.getId () == createLayoutBinding.previousCard.getId ()) {
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
|
||||
currentTemplateIndex--;
|
||||
if (currentTemplateIndex < 0) {
|
||||
currentTemplateIndex = mTemplates.size () - 1;
|
||||
currentTemplateIndex = mTemplates.size() - 1;
|
||||
}
|
||||
showTemplate (currentTemplateIndex);
|
||||
} else if (p1.getId () == createLayoutBinding.createprojectCreate.getId ()) {
|
||||
createNewProject ();
|
||||
} else if (p1.getId () == binding.gotoPreferences.getId ()) {
|
||||
gotoSettings ();
|
||||
} else if (p1.getId () == binding.openProject.getId ()) {
|
||||
pickProject ();
|
||||
} else if (p1.getId () == binding.openTerminal.getId ()) {
|
||||
startActivity (new Intent (this, TerminalActivity.class));
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.createprojectCreate.getId()) {
|
||||
createNewProject();
|
||||
} else if (p1.getId() == binding.gotoPreferences.getId()) {
|
||||
gotoSettings();
|
||||
} else if (p1.getId() == binding.openProject.getId()) {
|
||||
pickProject();
|
||||
} else if (p1.getId() == binding.openTerminal.getId()) {
|
||||
startActivity(new Intent(this, TerminalActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void beforeBegin () {
|
||||
public void beforeBegin() {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet ();
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
setMessage (R.string.msg_begin_project_write);
|
||||
|
||||
setMessage(R.string.msg_begin_project_write);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onProcessTask (String taskName) {
|
||||
setMessage (taskName);
|
||||
public void onProcessTask(String taskName) {
|
||||
setMessage(taskName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess (File root) {
|
||||
public void onSuccess(File root) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet ();
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing ()) {
|
||||
mProgressSheet.dismiss ();
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp ().toast (R.string.project_created_successfully, Toaster.Type.SUCCESS);
|
||||
|
||||
openProject (root);
|
||||
getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
|
||||
|
||||
openProject(root);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFailed (String reason) {
|
||||
public void onFailed(String reason) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet ();
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing ()) {
|
||||
mProgressSheet.dismiss ();
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp ().toast (reason, Toaster.Type.ERROR);
|
||||
getApp().toast(reason, Toaster.Type.ERROR);
|
||||
}
|
||||
|
||||
|
||||
@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<String> files = data.getStringArrayListExtra ("filePaths");
|
||||
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<String> files = data.getStringArrayListExtra("filePaths");
|
||||
if (files != null) {
|
||||
if (files.size () == 1) {
|
||||
File choseDir = new File (files.get (0));
|
||||
if (choseDir.exists () && choseDir.isDirectory ()) {
|
||||
openProject (choseDir);
|
||||
if (files.size() == 1) {
|
||||
File choseDir = new File(files.get(0));
|
||||
if (choseDir.exists() && choseDir.isDirectory()) {
|
||||
openProject(choseDir);
|
||||
} else {
|
||||
getApp ().toast (R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
|
||||
getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
|
||||
}
|
||||
} else {
|
||||
getApp ().toast (R.string.msg_pick_single_file, Toaster.Type.ERROR);
|
||||
getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed () {
|
||||
if (binding.createNewCard.getVisibility () == View.VISIBLE) {
|
||||
hideCreateProject ();
|
||||
public void onBackPressed() {
|
||||
if (binding.createNewCard.getVisibility() == View.VISIBLE) {
|
||||
hideCreateProject();
|
||||
} else {
|
||||
super.onBackPressed ();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume () {
|
||||
super.onResume ();
|
||||
if (mProgressSheet != null
|
||||
&& mProgressSheet.isShowing ()) {
|
||||
mProgressSheet.dismiss ();
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mProgressSheet != null && mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void showTemplate (int index) {
|
||||
if (index < 0 || index >= mTemplates.size ()) {
|
||||
|
||||
private void showTemplate(int index) {
|
||||
if (index < 0 || index >= mTemplates.size()) {
|
||||
return;
|
||||
}
|
||||
final ProjectTemplate template = mTemplates.get (index);
|
||||
createLayoutBinding.createprojectTemplateLabel.setText (template.getName ());
|
||||
createLayoutBinding.createprojectTemplateDescription.setText (template.getDescription ());
|
||||
createLayoutBinding.createprojectTemplateImage.setImageResource (template.getImageId ());
|
||||
final ProjectTemplate template = mTemplates.get(index);
|
||||
createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
|
||||
createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
|
||||
createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called in/after onViewCreated
|
||||
*/
|
||||
private void createTemplates () {
|
||||
mTemplates = new ArrayList<> ();
|
||||
|
||||
|
||||
/** Must be called in/after onViewCreated */
|
||||
private void createTemplates() {
|
||||
mTemplates = new ArrayList<>();
|
||||
|
||||
ProjectTemplate
|
||||
empty = new ProjectTemplate ()
|
||||
.setId (0)
|
||||
.setName (this, R.string.template_empty)
|
||||
.setDescription (this, R.string.template_description_empty)
|
||||
.setImageId (R.drawable.template_empty),
|
||||
basic = new ProjectTemplate ()
|
||||
.setId (1)
|
||||
.setName (this, R.string.template_basic)
|
||||
.setDescription (this, R.string.template_description_basic)
|
||||
.setImageId (R.drawable.template_basic),
|
||||
drawer = new ProjectTemplate ()
|
||||
.setId (2)
|
||||
.setName (this, R.string.template_navigation_drawer)
|
||||
.setDescription (this, R.string.template_description_navigation_drawer)
|
||||
.setImageId (R.drawable.template_navigation_drawer),
|
||||
kotlinBasic = new ProjectTemplate ()
|
||||
.setId (3)
|
||||
.setName (this, R.string.template_kotlin_basic)
|
||||
.setDescription (this, R.string.template_description_kotlin_basic)
|
||||
.setImageId (R.drawable.template_basic),
|
||||
libgdx = new ProjectTemplate ()
|
||||
.setId (4)
|
||||
.setName (this, R.string.template_libgdx)
|
||||
.setDescription (this, R.string.template_description_libgdx)
|
||||
.setImageId (R.drawable.template_libgdx);
|
||||
mTemplates.add (empty);
|
||||
mTemplates.add (basic);
|
||||
mTemplates.add (drawer);
|
||||
mTemplates.add (kotlinBasic);
|
||||
mTemplates.add (libgdx);
|
||||
|
||||
empty =
|
||||
new ProjectTemplate()
|
||||
.setId(0)
|
||||
.setName(this, R.string.template_empty)
|
||||
.setDescription(this, R.string.template_description_empty)
|
||||
.setImageId(R.drawable.template_empty),
|
||||
basic =
|
||||
new ProjectTemplate()
|
||||
.setId(1)
|
||||
.setName(this, R.string.template_basic)
|
||||
.setDescription(this, R.string.template_description_basic)
|
||||
.setImageId(R.drawable.template_basic),
|
||||
drawer =
|
||||
new ProjectTemplate()
|
||||
.setId(2)
|
||||
.setName(this, R.string.template_navigation_drawer)
|
||||
.setDescription(
|
||||
this, R.string.template_description_navigation_drawer)
|
||||
.setImageId(R.drawable.template_navigation_drawer),
|
||||
kotlinBasic =
|
||||
new ProjectTemplate()
|
||||
.setId(3)
|
||||
.setName(this, R.string.template_kotlin_basic)
|
||||
.setDescription(this, R.string.template_description_kotlin_basic)
|
||||
.setImageId(R.drawable.template_basic),
|
||||
libgdx =
|
||||
new ProjectTemplate()
|
||||
.setId(4)
|
||||
.setName(this, R.string.template_libgdx)
|
||||
.setDescription(this, R.string.template_description_libgdx)
|
||||
.setImageId(R.drawable.template_libgdx);
|
||||
mTemplates.add(empty);
|
||||
mTemplates.add(basic);
|
||||
mTemplates.add(drawer);
|
||||
mTemplates.add(kotlinBasic);
|
||||
mTemplates.add(libgdx);
|
||||
}
|
||||
|
||||
private void createProgressSheet () {
|
||||
mProgressSheet = new ProgressSheet ();
|
||||
mProgressSheet.setShowShadow (false);
|
||||
|
||||
private void createProgressSheet() {
|
||||
mProgressSheet = new ProgressSheet();
|
||||
mProgressSheet.setShowShadow(false);
|
||||
}
|
||||
|
||||
private void setMessage (int msg) {
|
||||
setMessage (getString (msg));
|
||||
|
||||
private void setMessage(int msg) {
|
||||
setMessage(getString(msg));
|
||||
}
|
||||
|
||||
private void setMessage (String msg) {
|
||||
mProgressSheet.setMessage (msg);
|
||||
|
||||
private void setMessage(String msg) {
|
||||
mProgressSheet.setMessage(msg);
|
||||
}
|
||||
|
||||
private void openProject (File root) {
|
||||
setMessage (R.string.msg_opening_project);
|
||||
mProgressSheet.setCancelable (false);
|
||||
mProgressSheet.show (getSupportFragmentManager (), "opening_project");
|
||||
|
||||
new TaskExecutor ().executeAsync (new ProjectReader (root), this::openEditor);
|
||||
|
||||
private void openProject(File root) {
|
||||
setMessage(R.string.msg_opening_project);
|
||||
mProgressSheet.setCancelable(false);
|
||||
mProgressSheet.show(getSupportFragmentManager(), "opening_project");
|
||||
|
||||
new TaskExecutor().executeAsync(new ProjectReader(root), this::openEditor);
|
||||
}
|
||||
|
||||
private void pickProject () {
|
||||
UnicornFilePicker.from (this)
|
||||
.addConfigBuilder ()
|
||||
.addItemDivider (false)
|
||||
.selectMultipleFiles (false)
|
||||
.setRootDirectory (Environment.getExternalStorageDirectory ().getAbsolutePath ())
|
||||
.showHiddenFiles (true)
|
||||
.showOnlyDirectory (true)
|
||||
.theme (R.style.AppTheme_FilePicker)
|
||||
.build ()
|
||||
.forResult (abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
|
||||
|
||||
private void pickProject() {
|
||||
UnicornFilePicker.from(this)
|
||||
.addConfigBuilder()
|
||||
.addItemDivider(false)
|
||||
.selectMultipleFiles(false)
|
||||
.setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath())
|
||||
.showHiddenFiles(true)
|
||||
.showOnlyDirectory(true)
|
||||
.theme(R.style.AppTheme_FilePicker)
|
||||
.build()
|
||||
.forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
|
||||
}
|
||||
|
||||
private void openEditor (AndroidProject project) {
|
||||
|
||||
private void openEditor(AndroidProject project) {
|
||||
if (project == null) {
|
||||
getApp ().toast (R.string.msg_invalid_project, Toaster.Type.ERROR);
|
||||
getApp().toast(R.string.msg_invalid_project, Toaster.Type.ERROR);
|
||||
} else {
|
||||
startActivity (new Intent (this, EditorActivity.class)
|
||||
.putExtra (EditorActivity.EXTRA_PROJECT, project));
|
||||
startActivity(
|
||||
new Intent(this, EditorActivity.class)
|
||||
.putExtra(EditorActivity.EXTRA_PROJECT, project));
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewProject () {
|
||||
final String appName = Objects.requireNonNull (createLayoutBinding.createprojectTextAppName.getEditText ()).getText ().toString ().trim ();
|
||||
final String packageName = Objects.requireNonNull (createLayoutBinding.createprojectTextPackageName.getEditText ()).getText ().toString ().trim ();
|
||||
final int minSdk = getMinSdk ();
|
||||
final int targetSdk = getTargetSdk ();
|
||||
|
||||
if (!isValid (appName, packageName, minSdk, targetSdk)) {
|
||||
getApp ().toast (R.string.invalid_values, Toaster.Type.ERROR);
|
||||
|
||||
private void createNewProject() {
|
||||
final String appName =
|
||||
Objects.requireNonNull(createLayoutBinding.createprojectTextAppName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final String packageName =
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextPackageName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final int minSdk = getMinSdk();
|
||||
final int targetSdk = getTargetSdk();
|
||||
|
||||
if (!isValid(appName, packageName, minSdk, targetSdk)) {
|
||||
getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
createProject (appName, packageName, minSdk, targetSdk);
|
||||
|
||||
createProject(appName, packageName, minSdk, targetSdk);
|
||||
}
|
||||
|
||||
private void createProject (String appName, String packageName, int minSdk, int targetSdk) {
|
||||
new TaskExecutor ().executeAsync (new ProjectCreatorCallable (mTemplates.get (currentTemplateIndex), new NewProjectDetails (appName, packageName, minSdk, targetSdk), this), r -> {
|
||||
});
|
||||
|
||||
private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
new ProjectCreatorCallable(
|
||||
mTemplates.get(currentTemplateIndex),
|
||||
new NewProjectDetails(appName, packageName, minSdk, targetSdk),
|
||||
this),
|
||||
r -> {});
|
||||
}
|
||||
|
||||
private boolean isValid (String appName, String packageName, int minSdk, int targetSdk) {
|
||||
return appName != null && appName.length () > 0
|
||||
&& packageName != null && packageName.length () > 0
|
||||
&& minSdk > 1 && minSdk < 99
|
||||
&& targetSdk > 1 && targetSdk < 99;
|
||||
|
||||
private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
return appName != null
|
||||
&& appName.length() > 0
|
||||
&& packageName != null
|
||||
&& packageName.length() > 0
|
||||
&& minSdk > 1
|
||||
&& minSdk < 99
|
||||
&& targetSdk > 1
|
||||
&& targetSdk < 99;
|
||||
}
|
||||
|
||||
private int getMinSdk () {
|
||||
|
||||
private int getMinSdk() {
|
||||
try {
|
||||
return Integer.parseInt (Objects.requireNonNull (createLayoutBinding.createprojectTextMinSdk.getEditText ()).getText ().toString ());
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextMinSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp ().toast (e.getMessage (), Toaster.Type.ERROR);
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getTargetSdk () {
|
||||
|
||||
private int getTargetSdk() {
|
||||
try {
|
||||
return Integer.parseInt (Objects.requireNonNull (createLayoutBinding.createprojectTextTargetSdk.getEditText ()).getText ().toString ());
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextTargetSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp ().toast (e.getMessage (), Toaster.Type.ERROR);
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void showCreateProject () {
|
||||
MaterialContainerTransform transform = TransformUtils.createContainerTransformFor (binding.createProject, binding.createNewCard, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), transform);
|
||||
binding.createNewCard.setVisibility (View.VISIBLE);
|
||||
|
||||
private void showCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createProject, binding.createNewCard, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hideCreateProject () {
|
||||
MaterialContainerTransform transform = TransformUtils.createContainerTransformFor (binding.createNewCard, binding.createProject, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), transform);
|
||||
binding.createNewCard.setVisibility (View.GONE);
|
||||
|
||||
private void hideCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createNewCard, binding.createProject, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
||||
private static final class ProjectReader implements Callable<AndroidProject> {
|
||||
|
||||
|
||||
private final File root;
|
||||
|
||||
public ProjectReader (File root) {
|
||||
|
||||
public ProjectReader(File root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AndroidProject call () throws Exception {
|
||||
return ProjectFinder.fromFolder (root);
|
||||
public AndroidProject call() throws Exception {
|
||||
return ProjectFinder.fromFolder(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,68 @@
|
||||
/************************************************************************************
|
||||
* 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;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
import com.itsaky.androidide.databinding.ActivityPreferencesBinding;
|
||||
import com.itsaky.androidide.fragments.preferences.BuildPreferences;
|
||||
import com.itsaky.androidide.fragments.preferences.EditorPreferences;
|
||||
import com.itsaky.androidide.fragments.preferences.IDEPreferences;
|
||||
|
||||
public class PreferencesActivity extends StudioActivity {
|
||||
|
||||
private ActivityPreferencesBinding binding;
|
||||
private IDEPreferences mPref;
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setSupportActionBar(binding.toolbar);
|
||||
getSupportActionBar().setTitle(R.string.ide_preferences);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
private ActivityPreferencesBinding binding;
|
||||
private IDEPreferences mPref;
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setSupportActionBar(binding.toolbar);
|
||||
getSupportActionBar().setTitle(R.string.ide_preferences);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
|
||||
loadFragment(getPrefsFragment());
|
||||
}
|
||||
|
||||
private IDEPreferences getPrefsFragment() {
|
||||
return mPref == null ? mPref = new IDEPreferences() : mPref;
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment frag) {
|
||||
super.loadFragment(frag, binding.fragmentContainer.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
loadFragment(getPrefsFragment());
|
||||
}
|
||||
|
||||
private IDEPreferences getPrefsFragment() {
|
||||
return mPref == null ? mPref = new IDEPreferences() : mPref;
|
||||
}
|
||||
|
||||
private void loadFragment(Fragment frag) {
|
||||
super.loadFragment(frag, binding.fragmentContainer.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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;
|
||||
|
||||
@@ -25,6 +24,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
@@ -32,69 +32,63 @@ import com.itsaky.androidide.databinding.ActivitySplashBinding;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import org.eclipse.jdt.core.Signature;
|
||||
public class SplashActivity extends StudioActivity {
|
||||
private ActivitySplashBinding binding;
|
||||
|
||||
public class SplashActivity extends StudioActivity
|
||||
{
|
||||
private ActivitySplashBinding binding;
|
||||
|
||||
private static final Handler mHandler = new Handler();
|
||||
private final Runnable mRunnable = new Runnable(){
|
||||
private final Runnable mRunnable =
|
||||
new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ConstantsBridge.SPLASH_TO_MAIN = true;
|
||||
startActivity(new Intent(SplashActivity.this, MainActivity.class));
|
||||
finish();
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void run() {
|
||||
ConstantsBridge.SPLASH_TO_MAIN = true;
|
||||
startActivity(new Intent(SplashActivity.this, MainActivity.class));
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivitySplashBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivitySplashBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
|
||||
binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStorageGranted()
|
||||
{
|
||||
super.onStorageGranted();
|
||||
proceed();
|
||||
}
|
||||
@Override
|
||||
protected void onStorageGranted() {
|
||||
super.onStorageGranted();
|
||||
proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStorageAlreadyGranted()
|
||||
{
|
||||
super.onStorageAlreadyGranted();
|
||||
proceed();
|
||||
}
|
||||
@Override
|
||||
protected void onStorageAlreadyGranted() {
|
||||
super.onStorageAlreadyGranted();
|
||||
proceed();
|
||||
}
|
||||
|
||||
private void proceed()
|
||||
{
|
||||
if(!StudioApp.isAbiSupported()) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (this);
|
||||
builder.setTitle(R.string.title_device_not_supported);
|
||||
builder.setMessage(R.string.msg_device_not_supported);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
|
||||
builder.create().show();
|
||||
} else {
|
||||
binding.splashText.setText(getString(R.string.msg_storage_granted));
|
||||
if (getApp().isFrameworkInstalled()) {
|
||||
goToMain();
|
||||
} else {
|
||||
startActivity(new Intent(this, DownloadActivity.class));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void proceed() {
|
||||
if (!StudioApp.isAbiSupported()) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_device_not_supported);
|
||||
builder.setMessage(R.string.msg_device_not_supported);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
|
||||
builder.create().show();
|
||||
} else {
|
||||
binding.splashText.setText(getString(R.string.msg_storage_granted));
|
||||
if (getApp().isFrameworkInstalled()) {
|
||||
goToMain();
|
||||
} else {
|
||||
startActivity(new Intent(this, DownloadActivity.class));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void goToMain() {
|
||||
mHandler.removeCallbacks(mRunnable);
|
||||
|
||||
@@ -64,230 +64,250 @@ import java.io.FileOutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
public class TerminalActivity extends StudioActivity {
|
||||
|
||||
|
||||
private ActivityTerminalBinding binding;
|
||||
private TerminalView terminal;
|
||||
private TerminalSession session;
|
||||
|
||||
|
||||
private boolean isVisible = false;
|
||||
|
||||
|
||||
private KeyListener listener;
|
||||
private final Client client = new Client ();
|
||||
private static final Logger LOG = Logger.instance ("TerminalActivity");
|
||||
|
||||
private final Client client = new Client();
|
||||
private static final Logger LOG = Logger.instance("TerminalActivity");
|
||||
|
||||
public static final String KEY_WORKING_DIRECTORY = "terminal_workingDirectory";
|
||||
|
||||
private static final byte[] SOURCES_LIST_CONTENT = "deb https://androidide.com/packages/ stable main".getBytes ();
|
||||
|
||||
|
||||
private static final byte[] SOURCES_LIST_CONTENT =
|
||||
"deb https://androidide.com/packages/ stable main".getBytes();
|
||||
|
||||
@Override
|
||||
protected View bindLayout () {
|
||||
binding = ActivityTerminalBinding.inflate (getLayoutInflater ());
|
||||
return binding.getRoot ();
|
||||
protected View bindLayout() {
|
||||
binding = ActivityTerminalBinding.inflate(getLayoutInflater());
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
|
||||
final var bash = new File (BIN_DIR, "bash");
|
||||
final var useSystemShell = getApp ().getPrefManager ().getBoolean (PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
if ((SYSROOT.exists () && SYSROOT.isDirectory () && bash.exists () && bash.isFile () && bash.canExecute ()) || useSystemShell) {
|
||||
setupTerminalView ();
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final var bash = new File(BIN_DIR, "bash");
|
||||
final var useSystemShell =
|
||||
getApp().getPrefManager()
|
||||
.getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
if ((SYSROOT.exists()
|
||||
&& SYSROOT.isDirectory()
|
||||
&& bash.exists()
|
||||
&& bash.isFile()
|
||||
&& bash.canExecute())
|
||||
|| useSystemShell) {
|
||||
setupTerminalView();
|
||||
} else {
|
||||
LOG.info ("Bootstrap is not installed.");
|
||||
|
||||
LOG.info("Bootstrap is not installed.");
|
||||
|
||||
// Show the progress sheet
|
||||
final var progress = new ProgressSheet ();
|
||||
progress.setShowShadow (false);
|
||||
progress.setSubMessageEnabled (true);
|
||||
progress.setShowTitle (false);
|
||||
progress.setMessage (getString (R.string.please_wait));
|
||||
progress.setSubMessage (getString (R.string.msg_reading_bootstrap));
|
||||
progress.setCancelable (false);
|
||||
progress.show (getSupportFragmentManager (), "extract_bootstrap_progress");
|
||||
|
||||
LOG.debug ("Starting installation...");
|
||||
final var progress = new ProgressSheet();
|
||||
progress.setShowShadow(false);
|
||||
progress.setSubMessageEnabled(true);
|
||||
progress.setShowTitle(false);
|
||||
progress.setMessage(getString(R.string.please_wait));
|
||||
progress.setSubMessage(getString(R.string.msg_reading_bootstrap));
|
||||
progress.setCancelable(false);
|
||||
progress.show(getSupportFragmentManager(), "extract_bootstrap_progress");
|
||||
|
||||
LOG.debug("Starting installation...");
|
||||
// Install bootstrap asynchronously
|
||||
final var future = BootstrapInstaller.doInstall (this, message -> runOnUiThread (() -> progress.setSubMessage (message)));
|
||||
|
||||
future.whenComplete ((voidResult, throwable) -> {
|
||||
LOG.debug ("Completable future has been complete.", throwable);
|
||||
|
||||
runOnUiThread (() -> {
|
||||
progress.dismissAllowingStateLoss ();
|
||||
|
||||
if (future.isCompletedExceptionally () || throwable != null) {
|
||||
LOG.error ("Future has been completed exceptionally.");
|
||||
showInstallationError (throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
setupTerminalView ();
|
||||
});
|
||||
});
|
||||
final var future =
|
||||
BootstrapInstaller.doInstall(
|
||||
this, message -> runOnUiThread(() -> progress.setSubMessage(message)));
|
||||
|
||||
future.whenComplete(
|
||||
(voidResult, throwable) -> {
|
||||
LOG.debug("Completable future has been complete.", throwable);
|
||||
|
||||
runOnUiThread(
|
||||
() -> {
|
||||
progress.dismissAllowingStateLoss();
|
||||
|
||||
if (future.isCompletedExceptionally() || throwable != null) {
|
||||
LOG.error("Future has been completed exceptionally.");
|
||||
showInstallationError(throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
setupTerminalView();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showInstallationError (Throwable throwable) {
|
||||
|
||||
binding.virtualKeyTable.setVisibility (View.GONE);
|
||||
|
||||
final var frag = CrashReportFragment.newInstance (
|
||||
getString (R.string.title_installation_failed),
|
||||
getString (R.string.msg_bootstrap_installation_error),
|
||||
ThrowableUtils.getFullStackTrace (throwable))
|
||||
.setCloseAppOnClick (false);
|
||||
|
||||
getSupportFragmentManager ().beginTransaction ()
|
||||
.replace (binding.root.getId (), frag, "bootstrap_install_error_fragment")
|
||||
.addToBackStack (null)
|
||||
.commit ();
|
||||
|
||||
private void showInstallationError(Throwable throwable) {
|
||||
|
||||
binding.virtualKeyTable.setVisibility(View.GONE);
|
||||
|
||||
final var frag =
|
||||
CrashReportFragment.newInstance(
|
||||
getString(R.string.title_installation_failed),
|
||||
getString(R.string.msg_bootstrap_installation_error),
|
||||
ThrowableUtils.getFullStackTrace(throwable))
|
||||
.setCloseAppOnClick(false);
|
||||
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(binding.root.getId(), frag, "bootstrap_install_error_fragment")
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
private void setupTerminalView () {
|
||||
terminal = new TerminalView (this, null);
|
||||
terminal.setTerminalViewClient (client);
|
||||
terminal.attachSession (createSession (getWorkingDirectory ()));
|
||||
terminal.setKeepScreenOn (true);
|
||||
terminal.setTextSize (SizeUtils.dp2px (10));
|
||||
terminal.setTypeface (TypefaceUtils.jetbrainsMono ());
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams (-1, 0);
|
||||
|
||||
private void setupTerminalView() {
|
||||
terminal = new TerminalView(this, null);
|
||||
terminal.setTerminalViewClient(client);
|
||||
terminal.attachSession(createSession(getWorkingDirectory()));
|
||||
terminal.setKeepScreenOn(true);
|
||||
terminal.setTextSize(SizeUtils.dp2px(10));
|
||||
terminal.setTypeface(TypefaceUtils.jetbrainsMono());
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, 0);
|
||||
params.weight = 1f;
|
||||
|
||||
binding.getRoot ().addView (terminal, 0, params);
|
||||
|
||||
binding.getRoot().addView(terminal, 0, params);
|
||||
try {
|
||||
binding.virtualKeyTable.setVirtualKeysViewClient (getKeyListener ());
|
||||
binding.virtualKeyTable.reload (new VirtualKeysInfo (ConstantsBridge.VIRTUAL_KEYS, "", VirtualKeysConstants.CONTROL_CHARS_ALIASES));
|
||||
binding.virtualKeyTable.setVirtualKeysViewClient(getKeyListener());
|
||||
binding.virtualKeyTable.reload(
|
||||
new VirtualKeysInfo(
|
||||
ConstantsBridge.VIRTUAL_KEYS,
|
||||
"",
|
||||
VirtualKeysConstants.CONTROL_CHARS_ALIASES));
|
||||
} catch (JSONException e) {
|
||||
LOG.error ("Unable to parse terminal virtual keys json data", e);
|
||||
LOG.error("Unable to parse terminal virtual keys json data", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private String getWorkingDirectory () {
|
||||
final Bundle extras = getIntent ().getExtras ();
|
||||
if (extras != null && extras.containsKey (KEY_WORKING_DIRECTORY)) {
|
||||
String directory = extras.getString (KEY_WORKING_DIRECTORY, null);
|
||||
if (directory == null || directory.trim ().length () <= 0) {
|
||||
directory = HOME.getAbsolutePath ();
|
||||
private String getWorkingDirectory() {
|
||||
final Bundle extras = getIntent().getExtras();
|
||||
if (extras != null && extras.containsKey(KEY_WORKING_DIRECTORY)) {
|
||||
String directory = extras.getString(KEY_WORKING_DIRECTORY, null);
|
||||
if (directory == null || directory.trim().length() <= 0) {
|
||||
directory = HOME.getAbsolutePath();
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
return HOME.getAbsolutePath ();
|
||||
return HOME.getAbsolutePath();
|
||||
}
|
||||
|
||||
private KeyListener getKeyListener () {
|
||||
return listener == null ? listener = new KeyListener (terminal) : listener;
|
||||
|
||||
private KeyListener getKeyListener() {
|
||||
return listener == null ? listener = new KeyListener(terminal) : listener;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume () {
|
||||
super.onResume ();
|
||||
setTerminalCursorBlinkingState (true);
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setTerminalCursorBlinkingState(true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStart () {
|
||||
super.onStart ();
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
isVisible = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop () {
|
||||
super.onStop ();
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
isVisible = false;
|
||||
setTerminalCursorBlinkingState (false);
|
||||
setTerminalCursorBlinkingState(false);
|
||||
}
|
||||
|
||||
public boolean isVisible () {
|
||||
|
||||
public boolean isVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
private void setTerminalCursorBlinkingState (boolean start) {
|
||||
|
||||
private void setTerminalCursorBlinkingState(boolean start) {
|
||||
if (terminal != null && terminal.mEmulator != null) {
|
||||
terminal.setTerminalCursorBlinkerState (start, true);
|
||||
terminal.setTerminalCursorBlinkerState(start, true);
|
||||
}
|
||||
}
|
||||
|
||||
private TerminalSession createSession (final String workingDirectory) {
|
||||
final Map<String, String> envs = getEnvironment ();
|
||||
final String[] env = new String[envs.size ()];
|
||||
|
||||
private TerminalSession createSession(final String workingDirectory) {
|
||||
final Map<String, String> envs = getEnvironment();
|
||||
final String[] env = new String[envs.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : envs.entrySet ()) {
|
||||
env[i] = entry.getKey () + "=" + entry.getValue ();
|
||||
for (Map.Entry<String, String> entry : envs.entrySet()) {
|
||||
env[i] = entry.getKey() + "=" + entry.getValue();
|
||||
i++;
|
||||
}
|
||||
|
||||
session = new TerminalSession (
|
||||
getShellPath (), // Shell command
|
||||
workingDirectory, // Working directory
|
||||
new String[]{}, // Arguments
|
||||
env, // Environment variables
|
||||
TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS, // Transcript rows
|
||||
client // TerminalSessionClient
|
||||
);
|
||||
|
||||
|
||||
session =
|
||||
new TerminalSession(
|
||||
getShellPath(), // Shell command
|
||||
workingDirectory, // Working directory
|
||||
new String[] {}, // Arguments
|
||||
env, // Environment variables
|
||||
TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS, // Transcript rows
|
||||
client // TerminalSessionClient
|
||||
);
|
||||
|
||||
try {
|
||||
final var file = new File (SYSROOT, "etc/apt/sources.list");
|
||||
final var out = new FileOutputStream (file);
|
||||
out.write (SOURCES_LIST_CONTENT);
|
||||
out.flush ();
|
||||
out.close ();
|
||||
final var file = new File(SYSROOT, "etc/apt/sources.list");
|
||||
final var out = new FileOutputStream(file);
|
||||
out.write(SOURCES_LIST_CONTENT);
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to update sources.list", th);
|
||||
LOG.error("Unable to update sources.list", th);
|
||||
}
|
||||
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private String getShellPath () {
|
||||
final var useSystemShell = getApp ().getPrefManager ().getBoolean (PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
if (!useSystemShell &&
|
||||
LOGIN_SHELL.exists () &&
|
||||
LOGIN_SHELL.isFile ()) {
|
||||
return LOGIN_SHELL.getAbsolutePath ();
|
||||
private String getShellPath() {
|
||||
final var useSystemShell =
|
||||
getApp().getPrefManager()
|
||||
.getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
if (!useSystemShell && LOGIN_SHELL.exists() && LOGIN_SHELL.isFile()) {
|
||||
return LOGIN_SHELL.getAbsolutePath();
|
||||
}
|
||||
|
||||
|
||||
if (!useSystemShell) {
|
||||
LOG.error ("Default shell does not exist. Falling back to '/system/bin/sh'.", "This should not happen in normal circumstances.");
|
||||
LOG.error(
|
||||
"Default shell does not exist. Falling back to '/system/bin/sh'.",
|
||||
"This should not happen in normal circumstances.");
|
||||
}
|
||||
|
||||
|
||||
return "/system/bin/sh";
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static final class KeyListener implements VirtualKeysView.IVirtualKeysView {
|
||||
|
||||
|
||||
private final TerminalView terminal;
|
||||
|
||||
public KeyListener (TerminalView terminal) {
|
||||
|
||||
public KeyListener(TerminalView terminal) {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onVirtualKeyButtonClick (View view, VirtualKeyButton buttonInfo, Button button) {
|
||||
public void onVirtualKeyButtonClick(View view, VirtualKeyButton buttonInfo, Button button) {
|
||||
if (terminal == null) {
|
||||
return;
|
||||
}
|
||||
if (buttonInfo.isMacro ()) {
|
||||
String[] keys = buttonInfo.getKey ().split (" ");
|
||||
if (buttonInfo.isMacro()) {
|
||||
String[] keys = buttonInfo.getKey().split(" ");
|
||||
boolean ctrlDown = false;
|
||||
boolean altDown = false;
|
||||
boolean shiftDown = false;
|
||||
boolean fnDown = false;
|
||||
for (String key : keys) {
|
||||
if (SpecialButton.CTRL.getKey ().equals (key)) {
|
||||
if (SpecialButton.CTRL.getKey().equals(key)) {
|
||||
ctrlDown = true;
|
||||
} else if (SpecialButton.ALT.getKey ().equals (key)) {
|
||||
} else if (SpecialButton.ALT.getKey().equals(key)) {
|
||||
altDown = true;
|
||||
} else if (SpecialButton.SHIFT.getKey ().equals (key)) {
|
||||
} else if (SpecialButton.SHIFT.getKey().equals(key)) {
|
||||
shiftDown = true;
|
||||
} else if (SpecialButton.FN.getKey ().equals (key)) {
|
||||
} else if (SpecialButton.FN.getKey().equals(key)) {
|
||||
fnDown = true;
|
||||
} else {
|
||||
onTerminalExtraKeyButtonClick (key, ctrlDown, altDown, shiftDown, fnDown);
|
||||
onTerminalExtraKeyButtonClick(key, ctrlDown, altDown, shiftDown, fnDown);
|
||||
ctrlDown = false;
|
||||
altDown = false;
|
||||
shiftDown = false;
|
||||
@@ -295,13 +315,14 @@ public class TerminalActivity extends StudioActivity {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onTerminalExtraKeyButtonClick (buttonInfo.getKey (), false, false, false, false);
|
||||
onTerminalExtraKeyButtonClick(buttonInfo.getKey(), false, false, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onTerminalExtraKeyButtonClick (String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) {
|
||||
if (VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.containsKey (key)) {
|
||||
Integer keyCode = VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.get (key);
|
||||
|
||||
protected void onTerminalExtraKeyButtonClick(
|
||||
String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) {
|
||||
if (VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) {
|
||||
Integer keyCode = VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.get(key);
|
||||
if (keyCode == null) {
|
||||
return;
|
||||
}
|
||||
@@ -318,197 +339,183 @@ public class TerminalActivity extends StudioActivity {
|
||||
if (fnDown) {
|
||||
metaState |= KeyEvent.META_FUNCTION_ON;
|
||||
}
|
||||
|
||||
KeyEvent keyEvent = new KeyEvent (0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState);
|
||||
terminal.onKeyDown (keyCode, keyEvent);
|
||||
|
||||
KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState);
|
||||
terminal.onKeyDown(keyCode, keyEvent);
|
||||
} else {
|
||||
// not a control char
|
||||
for (int off = 0; off < key.length (); ) {
|
||||
int codePoint = key.codePointAt (off);
|
||||
terminal.inputCodePoint (codePoint, ctrlDown, altDown);
|
||||
off += Character.charCount (codePoint);
|
||||
for (int off = 0; off < key.length(); ) {
|
||||
int codePoint = key.codePointAt(off);
|
||||
terminal.inputCodePoint(codePoint, ctrlDown, altDown);
|
||||
off += Character.charCount(codePoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean performVirtualKeyButtonHapticFeedback (View view, VirtualKeyButton buttonInfo, Button button) {
|
||||
public boolean performVirtualKeyButtonHapticFeedback(
|
||||
View view, VirtualKeyButton buttonInfo, Button button) {
|
||||
// No need to handle this
|
||||
// VirtualKeysView will take care of performing haptic feedback
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final class Client implements TerminalViewClient, TerminalSessionClient {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp (int keyCode, KeyEvent e) {
|
||||
public boolean onKeyUp(int keyCode, KeyEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown (int keyCode, KeyEvent e, TerminalSession session) {
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && !session.isRunning ()) {
|
||||
finish ();
|
||||
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session) {
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && !session.isRunning()) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCodePoint (int codePoint, boolean ctrlDown, TerminalSession session) {
|
||||
public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTextChanged (TerminalSession changedSession) {
|
||||
terminal.onScreenUpdated ();
|
||||
public void onTextChanged(TerminalSession changedSession) {
|
||||
terminal.onScreenUpdated();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onTitleChanged (TerminalSession changedSession) {
|
||||
public void onTitleChanged(TerminalSession changedSession) {}
|
||||
|
||||
@Override
|
||||
public void onSessionFinished(TerminalSession finishedSession) {
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSessionFinished (TerminalSession finishedSession) {
|
||||
finish ();
|
||||
public void onCopyTextToClipboard(TerminalSession session, String text) {
|
||||
ClipboardUtils.copyText("AndroidIDE Terminal", text);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCopyTextToClipboard (TerminalSession session, String text) {
|
||||
ClipboardUtils.copyText ("AndroidIDE Terminal", text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPasteTextFromClipboard (TerminalSession session) {
|
||||
String clip = ClipboardUtils.getText ().toString ();
|
||||
if (clip.trim ().length () > 0
|
||||
&& terminal != null
|
||||
&& terminal.mEmulator != null) {
|
||||
terminal.mEmulator.paste (clip);
|
||||
public void onPasteTextFromClipboard(TerminalSession session) {
|
||||
String clip = ClipboardUtils.getText().toString();
|
||||
if (clip.trim().length() > 0 && terminal != null && terminal.mEmulator != null) {
|
||||
terminal.mEmulator.paste(clip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBell (TerminalSession session) {
|
||||
}
|
||||
|
||||
public void onBell(TerminalSession session) {}
|
||||
|
||||
@Override
|
||||
public void onColorsChanged (TerminalSession session) {
|
||||
}
|
||||
|
||||
public void onColorsChanged(TerminalSession session) {}
|
||||
|
||||
@Override
|
||||
public void onTerminalCursorStateChange (boolean state) {
|
||||
}
|
||||
|
||||
public void onTerminalCursorStateChange(boolean state) {}
|
||||
|
||||
@Override
|
||||
public Integer getTerminalCursorStyle () {
|
||||
public Integer getTerminalCursorStyle() {
|
||||
return TerminalEmulator.DEFAULT_TERMINAL_CURSOR_STYLE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float onScale (float scale) {
|
||||
public float onScale(float scale) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSingleTapUp (MotionEvent e) {
|
||||
KeyboardUtils.showSoftInput (terminal);
|
||||
public void onSingleTapUp(MotionEvent e) {
|
||||
KeyboardUtils.showSoftInput(terminal);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldBackButtonBeMappedToEscape () {
|
||||
public boolean shouldBackButtonBeMappedToEscape() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldEnforceCharBasedInput () {
|
||||
public boolean shouldEnforceCharBasedInput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldUseCtrlSpaceWorkaround () {
|
||||
public boolean shouldUseCtrlSpaceWorkaround() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isTerminalViewSelected () {
|
||||
public boolean isTerminalViewSelected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void copyModeChanged (boolean copyMode) {
|
||||
}
|
||||
|
||||
public void copyModeChanged(boolean copyMode) {}
|
||||
|
||||
@Override
|
||||
public boolean onLongPress (MotionEvent event) {
|
||||
public boolean onLongPress(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean readControlKey () {
|
||||
Boolean state = binding.virtualKeyTable.readSpecialButton (SpecialButton.CTRL, true);
|
||||
public boolean readControlKey() {
|
||||
Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.CTRL, true);
|
||||
return state != null && state;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean readAltKey () {
|
||||
Boolean state = binding.virtualKeyTable.readSpecialButton (SpecialButton.ALT, true);
|
||||
public boolean readAltKey() {
|
||||
Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.ALT, true);
|
||||
return state != null && state;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean readFnKey () {
|
||||
public boolean readFnKey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean readShiftKey () {
|
||||
public boolean readShiftKey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onEmulatorSet () {
|
||||
setTerminalCursorBlinkingState (true);
|
||||
|
||||
public void onEmulatorSet() {
|
||||
setTerminalCursorBlinkingState(true);
|
||||
|
||||
if (session != null) {
|
||||
binding.getRoot ().setBackgroundColor (session.getEmulator ().mColors.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
|
||||
binding.getRoot()
|
||||
.setBackgroundColor(
|
||||
session.getEmulator()
|
||||
.mColors
|
||||
.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void logError (String tag, String message) {
|
||||
public void logError(String tag, String message) {
|
||||
// No logs
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void logWarn (String tag, String message) {
|
||||
|
||||
}
|
||||
|
||||
public void logWarn(String tag, String message) {}
|
||||
|
||||
@Override
|
||||
public void logInfo (String tag, String message) {
|
||||
|
||||
}
|
||||
|
||||
public void logInfo(String tag, String message) {}
|
||||
|
||||
@Override
|
||||
public void logDebug (String tag, String message) {
|
||||
|
||||
}
|
||||
|
||||
public void logDebug(String tag, String message) {}
|
||||
|
||||
@Override
|
||||
public void logVerbose (String tag, String message) {
|
||||
|
||||
}
|
||||
|
||||
public void logVerbose(String tag, String message) {}
|
||||
|
||||
@Override
|
||||
public void logStackTraceWithMessage (String tag, String message, Exception e) {
|
||||
|
||||
}
|
||||
|
||||
public void logStackTraceWithMessage(String tag, String message, Exception e) {}
|
||||
|
||||
@Override
|
||||
public void logStackTrace (String tag, Exception e) {
|
||||
|
||||
}
|
||||
public void logStackTrace(String tag, Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,52 +27,55 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An {@link com.itsaky.androidide.adapters.IconTextAdapter} for {@link com.itsaky.attrinfo.models.Attr}.
|
||||
* An {@link com.itsaky.androidide.adapters.IconTextAdapter} for {@link
|
||||
* com.itsaky.attrinfo.models.Attr}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class AttrListAdapter extends IconTextAdapter<Attr> {
|
||||
|
||||
|
||||
private final Consumer<Attr> clickConsumer;
|
||||
private final List<Attr> attrs;
|
||||
|
||||
public AttrListAdapter (Consumer<Attr> clickConsumer, List<Attr> attrs) {
|
||||
|
||||
public AttrListAdapter(Consumer<Attr> clickConsumer, List<Attr> attrs) {
|
||||
this.clickConsumer = clickConsumer;
|
||||
if (attrs == null) {
|
||||
attrs = new ArrayList<> ();
|
||||
attrs = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
this.attrs = attrs;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Attr getItemAt (int index) {
|
||||
return attrs.get (index);
|
||||
public Attr getItemAt(int index) {
|
||||
return attrs.get(index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getIconResource (int index) {
|
||||
public int getIconResource(int index) {
|
||||
return R.drawable.ic_xml_attribute;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getItemText (int index) {
|
||||
return getItemAt (index).name;
|
||||
public String getItemText(int index) {
|
||||
return getItemAt(index).name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return attrs.size ();
|
||||
public int getItemCount() {
|
||||
return attrs.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
super.onBindViewHolder (holder, position);
|
||||
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
super.onBindViewHolder(holder, position);
|
||||
|
||||
if (clickConsumer != null) {
|
||||
holder.binding.getRoot ().setOnClickListener (v -> clickConsumer.accept (getItemAt (position)));
|
||||
holder.binding
|
||||
.getRoot()
|
||||
.setOnClickListener(v -> clickConsumer.accept(getItemAt(position)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -48,119 +48,153 @@ import com.itsaky.lsp.models.CompletionItemKind;
|
||||
import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter;
|
||||
|
||||
public class CompletionListAdapter extends EditorCompletionAdapter {
|
||||
|
||||
|
||||
private static final Logger LOG = Logger.instance("CompletionListAdapter");
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemHeight() {
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
|
||||
return (int)
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getView(int position, View convertView, ViewGroup parent, boolean isCurrentCursorPosition) {
|
||||
final LayoutCompletionItemBinding binding = LayoutCompletionItemBinding.inflate(LayoutInflater.from(getContext()), parent, false);
|
||||
|
||||
var item = (CompletionItem) getItem(position);
|
||||
String label = item.getLabel(), desc = item.getDetail(), type = item.getKind().toString();
|
||||
String header = type.length() <= 0 ? "O" : String.valueOf(type.charAt(0));
|
||||
|
||||
protected View getView(
|
||||
int position, View convertView, ViewGroup parent, boolean isCurrentCursorPosition) {
|
||||
final LayoutCompletionItemBinding binding =
|
||||
LayoutCompletionItemBinding.inflate(
|
||||
LayoutInflater.from(getContext()), parent, false);
|
||||
|
||||
var item = (CompletionItem) getItem(position);
|
||||
String label = item.getLabel(), desc = item.getDetail(), type = item.getKind().toString();
|
||||
String header = type.length() <= 0 ? "O" : String.valueOf(type.charAt(0));
|
||||
|
||||
binding.completionIconText.setText(header);
|
||||
binding.completionLabel.setText(label);
|
||||
binding.completionType.setText(type);
|
||||
binding.completionDetail.setText(desc);
|
||||
binding.completionIconText.setTypeface(TypefaceUtils.jetbrainsMono(), Typeface.BOLD);
|
||||
if (desc.isEmpty()) {
|
||||
if (desc.isEmpty()) {
|
||||
binding.completionDetail.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (isCurrentCursorPosition) {
|
||||
binding.getRoot().setBackgroundColor(ContextCompat.getColor(getContext(), R.color.completionList_backgroundSelected));
|
||||
binding.getRoot()
|
||||
.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
getContext(), R.color.completionList_backgroundSelected));
|
||||
} else {
|
||||
binding.getRoot().setBackgroundColor(ContextCompat.getColor(getContext(), R.color.completionList_background));
|
||||
binding.getRoot()
|
||||
.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
getContext(), R.color.completionList_background));
|
||||
}
|
||||
|
||||
binding.completionApiInfo.setVisibility(View.GONE);
|
||||
|
||||
|
||||
showApiInfoIfNeeded(item, binding.completionApiInfo);
|
||||
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void showApiInfoIfNeeded(final CompletionItem item, final TextView completionApiInfo) {
|
||||
|
||||
new Thread(() -> {
|
||||
final ApiInfo apiInfo = StudioApp.getInstance().apiInfo();
|
||||
boolean hasRead = apiInfo != null && apiInfo.hasRead();
|
||||
boolean isValid = isValidForApiVersion(item);
|
||||
|
||||
if (hasRead && isValid) {
|
||||
|
||||
JsonElement element = new Gson().toJsonTree(item.getData());
|
||||
if (element == null || !element.isJsonObject()) return;
|
||||
JsonObject data = element.getAsJsonObject();
|
||||
if (!data.has("className")) return;
|
||||
|
||||
final String className = data.get("className").getAsString();
|
||||
CompletionItemKind kind = item.getKind();
|
||||
|
||||
ClassInfo clazz = apiInfo.getClassByName(className);
|
||||
if(clazz == null) return;
|
||||
|
||||
Info info = clazz;
|
||||
|
||||
// If this Info is not a class info, find the right member
|
||||
if (kind == CompletionItemKind.METHOD
|
||||
&& data.has("erasedParameterTypes")
|
||||
&& data.has("memberName")) {
|
||||
JsonElement erasedParameterTypesElement = data.get("erasedParameterTypes");
|
||||
if(erasedParameterTypesElement.isJsonArray()) {
|
||||
String simpleName = data.get("memberName").getAsString();
|
||||
JsonArray erasedParameterTypes = erasedParameterTypesElement.getAsJsonArray();
|
||||
String[] paramTypes = new String[erasedParameterTypes.size()];
|
||||
for(int i=0;i<erasedParameterTypes.size();i++) {
|
||||
paramTypes[i] = erasedParameterTypes.get(i).getAsString();
|
||||
}
|
||||
|
||||
MethodInfo method = clazz.getMethod(simpleName, paramTypes);
|
||||
|
||||
if(method != null) {
|
||||
info = method;
|
||||
}
|
||||
}
|
||||
} else if(kind == CompletionItemKind.FIELD
|
||||
&& data.has("memberName")) {
|
||||
String simpleName = data.get("memberName").getAsString();
|
||||
FieldInfo field = clazz.getFieldByName(simpleName);
|
||||
|
||||
if(field != null) {
|
||||
info = field;
|
||||
}
|
||||
}
|
||||
new Thread(
|
||||
() -> {
|
||||
final ApiInfo apiInfo = StudioApp.getInstance().apiInfo();
|
||||
boolean hasRead = apiInfo != null && apiInfo.hasRead();
|
||||
boolean isValid = isValidForApiVersion(item);
|
||||
|
||||
final StringBuilder infoBuilder = new StringBuilder();
|
||||
if (info.since > 1) {
|
||||
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_since, info.since));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
if (hasRead && isValid) {
|
||||
|
||||
if (info.removed > 0) {
|
||||
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_removed, info.removed));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
JsonElement element = new Gson().toJsonTree(item.getData());
|
||||
if (element == null || !element.isJsonObject()) return;
|
||||
JsonObject data = element.getAsJsonObject();
|
||||
if (!data.has("className")) return;
|
||||
|
||||
if (info.deprecated > 0) {
|
||||
infoBuilder.append(completionApiInfo.getContext().getString(R.string.msg_api_info_deprecated, info.deprecated));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
|
||||
ThreadUtils.runOnUiThread(() -> {
|
||||
if(infoBuilder.toString().trim().length() > 0) {
|
||||
completionApiInfo.setText(infoBuilder.toString().trim());
|
||||
completionApiInfo.setVisibility(View.VISIBLE);
|
||||
} else completionApiInfo.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
final String className = data.get("className").getAsString();
|
||||
CompletionItemKind kind = item.getKind();
|
||||
|
||||
ClassInfo clazz = apiInfo.getClassByName(className);
|
||||
if (clazz == null) return;
|
||||
|
||||
Info info = clazz;
|
||||
|
||||
// If this Info is not a class info, find the right member
|
||||
if (kind == CompletionItemKind.METHOD
|
||||
&& data.has("erasedParameterTypes")
|
||||
&& data.has("memberName")) {
|
||||
JsonElement erasedParameterTypesElement =
|
||||
data.get("erasedParameterTypes");
|
||||
if (erasedParameterTypesElement.isJsonArray()) {
|
||||
String simpleName = data.get("memberName").getAsString();
|
||||
JsonArray erasedParameterTypes =
|
||||
erasedParameterTypesElement.getAsJsonArray();
|
||||
String[] paramTypes =
|
||||
new String[erasedParameterTypes.size()];
|
||||
for (int i = 0; i < erasedParameterTypes.size(); i++) {
|
||||
paramTypes[i] =
|
||||
erasedParameterTypes.get(i).getAsString();
|
||||
}
|
||||
|
||||
MethodInfo method = clazz.getMethod(simpleName, paramTypes);
|
||||
|
||||
if (method != null) {
|
||||
info = method;
|
||||
}
|
||||
}
|
||||
} else if (kind == CompletionItemKind.FIELD
|
||||
&& data.has("memberName")) {
|
||||
String simpleName = data.get("memberName").getAsString();
|
||||
FieldInfo field = clazz.getFieldByName(simpleName);
|
||||
|
||||
if (field != null) {
|
||||
info = field;
|
||||
}
|
||||
}
|
||||
|
||||
final StringBuilder infoBuilder = new StringBuilder();
|
||||
if (info.since > 1) {
|
||||
infoBuilder.append(
|
||||
completionApiInfo
|
||||
.getContext()
|
||||
.getString(
|
||||
R.string.msg_api_info_since,
|
||||
info.since));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
|
||||
if (info.removed > 0) {
|
||||
infoBuilder.append(
|
||||
completionApiInfo
|
||||
.getContext()
|
||||
.getString(
|
||||
R.string.msg_api_info_removed,
|
||||
info.removed));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
|
||||
if (info.deprecated > 0) {
|
||||
infoBuilder.append(
|
||||
completionApiInfo
|
||||
.getContext()
|
||||
.getString(
|
||||
R.string.msg_api_info_deprecated,
|
||||
info.deprecated));
|
||||
infoBuilder.append("\n");
|
||||
}
|
||||
|
||||
ThreadUtils.runOnUiThread(
|
||||
() -> {
|
||||
if (infoBuilder.toString().trim().length() > 0) {
|
||||
completionApiInfo.setText(
|
||||
infoBuilder.toString().trim());
|
||||
completionApiInfo.setVisibility(View.VISIBLE);
|
||||
} else completionApiInfo.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private boolean isValidForApiVersion(CompletionItem item) {
|
||||
@@ -168,23 +202,23 @@ public class CompletionListAdapter extends EditorCompletionAdapter {
|
||||
final CompletionItemKind type = item.getKind();
|
||||
JsonElement element = new Gson().toJsonTree(item.getData());
|
||||
if (
|
||||
|
||||
// These represent a class type
|
||||
|
||||
// These represent a class type
|
||||
(type == CompletionItemKind.CLASS
|
||||
|| type == CompletionItemKind.INTERFACE
|
||||
|| type == CompletionItemKind.ENUM
|
||||
|
||||
// These represent a method type
|
||||
|| type == CompletionItemKind.METHOD
|
||||
|| type == CompletionItemKind.CONSTRUCTOR
|
||||
|
||||
// A field type
|
||||
|| type == CompletionItemKind.FIELD)
|
||||
|
||||
&& element != null && element.isJsonObject()) {
|
||||
|| type == CompletionItemKind.INTERFACE
|
||||
|| type == CompletionItemKind.ENUM
|
||||
|
||||
// These represent a method type
|
||||
|| type == CompletionItemKind.METHOD
|
||||
|| type == CompletionItemKind.CONSTRUCTOR
|
||||
|
||||
// A field type
|
||||
|| type == CompletionItemKind.FIELD)
|
||||
&& element != null
|
||||
&& element.isJsonObject()) {
|
||||
JsonObject data = element.getAsJsonObject();
|
||||
return data.has("className");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -35,56 +35,67 @@ import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class DiagnosticItemAdapter extends RecyclerView.Adapter<DiagnosticItemAdapter.VH> {
|
||||
|
||||
|
||||
private final List<DiagnosticItem> diags;
|
||||
private final File file;
|
||||
private final DiagnosticClickListener listener;
|
||||
|
||||
public DiagnosticItemAdapter (List<DiagnosticItem> diags, File file, DiagnosticClickListener listener) {
|
||||
public DiagnosticItemAdapter(
|
||||
List<DiagnosticItem> diags, File file, DiagnosticClickListener listener) {
|
||||
this.diags = diags;
|
||||
this.file = file;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DiagnosticItemAdapter.VH onCreateViewHolder(@NonNull ViewGroup p1, int p2) {
|
||||
return new VH (LayoutDiagnosticItemBinding.inflate (LayoutInflater.from (p1.getContext ()), p1, false));
|
||||
return new VH(
|
||||
LayoutDiagnosticItemBinding.inflate(
|
||||
LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(DiagnosticItemAdapter.VH p1, int p2) {
|
||||
final DiagnosticItem diagnostic = diags.get(p2);
|
||||
final LayoutDiagnosticItemBinding binding = p1.binding;
|
||||
|
||||
|
||||
binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
|
||||
binding.icon.setColorFilter(ContextCompat.getColor(binding.icon.getContext(), diagnostic.getSeverity() == DiagnosticSeverity.ERROR ? R.color.diagnostic_error : R.color.diagnostic_warning), PorterDuff.Mode.SRC_ATOP);
|
||||
binding.icon.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
binding.icon.getContext(),
|
||||
diagnostic.getSeverity() == DiagnosticSeverity.ERROR
|
||||
? R.color.diagnostic_error
|
||||
: R.color.diagnostic_warning),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
binding.title.setText(diagnostic.getMessage());
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
listener.onDiagnosticClick(file, diagnostic);
|
||||
}
|
||||
});
|
||||
|
||||
binding.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (listener != null) {
|
||||
listener.onDiagnosticClick(file, diagnostic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return diags.size();
|
||||
}
|
||||
|
||||
|
||||
private int getDiagnosticIconId(DiagnosticItem diagnostic) {
|
||||
if(diagnostic.getSeverity() == DiagnosticSeverity.ERROR)
|
||||
if (diagnostic.getSeverity() == DiagnosticSeverity.ERROR)
|
||||
return R.drawable.ic_compilation_error;
|
||||
return R.drawable.ic_info;
|
||||
}
|
||||
|
||||
|
||||
public static class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutDiagnosticItemBinding binding;
|
||||
|
||||
public VH(LayoutDiagnosticItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,42 @@
|
||||
/************************************************************************************
|
||||
* 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.adapters;
|
||||
|
||||
import android.graphics.PorterDuff;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding;
|
||||
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DiagnosticsAdapter extends RecyclerView.Adapter<DiagnosticsAdapter.VH> {
|
||||
|
||||
|
||||
private final List<DiagnosticGroup> diags;
|
||||
private final DiagnosticClickListener listener;
|
||||
|
||||
@@ -42,43 +44,51 @@ public class DiagnosticsAdapter extends RecyclerView.Adapter<DiagnosticsAdapter.
|
||||
this.diags = diags;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DiagnosticsAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutDiagnosticGroupBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
return new VH(
|
||||
LayoutDiagnosticGroupBinding.inflate(
|
||||
LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(DiagnosticsAdapter.VH p1, int p2) {
|
||||
final DiagnosticGroup group = diags.get(p2);
|
||||
final LayoutDiagnosticGroupBinding binding = p1.binding;
|
||||
|
||||
final int color = ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
|
||||
|
||||
final int color =
|
||||
ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
|
||||
binding.info.icon.setImageResource(group.icon);
|
||||
binding.info.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
binding.info.title.setText(group.text);
|
||||
binding.info.title.setTextColor(color);
|
||||
binding.diagnostics.setLayoutManager(new LinearLayoutManager(binding.diagnostics.getContext()));
|
||||
binding.diagnostics.setAdapter(new DiagnosticItemAdapter (group.diagnostics, group.file, listener));
|
||||
|
||||
binding.info.getRoot().setOnClickListener(v -> {
|
||||
if(listener != null) {
|
||||
listener.onGroupClick(group);
|
||||
}
|
||||
});
|
||||
binding.diagnostics.setLayoutManager(
|
||||
new LinearLayoutManager(binding.diagnostics.getContext()));
|
||||
binding.diagnostics.setAdapter(
|
||||
new DiagnosticItemAdapter(group.diagnostics, group.file, listener));
|
||||
|
||||
binding.info
|
||||
.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (listener != null) {
|
||||
listener.onGroupClick(group);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return diags.size();
|
||||
}
|
||||
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutDiagnosticGroupBinding binding;
|
||||
|
||||
public VH(LayoutDiagnosticGroupBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,98 +24,109 @@ import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.fragments.SimpleOutputFragment;
|
||||
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
|
||||
import com.itsaky.androidide.fragments.IDELogFragment;
|
||||
import com.itsaky.androidide.fragments.LogViewFragment;
|
||||
import com.itsaky.androidide.fragments.SearchResultFragment;
|
||||
import com.itsaky.androidide.fragments.SimpleOutputFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
||||
|
||||
|
||||
private final List<Tab> fragments;
|
||||
|
||||
public EditorBottomSheetTabAdapter (@NonNull FragmentActivity fragmentActivity) {
|
||||
super (fragmentActivity);
|
||||
|
||||
this.fragments = new ArrayList<> ();
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.build_output), new SimpleOutputFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.app_logs), new LogViewFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.ide_logs), new IDELogFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_diags), new DiagnosticsListFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_search_results), new SearchResultFragment ()));
|
||||
|
||||
public EditorBottomSheetTabAdapter(@NonNull FragmentActivity fragmentActivity) {
|
||||
super(fragmentActivity);
|
||||
|
||||
this.fragments = new ArrayList<>();
|
||||
this.fragments.add(
|
||||
new Tab(
|
||||
fragmentActivity.getString(R.string.build_output),
|
||||
new SimpleOutputFragment()));
|
||||
this.fragments.add(
|
||||
new Tab(fragmentActivity.getString(R.string.app_logs), new LogViewFragment()));
|
||||
this.fragments.add(
|
||||
new Tab(fragmentActivity.getString(R.string.ide_logs), new IDELogFragment()));
|
||||
this.fragments.add(
|
||||
new Tab(
|
||||
fragmentActivity.getString(R.string.view_diags),
|
||||
new DiagnosticsListFragment()));
|
||||
this.fragments.add(
|
||||
new Tab(
|
||||
fragmentActivity.getString(R.string.view_search_results),
|
||||
new SearchResultFragment()));
|
||||
}
|
||||
|
||||
public Fragment getFragmentAtIndex (int index) {
|
||||
return this.fragments.get (index).fragment;
|
||||
|
||||
public Fragment getFragmentAtIndex(int index) {
|
||||
return this.fragments.get(index).fragment;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment (int position) {
|
||||
return this.fragments.get (position).fragment;
|
||||
public Fragment createFragment(int position) {
|
||||
return this.fragments.get(position).fragment;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return fragments.size ();
|
||||
public int getItemCount() {
|
||||
return fragments.size();
|
||||
}
|
||||
|
||||
public String getTitle (int position) {
|
||||
return fragments.get (position).title;
|
||||
|
||||
public String getTitle(int position) {
|
||||
return fragments.get(position).title;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public SimpleOutputFragment getBuildOutputFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SimpleOutputFragment.class));
|
||||
public SimpleOutputFragment getBuildOutputFragment() {
|
||||
return Objects.requireNonNull(findFragmentByClass(SimpleOutputFragment.class));
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public LogViewFragment getLogFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (LogViewFragment.class));
|
||||
public LogViewFragment getLogFragment() {
|
||||
return Objects.requireNonNull(findFragmentByClass(LogViewFragment.class));
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public DiagnosticsListFragment getDiagnosticsFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (DiagnosticsListFragment.class));
|
||||
public DiagnosticsListFragment getDiagnosticsFragment() {
|
||||
return Objects.requireNonNull(findFragmentByClass(DiagnosticsListFragment.class));
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public SearchResultFragment getSearchResultFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SearchResultFragment.class));
|
||||
public SearchResultFragment getSearchResultFragment() {
|
||||
return Objects.requireNonNull(findFragmentByClass(SearchResultFragment.class));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private <T extends Fragment> T findFragmentByClass (Class<T> clazz) {
|
||||
private <T extends Fragment> T findFragmentByClass(Class<T> clazz) {
|
||||
for (final var tab : this.fragments) {
|
||||
if (tab.fragment.getClass () == clazz) {
|
||||
if (tab.fragment.getClass() == clazz) {
|
||||
return (T) tab.fragment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends Fragment> int findIndexOfFragmentByClass (Class<T> tClass) {
|
||||
for (int i = 0; i < this.fragments.size (); i++) {
|
||||
final var tab = this.fragments.get (i);
|
||||
if (tab.fragment.getClass () == tClass) {
|
||||
|
||||
public <T extends Fragment> int findIndexOfFragmentByClass(Class<T> tClass) {
|
||||
for (int i = 0; i < this.fragments.size(); i++) {
|
||||
final var tab = this.fragments.get(i);
|
||||
if (tab.fragment.getClass() == tClass) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static class Tab {
|
||||
|
||||
public Tab (String title, Fragment fragment) {
|
||||
|
||||
public Tab(String title, Fragment fragment) {
|
||||
this.title = title;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
|
||||
String title;
|
||||
Fragment fragment;
|
||||
}
|
||||
|
||||
@@ -31,45 +31,47 @@ import com.itsaky.androidide.databinding.LayoutSimpleIconTextBinding;
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class IconTextAdapter <E> extends RecyclerView.Adapter<IconTextAdapter.VH> {
|
||||
|
||||
public abstract class IconTextAdapter<E> extends RecyclerView.Adapter<IconTextAdapter.VH> {
|
||||
|
||||
private OnBindListener<E> bindListener;
|
||||
|
||||
public IconTextAdapter<E> setOnBindListener (OnBindListener<E> listener) {
|
||||
|
||||
public IconTextAdapter<E> setOnBindListener(OnBindListener<E> listener) {
|
||||
this.bindListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH (LayoutSimpleIconTextBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH(
|
||||
LayoutSimpleIconTextBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var item = getItemAt (position);
|
||||
|
||||
if (this.bindListener != null && this.bindListener.onBind (item, holder, position)) {
|
||||
final var item = getItemAt(position);
|
||||
|
||||
if (this.bindListener != null && this.bindListener.onBind(item, holder, position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var icon = getIconResource (position);
|
||||
|
||||
|
||||
final var icon = getIconResource(position);
|
||||
|
||||
if (icon == -1) {
|
||||
binding.icon.setVisibility (View.GONE);
|
||||
binding.icon.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.icon.setImageResource (icon);
|
||||
binding.icon.setImageResource(icon);
|
||||
}
|
||||
|
||||
binding.text.setText (getItemText (position));
|
||||
|
||||
|
||||
binding.text.setText(getItemText(position));
|
||||
|
||||
if (this.bindListener != null) {
|
||||
this.bindListener.postBind (item, holder, position);
|
||||
this.bindListener.postBind(item, holder, position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list item at the given position.
|
||||
*
|
||||
@@ -77,16 +79,16 @@ public abstract class IconTextAdapter <E> extends RecyclerView.Adapter<IconTextA
|
||||
* @return The item at the given index. Must not be <code>null</code>.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract E getItemAt (int index);
|
||||
|
||||
public abstract E getItemAt(int index);
|
||||
|
||||
/**
|
||||
* Get the icon resource ID of the item the given index.
|
||||
*
|
||||
* @param index The index of the item.
|
||||
* @return The icon resource id or <b>-1</b> to hide the icon.
|
||||
*/
|
||||
public abstract int getIconResource (int index);
|
||||
|
||||
public abstract int getIconResource(int index);
|
||||
|
||||
/**
|
||||
* Get the title of the item at the given index.
|
||||
*
|
||||
@@ -94,25 +96,23 @@ public abstract class IconTextAdapter <E> extends RecyclerView.Adapter<IconTextA
|
||||
* @return The title of the item.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract String getItemText (int index);
|
||||
|
||||
public interface OnBindListener <T> {
|
||||
default boolean onBind (T item, VH holder, int position) {
|
||||
public abstract String getItemText(int index);
|
||||
|
||||
public interface OnBindListener<T> {
|
||||
default boolean onBind(T item, VH holder, int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default void postBind (T item, VH holder, int position) {
|
||||
}
|
||||
|
||||
default void postBind(T item, VH holder, int position) {}
|
||||
}
|
||||
|
||||
|
||||
public static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
|
||||
public final LayoutSimpleIconTextBinding binding;
|
||||
|
||||
public VH (@NonNull LayoutSimpleIconTextBinding binding) {
|
||||
super (binding.getRoot ());
|
||||
|
||||
public VH(@NonNull LayoutSimpleIconTextBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,82 @@
|
||||
/************************************************************************************
|
||||
* 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.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.databinding.LayoutOptionssheetItemBinding;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OptionsSheetAdapter extends RecyclerView.Adapter<OptionsSheetAdapter.VH> {
|
||||
|
||||
private OnOptionsClickListener listener;
|
||||
private List<SheetOption> options;
|
||||
private OnOptionsClickListener listener;
|
||||
private List<SheetOption> options;
|
||||
|
||||
public OptionsSheetAdapter(List<SheetOption> options, OnOptionsClickListener listener) {
|
||||
this.options = options;
|
||||
this.listener = listener;
|
||||
}
|
||||
public OptionsSheetAdapter(List<SheetOption> options, OnOptionsClickListener listener) {
|
||||
this.options = options;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionsSheetAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutOptionssheetItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
@Override
|
||||
public OptionsSheetAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(
|
||||
LayoutOptionssheetItemBinding.inflate(
|
||||
LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(OptionsSheetAdapter.VH p1, int p2) {
|
||||
final LayoutOptionssheetItemBinding binding = p1.binding;
|
||||
final SheetOption option = options.get(p2);
|
||||
@Override
|
||||
public void onBindViewHolder(OptionsSheetAdapter.VH p1, int p2) {
|
||||
final LayoutOptionssheetItemBinding binding = p1.binding;
|
||||
final SheetOption option = options.get(p2);
|
||||
|
||||
binding.text.setText(option.titleRes);
|
||||
binding.icon.setImageResource(option.iconRes);
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> { if(listener != null) listener.onOptionClick(option); });
|
||||
}
|
||||
binding.text.setText(option.titleRes);
|
||||
binding.icon.setImageResource(option.iconRes);
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return options.size();
|
||||
}
|
||||
binding.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (listener != null) listener.onOptionClick(option);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return options.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutOptionssheetItemBinding binding;
|
||||
public VH(LayoutOptionssheetItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface OnOptionsClickListener {
|
||||
public void onOptionClick(SheetOption option);
|
||||
}
|
||||
private LayoutOptionssheetItemBinding binding;
|
||||
|
||||
public VH(LayoutOptionssheetItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface OnOptionsClickListener {
|
||||
public void onOptionClick(SheetOption option);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.adapters;
|
||||
|
||||
@@ -25,121 +24,148 @@ import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectsItemBinding;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import androidx.transition.TransitionManager;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
public class ProjectsListAdapter extends RecyclerView.Adapter<ProjectsListAdapter.VH> {
|
||||
private ArrayList<AndroidProject> mProjects;
|
||||
private OnProjectClickListener listener;
|
||||
|
||||
private final Comparator<AndroidProject> PROJECTS_SORTER = new Comparator<AndroidProject>(){
|
||||
private ArrayList<AndroidProject> mProjects;
|
||||
private OnProjectClickListener listener;
|
||||
|
||||
@Override
|
||||
public int compare(AndroidProject p1, AndroidProject p2) {
|
||||
return p1.getAppName().compareTo(p2.getAppName());
|
||||
}
|
||||
};
|
||||
private final Comparator<AndroidProject> PROJECTS_SORTER =
|
||||
new Comparator<AndroidProject>() {
|
||||
|
||||
public ProjectsListAdapter(ArrayList<AndroidProject> mProjects, OnProjectClickListener listener) {
|
||||
this.mProjects = mProjects;
|
||||
this.listener = listener;
|
||||
Collections.sort(this.mProjects, PROJECTS_SORTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
@Override
|
||||
public int compare(AndroidProject p1, AndroidProject p2) {
|
||||
return p1.getAppName().compareTo(p2.getAppName());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
|
||||
final AndroidProject project = mProjects.get(p2);
|
||||
holder.binding.projecsitemName.setText(project.getAppName());
|
||||
holder.binding.projecsitemPkgName.setText(project.getPackageName());
|
||||
holder.binding.deleteMsg.setText(holder.binding.deleteMsg.getContext().getString(R.string.msg_confirm_delete, project.getAppName()));
|
||||
if(!TextUtils.isEmpty(project.getIconPath())) Glide.with(holder.itemView.getContext().getApplicationContext()).load(new File(project.getIconPath())).into(holder.binding.projecsitemIcon);
|
||||
else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
|
||||
|
||||
holder.binding.projectsitemClickableContainer.setOnClickListener(v -> sendClickEvent(project));
|
||||
holder.binding.projectsitemClickableContainer.setOnLongClickListener(v -> holder.toggleDelete());
|
||||
holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
|
||||
holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
|
||||
holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
|
||||
}
|
||||
public ProjectsListAdapter(
|
||||
ArrayList<AndroidProject> mProjects, OnProjectClickListener listener) {
|
||||
this.mProjects = mProjects;
|
||||
this.listener = listener;
|
||||
Collections.sort(this.mProjects, PROJECTS_SORTER);
|
||||
}
|
||||
|
||||
private void deleteProject(AndroidProject project, int index) {
|
||||
index = index >= mProjects.size() ? mProjects.size() - 1 : index;
|
||||
if(index < 0) return;
|
||||
mProjects.remove(index);
|
||||
notifyItemRemoved(index);
|
||||
|
||||
new TaskExecutor().executeAsync(() -> FileUtils.delete(project.getProjectPath()), __ -> {
|
||||
StudioApp.getInstance().toast(__ ? R.string.msg_project_deleted : R.string.msg_project_delete_failed, Toaster.Type.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendClickEvent(AndroidProject project) {
|
||||
if(listener != null) listener.onProjectClick(project);
|
||||
}
|
||||
@Override
|
||||
public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(
|
||||
LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mProjects.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
LayoutProjectsItemBinding binding;
|
||||
|
||||
public VH(LayoutProjectsItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
@Override
|
||||
public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
|
||||
final AndroidProject project = mProjects.get(p2);
|
||||
holder.binding.projecsitemName.setText(project.getAppName());
|
||||
holder.binding.projecsitemPkgName.setText(project.getPackageName());
|
||||
holder.binding.deleteMsg.setText(
|
||||
holder.binding
|
||||
.deleteMsg
|
||||
.getContext()
|
||||
.getString(R.string.msg_confirm_delete, project.getAppName()));
|
||||
if (!TextUtils.isEmpty(project.getIconPath()))
|
||||
Glide.with(holder.itemView.getContext().getApplicationContext())
|
||||
.load(new File(project.getIconPath()))
|
||||
.into(holder.binding.projecsitemIcon);
|
||||
else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
|
||||
|
||||
public void showConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(true);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideAllDelete() {
|
||||
hideDelete();
|
||||
hideConfirmDelete();
|
||||
}
|
||||
|
||||
public boolean toggleDelete() {
|
||||
binding.delete.setEnabled(!binding.delete.isEnabled());
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hideDelete() {
|
||||
binding.delete.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void hideConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProjectClickListener {
|
||||
public void onProjectClick(AndroidProject project);
|
||||
}
|
||||
holder.binding.projectsitemClickableContainer.setOnClickListener(
|
||||
v -> sendClickEvent(project));
|
||||
holder.binding.projectsitemClickableContainer.setOnLongClickListener(
|
||||
v -> holder.toggleDelete());
|
||||
holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
|
||||
holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
|
||||
holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
|
||||
}
|
||||
|
||||
private void deleteProject(AndroidProject project, int index) {
|
||||
index = index >= mProjects.size() ? mProjects.size() - 1 : index;
|
||||
if (index < 0) return;
|
||||
mProjects.remove(index);
|
||||
notifyItemRemoved(index);
|
||||
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
() -> FileUtils.delete(project.getProjectPath()),
|
||||
__ -> {
|
||||
StudioApp.getInstance()
|
||||
.toast(
|
||||
__
|
||||
? R.string.msg_project_deleted
|
||||
: R.string.msg_project_delete_failed,
|
||||
Toaster.Type.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendClickEvent(AndroidProject project) {
|
||||
if (listener != null) listener.onProjectClick(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mProjects.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
LayoutProjectsItemBinding binding;
|
||||
|
||||
public VH(LayoutProjectsItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void showConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(true);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideAllDelete() {
|
||||
hideDelete();
|
||||
hideConfirmDelete();
|
||||
}
|
||||
|
||||
public boolean toggleDelete() {
|
||||
binding.delete.setEnabled(!binding.delete.isEnabled());
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hideDelete() {
|
||||
binding.delete.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void hideConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProjectClickListener {
|
||||
public void onProjectClick(AndroidProject project);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.adapters;
|
||||
|
||||
@@ -25,16 +24,17 @@ import android.graphics.PorterDuff;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.itsaky.androidide.databinding.LayoutSearchResultGroupBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutSearchResultItemBinding;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.syntax.highlighters.JavaHighlighter;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -49,9 +49,11 @@ public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.VH
|
||||
private final OnFileClickListener fileListener;
|
||||
private final OnMatchClickListener matchListener;
|
||||
|
||||
public SearchListAdapter(Map<File, List<SearchResult>> results, OnFileClickListener fileListener, OnMatchClickListener matchListener) {
|
||||
if (results == null)
|
||||
results = new HashMap<>();
|
||||
public SearchListAdapter(
|
||||
Map<File, List<SearchResult>> results,
|
||||
OnFileClickListener fileListener,
|
||||
OnMatchClickListener matchListener) {
|
||||
if (results == null) results = new HashMap<>();
|
||||
this.results = results;
|
||||
this.keys = new ArrayList<File>(results.keySet());
|
||||
this.fileListener = fileListener;
|
||||
@@ -68,17 +70,20 @@ public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.VH
|
||||
final LayoutSearchResultGroupBinding binding = p1.binding;
|
||||
final File file = keys.get(p2);
|
||||
final List<SearchResult> matches = results.get(file);
|
||||
final int color = ContextCompat.getColor(binding.icon.getContext(), com.itsaky.androidide.R.color.secondaryColor);
|
||||
final int color =
|
||||
ContextCompat.getColor(
|
||||
binding.icon.getContext(), com.itsaky.androidide.R.color.secondaryColor);
|
||||
binding.title.setText(file.getName());
|
||||
binding.icon.setImageResource(com.itsaky.androidide.R.drawable.ic_language_java);
|
||||
binding.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
binding.items.setLayoutManager(new LinearLayoutManager(binding.items.getContext()));
|
||||
binding.items.setAdapter(new ChildAdapter(matches));
|
||||
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(fileListener != null)
|
||||
fileListener.onClick(file);
|
||||
});
|
||||
|
||||
binding.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (fileListener != null) fileListener.onClick(file);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,33 +96,40 @@ public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.VH
|
||||
private final List<SearchResult> matches;
|
||||
|
||||
public ChildAdapter(List<SearchResult> matches) {
|
||||
if (matches == null)
|
||||
matches = new ArrayList<>();
|
||||
if (matches == null) matches = new ArrayList<>();
|
||||
this.matches = matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchListAdapter.ChildVH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new ChildVH(LayoutSearchResultItemBinding.inflate(LayoutInflater.from(p1.getContext())));
|
||||
return new ChildVH(
|
||||
LayoutSearchResultItemBinding.inflate(LayoutInflater.from(p1.getContext())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SearchListAdapter.ChildVH p1, int p2) {
|
||||
final SearchResult match = matches.get(p2);
|
||||
final LayoutSearchResultItemBinding binding = p1.binding;
|
||||
new Thread(() -> {
|
||||
try {
|
||||
final SpannableStringBuilder sb = new JavaHighlighter().highlight(match.line, match.match);
|
||||
ThreadUtils.runOnUiThread(() -> binding.text.setText(sb));
|
||||
} catch (Exception e) {
|
||||
ThreadUtils.runOnUiThread(() -> binding.text.setText(match.match));
|
||||
}
|
||||
}).start();
|
||||
binding.getRoot().setOnClickListener(v -> {
|
||||
if(matchListener != null) {
|
||||
matchListener.onClick(match);
|
||||
}
|
||||
});
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
final SpannableStringBuilder sb =
|
||||
new JavaHighlighter()
|
||||
.highlight(match.line, match.match);
|
||||
ThreadUtils.runOnUiThread(() -> binding.text.setText(sb));
|
||||
} catch (Exception e) {
|
||||
ThreadUtils.runOnUiThread(
|
||||
() -> binding.text.setText(match.match));
|
||||
}
|
||||
})
|
||||
.start();
|
||||
binding.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (matchListener != null) {
|
||||
matchListener.onClick(match);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,19 +140,20 @@ public class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.VH
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
private LayoutSearchResultGroupBinding binding;
|
||||
|
||||
public VH(LayoutSearchResultGroupBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ChildVH extends RecyclerView.ViewHolder {
|
||||
private LayoutSearchResultItemBinding binding;
|
||||
|
||||
public ChildVH(LayoutSearchResultItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface OnFileClickListener {
|
||||
|
||||
@@ -29,32 +29,32 @@ import java.util.List;
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SimpleIconTextAdapter extends IconTextAdapter<IconTextListItem> {
|
||||
|
||||
|
||||
private final List<? extends IconTextListItem> items;
|
||||
|
||||
public SimpleIconTextAdapter (List<? extends IconTextListItem> items) {
|
||||
|
||||
public SimpleIconTextAdapter(List<? extends IconTextListItem> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public IconTextListItem getItemAt (int index) {
|
||||
return items.get (index);
|
||||
public IconTextListItem getItemAt(int index) {
|
||||
return items.get(index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getIconResource (int index) {
|
||||
return getItemAt (index).getIconResource ();
|
||||
public int getIconResource(int index) {
|
||||
return getItemAt(index).getIconResource();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getItemText (int index) {
|
||||
return getItemAt (index).getText ();
|
||||
public String getItemText(int index) {
|
||||
return getItemAt(index).getText();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return items.size ();
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -25,98 +25,103 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.databinding.LayoutSymbolItemBinding;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import com.itsaky.androidide.views.SymbolInputView.Symbol;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.VH> {
|
||||
|
||||
private final IDEEditor editor;
|
||||
private Symbol[] symbols;
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor) {
|
||||
this(editor, null);
|
||||
}
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor, Symbol[] symbols) {
|
||||
this.editor = editor;
|
||||
this.symbols = symbols == null ? new Symbol[0] : symbols;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void setSymbols(boolean notify, Symbol... symbols) {
|
||||
this.symbols = symbols;
|
||||
if(notify) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int itemType) {
|
||||
return new VH (LayoutSymbolItemBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
if(symbols == null || symbols[position] == null) return;
|
||||
final Symbol symbol = symbols[position];
|
||||
holder.binding.symbol.setText(symbol.label);
|
||||
private final IDEEditor editor;
|
||||
private Symbol[] symbols;
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor) {
|
||||
this(editor, null);
|
||||
}
|
||||
|
||||
public SymbolInputAdapter(IDEEditor editor, Symbol[] symbols) {
|
||||
this.editor = editor;
|
||||
this.symbols = symbols == null ? new Symbol[0] : symbols;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void setSymbols(boolean notify, Symbol... symbols) {
|
||||
this.symbols = symbols;
|
||||
if (notify) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int itemType) {
|
||||
return new VH(
|
||||
LayoutSymbolItemBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
if (symbols == null || symbols[position] == null) return;
|
||||
final Symbol symbol = symbols[position];
|
||||
holder.binding.symbol.setText(symbol.label);
|
||||
holder.binding.symbol.setOnClickListener(__ -> insertSymbol(symbol.commit, symbol.offset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return symbols == null ? 0 : symbols.length;
|
||||
}
|
||||
|
||||
public static class VH extends RecyclerView.ViewHolder {
|
||||
LayoutSymbolItemBinding binding;
|
||||
|
||||
public VH(LayoutSymbolItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
void insertSymbol(String text, int selectionOffset) {
|
||||
if (selectionOffset < 0 || selectionOffset > text.length()) {
|
||||
return;
|
||||
}
|
||||
var cur = editor.getText().getCursor();
|
||||
if (cur.isSelected()) {
|
||||
editor.getText ().delete (cur.getLeftLine (),
|
||||
cur.getLeftColumn (),
|
||||
cur.getRightLine (),
|
||||
cur.getRightColumn ()
|
||||
);
|
||||
editor.notifyIMEExternalCursorChange ();
|
||||
}
|
||||
|
||||
if(cur.getLeftColumn() < editor.getText().getColumnCount(cur.getLeftLine())
|
||||
&& text.length() == 1
|
||||
&& text.charAt(0) == editor.getText().charAt(cur.getLeftLine(), cur.getLeftColumn())
|
||||
&& pairs.contains(text.charAt(0))) {
|
||||
editor.moveSelectionRight();
|
||||
} else {
|
||||
editor.commitText(text);
|
||||
if (selectionOffset != text.length()) {
|
||||
editor.setSelection(cur.getRightLine(), cur.getRightColumn() - (text.length() - selectionOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<Character> pairs;
|
||||
static {
|
||||
pairs = new ArrayList<>();
|
||||
pairs.add('}');
|
||||
pairs.add(')');
|
||||
pairs.add(']');
|
||||
pairs.add('"');
|
||||
pairs.add('\'');
|
||||
pairs.add('>');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return symbols == null ? 0 : symbols.length;
|
||||
}
|
||||
|
||||
public static class VH extends RecyclerView.ViewHolder {
|
||||
LayoutSymbolItemBinding binding;
|
||||
|
||||
public VH(LayoutSymbolItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
void insertSymbol(String text, int selectionOffset) {
|
||||
if (selectionOffset < 0 || selectionOffset > text.length()) {
|
||||
return;
|
||||
}
|
||||
var cur = editor.getText().getCursor();
|
||||
if (cur.isSelected()) {
|
||||
editor.getText()
|
||||
.delete(
|
||||
cur.getLeftLine(),
|
||||
cur.getLeftColumn(),
|
||||
cur.getRightLine(),
|
||||
cur.getRightColumn());
|
||||
editor.notifyIMEExternalCursorChange();
|
||||
}
|
||||
|
||||
if (cur.getLeftColumn() < editor.getText().getColumnCount(cur.getLeftLine())
|
||||
&& text.length() == 1
|
||||
&& text.charAt(0) == editor.getText().charAt(cur.getLeftLine(), cur.getLeftColumn())
|
||||
&& pairs.contains(text.charAt(0))) {
|
||||
editor.moveSelectionRight();
|
||||
} else {
|
||||
editor.commitText(text);
|
||||
if (selectionOffset != text.length()) {
|
||||
editor.setSelection(
|
||||
cur.getRightLine(),
|
||||
cur.getRightColumn() - (text.length() - selectionOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<Character> pairs;
|
||||
|
||||
static {
|
||||
pairs = new ArrayList<>();
|
||||
pairs.add('}');
|
||||
pairs.add(')');
|
||||
pairs.add(']');
|
||||
pairs.add('"');
|
||||
pairs.add('\'');
|
||||
pairs.add('>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,61 +39,68 @@ import java.util.function.Consumer;
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class TextActionItemAdapter extends RecyclerView.Adapter<TextActionItemAdapter.VH> {
|
||||
|
||||
|
||||
private final List<IDEEditor.TextAction> actions;
|
||||
private final Consumer<IDEEditor.TextAction> onClick;
|
||||
private final boolean isHorizontal;
|
||||
|
||||
public TextActionItemAdapter (List<IDEEditor.TextAction> actions, Consumer<IDEEditor.TextAction> onClick) {
|
||||
Objects.requireNonNull (actions);
|
||||
|
||||
public TextActionItemAdapter(
|
||||
List<IDEEditor.TextAction> actions, Consumer<IDEEditor.TextAction> onClick) {
|
||||
Objects.requireNonNull(actions);
|
||||
this.actions = actions;
|
||||
this.onClick = onClick;
|
||||
this.isHorizontal = StudioApp.getInstance ().getPrefManager ().getBoolean (PreferenceManager.KEY_EDITOR_HORIZONTAL_POPUP, false);
|
||||
this.isHorizontal =
|
||||
StudioApp.getInstance()
|
||||
.getPrefManager()
|
||||
.getBoolean(PreferenceManager.KEY_EDITOR_HORIZONTAL_POPUP, false);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH (LayoutTextActionItemBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH(
|
||||
LayoutTextActionItemBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var action = actions.get (position);
|
||||
final var button = binding.getRoot ();
|
||||
|
||||
final var action = actions.get(position);
|
||||
final var button = binding.getRoot();
|
||||
|
||||
if (isHorizontal) {
|
||||
// If not set to wrap_content, one item will take whole screen width.
|
||||
final var params = button.getLayoutParams ();
|
||||
final var params = button.getLayoutParams();
|
||||
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
button.setLayoutParams (params);
|
||||
|
||||
button.setLayoutParams(params);
|
||||
|
||||
// looks better
|
||||
button.setGravity (Gravity.CENTER);
|
||||
button.setGravity(Gravity.CENTER);
|
||||
}
|
||||
|
||||
button.setText (action.titleId);
|
||||
button.setCompoundDrawablesRelative (action.icon, null, null, null);
|
||||
|
||||
button.setOnClickListener (v -> {
|
||||
if (onClick != null) {
|
||||
onClick.accept (action);
|
||||
}
|
||||
});
|
||||
|
||||
button.setText(action.titleId);
|
||||
button.setCompoundDrawablesRelative(action.icon, null, null, null);
|
||||
|
||||
button.setOnClickListener(
|
||||
v -> {
|
||||
if (onClick != null) {
|
||||
onClick.accept(action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return actions.size ();
|
||||
public int getItemCount() {
|
||||
return actions.size();
|
||||
}
|
||||
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
|
||||
|
||||
private final LayoutTextActionItemBinding binding;
|
||||
|
||||
public VH (@NonNull LayoutTextActionItemBinding binding) {
|
||||
super (binding.getRoot ());
|
||||
|
||||
public VH(@NonNull LayoutTextActionItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,40 +29,46 @@ import com.itsaky.androidide.models.UIWidgetGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WidgetGroupItemAdapter extends SimpleIconTextAdapter implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
|
||||
public class WidgetGroupItemAdapter extends SimpleIconTextAdapter
|
||||
implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
|
||||
private final OnGroupClickListener clickListener;
|
||||
|
||||
public WidgetGroupItemAdapter (@NonNull List<UIWidgetGroup> groups, @Nullable OnGroupClickListener clickListener) {
|
||||
super (groups);
|
||||
|
||||
public WidgetGroupItemAdapter(
|
||||
@NonNull List<UIWidgetGroup> groups, @Nullable OnGroupClickListener clickListener) {
|
||||
super(groups);
|
||||
this.clickListener = clickListener;
|
||||
|
||||
super.setOnBindListener (this);
|
||||
|
||||
super.setOnBindListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onBind (IconTextListItem item, VH holder, int position) {
|
||||
public boolean onBind(IconTextListItem item, VH holder, int position) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postBind (IconTextListItem item, @NonNull VH holder, int position) {
|
||||
public void postBind(IconTextListItem item, @NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var group = (UIWidgetGroup) item;
|
||||
|
||||
if (group.isSelected ()) {
|
||||
binding.getRoot ().setBackground (ContextCompat.getDrawable (binding.getRoot ().getContext (), R.drawable.bg_widget_group_selected));
|
||||
|
||||
if (group.isSelected()) {
|
||||
binding.getRoot()
|
||||
.setBackground(
|
||||
ContextCompat.getDrawable(
|
||||
binding.getRoot().getContext(),
|
||||
R.drawable.bg_widget_group_selected));
|
||||
} else {
|
||||
binding.getRoot ().setBackgroundColor (Color.TRANSPARENT);
|
||||
binding.getRoot().setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
|
||||
if (this.clickListener != null) {
|
||||
binding.getRoot ().setOnClickListener (v -> this.clickListener.onGroupClick (group));
|
||||
binding.getRoot().setOnClickListener(v -> this.clickListener.onGroupClick(group));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface OnGroupClickListener {
|
||||
void onGroupClick (UIWidgetGroup group);
|
||||
void onGroupClick(UIWidgetGroup group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,46 +30,50 @@ import com.itsaky.androidide.ui.WidgetDragShadowBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WidgetItemAdapter extends SimpleIconTextAdapter implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
|
||||
public class WidgetItemAdapter extends SimpleIconTextAdapter
|
||||
implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
|
||||
private final OnDragStartListener dragStartListener;
|
||||
|
||||
public WidgetItemAdapter (List<UIWidget> widgets, OnDragStartListener dragStartListener) {
|
||||
super (widgets);
|
||||
|
||||
public WidgetItemAdapter(List<UIWidget> widgets, OnDragStartListener dragStartListener) {
|
||||
super(widgets);
|
||||
this.dragStartListener = dragStartListener;
|
||||
|
||||
setOnBindListener (this);
|
||||
|
||||
setOnBindListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onBind (IconTextListItem item, @NonNull VH holder, int position) {
|
||||
public boolean onBind(IconTextListItem item, @NonNull VH holder, int position) {
|
||||
return false; // Returning true will not set the text and icon
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postBind (IconTextListItem item, VH holder, int position) {
|
||||
public void postBind(IconTextListItem item, VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var root = binding.getRoot ();
|
||||
final var widget = (UIWidget) getItemAt (position);
|
||||
|
||||
root.setOnLongClickListener (v -> {
|
||||
final var dragData = new WidgetDragData (false, null, widget);
|
||||
final var shadow = new WidgetDragShadowBuilder (binding.icon);
|
||||
final var dataItem = new ClipData.Item (DesignerActivity.DRAGGING_WIDGET_TAG);
|
||||
final var data = new ClipData (DesignerActivity.DRAGGING_WIDGET_TAG,
|
||||
new String[]{DesignerActivity.DRAGGING_WIDGET_MIME},
|
||||
dataItem);
|
||||
binding.icon.startDragAndDrop (data, shadow, dragData, 0);
|
||||
|
||||
if (this.dragStartListener != null) {
|
||||
this.dragStartListener.onDragStarted (v);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
final var root = binding.getRoot();
|
||||
final var widget = (UIWidget) getItemAt(position);
|
||||
|
||||
root.setOnLongClickListener(
|
||||
v -> {
|
||||
final var dragData = new WidgetDragData(false, null, widget);
|
||||
final var shadow = new WidgetDragShadowBuilder(binding.icon);
|
||||
final var dataItem = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
|
||||
final var data =
|
||||
new ClipData(
|
||||
DesignerActivity.DRAGGING_WIDGET_TAG,
|
||||
new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
|
||||
dataItem);
|
||||
binding.icon.startDragAndDrop(data, shadow, dragData, 0);
|
||||
|
||||
if (this.dragStartListener != null) {
|
||||
this.dragStartListener.onDragStarted(v);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public interface OnDragStartListener {
|
||||
void onDragStarted (View view);
|
||||
void onDragStarted(View view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,60 +36,70 @@ import com.itsaky.androidide.models.XMLAttribute;
|
||||
import java.util.List;
|
||||
|
||||
public class XMLAttributeListAdapter extends RecyclerView.Adapter<XMLAttributeListAdapter.VH> {
|
||||
|
||||
|
||||
private final List<XMLAttribute> attributes;
|
||||
private final OnClickListener clickListener;
|
||||
|
||||
public XMLAttributeListAdapter (List<XMLAttribute> attributes, OnClickListener clickListener) {
|
||||
|
||||
public XMLAttributeListAdapter(List<XMLAttribute> attributes, OnClickListener clickListener) {
|
||||
this.attributes = attributes;
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH (LayoutAttrEditorSheetItemBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH(
|
||||
LayoutAttrEditorSheetItemBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var attr = attributes.get (position);
|
||||
final var sb = new SpannableStringBuilder ();
|
||||
sb.append (attr.getAttributeName ());
|
||||
if (attr.isApplied ()) {
|
||||
sb.append (" = ");
|
||||
sb.append (attr.getValue (), new StyleSpan (Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
final var length = attr.getValue ().length ();
|
||||
sb.setSpan (new ForegroundColorSpan (ContextCompat.getColor (binding.text.getContext (), R.color.primaryTextColor)),
|
||||
sb.length () - length,
|
||||
sb.length () - 1,
|
||||
final var attr = attributes.get(position);
|
||||
final var sb = new SpannableStringBuilder();
|
||||
sb.append(attr.getAttributeName());
|
||||
if (attr.isApplied()) {
|
||||
sb.append(" = ");
|
||||
sb.append(
|
||||
attr.getValue(),
|
||||
new StyleSpan(Typeface.BOLD),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
final var length = attr.getValue().length();
|
||||
sb.setSpan(
|
||||
new ForegroundColorSpan(
|
||||
ContextCompat.getColor(
|
||||
binding.text.getContext(), R.color.primaryTextColor)),
|
||||
sb.length() - length,
|
||||
sb.length() - 1,
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
binding.text.setText (sb);
|
||||
binding.getRoot ().setOnClickListener (v -> {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick (binding, attr);
|
||||
}
|
||||
});
|
||||
|
||||
binding.text.setText(sb);
|
||||
binding.getRoot()
|
||||
.setOnClickListener(
|
||||
v -> {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick(binding, attr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return attributes.size ();
|
||||
public int getItemCount() {
|
||||
return attributes.size();
|
||||
}
|
||||
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick (LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
||||
void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
||||
}
|
||||
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
LayoutAttrEditorSheetItemBinding binding;
|
||||
|
||||
VH (@NonNull LayoutAttrEditorSheetItemBinding binding) {
|
||||
super (binding.getRoot ());
|
||||
|
||||
VH(@NonNull LayoutAttrEditorSheetItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
+72
-53
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.adapters.viewholders;
|
||||
|
||||
@@ -39,56 +38,76 @@ import com.unnamed.b.atv.model.TreeNode;
|
||||
import java.io.File;
|
||||
|
||||
public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder<File> {
|
||||
|
||||
private Context context;
|
||||
private ImageView chevron;
|
||||
private LayoutFiletreeItemBinding binding;
|
||||
|
||||
public FileTreeViewHolder(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
private Context context;
|
||||
private ImageView chevron;
|
||||
private LayoutFiletreeItemBinding binding;
|
||||
|
||||
@Override
|
||||
public View createNodeView(TreeNode node, File file) {
|
||||
binding = LayoutFiletreeItemBinding.inflate(LayoutInflater.from(context));
|
||||
int dp15 = context instanceof StudioActivity ? ((StudioActivity) context).dpToPx(15) : (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics()));
|
||||
int icon = 0;
|
||||
if (file.isDirectory())
|
||||
icon = R.drawable.ic_folder;
|
||||
else if (file.getName().endsWith(".java"))
|
||||
icon = R.drawable.ic_language_java;
|
||||
else if (file.getName().endsWith(".kt"))
|
||||
icon = R.drawable.ic_language_kotlin;
|
||||
else if (file.getName().endsWith(".xml"))
|
||||
icon = R.drawable.ic_language_xml;
|
||||
else if (file.getName().endsWith(".gradle"))
|
||||
icon = R.drawable.ic_language_gradle;
|
||||
else if (file.getName().endsWith(".json"))
|
||||
icon = R.drawable.ic_language_json;
|
||||
else
|
||||
icon = R.drawable.ic_file_unknown;
|
||||
public FileTreeViewHolder(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
final boolean isGradle = file.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath());
|
||||
chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
|
||||
binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
|
||||
binding.filetreeIcon.setImageResource(icon);
|
||||
binding.getRoot().setPadding(binding.getRoot().getPaddingLeft() + (dp15 * (node.getLevel() - 1)), binding.getRoot().getPaddingTop(), binding.getRoot().getPaddingRight(), binding.getRoot().getPaddingBottom());
|
||||
chevron.setVisibility(file.isFile() ? View.INVISIBLE : View.VISIBLE);
|
||||
chevron.setImageResource(node.isExpanded() ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
|
||||
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
|
||||
binding.filetreeIcon.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public void setLoading() {
|
||||
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
@Override
|
||||
public View createNodeView(TreeNode node, File file) {
|
||||
binding = LayoutFiletreeItemBinding.inflate(LayoutInflater.from(context));
|
||||
int dp15 =
|
||||
context instanceof StudioActivity
|
||||
? ((StudioActivity) context).dpToPx(15)
|
||||
: (int)
|
||||
(TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
15,
|
||||
context.getResources().getDisplayMetrics()));
|
||||
int icon = 0;
|
||||
if (file.isDirectory()) icon = R.drawable.ic_folder;
|
||||
else if (file.getName().endsWith(".java")) icon = R.drawable.ic_language_java;
|
||||
else if (file.getName().endsWith(".kt")) icon = R.drawable.ic_language_kotlin;
|
||||
else if (file.getName().endsWith(".xml")) icon = R.drawable.ic_language_xml;
|
||||
else if (file.getName().endsWith(".gradle")) icon = R.drawable.ic_language_gradle;
|
||||
else if (file.getName().endsWith(".json")) icon = R.drawable.ic_language_json;
|
||||
else icon = R.drawable.ic_file_unknown;
|
||||
|
||||
public void updateChevron(boolean isExpanded) {
|
||||
chevron.setImageResource(isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
|
||||
chevron.getDrawable().setColorFilter(ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
final boolean isGradle =
|
||||
file.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath());
|
||||
chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
|
||||
binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
|
||||
binding.filetreeIcon.setImageResource(icon);
|
||||
binding.getRoot()
|
||||
.setPadding(
|
||||
binding.getRoot().getPaddingLeft() + (dp15 * (node.getLevel() - 1)),
|
||||
binding.getRoot().getPaddingTop(),
|
||||
binding.getRoot().getPaddingRight(),
|
||||
binding.getRoot().getPaddingBottom());
|
||||
chevron.setVisibility(file.isFile() ? View.INVISIBLE : View.VISIBLE);
|
||||
chevron.setImageResource(
|
||||
node.isExpanded() ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
|
||||
chevron.getDrawable()
|
||||
.setColorFilter(
|
||||
ContextCompat.getColor(context, R.color.secondaryLightColor),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
binding.filetreeIcon
|
||||
.getDrawable()
|
||||
.setColorFilter(
|
||||
ContextCompat.getColor(context, R.color.secondaryLightColor),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public void setLoading() {
|
||||
chevron.getDrawable()
|
||||
.setColorFilter(
|
||||
ContextCompat.getColor(context, R.color.secondaryLightColor),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
|
||||
public void updateChevron(boolean isExpanded) {
|
||||
chevron.setImageResource(
|
||||
isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
|
||||
chevron.getDrawable()
|
||||
.setColorFilter(
|
||||
ContextCompat.getColor(context, R.color.secondaryLightColor),
|
||||
PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,97 +1,104 @@
|
||||
/************************************************************************************
|
||||
* 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.app;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
public abstract class StudioActivity extends AppCompatActivity {
|
||||
private boolean toRequestStorage = true;
|
||||
private boolean toRequestStorage = true;
|
||||
|
||||
public static final String TAG = "StudioActivity";
|
||||
public static final int REQCODE_STORAGE = 1009;
|
||||
|
||||
public static final String TAG = "StudioActivity";
|
||||
public static final int REQCODE_STORAGE = 1009;
|
||||
|
||||
protected static Logger LOG = Logger.instance("StudioActivity");
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(bindLayout());
|
||||
onSetContentView();
|
||||
|
||||
if (toRequestStorage && !isStoragePermissionGranted()) {
|
||||
requestStorage();
|
||||
}
|
||||
|
||||
if (isStoragePermissionGranted()) {
|
||||
onStorageAlreadyGranted();
|
||||
} else {
|
||||
onStorageDenied ();
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(bindLayout());
|
||||
onSetContentView();
|
||||
|
||||
if (toRequestStorage && !isStoragePermissionGranted()) {
|
||||
requestStorage();
|
||||
}
|
||||
|
||||
if (isStoragePermissionGranted()) {
|
||||
onStorageAlreadyGranted();
|
||||
} else {
|
||||
onStorageDenied();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStoragePermissionGranted() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
&& ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
public boolean isStoragePermissionGranted() {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED
|
||||
&& ContextCompat.checkSelfPermission(
|
||||
this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
protected void doNotRequestStorage() {
|
||||
this.toRequestStorage = false;
|
||||
}
|
||||
protected void doNotRequestStorage() {
|
||||
this.toRequestStorage = false;
|
||||
}
|
||||
|
||||
protected void requestStorage() {
|
||||
if (isStoragePermissionGranted()) { onStorageGranted(); return; }
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQCODE_STORAGE);
|
||||
}
|
||||
|
||||
public void loadFrament(Fragment frag, View view) {
|
||||
loadFragment(frag, view.getId());
|
||||
}
|
||||
|
||||
public void loadFragment(Fragment fragment, int id) {
|
||||
protected void requestStorage() {
|
||||
if (isStoragePermissionGranted()) {
|
||||
onStorageGranted();
|
||||
return;
|
||||
}
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[] {
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
},
|
||||
REQCODE_STORAGE);
|
||||
}
|
||||
|
||||
public void loadFrament(Fragment frag, View view) {
|
||||
loadFragment(frag, view.getId());
|
||||
}
|
||||
|
||||
public void loadFragment(Fragment fragment, int id) {
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
transaction.replace(id, fragment);
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
public StudioApp getApp() {
|
||||
return (StudioApp) getApplication();
|
||||
public StudioApp getApp() {
|
||||
return (StudioApp) getApplication();
|
||||
}
|
||||
|
||||
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
|
||||
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
getTheme().resolveAttribute(attrRes, typedValue, true);
|
||||
return typedValue;
|
||||
@@ -109,24 +116,31 @@ public abstract class StudioActivity extends AppCompatActivity {
|
||||
return getTypedValueForAttr(R.attr.colorAccent).data;
|
||||
}
|
||||
|
||||
public int dpToPx(int dp) {
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == REQCODE_STORAGE) {
|
||||
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
||||
onStorageGranted();
|
||||
else onStorageDenied();
|
||||
}
|
||||
}
|
||||
public int dpToPx(int dp) {
|
||||
return (int)
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
protected void onStorageAlreadyGranted() {}
|
||||
protected void onStorageGranted() {}
|
||||
protected void onStorageDenied() {}
|
||||
protected void onSetContentView() {}
|
||||
|
||||
protected abstract View bindLayout();
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == REQCODE_STORAGE) {
|
||||
if (grantResults != null
|
||||
&& grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) onStorageGranted();
|
||||
else onStorageDenied();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onStorageAlreadyGranted() {}
|
||||
|
||||
protected void onStorageGranted() {}
|
||||
|
||||
protected void onStorageDenied() {}
|
||||
|
||||
protected void onSetContentView() {}
|
||||
|
||||
protected abstract View bindLayout();
|
||||
}
|
||||
|
||||
@@ -43,111 +43,114 @@ import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
public class StudioApp extends BaseApplication {
|
||||
|
||||
private static final Logger LOG = Logger.instance ("StudioApp");
|
||||
|
||||
private static final Logger LOG = Logger.instance("StudioApp");
|
||||
private static StudioApp instance;
|
||||
private static SDKInfo sdkInfo;
|
||||
private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer ();
|
||||
private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer ();
|
||||
private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer();
|
||||
private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer();
|
||||
private IResourceTable mResTable;
|
||||
private ILayoutInflater mLayoutInflater;
|
||||
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||
|
||||
public static StudioApp getInstance () {
|
||||
|
||||
public static StudioApp getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate () {
|
||||
public void onCreate() {
|
||||
instance = this;
|
||||
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler ();
|
||||
Thread.setDefaultUncaughtExceptionHandler (this::handleCrash);
|
||||
super.onCreate ();
|
||||
|
||||
FirebaseMessaging.getInstance ().subscribeToTopic (MessagingService.TOPIC_UPDATE);
|
||||
FirebaseMessaging.getInstance ().subscribeToTopic (MessagingService.TOPIC_DEV_MSGS);
|
||||
|
||||
initializeApiInformation ();
|
||||
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
|
||||
super.onCreate();
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
|
||||
|
||||
initializeApiInformation();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public ILanguageServer getJavaLanguageServer () {
|
||||
public ILanguageServer getJavaLanguageServer() {
|
||||
return mJavaLanguageServer;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public ILanguageServer getXMLLanguageServer () {
|
||||
public ILanguageServer getXMLLanguageServer() {
|
||||
return mXMLLanguageServer;
|
||||
}
|
||||
|
||||
private void handleCrash (Thread thread, Throwable th) {
|
||||
writeException (th);
|
||||
|
||||
|
||||
private void handleCrash(Thread thread, Throwable th) {
|
||||
writeException(th);
|
||||
|
||||
try {
|
||||
final var intent = new Intent ();
|
||||
intent.setAction (CrashHandlerActivity.REPORT_ACTION);
|
||||
intent.putExtra (CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace (th));
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity (intent);
|
||||
|
||||
final var intent = new Intent();
|
||||
intent.setAction(CrashHandlerActivity.REPORT_ACTION);
|
||||
intent.putExtra(CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace(th));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
|
||||
if (this.uncaughtExceptionHandler != null) {
|
||||
this.uncaughtExceptionHandler.uncaughtException (thread, th);
|
||||
this.uncaughtExceptionHandler.uncaughtException(thread, th);
|
||||
}
|
||||
System.exit (1);
|
||||
System.exit(1);
|
||||
} catch (Throwable error) {
|
||||
LOG.error ("Unable to show crash handler activity", error);
|
||||
LOG.error("Unable to show crash handler activity", error);
|
||||
}
|
||||
}
|
||||
|
||||
public void createInflater (LayoutInflaterConfiguration config) {
|
||||
this.mLayoutInflater = ILayoutInflater.newInstance (config);
|
||||
|
||||
public void createInflater(LayoutInflaterConfiguration config) {
|
||||
this.mLayoutInflater = ILayoutInflater.newInstance(config);
|
||||
}
|
||||
|
||||
public LayoutInflaterConfiguration createInflaterConfig (ILayoutInflater.ContextProvider contextProvider, Set<File> resDirs) {
|
||||
return new LayoutInflaterConfiguration.Builder ()
|
||||
.setAttrInfo (this.attrInfo ())
|
||||
.setWidgetInfo (this.widgetInfo ())
|
||||
.setResourceFinder (mResTable == null ? mResTable = new ProjectResourceTable () : mResTable)
|
||||
.setResourceDirectories (resDirs)
|
||||
.setContextProvider (contextProvider)
|
||||
.create ();
|
||||
|
||||
public LayoutInflaterConfiguration createInflaterConfig(
|
||||
ILayoutInflater.ContextProvider contextProvider, Set<File> resDirs) {
|
||||
return new LayoutInflaterConfiguration.Builder()
|
||||
.setAttrInfo(this.attrInfo())
|
||||
.setWidgetInfo(this.widgetInfo())
|
||||
.setResourceFinder(
|
||||
mResTable == null ? mResTable = new ProjectResourceTable() : mResTable)
|
||||
.setResourceDirectories(resDirs)
|
||||
.setContextProvider(contextProvider)
|
||||
.create();
|
||||
}
|
||||
|
||||
public ILayoutInflater getLayoutInflater () {
|
||||
|
||||
public ILayoutInflater getLayoutInflater() {
|
||||
return mLayoutInflater;
|
||||
}
|
||||
|
||||
public IResourceTable getResourceTable () {
|
||||
|
||||
public IResourceTable getResourceTable() {
|
||||
return this.mResTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads API version information from api-versions.xml
|
||||
*/
|
||||
public void initializeApiInformation () {
|
||||
new Thread (() -> {
|
||||
try {
|
||||
sdkInfo = new SDKInfo (StudioApp.this);
|
||||
((XMLLanguageServer) mXMLLanguageServer).setupSDK (sdkInfo);
|
||||
} catch (Throwable th) {
|
||||
LOG.error (getString (R.string.err_init_sdkinfo), th);
|
||||
}
|
||||
}, "SDKInfo Loader").start ();
|
||||
|
||||
/** Reads API version information from api-versions.xml */
|
||||
public void initializeApiInformation() {
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
sdkInfo = new SDKInfo(StudioApp.this);
|
||||
((XMLLanguageServer) mXMLLanguageServer).setupSDK(sdkInfo);
|
||||
} catch (Throwable th) {
|
||||
LOG.error(getString(R.string.err_init_sdkinfo), th);
|
||||
}
|
||||
},
|
||||
"SDKInfo Loader")
|
||||
.start();
|
||||
}
|
||||
|
||||
public void stopAllDaemons () {
|
||||
newShell (null).bgAppend ("gradle --stop");
|
||||
|
||||
public void stopAllDaemons() {
|
||||
newShell(null).bgAppend("gradle --stop");
|
||||
}
|
||||
|
||||
public ApiInfo apiInfo () {
|
||||
return sdkInfo.getApiInfo ();
|
||||
|
||||
public ApiInfo apiInfo() {
|
||||
return sdkInfo.getApiInfo();
|
||||
}
|
||||
|
||||
public AttrInfo attrInfo () {
|
||||
return sdkInfo.getAttrInfo ();
|
||||
|
||||
public AttrInfo attrInfo() {
|
||||
return sdkInfo.getAttrInfo();
|
||||
}
|
||||
|
||||
public WidgetInfo widgetInfo () {
|
||||
return sdkInfo.getWidgetInfo ();
|
||||
|
||||
public WidgetInfo widgetInfo() {
|
||||
return sdkInfo.getWidgetInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.itsaky.androidide.app.StudioActivity;
|
||||
|
||||
public class BaseFragment extends Fragment
|
||||
{
|
||||
protected StudioActivity getStudioActivity() {
|
||||
return (StudioActivity) getActivity();
|
||||
}
|
||||
public class BaseFragment extends Fragment {
|
||||
protected StudioActivity getStudioActivity() {
|
||||
return (StudioActivity) getActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,112 +38,119 @@ import com.itsaky.androidide.app.BaseApplication;
|
||||
import com.itsaky.androidide.databinding.LayoutCrashReportBinding;
|
||||
|
||||
public class CrashReportFragment extends Fragment {
|
||||
|
||||
|
||||
private LayoutCrashReportBinding binding;
|
||||
private boolean closeAppOnClick = true;
|
||||
|
||||
|
||||
public static final String KEY_TITLE = "crash_title";
|
||||
public static final String KEY_MESSAGE = "crash_message";
|
||||
public static final String KEY_TRACE = "crash_trace";
|
||||
|
||||
|
||||
@NonNull
|
||||
public static CrashReportFragment newInstance (@NonNull final String trace) {
|
||||
return CrashReportFragment.newInstance (null, null, trace);
|
||||
public static CrashReportFragment newInstance(@NonNull final String trace) {
|
||||
return CrashReportFragment.newInstance(null, null, trace);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public static CrashReportFragment newInstance (@Nullable final String title, @Nullable final String message, @NonNull final String trace) {
|
||||
final var frag = new CrashReportFragment ();
|
||||
final var args = new Bundle ();
|
||||
|
||||
args.putString (KEY_TRACE, trace);
|
||||
|
||||
public static CrashReportFragment newInstance(
|
||||
@Nullable final String title,
|
||||
@Nullable final String message,
|
||||
@NonNull final String trace) {
|
||||
final var frag = new CrashReportFragment();
|
||||
final var args = new Bundle();
|
||||
|
||||
args.putString(KEY_TRACE, trace);
|
||||
|
||||
if (title != null) {
|
||||
args.putString (KEY_TITLE, title);
|
||||
args.putString(KEY_TITLE, title);
|
||||
}
|
||||
|
||||
|
||||
if (message != null) {
|
||||
args.putString (KEY_MESSAGE, message);
|
||||
args.putString(KEY_MESSAGE, message);
|
||||
}
|
||||
|
||||
frag.setArguments (args);
|
||||
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
public CrashReportFragment setCloseAppOnClick (boolean closeAppOnClick) {
|
||||
|
||||
public CrashReportFragment setCloseAppOnClick(boolean closeAppOnClick) {
|
||||
this.closeAppOnClick = closeAppOnClick;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return (binding = LayoutCrashReportBinding.inflate (inflater, container, false)).getRoot ();
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return (binding = LayoutCrashReportBinding.inflate(inflater, container, false)).getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
final var args = requireArguments ();
|
||||
var title = getString (R.string.msg_ide_crashed);
|
||||
var message = getString (R.string.msg_report_crash);
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final var args = requireArguments();
|
||||
var title = getString(R.string.msg_ide_crashed);
|
||||
var message = getString(R.string.msg_report_crash);
|
||||
var trace = "";
|
||||
if (args.containsKey (KEY_TITLE)) {
|
||||
title = args.getString (KEY_TITLE);
|
||||
if (args.containsKey(KEY_TITLE)) {
|
||||
title = args.getString(KEY_TITLE);
|
||||
}
|
||||
|
||||
if (args.containsKey (KEY_MESSAGE)) {
|
||||
message = args.getString (KEY_MESSAGE);
|
||||
|
||||
if (args.containsKey(KEY_MESSAGE)) {
|
||||
message = args.getString(KEY_MESSAGE);
|
||||
}
|
||||
|
||||
if (args.containsKey (KEY_TRACE)) {
|
||||
trace = args.getString (KEY_TRACE);
|
||||
trace = buildReportText (trace);
|
||||
|
||||
if (args.containsKey(KEY_TRACE)) {
|
||||
trace = args.getString(KEY_TRACE);
|
||||
trace = buildReportText(trace);
|
||||
} else {
|
||||
trace = "No stack strace was provided for the report";
|
||||
}
|
||||
|
||||
binding.crashTitle.setText (title);
|
||||
binding.crashSubtitle.setText (message);
|
||||
binding.logText.setText (trace);
|
||||
|
||||
|
||||
binding.crashTitle.setText(title);
|
||||
binding.crashSubtitle.setText(message);
|
||||
binding.logText.setText(trace);
|
||||
|
||||
final var report = trace;
|
||||
binding.closeButton.setOnClickListener (v -> {
|
||||
if (closeAppOnClick) {
|
||||
requireActivity ().finishAffinity ();
|
||||
} else {
|
||||
requireActivity ().finish ();
|
||||
}
|
||||
});
|
||||
binding.reportButton.setOnClickListener (v -> reportTrace (report));
|
||||
binding.closeButton.setOnClickListener(
|
||||
v -> {
|
||||
if (closeAppOnClick) {
|
||||
requireActivity().finishAffinity();
|
||||
} else {
|
||||
requireActivity().finish();
|
||||
}
|
||||
});
|
||||
binding.reportButton.setOnClickListener(v -> reportTrace(report));
|
||||
}
|
||||
|
||||
private void reportTrace (String report) {
|
||||
ClipboardUtils.copyText ("AndroidIDE CrashLog", report);
|
||||
|
||||
final var url = BaseApplication.GITHUB_URL.concat ("/issues");
|
||||
final var intent = new Intent ();
|
||||
|
||||
private void reportTrace(String report) {
|
||||
ClipboardUtils.copyText("AndroidIDE CrashLog", report);
|
||||
|
||||
final var url = BaseApplication.GITHUB_URL.concat("/issues");
|
||||
final var intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setData (Uri.parse (url));
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity (intent);
|
||||
intent.setData(Uri.parse(url));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private String buildReportText (String trace) {
|
||||
return "AndroidIDE crash report\n" +
|
||||
"Manufacturer: " +
|
||||
getManufacturer () +
|
||||
"\n" +
|
||||
"Device: " +
|
||||
getModel () +
|
||||
"\n" +
|
||||
"App version: " +
|
||||
getAppVersionName () +
|
||||
" (" +
|
||||
getAppVersionCode () +
|
||||
")\n\n Stacktrace: \n" +
|
||||
trace;
|
||||
private String buildReportText(String trace) {
|
||||
return "AndroidIDE crash report\n"
|
||||
+ "Manufacturer: "
|
||||
+ getManufacturer()
|
||||
+ "\n"
|
||||
+ "Device: "
|
||||
+ getModel()
|
||||
+ "\n"
|
||||
+ "App version: "
|
||||
+ getAppVersionName()
|
||||
+ " ("
|
||||
+ getAppVersionCode()
|
||||
+ ")\n\n Stacktrace: \n"
|
||||
+ trace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,49 +35,55 @@ import com.itsaky.androidide.views.EmptyView;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DiagnosticsListFragment extends Fragment {
|
||||
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
|
||||
private DiagnosticsAdapter unsavedAdapter;
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup parent,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout(inflater.getContext());
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
|
||||
|
||||
this.list = new RecyclerView(inflater.getContext());
|
||||
this.emptyView = new EmptyView(inflater.getContext());
|
||||
|
||||
container.addView(list, new LinearLayout.LayoutParams(-1, -1));
|
||||
container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new DiagnosticsAdapter (new ArrayList<> (), null));
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager(new LinearLayoutManager(list.getContext()));
|
||||
list.setAdapter(
|
||||
unsavedAdapter != null
|
||||
? unsavedAdapter
|
||||
: new DiagnosticsAdapter(new ArrayList<>(), null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull DiagnosticsAdapter adapter) {
|
||||
|
||||
public void setAdapter(@NonNull DiagnosticsAdapter adapter) {
|
||||
if (list != null) {
|
||||
list.setAdapter (adapter);
|
||||
list.setAdapter(adapter);
|
||||
} else {
|
||||
unsavedAdapter = adapter;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
|
||||
public void handleResultVisibility(boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -53,209 +52,228 @@ import com.unnamed.b.atv.view.AndroidTreeView;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FileTreeFragment extends BottomSheetDialogFragment implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
|
||||
|
||||
public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
|
||||
|
||||
private LayoutEditorFileTreeBinding binding;
|
||||
private AndroidTreeView mFileTreeView;
|
||||
private FileActionListener mFileActionListener;
|
||||
private AndroidProject mProject;
|
||||
private TreeNode mRoot;
|
||||
|
||||
public FileTreeFragment () {
|
||||
}
|
||||
|
||||
public FileTreeFragment setFileActionListener (FileActionListener listener) {
|
||||
|
||||
public FileTreeFragment() {}
|
||||
|
||||
public FileTreeFragment setFileActionListener(FileActionListener listener) {
|
||||
this.mFileActionListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public static FileTreeFragment newInstance (AndroidProject project) {
|
||||
Bundle bundle = new Bundle ();
|
||||
bundle.putParcelable ("project", project);
|
||||
FileTreeFragment frag = new FileTreeFragment ();
|
||||
frag.setArguments (bundle);
|
||||
public static FileTreeFragment newInstance(AndroidProject project) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("project", project);
|
||||
FileTreeFragment frag = new FileTreeFragment();
|
||||
frag.setArguments(bundle);
|
||||
return frag;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutEditorFileTreeBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutEditorFileTreeBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
mProject = requireArguments ().getParcelable ("project");
|
||||
|
||||
listProjectFiles ();
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mProject = requireArguments().getParcelable("project");
|
||||
|
||||
listProjectFiles();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick (TreeNode node, Object p2) {
|
||||
public void onClick(TreeNode node, Object p2) {
|
||||
final File f = (File) p2;
|
||||
if (!f.exists ()) {
|
||||
if (!f.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (f.isFile ()) {
|
||||
if (f.getName ().endsWith (".apk")) {
|
||||
final var intent = IntentUtils.getInstallAppIntent (f);
|
||||
|
||||
if (f.isFile()) {
|
||||
if (f.getName().endsWith(".apk")) {
|
||||
final var intent = IntentUtils.getInstallAppIntent(f);
|
||||
if (intent != null) {
|
||||
requireContext ().startActivity (intent);
|
||||
requireContext().startActivity(intent);
|
||||
} else {
|
||||
StudioApp.getInstance ().toast (getString(R.string.msg_apk_install_intent_failed), Toaster.Type.ERROR);
|
||||
StudioApp.getInstance()
|
||||
.toast(
|
||||
getString(R.string.msg_apk_install_intent_failed),
|
||||
Toaster.Type.ERROR);
|
||||
}
|
||||
} else if (mFileActionListener != null && (FileUtils.isUtf8 (f) || f.length () == 0)) {
|
||||
mFileActionListener.openFile (f);
|
||||
} else if (mFileActionListener != null && (FileUtils.isUtf8(f) || f.length() == 0)) {
|
||||
mFileActionListener.openFile(f);
|
||||
}
|
||||
} else if (f.isDirectory () && f.exists ()) {
|
||||
if (node.isExpanded ()) {
|
||||
collapseNode (node);
|
||||
} else if (f.getAbsolutePath ().equals (Environment.GRADLE_USER_HOME.getAbsolutePath ()) && !node.isExpanded ()) {
|
||||
expandNode (node);
|
||||
} else if (f.isDirectory() && f.exists()) {
|
||||
if (node.isExpanded()) {
|
||||
collapseNode(node);
|
||||
} else if (f.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath())
|
||||
&& !node.isExpanded()) {
|
||||
expandNode(node);
|
||||
} else {
|
||||
setLoading (node);
|
||||
listNode (node);
|
||||
setLoading(node);
|
||||
listNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void listNode (@NonNull TreeNode node) {
|
||||
node.getChildren ().clear ();
|
||||
node.setExpanded (false);
|
||||
new TaskExecutor ().executeAsync (() -> {
|
||||
getNodeFromFiles (node.getValue ().listFiles (/* new FileTreeCallable.HiddenFilesFilter() */), node);
|
||||
TreeNode temp = node;
|
||||
while (temp.size () == 1) {
|
||||
temp = temp.childAt (0);
|
||||
if (!temp.getValue ().isDirectory ()) {
|
||||
break;
|
||||
}
|
||||
getNodeFromFiles (temp.getValue ().listFiles (/* new FileTreeCallable.HiddenFilesFilter() */), temp);
|
||||
temp.setExpanded (true);
|
||||
}
|
||||
return null;
|
||||
}, __ -> {
|
||||
updateChevron (node);
|
||||
expandNode (node);
|
||||
});
|
||||
|
||||
private void listNode(@NonNull TreeNode node) {
|
||||
node.getChildren().clear();
|
||||
node.setExpanded(false);
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
() -> {
|
||||
getNodeFromFiles(
|
||||
node.getValue()
|
||||
.listFiles(
|
||||
/* new FileTreeCallable.HiddenFilesFilter() */ ),
|
||||
node);
|
||||
TreeNode temp = node;
|
||||
while (temp.size() == 1) {
|
||||
temp = temp.childAt(0);
|
||||
if (!temp.getValue().isDirectory()) {
|
||||
break;
|
||||
}
|
||||
getNodeFromFiles(
|
||||
temp.getValue()
|
||||
.listFiles(
|
||||
/* new FileTreeCallable.HiddenFilesFilter() */ ),
|
||||
temp);
|
||||
temp.setExpanded(true);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
__ -> {
|
||||
updateChevron(node);
|
||||
expandNode(node);
|
||||
});
|
||||
}
|
||||
|
||||
private void getNodeFromFiles (File[] files, TreeNode parent) {
|
||||
Arrays.sort (files, new FileTreeCallable.SortFileName ());
|
||||
Arrays.sort (files, new FileTreeCallable.SortFolder ());
|
||||
|
||||
private void getNodeFromFiles(File[] files, TreeNode parent) {
|
||||
Arrays.sort(files, new FileTreeCallable.SortFileName());
|
||||
Arrays.sort(files, new FileTreeCallable.SortFolder());
|
||||
for (File file : files) {
|
||||
TreeNode node = new TreeNode (file);
|
||||
node.setViewHolder (new FileTreeViewHolder (getContext ()));
|
||||
parent.addChild (node);
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(getContext()));
|
||||
parent.addChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading (@NonNull TreeNode node) {
|
||||
if (node.getViewHolder () instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder ()).setLoading ();
|
||||
|
||||
private void setLoading(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).setLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChevron (@NonNull TreeNode node) {
|
||||
if (node.getViewHolder () instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder ()).updateChevron (!node.isExpanded ());
|
||||
|
||||
private void updateChevron(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onLongClick (TreeNode node, Object value) {
|
||||
public boolean onLongClick(TreeNode node, Object value) {
|
||||
if (mFileActionListener != null) {
|
||||
mFileActionListener.showFileOptions ((File) value, node);
|
||||
mFileActionListener.showFileOptions((File) value, node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void expandNode (TreeNode node) {
|
||||
|
||||
public void expandNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), new ChangeBounds ());
|
||||
mFileTreeView.expandNode (node);
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.expandNode(node);
|
||||
}
|
||||
|
||||
public void collapseNode (TreeNode node) {
|
||||
|
||||
public void collapseNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), new ChangeBounds ());
|
||||
mFileTreeView.collapseNode (node);
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.collapseNode(node);
|
||||
}
|
||||
|
||||
|
||||
public void listProjectFiles () {
|
||||
|
||||
public void listProjectFiles() {
|
||||
if (mProject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final File gradleProps = Environment.GRADLE_PROPS;
|
||||
final File gradleHome = Environment.GRADLE_USER_HOME;
|
||||
File projectDir = new File (mProject.getProjectPath ());
|
||||
mRoot = TreeNode.root (projectDir);
|
||||
if (gradleHome.exists () && gradleHome.isDirectory ()) {
|
||||
if (!gradleProps.exists ()) {
|
||||
FileIOUtils.writeFileFromString (gradleProps, Environment.SAMPLE_GRADLE_PROP_CONTENTS);
|
||||
File projectDir = new File(mProject.getProjectPath());
|
||||
mRoot = TreeNode.root(projectDir);
|
||||
if (gradleHome.exists() && gradleHome.isDirectory()) {
|
||||
if (!gradleProps.exists()) {
|
||||
FileIOUtils.writeFileFromString(
|
||||
gradleProps, Environment.SAMPLE_GRADLE_PROP_CONTENTS);
|
||||
}
|
||||
TreeNode home = new TreeNode (gradleHome);
|
||||
home.setViewHolder (new FileTreeViewHolder (getContext ()));
|
||||
TreeNode prop = new TreeNode (gradleProps);
|
||||
prop.setViewHolder (new FileTreeViewHolder (getContext ()));
|
||||
home.addChild (prop);
|
||||
mRoot.addChild (home);
|
||||
TreeNode home = new TreeNode(gradleHome);
|
||||
home.setViewHolder(new FileTreeViewHolder(getContext()));
|
||||
TreeNode prop = new TreeNode(gradleProps);
|
||||
prop.setViewHolder(new FileTreeViewHolder(getContext()));
|
||||
home.addChild(prop);
|
||||
mRoot.addChild(home);
|
||||
}
|
||||
mRoot.setViewHolder (new FileTreeViewHolder (getContext ()));
|
||||
|
||||
getScrollView ().setVisibility (View.GONE);
|
||||
getLoadingProgress ().setVisibility (View.VISIBLE);
|
||||
new TaskExecutor ().executeAsync (new FileTreeCallable (getContext (), mRoot, projectDir), (result) -> {
|
||||
getScrollView ().setVisibility (View.VISIBLE);
|
||||
getLoadingProgress ().setVisibility (View.GONE);
|
||||
AndroidTreeView tree = createTreeView (mRoot);
|
||||
if (tree != null) {
|
||||
tree.setUseAutoToggle (false);
|
||||
tree.setDefaultNodeClickListener (FileTreeFragment.this);
|
||||
tree.setDefaultNodeLongClickListener (FileTreeFragment.this);
|
||||
getScrollView ().removeAllViews ();
|
||||
getScrollView ().addView (tree.getView ());
|
||||
}
|
||||
});
|
||||
mRoot.setViewHolder(new FileTreeViewHolder(getContext()));
|
||||
|
||||
getScrollView().setVisibility(View.GONE);
|
||||
getLoadingProgress().setVisibility(View.VISIBLE);
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
new FileTreeCallable(getContext(), mRoot, projectDir),
|
||||
(result) -> {
|
||||
getScrollView().setVisibility(View.VISIBLE);
|
||||
getLoadingProgress().setVisibility(View.GONE);
|
||||
AndroidTreeView tree = createTreeView(mRoot);
|
||||
if (tree != null) {
|
||||
tree.setUseAutoToggle(false);
|
||||
tree.setDefaultNodeClickListener(FileTreeFragment.this);
|
||||
tree.setDefaultNodeLongClickListener(FileTreeFragment.this);
|
||||
getScrollView().removeAllViews();
|
||||
getScrollView().addView(tree.getView());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public AndroidTreeView createTreeView (TreeNode node) {
|
||||
|
||||
public AndroidTreeView createTreeView(TreeNode node) {
|
||||
Context ctx = null;
|
||||
if (getActivity () != null) {
|
||||
ctx = getActivity ();
|
||||
} else if (getContext () != null) {
|
||||
ctx = getContext ();
|
||||
if (getActivity() != null) {
|
||||
ctx = getActivity();
|
||||
} else if (getContext() != null) {
|
||||
ctx = getContext();
|
||||
}
|
||||
|
||||
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mFileTreeView = new AndroidTreeView (ctx, node, R.drawable.bg_ripple);
|
||||
|
||||
return mFileTreeView = new AndroidTreeView(ctx, node, R.drawable.bg_ripple);
|
||||
}
|
||||
|
||||
public HorizontalScrollView getScrollView () {
|
||||
|
||||
public HorizontalScrollView getScrollView() {
|
||||
return binding.filetreeHorizontalScrollView;
|
||||
}
|
||||
|
||||
public ProgressBar getLoadingProgress () {
|
||||
|
||||
public ProgressBar getLoadingProgress() {
|
||||
return binding.fileTreeLoadingProgress;
|
||||
}
|
||||
|
||||
|
||||
public interface FileActionListener {
|
||||
CodeEditorView openFile (File file);
|
||||
|
||||
void showFileOptions (File file, TreeNode node);
|
||||
CodeEditorView openFile(File file);
|
||||
|
||||
void showFileOptions(File file, TreeNode node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ package com.itsaky.androidide.fragments;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class IDELogFragment extends SimpleOutputFragment {
|
||||
|
||||
|
||||
private final Logger.LogListener listener = this::log;
|
||||
|
||||
public IDELogFragment () {
|
||||
Logger.addLogListener (listener);
|
||||
|
||||
public IDELogFragment() {
|
||||
Logger.addLogListener(listener);
|
||||
}
|
||||
|
||||
private void log (int priority, String tag, String message) {
|
||||
appendOutput (String.format ("%s: %s", tag, message));
|
||||
|
||||
private void log(int priority, String tag, String message) {
|
||||
appendOutput(String.format("%s: %s", tag, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,51 +27,51 @@ import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
|
||||
import io.github.rosemoe.sora.lang.Language;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.rosemoe.sora.lang.Language;
|
||||
|
||||
public class LogViewFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final Language language = getLanguage ();
|
||||
private final List<LogLine> unsavedLines = new ArrayList<> ();
|
||||
|
||||
|
||||
private final Language language = getLanguage();
|
||||
private final List<LogLine> unsavedLines = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
Objects.requireNonNull (getEditor ()).setEditorLanguage (language);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
Objects.requireNonNull(getEditor()).setEditorLanguage(language);
|
||||
|
||||
if (!unsavedLines.isEmpty()) {
|
||||
for (var line : unsavedLines) {
|
||||
applyToLanguage (line);
|
||||
getEditor ().append (line.toString ().trim () + "\n");
|
||||
applyToLanguage(line);
|
||||
getEditor().append(line.toString().trim() + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
unsavedLines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendLog (LogLine line) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (line);
|
||||
|
||||
public void appendLog(LogLine line) {
|
||||
if (getEditor() == null) {
|
||||
unsavedLines.add(line);
|
||||
return;
|
||||
}
|
||||
|
||||
applyToLanguage (line);
|
||||
|
||||
final var lineString = line.toString ();
|
||||
final var msg = lineString.endsWith ("\n") ? lineString : lineString + "\n";
|
||||
ThreadUtils.runOnUiThread (() -> getEditor ().append (msg));
|
||||
|
||||
applyToLanguage(line);
|
||||
|
||||
final var lineString = line.toString();
|
||||
final var msg = lineString.endsWith("\n") ? lineString : lineString + "\n";
|
||||
ThreadUtils.runOnUiThread(() -> getEditor().append(msg));
|
||||
}
|
||||
|
||||
private void applyToLanguage (LogLine line) {
|
||||
|
||||
private void applyToLanguage(LogLine line) {
|
||||
if (language instanceof LogLanguageImpl) {
|
||||
((LogLanguageImpl)this.language).addLine (line);
|
||||
((LogLanguageImpl) this.language).addLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
protected Language getLanguage () {
|
||||
return new LogLanguageImpl ();
|
||||
|
||||
protected Language getLanguage() {
|
||||
return new LogLanguageImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,34 +34,37 @@ import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import io.github.rosemoe.sora.lang.EmptyLanguage;
|
||||
|
||||
public abstract class NonEditableEditorFragment extends Fragment {
|
||||
|
||||
|
||||
private FragmentNonEditableEditorBinding binding;
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = FragmentNonEditableEditorBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
this.binding = FragmentNonEditableEditorBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
final var editor = binding.getRoot ();
|
||||
editor.setEditable (false);
|
||||
editor.setDividerWidth (0);
|
||||
editor.setEditorLanguage (new EmptyLanguage ());
|
||||
editor.setWordwrap (false);
|
||||
editor.setUndoEnabled (false);
|
||||
editor.setTypefaceLineNumber (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTypefaceText (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTextSize (12);
|
||||
editor.setColorScheme (new SchemeAndroidIDE ());
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final var editor = binding.getRoot();
|
||||
editor.setEditable(false);
|
||||
editor.setDividerWidth(0);
|
||||
editor.setEditorLanguage(new EmptyLanguage());
|
||||
editor.setWordwrap(false);
|
||||
editor.setUndoEnabled(false);
|
||||
editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
|
||||
editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
|
||||
editor.setTextSize(12);
|
||||
editor.setColorScheme(new SchemeAndroidIDE());
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public IDEEditor getEditor () {
|
||||
public IDEEditor getEditor() {
|
||||
if (binding == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,146 +1,148 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
|
||||
import com.itsaky.androidide.EditorActivity;
|
||||
import com.itsaky.androidide.adapters.ProjectsListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectsBinding;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import fr.arnaudguyon.xmltojsonlib.XmlToJson;
|
||||
import com.itsaky.androidide.utils.ProjectFinder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.regex.Pattern;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.itsaky.androidide.utils.ProjectFinder;
|
||||
|
||||
public class ProjectsFragment extends BaseFragment {
|
||||
|
||||
private LayoutProjectsBinding binding;
|
||||
private ArrayList<AndroidProject> mProjects;
|
||||
private boolean isLoading = false;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutProjectsBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
private LayoutProjectsBinding binding;
|
||||
private ArrayList<AndroidProject> mProjects;
|
||||
private boolean isLoading = false;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (getActivity() == null) return;
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutProjectsBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
loadProjects();
|
||||
}
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (getActivity() == null) return;
|
||||
|
||||
public void loadProjects() {
|
||||
if (!isLoading && getStudioActivity().isStoragePermissionGranted()) {
|
||||
getProgressBar().setVisibility(View.VISIBLE);
|
||||
getRecyclerView().setVisibility(View.GONE);
|
||||
setLoading(true);
|
||||
final ProjectsFragment frag = this;
|
||||
new TaskExecutor().executeAsync(new GetProjectsTask(), (result) -> {
|
||||
frag.setLoading(false);
|
||||
loadProjects();
|
||||
}
|
||||
|
||||
if (frag.getProjectsList() == null || frag.getProjectsList().size() == 0) {
|
||||
frag.getProgressBar().setVisibility(View.GONE);
|
||||
frag.getRecyclerView().setVisibility(View.GONE);
|
||||
frag.getNothingLayout().setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
frag.getNothingLayout().setVisibility(View.GONE);
|
||||
frag.getProgressBar().setVisibility(View.GONE);
|
||||
frag.getRecyclerView().setVisibility(View.VISIBLE);
|
||||
frag.getRecyclerView().setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
frag.getRecyclerView().setAdapter(new ProjectsListAdapter(mProjects, (project) -> openProject(project)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
public void loadProjects() {
|
||||
if (!isLoading && getStudioActivity().isStoragePermissionGranted()) {
|
||||
getProgressBar().setVisibility(View.VISIBLE);
|
||||
getRecyclerView().setVisibility(View.GONE);
|
||||
setLoading(true);
|
||||
final ProjectsFragment frag = this;
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
new GetProjectsTask(),
|
||||
(result) -> {
|
||||
frag.setLoading(false);
|
||||
|
||||
private void openProject(AndroidProject project) {
|
||||
startActivity(new Intent(getActivity(), EditorActivity.class)
|
||||
.putExtra("project", project));
|
||||
}
|
||||
if (frag.getProjectsList() == null
|
||||
|| frag.getProjectsList().size() == 0) {
|
||||
frag.getProgressBar().setVisibility(View.GONE);
|
||||
frag.getRecyclerView().setVisibility(View.GONE);
|
||||
frag.getNothingLayout().setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
frag.getNothingLayout().setVisibility(View.GONE);
|
||||
frag.getProgressBar().setVisibility(View.GONE);
|
||||
frag.getRecyclerView().setVisibility(View.VISIBLE);
|
||||
frag.getRecyclerView()
|
||||
.setLayoutManager(
|
||||
new LinearLayoutManager(getActivity()));
|
||||
frag.getRecyclerView()
|
||||
.setAdapter(
|
||||
new ProjectsListAdapter(
|
||||
mProjects,
|
||||
(project) -> openProject(project)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void openProject(AndroidProject project) {
|
||||
startActivity(new Intent(getActivity(), EditorActivity.class).putExtra("project", project));
|
||||
}
|
||||
|
||||
public void getProjects() throws Exception {
|
||||
mProjects = new ArrayList<>();
|
||||
File[] files = StudioApp.getInstance().listProjects();
|
||||
for (File file : files) {
|
||||
AndroidProject project = ProjectFinder.fromFolder(file);
|
||||
|
||||
if (project == null) continue;
|
||||
|
||||
public void getProjects() throws Exception {
|
||||
mProjects = new ArrayList<>();
|
||||
File[] files = StudioApp.getInstance().listProjects();
|
||||
for (File file : files) {
|
||||
AndroidProject project = ProjectFinder.fromFolder(file);
|
||||
|
||||
if(project == null)
|
||||
continue;
|
||||
|
||||
mProjects.add(project);
|
||||
}
|
||||
}
|
||||
|
||||
public ProgressBar getProgressBar() {
|
||||
return binding.projectsProgressBar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RecyclerView getRecyclerView() {
|
||||
return binding.mainProjectsRecyclerView;
|
||||
}
|
||||
public ProgressBar getProgressBar() {
|
||||
return binding.projectsProgressBar;
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
public RecyclerView getRecyclerView() {
|
||||
return binding.mainProjectsRecyclerView;
|
||||
}
|
||||
|
||||
public void setLoading(boolean isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
}
|
||||
public boolean isLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
||||
public ArrayList<AndroidProject> getProjectsList() {
|
||||
return mProjects;
|
||||
}
|
||||
public void setLoading(boolean isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
}
|
||||
|
||||
public View getNothingLayout() {
|
||||
return binding.projectsNothingLayout;
|
||||
}
|
||||
public ArrayList<AndroidProject> getProjectsList() {
|
||||
return mProjects;
|
||||
}
|
||||
|
||||
private class GetProjectsTask implements Callable<List<AndroidProject>> {
|
||||
@Override
|
||||
public List<AndroidProject> call() throws Exception {
|
||||
getProjects();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public View getNothingLayout() {
|
||||
return binding.projectsNothingLayout;
|
||||
}
|
||||
|
||||
private class GetProjectsTask implements Callable<List<AndroidProject>> {
|
||||
@Override
|
||||
public List<AndroidProject> call() throws Exception {
|
||||
getProjects();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,49 +33,53 @@ import com.itsaky.androidide.adapters.SearchListAdapter;
|
||||
import com.itsaky.androidide.views.EmptyView;
|
||||
|
||||
public class SearchResultFragment extends Fragment {
|
||||
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
|
||||
private SearchListAdapter unsavedAdapter;
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup parent,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout(inflater.getContext());
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
|
||||
|
||||
this.list = new RecyclerView(inflater.getContext());
|
||||
this.emptyView = new EmptyView(inflater.getContext());
|
||||
|
||||
container.addView(list, new LinearLayout.LayoutParams(-1, -1));
|
||||
container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter (null, null, null));
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager(new LinearLayoutManager(list.getContext()));
|
||||
list.setAdapter(
|
||||
unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter(null, null, null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull final SearchListAdapter adapter) {
|
||||
if (list == null) {
|
||||
|
||||
public void setAdapter(@NonNull final SearchListAdapter adapter) {
|
||||
if (list == null) {
|
||||
unsavedAdapter = adapter;
|
||||
return;
|
||||
}
|
||||
this.list.setAdapter (adapter);
|
||||
this.list.setAdapter(adapter);
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
|
||||
public void handleResultVisibility(boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,29 +30,31 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SimpleOutputFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final List<String> unsavedLines = new ArrayList<> ();
|
||||
|
||||
|
||||
private final List<String> unsavedLines = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if (!unsavedLines.isEmpty()) {
|
||||
for (String line : unsavedLines) {
|
||||
Objects.requireNonNull (getEditor ()).append (line.trim () + "\n");
|
||||
Objects.requireNonNull(getEditor()).append(line.trim() + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
unsavedLines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendOutput (String output) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (output);
|
||||
|
||||
public void appendOutput(String output) {
|
||||
if (getEditor() == null) {
|
||||
unsavedLines.add(output);
|
||||
return;
|
||||
}
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
final var message = output == null || output.endsWith ("\n") ? output : output + "\n";
|
||||
getEditor ().append (message);
|
||||
});
|
||||
ThreadUtils.runOnUiThread(
|
||||
() -> {
|
||||
final var message =
|
||||
output == null || output.endsWith("\n") ? output : output + "\n";
|
||||
getEditor().append(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+28
-22
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments.preferences;
|
||||
|
||||
@@ -25,28 +24,35 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
|
||||
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
||||
|
||||
protected PreferenceManager mPrefManager;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
v.setBackgroundColor(ContextCompat.getColor(StudioApp.getInstance(), com.itsaky.androidide.R.color.content_background));
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
|
||||
}
|
||||
|
||||
protected PreferenceManager getPrefManager() {
|
||||
return mPrefManager == null ? mPrefManager = new PreferenceManager(getContext()) : mPrefManager;
|
||||
}
|
||||
protected PreferenceManager mPrefManager;
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = super.onCreateView(inflater, container, savedInstanceState);
|
||||
v.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
StudioApp.getInstance(), com.itsaky.androidide.R.color.content_background));
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
|
||||
}
|
||||
|
||||
protected PreferenceManager getPrefManager() {
|
||||
return mPrefManager == null
|
||||
? mPrefManager = new PreferenceManager(getContext())
|
||||
: mPrefManager;
|
||||
}
|
||||
}
|
||||
|
||||
+124
-116
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments.preferences;
|
||||
|
||||
@@ -37,117 +36,126 @@ import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class BuildPreferences extends BasePreferenceFragment implements Preference.OnPreferenceClickListener {
|
||||
|
||||
public static final String KEY_GRADLE_COMMAMDS = "idepref_build_gradleCommands";
|
||||
public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache";
|
||||
|
||||
private ProgressSheet progressSheet;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
super.onCreatePreferences(p1, p2);
|
||||
if(getContext() == null) return;
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
final PreferenceCategory categoryGradle = new PreferenceCategory(getContext());
|
||||
final Preference customCommands = new Preference(getContext());
|
||||
final Preference clearCache = new Preference(getContext());
|
||||
|
||||
screen.addPreference(categoryGradle);
|
||||
|
||||
customCommands.setKey(KEY_GRADLE_COMMAMDS);
|
||||
customCommands.setIcon(R.drawable.ic_bash_commands);
|
||||
customCommands.setTitle(R.string.idepref_build_customgradlecommands_title);
|
||||
customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary);
|
||||
|
||||
clearCache.setKey(KEY_GRADLE_CLEAR_CACHE);
|
||||
clearCache.setIcon(R.drawable.ic_delete);
|
||||
clearCache.setTitle(R.string.idepref_build_clearCache_title);
|
||||
clearCache.setSummary(R.string.idepref_build_clearCache_summary);
|
||||
|
||||
categoryGradle.setTitle(R.string.gradle);
|
||||
categoryGradle.addPreference(customCommands);
|
||||
categoryGradle.addPreference(clearCache);
|
||||
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
customCommands.setOnPreferenceClickListener(this);
|
||||
clearCache.setOnPreferenceClickListener(this);
|
||||
}
|
||||
public class BuildPreferences extends BasePreferenceFragment
|
||||
implements Preference.OnPreferenceClickListener {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p1) {
|
||||
final String key = p1.getKey();
|
||||
if(key.equals(KEY_GRADLE_COMMAMDS)) {
|
||||
showGradleCommandsDialog();
|
||||
} else if(key.equals(KEY_GRADLE_CLEAR_CACHE)) {
|
||||
showClearCacheDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showGradleCommandsDialog() {
|
||||
final String[] labels = {
|
||||
"--stacktrace",
|
||||
"--info",
|
||||
"--debug",
|
||||
"--scan",
|
||||
"--warning-mode all"
|
||||
};
|
||||
final boolean[] checked = {
|
||||
getPrefManager().isStackTraceEnabled (),
|
||||
getPrefManager().isGradleInfoEnabled(),
|
||||
getPrefManager().isGradleDebugEnabled(),
|
||||
getPrefManager().isGradleScanEnabled(),
|
||||
getPrefManager().isGradleWarningEnabled()
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
builder.setTitle(R.string.idepref_build_customgradlecommands_title);
|
||||
builder.setMultiChoiceItems(labels, checked, (p1, p2, p3) -> {
|
||||
boolean isChecked = p3;
|
||||
int pos = p2;
|
||||
if(pos == 0) {
|
||||
getPrefManager().setGradleStacktraceEnabled(isChecked);
|
||||
} else if(pos == 1) {
|
||||
getPrefManager().setGradleInfoEnabled(isChecked);
|
||||
} else if(pos == 2) {
|
||||
getPrefManager().setGradleDebugEnabled(isChecked);
|
||||
} else if(pos == 3) {
|
||||
getPrefManager().setGradleScanEnabled(isChecked);
|
||||
} else if(pos == 4) {
|
||||
getPrefManager().setGradleWarningEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.setCancelable(false);
|
||||
builder.create().show();
|
||||
}
|
||||
public static final String KEY_GRADLE_COMMAMDS = "idepref_build_gradleCommands";
|
||||
public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache";
|
||||
|
||||
private void showClearCacheDialog() {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
builder.setTitle(R.string.idepref_build_clearCache_title);
|
||||
builder.setMessage(R.string.msg_clear_cache);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.yes, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
getProgressSheet().show(getChildFragmentManager(), "progress_sheet");
|
||||
new TaskExecutor().executeAsync(this::deleteCaches, __ -> {
|
||||
getProgressSheet().dismiss();
|
||||
});
|
||||
});
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private Object deleteCaches() {
|
||||
File file = new File(Environment.GRADLE_USER_HOME, "caches");
|
||||
if(file.exists()) {
|
||||
FileUtils.delete(file);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProgressSheet getProgressSheet() {
|
||||
return progressSheet == null ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait)) : progressSheet;
|
||||
}
|
||||
private ProgressSheet progressSheet;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
super.onCreatePreferences(p1, p2);
|
||||
if (getContext() == null) return;
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
final PreferenceCategory categoryGradle = new PreferenceCategory(getContext());
|
||||
final Preference customCommands = new Preference(getContext());
|
||||
final Preference clearCache = new Preference(getContext());
|
||||
|
||||
screen.addPreference(categoryGradle);
|
||||
|
||||
customCommands.setKey(KEY_GRADLE_COMMAMDS);
|
||||
customCommands.setIcon(R.drawable.ic_bash_commands);
|
||||
customCommands.setTitle(R.string.idepref_build_customgradlecommands_title);
|
||||
customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary);
|
||||
|
||||
clearCache.setKey(KEY_GRADLE_CLEAR_CACHE);
|
||||
clearCache.setIcon(R.drawable.ic_delete);
|
||||
clearCache.setTitle(R.string.idepref_build_clearCache_title);
|
||||
clearCache.setSummary(R.string.idepref_build_clearCache_summary);
|
||||
|
||||
categoryGradle.setTitle(R.string.gradle);
|
||||
categoryGradle.addPreference(customCommands);
|
||||
categoryGradle.addPreference(clearCache);
|
||||
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
customCommands.setOnPreferenceClickListener(this);
|
||||
clearCache.setOnPreferenceClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p1) {
|
||||
final String key = p1.getKey();
|
||||
if (key.equals(KEY_GRADLE_COMMAMDS)) {
|
||||
showGradleCommandsDialog();
|
||||
} else if (key.equals(KEY_GRADLE_CLEAR_CACHE)) {
|
||||
showClearCacheDialog();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showGradleCommandsDialog() {
|
||||
final String[] labels = {
|
||||
"--stacktrace", "--info", "--debug", "--scan", "--warning-mode all"
|
||||
};
|
||||
final boolean[] checked = {
|
||||
getPrefManager().isStackTraceEnabled(),
|
||||
getPrefManager().isGradleInfoEnabled(),
|
||||
getPrefManager().isGradleDebugEnabled(),
|
||||
getPrefManager().isGradleScanEnabled(),
|
||||
getPrefManager().isGradleWarningEnabled()
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
builder.setTitle(R.string.idepref_build_customgradlecommands_title);
|
||||
builder.setMultiChoiceItems(
|
||||
labels,
|
||||
checked,
|
||||
(p1, p2, p3) -> {
|
||||
boolean isChecked = p3;
|
||||
int pos = p2;
|
||||
if (pos == 0) {
|
||||
getPrefManager().setGradleStacktraceEnabled(isChecked);
|
||||
} else if (pos == 1) {
|
||||
getPrefManager().setGradleInfoEnabled(isChecked);
|
||||
} else if (pos == 2) {
|
||||
getPrefManager().setGradleDebugEnabled(isChecked);
|
||||
} else if (pos == 3) {
|
||||
getPrefManager().setGradleScanEnabled(isChecked);
|
||||
} else if (pos == 4) {
|
||||
getPrefManager().setGradleWarningEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.setCancelable(false);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void showClearCacheDialog() {
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
builder.setTitle(R.string.idepref_build_clearCache_title);
|
||||
builder.setMessage(R.string.msg_clear_cache);
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(
|
||||
R.string.yes,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
getProgressSheet().show(getChildFragmentManager(), "progress_sheet");
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
this::deleteCaches,
|
||||
__ -> {
|
||||
getProgressSheet().dismiss();
|
||||
});
|
||||
});
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private Object deleteCaches() {
|
||||
File file = new File(Environment.GRADLE_USER_HOME, "caches");
|
||||
if (file.exists()) {
|
||||
FileUtils.delete(file);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProgressSheet getProgressSheet() {
|
||||
return progressSheet == null
|
||||
? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait))
|
||||
: progressSheet;
|
||||
}
|
||||
}
|
||||
|
||||
+209
-196
@@ -49,115 +49,115 @@ import com.itsaky.androidide.models.PrefBasedJavaServerSettings;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
|
||||
public class EditorPreferences extends BasePreferenceFragment {
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences (Bundle p1, String p2) {
|
||||
super.onCreatePreferences (p1, p2);
|
||||
|
||||
if (getContext () == null) {
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
super.onCreatePreferences(p1, p2);
|
||||
|
||||
if (getContext() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var screen = getPreferenceScreen ();
|
||||
final var commonCategory = new PreferenceCategory (getContext ());
|
||||
final var javaCategory = new PreferenceCategory (getContext ());
|
||||
|
||||
final var fontSize = new Preference (getContext ());
|
||||
final var nonPrintable = new Preference (getContext ());
|
||||
final var tabSize = new Preference (getContext ());
|
||||
final var drawHex = new SwitchPreference (getContext ());
|
||||
final var fontLigatures = new SwitchPreference (getContext ());
|
||||
final var autoSave = new SwitchPreference (getContext ());
|
||||
final var usePopupActions = new SwitchPreference (getContext ());
|
||||
final var horizontalPopup = new SwitchPreference (getContext ());
|
||||
|
||||
final var javaMatchLower = new SwitchPreference (getContext ());
|
||||
|
||||
screen.addPreference (commonCategory);
|
||||
screen.addPreference (javaCategory);
|
||||
|
||||
fontSize.setIcon (R.drawable.ic_text_size);
|
||||
fontSize.setKey (KEY_EDITOR_FONT_SIZE);
|
||||
fontSize.setTitle (R.string.idepref_editor_fontsize_title);
|
||||
fontSize.setSummary (R.string.idepref_editor_fontsize_summary);
|
||||
|
||||
nonPrintable.setIcon (R.drawable.ic_drawing);
|
||||
nonPrintable.setKey (KEY_EDITOR_PRINTABLE_CHARS);
|
||||
nonPrintable.setTitle (R.string.idepref_editor_paintingflags_title);
|
||||
nonPrintable.setSummary (R.string.idepref_editor_paintingflags_summary);
|
||||
|
||||
tabSize.setIcon (R.drawable.ic_tab);
|
||||
tabSize.setKey (KEY_EDITOR_TAB_SIZE);
|
||||
tabSize.setTitle (R.string.title_tab_size);
|
||||
tabSize.setSummary (R.string.msg_tab_size);
|
||||
|
||||
drawHex.setIcon (R.drawable.ic_hexadecimal);
|
||||
drawHex.setKey (KEY_EDITOR_DRAW_HEX);
|
||||
drawHex.setTitle (R.string.idepref_editor_drawhexcolors_title);
|
||||
drawHex.setSummary (R.string.idepref_editor_drawhexcolors_summary);
|
||||
|
||||
fontLigatures.setIcon (R.drawable.ic_font_ligatures);
|
||||
fontLigatures.setKey (KEY_EDITOR_FONT_LIGATURES);
|
||||
fontLigatures.setTitle (getString (R.string.idepref_editor_ligatures_title));
|
||||
fontLigatures.setSummary (getString (R.string.idepref_editor_ligatures_summary));
|
||||
|
||||
usePopupActions.setIcon (R.drawable.ic_text_actions);
|
||||
usePopupActions.setKey (KEY_EDITOR_USE_POPUP);
|
||||
usePopupActions.setTitle (getString (R.string.title_use_popup_actions));
|
||||
usePopupActions.setSummary (getString (R.string.msg_use_popup_actions));
|
||||
|
||||
horizontalPopup.setIcon (R.drawable.ic_text_actions_horizontal);
|
||||
horizontalPopup.setKey (KEY_EDITOR_HORIZONTAL_POPUP);
|
||||
horizontalPopup.setTitle (getString (R.string.title_use_horizontal_popup));
|
||||
horizontalPopup.setSummary (getString (R.string.msg_use_horizontal_popup));
|
||||
|
||||
autoSave.setIcon (R.drawable.ic_save);
|
||||
autoSave.setKey (KEY_EDITOR_AUTO_SAVE);
|
||||
autoSave.setTitle (getString (R.string.idepref_editor_autoSave_title));
|
||||
autoSave.setSummary (getString (R.string.idepref_editor_autoSave_summary));
|
||||
|
||||
javaMatchLower.setKey (KEY_JAVA_PREF_MATCH_LOWER);
|
||||
javaMatchLower.setIcon (R.drawable.ic_text_lower);
|
||||
javaMatchLower.setTitle (getString (R.string.idepref_java_matchLower_title));
|
||||
javaMatchLower.setSummary (getString (R.string.idepref_java_matchLower_summary));
|
||||
|
||||
commonCategory.setTitle (getString (R.string.idepref_editor_category_common));
|
||||
commonCategory.addPreference (fontSize);
|
||||
commonCategory.addPreference (fontLigatures);
|
||||
commonCategory.addPreference (usePopupActions);
|
||||
commonCategory.addPreference (horizontalPopup);
|
||||
commonCategory.addPreference (nonPrintable);
|
||||
commonCategory.addPreference (tabSize);
|
||||
commonCategory.addPreference (drawHex);
|
||||
commonCategory.addPreference (autoSave);
|
||||
|
||||
javaCategory.setTitle (getString (R.string.idepref_editor_category_java));
|
||||
javaCategory.addPreference (javaMatchLower);
|
||||
|
||||
setPreferenceScreen (screen);
|
||||
|
||||
fontSize.setOnPreferenceClickListener (this::onPreferenceClick);
|
||||
fontLigatures.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
usePopupActions.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
horizontalPopup.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
nonPrintable.setOnPreferenceClickListener (this::onPreferenceClick);
|
||||
tabSize.setOnPreferenceClickListener (this::onPreferenceClick);
|
||||
drawHex.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
autoSave.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
javaMatchLower.setOnPreferenceChangeListener (this::onPreferenceChange);
|
||||
|
||||
fontLigatures.setChecked (getPrefManager ().getBoolean (KEY_EDITOR_FONT_LIGATURES, true));
|
||||
usePopupActions.setChecked (getPrefManager ().getBoolean (KEY_EDITOR_USE_POPUP, false));
|
||||
horizontalPopup.setChecked (getPrefManager ().getBoolean (KEY_EDITOR_HORIZONTAL_POPUP, false));
|
||||
drawHex.setChecked (getPrefManager ().getBoolean (KEY_EDITOR_DRAW_HEX, true));
|
||||
autoSave.setChecked (getPrefManager ().getBoolean (KEY_EDITOR_AUTO_SAVE, false));
|
||||
javaMatchLower.setChecked (getPrefManager ().getBoolean (KEY_JAVA_PREF_MATCH_LOWER, false));
|
||||
|
||||
final var screen = getPreferenceScreen();
|
||||
final var commonCategory = new PreferenceCategory(getContext());
|
||||
final var javaCategory = new PreferenceCategory(getContext());
|
||||
|
||||
final var fontSize = new Preference(getContext());
|
||||
final var nonPrintable = new Preference(getContext());
|
||||
final var tabSize = new Preference(getContext());
|
||||
final var drawHex = new SwitchPreference(getContext());
|
||||
final var fontLigatures = new SwitchPreference(getContext());
|
||||
final var autoSave = new SwitchPreference(getContext());
|
||||
final var usePopupActions = new SwitchPreference(getContext());
|
||||
final var horizontalPopup = new SwitchPreference(getContext());
|
||||
|
||||
final var javaMatchLower = new SwitchPreference(getContext());
|
||||
|
||||
screen.addPreference(commonCategory);
|
||||
screen.addPreference(javaCategory);
|
||||
|
||||
fontSize.setIcon(R.drawable.ic_text_size);
|
||||
fontSize.setKey(KEY_EDITOR_FONT_SIZE);
|
||||
fontSize.setTitle(R.string.idepref_editor_fontsize_title);
|
||||
fontSize.setSummary(R.string.idepref_editor_fontsize_summary);
|
||||
|
||||
nonPrintable.setIcon(R.drawable.ic_drawing);
|
||||
nonPrintable.setKey(KEY_EDITOR_PRINTABLE_CHARS);
|
||||
nonPrintable.setTitle(R.string.idepref_editor_paintingflags_title);
|
||||
nonPrintable.setSummary(R.string.idepref_editor_paintingflags_summary);
|
||||
|
||||
tabSize.setIcon(R.drawable.ic_tab);
|
||||
tabSize.setKey(KEY_EDITOR_TAB_SIZE);
|
||||
tabSize.setTitle(R.string.title_tab_size);
|
||||
tabSize.setSummary(R.string.msg_tab_size);
|
||||
|
||||
drawHex.setIcon(R.drawable.ic_hexadecimal);
|
||||
drawHex.setKey(KEY_EDITOR_DRAW_HEX);
|
||||
drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title);
|
||||
drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary);
|
||||
|
||||
fontLigatures.setIcon(R.drawable.ic_font_ligatures);
|
||||
fontLigatures.setKey(KEY_EDITOR_FONT_LIGATURES);
|
||||
fontLigatures.setTitle(getString(R.string.idepref_editor_ligatures_title));
|
||||
fontLigatures.setSummary(getString(R.string.idepref_editor_ligatures_summary));
|
||||
|
||||
usePopupActions.setIcon(R.drawable.ic_text_actions);
|
||||
usePopupActions.setKey(KEY_EDITOR_USE_POPUP);
|
||||
usePopupActions.setTitle(getString(R.string.title_use_popup_actions));
|
||||
usePopupActions.setSummary(getString(R.string.msg_use_popup_actions));
|
||||
|
||||
horizontalPopup.setIcon(R.drawable.ic_text_actions_horizontal);
|
||||
horizontalPopup.setKey(KEY_EDITOR_HORIZONTAL_POPUP);
|
||||
horizontalPopup.setTitle(getString(R.string.title_use_horizontal_popup));
|
||||
horizontalPopup.setSummary(getString(R.string.msg_use_horizontal_popup));
|
||||
|
||||
autoSave.setIcon(R.drawable.ic_save);
|
||||
autoSave.setKey(KEY_EDITOR_AUTO_SAVE);
|
||||
autoSave.setTitle(getString(R.string.idepref_editor_autoSave_title));
|
||||
autoSave.setSummary(getString(R.string.idepref_editor_autoSave_summary));
|
||||
|
||||
javaMatchLower.setKey(KEY_JAVA_PREF_MATCH_LOWER);
|
||||
javaMatchLower.setIcon(R.drawable.ic_text_lower);
|
||||
javaMatchLower.setTitle(getString(R.string.idepref_java_matchLower_title));
|
||||
javaMatchLower.setSummary(getString(R.string.idepref_java_matchLower_summary));
|
||||
|
||||
commonCategory.setTitle(getString(R.string.idepref_editor_category_common));
|
||||
commonCategory.addPreference(fontSize);
|
||||
commonCategory.addPreference(fontLigatures);
|
||||
commonCategory.addPreference(usePopupActions);
|
||||
commonCategory.addPreference(horizontalPopup);
|
||||
commonCategory.addPreference(nonPrintable);
|
||||
commonCategory.addPreference(tabSize);
|
||||
commonCategory.addPreference(drawHex);
|
||||
commonCategory.addPreference(autoSave);
|
||||
|
||||
javaCategory.setTitle(getString(R.string.idepref_editor_category_java));
|
||||
javaCategory.addPreference(javaMatchLower);
|
||||
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
fontSize.setOnPreferenceClickListener(this::onPreferenceClick);
|
||||
fontLigatures.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
usePopupActions.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
horizontalPopup.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
nonPrintable.setOnPreferenceClickListener(this::onPreferenceClick);
|
||||
tabSize.setOnPreferenceClickListener(this::onPreferenceClick);
|
||||
drawHex.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
autoSave.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
javaMatchLower.setOnPreferenceChangeListener(this::onPreferenceChange);
|
||||
|
||||
fontLigatures.setChecked(getPrefManager().getBoolean(KEY_EDITOR_FONT_LIGATURES, true));
|
||||
usePopupActions.setChecked(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP, false));
|
||||
horizontalPopup.setChecked(getPrefManager().getBoolean(KEY_EDITOR_HORIZONTAL_POPUP, false));
|
||||
drawHex.setChecked(getPrefManager().getBoolean(KEY_EDITOR_DRAW_HEX, true));
|
||||
autoSave.setChecked(getPrefManager().getBoolean(KEY_EDITOR_AUTO_SAVE, false));
|
||||
javaMatchLower.setChecked(getPrefManager().getBoolean(KEY_JAVA_PREF_MATCH_LOWER, false));
|
||||
}
|
||||
|
||||
public boolean onPreferenceChange (@NonNull Preference preference, Object newValue) {
|
||||
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
|
||||
final boolean value = (boolean) newValue;
|
||||
|
||||
switch (preference.getKey ()) {
|
||||
|
||||
switch (preference.getKey()) {
|
||||
case KEY_EDITOR_DRAW_HEX:
|
||||
ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true;
|
||||
break;
|
||||
@@ -165,130 +165,143 @@ public class EditorPreferences extends BasePreferenceFragment {
|
||||
ConstantsBridge.EDITOR_PREF_LIGATURES_CHANGED = true;
|
||||
break;
|
||||
case KEY_JAVA_PREF_MATCH_LOWER:
|
||||
final var javaServer = StudioApp.getInstance ().getJavaLanguageServer ();
|
||||
javaServer.applySettings (PrefBasedJavaServerSettings.getInstance ());
|
||||
final var javaServer = StudioApp.getInstance().getJavaLanguageServer();
|
||||
javaServer.applySettings(PrefBasedJavaServerSettings.getInstance());
|
||||
break;
|
||||
}
|
||||
|
||||
getPrefManager ().putBoolean (preference.getKey (), value);
|
||||
|
||||
final var horizontalPopup = findPreference (KEY_EDITOR_HORIZONTAL_POPUP);
|
||||
|
||||
getPrefManager().putBoolean(preference.getKey(), value);
|
||||
|
||||
final var horizontalPopup = findPreference(KEY_EDITOR_HORIZONTAL_POPUP);
|
||||
if (horizontalPopup != null) {
|
||||
horizontalPopup.setEnabled (getPrefManager ().getBoolean (KEY_EDITOR_USE_POPUP));
|
||||
horizontalPopup.setEnabled(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onPreferenceClick (@NonNull Preference preference) {
|
||||
final String key = preference.getKey ();
|
||||
|
||||
public boolean onPreferenceClick(@NonNull Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
switch (key) {
|
||||
case KEY_EDITOR_FONT_SIZE:
|
||||
showTextSizeDialog ();
|
||||
showTextSizeDialog();
|
||||
break;
|
||||
case KEY_EDITOR_PRINTABLE_CHARS:
|
||||
showPrintableCharsDialog ();
|
||||
showPrintableCharsDialog();
|
||||
break;
|
||||
case KEY_EDITOR_TAB_SIZE:
|
||||
showTabSizeDialog ();
|
||||
showTabSizeDialog();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showTabSizeDialog () {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
final String[] sizes = new String[]{"2", "4", "6", "8"};
|
||||
|
||||
|
||||
private void showTabSizeDialog() {
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
final String[] sizes = new String[] {"2", "4", "6", "8"};
|
||||
|
||||
// We apply simple maths,
|
||||
// assuming that tab size options have a difference of 2
|
||||
// And the array is sorted
|
||||
// Like, 2, 4, 6, 8, 10, etc
|
||||
//
|
||||
// If current tab size if 4, 4/2 = 2 and 2 - 1 = 1, so option at index 1 will be selected which is current tab size (4)!
|
||||
int current = (getPrefManager ().getEditorTabSize () / 2) - 1;
|
||||
//
|
||||
// If current tab size if 4, 4/2 = 2 and 2 - 1 = 1, so option at index 1 will be selected
|
||||
// which
|
||||
// is current tab size (4)!
|
||||
int current = (getPrefManager().getEditorTabSize() / 2) - 1;
|
||||
if (current < 0 || current >= sizes.length) {
|
||||
current = 1;
|
||||
}
|
||||
builder.setTitle (R.string.title_tab_size);
|
||||
builder.setSingleChoiceItems (sizes, current, (d, i) -> {
|
||||
d.dismiss ();
|
||||
|
||||
// Reversing the logic applied above...
|
||||
int tabSize = (i + 1) * 2;
|
||||
if (tabSize < 2 || tabSize > 8) {
|
||||
tabSize = 4;
|
||||
}
|
||||
getPrefManager ().putInt (KEY_EDITOR_TAB_SIZE, tabSize);
|
||||
});
|
||||
builder.setCancelable (true);
|
||||
builder.show ();
|
||||
builder.setTitle(R.string.title_tab_size);
|
||||
builder.setSingleChoiceItems(
|
||||
sizes,
|
||||
current,
|
||||
(d, i) -> {
|
||||
d.dismiss();
|
||||
|
||||
// Reversing the logic applied above...
|
||||
int tabSize = (i + 1) * 2;
|
||||
if (tabSize < 2 || tabSize > 8) {
|
||||
tabSize = 4;
|
||||
}
|
||||
getPrefManager().putInt(KEY_EDITOR_TAB_SIZE, tabSize);
|
||||
});
|
||||
builder.setCancelable(true);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void showTextSizeDialog () {
|
||||
final LayoutTextSizeSliderBinding binding = LayoutTextSizeSliderBinding.inflate (LayoutInflater.from (getContext ()));
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
float size = getPrefManager ().getFloat (KEY_EDITOR_FONT_SIZE, 14);
|
||||
|
||||
private void showTextSizeDialog() {
|
||||
final LayoutTextSizeSliderBinding binding =
|
||||
LayoutTextSizeSliderBinding.inflate(LayoutInflater.from(getContext()));
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
float size = getPrefManager().getFloat(KEY_EDITOR_FONT_SIZE, 14);
|
||||
if (size < 6 || size > 32) {
|
||||
size = 14;
|
||||
}
|
||||
changeTextSize (binding, size);
|
||||
|
||||
binding.slider.setLabelFormatter (p1 -> String.valueOf ((int) p1));
|
||||
builder.setTitle (R.string.title_change_text_size);
|
||||
builder.setMessage (R.string.msg_editor_font_size);
|
||||
builder.setView (binding.getRoot ());
|
||||
builder.setPositiveButton (android.R.string.ok, (p1, p2) -> {
|
||||
p1.dismiss ();
|
||||
float s = binding.slider.getValue ();
|
||||
changeTextSize (binding, s);
|
||||
});
|
||||
builder.setNeutralButton (R.string.reset, (p1, p2) -> changeTextSize (binding, 14));
|
||||
builder.setCancelable (false);
|
||||
builder.show ();
|
||||
changeTextSize(binding, size);
|
||||
|
||||
binding.slider.setLabelFormatter(p1 -> String.valueOf((int) p1));
|
||||
builder.setTitle(R.string.title_change_text_size);
|
||||
builder.setMessage(R.string.msg_editor_font_size);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setPositiveButton(
|
||||
android.R.string.ok,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
float s = binding.slider.getValue();
|
||||
changeTextSize(binding, s);
|
||||
});
|
||||
builder.setNeutralButton(R.string.reset, (p1, p2) -> changeTextSize(binding, 14));
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void changeTextSize (LayoutTextSizeSliderBinding binding, float size) {
|
||||
getPrefManager ().putFloat (KEY_EDITOR_FONT_SIZE, size);
|
||||
binding.slider.setValue (size);
|
||||
|
||||
private void changeTextSize(LayoutTextSizeSliderBinding binding, float size) {
|
||||
getPrefManager().putFloat(KEY_EDITOR_FONT_SIZE, size);
|
||||
binding.slider.setValue(size);
|
||||
ConstantsBridge.EDITOR_PREF_SIZE_CHANGED = true;
|
||||
}
|
||||
|
||||
private void showPrintableCharsDialog () {
|
||||
|
||||
private void showPrintableCharsDialog() {
|
||||
String[] labels = {
|
||||
"Leading whitespaces",
|
||||
"Trailing whitespaces",
|
||||
"Inner whitespaces",
|
||||
"Whitespaces in empty lines",
|
||||
"Line breaks"
|
||||
"Leading whitespaces",
|
||||
"Trailing whitespaces",
|
||||
"Inner whitespaces",
|
||||
"Whitespaces in empty lines",
|
||||
"Line breaks"
|
||||
};
|
||||
final boolean[] checked = {
|
||||
getPrefManager ().getBoolean (KEY_EDITOR_FLAG_WS_LEADING, true),
|
||||
getPrefManager ().getBoolean (KEY_EDITOR_FLAG_WS_TRAILING, false),
|
||||
getPrefManager ().getBoolean (KEY_EDITOR_FLAG_WS_INNER, true),
|
||||
getPrefManager ().getBoolean (KEY_EDITOR_FLAG_WS_EMPTY_LINE, true),
|
||||
getPrefManager ().getBoolean (KEY_EDITOR_FLAG_LINE_BREAK, true)
|
||||
getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_LEADING, true),
|
||||
getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_TRAILING, false),
|
||||
getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_INNER, true),
|
||||
getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, true),
|
||||
getPrefManager().getBoolean(KEY_EDITOR_FLAG_LINE_BREAK, true)
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
builder.setTitle (R.string.idepref_editor_paintingflags_title);
|
||||
builder.setMultiChoiceItems (labels, checked, (dialog, which, isChecked) -> {
|
||||
|
||||
if (which == 0) {
|
||||
getPrefManager ().putBoolean (KEY_EDITOR_FLAG_WS_LEADING, isChecked);
|
||||
} else if (which == 1) {
|
||||
getPrefManager ().putBoolean (KEY_EDITOR_FLAG_WS_TRAILING, isChecked);
|
||||
} else if (which == 2) {
|
||||
getPrefManager ().putBoolean (KEY_EDITOR_FLAG_WS_INNER, isChecked);
|
||||
} else if (which == 3) {
|
||||
getPrefManager ().putBoolean (KEY_EDITOR_FLAG_WS_EMPTY_LINE, isChecked);
|
||||
} else if (which == 4) {
|
||||
getPrefManager ().putBoolean (KEY_EDITOR_FLAG_LINE_BREAK, isChecked);
|
||||
}
|
||||
|
||||
ConstantsBridge.EDITOR_PREF_FLAGS_CHANGED = true;
|
||||
});
|
||||
builder.setPositiveButton (android.R.string.ok, null);
|
||||
builder.setCancelable (false);
|
||||
builder.show ();
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
builder.setTitle(R.string.idepref_editor_paintingflags_title);
|
||||
builder.setMultiChoiceItems(
|
||||
labels,
|
||||
checked,
|
||||
(dialog, which, isChecked) -> {
|
||||
if (which == 0) {
|
||||
getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_LEADING, isChecked);
|
||||
} else if (which == 1) {
|
||||
getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_TRAILING, isChecked);
|
||||
} else if (which == 2) {
|
||||
getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_INNER, isChecked);
|
||||
} else if (which == 3) {
|
||||
getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, isChecked);
|
||||
} else if (which == 4) {
|
||||
getPrefManager().putBoolean(KEY_EDITOR_FLAG_LINE_BREAK, isChecked);
|
||||
}
|
||||
|
||||
ConstantsBridge.EDITOR_PREF_FLAGS_CHANGED = true;
|
||||
});
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-29
@@ -5,83 +5,85 @@
|
||||
* 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.fragments.preferences;
|
||||
|
||||
import static com.itsaky.androidide.managers.PreferenceManager.*;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
import static com.itsaky.androidide.managers.PreferenceManager.*;
|
||||
public class GeneralPreferences extends BasePreferenceFragment
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
public class GeneralPreferences extends BasePreferenceFragment implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle p1, String p2) {
|
||||
super.onCreatePreferences(p1, p2);
|
||||
if(getContext() == null) return;
|
||||
|
||||
if (getContext() == null) return;
|
||||
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
final SwitchPreference openProjects = new SwitchPreference(getContext());
|
||||
final SwitchPreference openProjects = new SwitchPreference(getContext());
|
||||
final SwitchPreference confirmProjectOpen = new SwitchPreference(getContext());
|
||||
final SwitchPreference shell = new SwitchPreference (getContext ());
|
||||
|
||||
final SwitchPreference shell = new SwitchPreference(getContext());
|
||||
|
||||
openProjects.setKey(KEY_OPEN_PROJECTS);
|
||||
openProjects.setTitle(R.string.title_open_projects);
|
||||
openProjects.setIcon(R.drawable.ic_open_project);
|
||||
openProjects.setSummary(R.string.msg_open_projects);
|
||||
|
||||
|
||||
confirmProjectOpen.setKey(KEY_CONFIRM_PROJECT_OPEN);
|
||||
confirmProjectOpen.setTitle(R.string.title_confirm_project_open);
|
||||
confirmProjectOpen.setSummary(R.string.msg_confirm_project_open);
|
||||
confirmProjectOpen.setIcon(R.drawable.ic_open_project);
|
||||
|
||||
shell.setKey (KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
shell.setTitle (getString (R.string.title_default_shell));
|
||||
shell.setSummary (getString (R.string.msg_default_shell));
|
||||
shell.setIcon (R.drawable.ic_bash_commands);
|
||||
|
||||
|
||||
shell.setKey(KEY_TERMINAL_USE_SYSTEM_SHELL);
|
||||
shell.setTitle(getString(R.string.title_default_shell));
|
||||
shell.setSummary(getString(R.string.msg_default_shell));
|
||||
shell.setIcon(R.drawable.ic_bash_commands);
|
||||
|
||||
openProjects.setChecked(getPrefManager().autoOpenProject());
|
||||
confirmProjectOpen.setChecked(getPrefManager().confirmProjectOpen());
|
||||
shell.setChecked (getPrefManager ().getBoolean (KEY_TERMINAL_USE_SYSTEM_SHELL));
|
||||
|
||||
shell.setChecked(getPrefManager().getBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL));
|
||||
|
||||
screen.addPreference(openProjects);
|
||||
screen.addPreference(confirmProjectOpen);
|
||||
screen.addPreference (shell);
|
||||
|
||||
screen.addPreference(shell);
|
||||
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
|
||||
openProjects.setOnPreferenceChangeListener(this);
|
||||
confirmProjectOpen.setOnPreferenceChangeListener(this);
|
||||
shell.setOnPreferenceChangeListener (this);
|
||||
shell.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(@NonNull Preference p1, Object p2) {
|
||||
boolean checked = (Boolean) p2;
|
||||
final var key = p1.getKey ();
|
||||
final var key = p1.getKey();
|
||||
switch (key) {
|
||||
case KEY_OPEN_PROJECTS:
|
||||
getPrefManager ().putBoolean (KEY_OPEN_PROJECTS, checked);
|
||||
getPrefManager().putBoolean(KEY_OPEN_PROJECTS, checked);
|
||||
break;
|
||||
case KEY_CONFIRM_PROJECT_OPEN:
|
||||
getPrefManager ().putBoolean (KEY_CONFIRM_PROJECT_OPEN, checked);
|
||||
getPrefManager().putBoolean(KEY_CONFIRM_PROJECT_OPEN, checked);
|
||||
break;
|
||||
case KEY_TERMINAL_USE_SYSTEM_SHELL:
|
||||
getPrefManager ().putBoolean (KEY_TERMINAL_USE_SYSTEM_SHELL, checked);
|
||||
getPrefManager().putBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL, checked);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
||||
+156
-147
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments.preferences;
|
||||
|
||||
@@ -32,14 +31,16 @@ import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.blankj.utilcode.util.ResourceUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.AboutActivity;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
|
||||
@@ -47,160 +48,168 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class IDEPreferences extends BasePreferenceFragment {
|
||||
|
||||
private GeneralPreferences mGeneralPrefs;
|
||||
private BuildPreferences mBuildPreferences;
|
||||
private EditorPreferences mEditorPreferences;
|
||||
|
||||
private String CHANGELOG = null;
|
||||
|
||||
|
||||
private GeneralPreferences mGeneralPrefs;
|
||||
private BuildPreferences mBuildPreferences;
|
||||
private EditorPreferences mEditorPreferences;
|
||||
|
||||
private String CHANGELOG = null;
|
||||
|
||||
public static final String KEY_GENERAL = "idepref_general";
|
||||
public static final String KEY_APPEARANCE = "idepref_appearance";
|
||||
public static final String KEY_EDITOR = "idepref_editor";
|
||||
public static final String KEY_BUILD = "idepref_build";
|
||||
public static final String KEY_TELEGRAM = "idepref_telegram";
|
||||
public static final String KEY_ISSUES = "idepref_issues";
|
||||
public static final String KEY_CHANGELOG = "idepref_changelog";
|
||||
public static final String KEY_APPEARANCE = "idepref_appearance";
|
||||
public static final String KEY_EDITOR = "idepref_editor";
|
||||
public static final String KEY_BUILD = "idepref_build";
|
||||
public static final String KEY_TELEGRAM = "idepref_telegram";
|
||||
public static final String KEY_ISSUES = "idepref_issues";
|
||||
public static final String KEY_CHANGELOG = "idepref_changelog";
|
||||
public static final String KEY_ABOUT = "idepref_about";
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedState, String rootKey) {
|
||||
super.onCreatePreferences(savedState, rootKey);
|
||||
if(getContext() == null) return;
|
||||
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedState, String rootKey) {
|
||||
super.onCreatePreferences(savedState, rootKey);
|
||||
if (getContext() == null) return;
|
||||
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
final Preference general = new Preference(getContext());
|
||||
final Preference editor = new Preference(getContext());
|
||||
final Preference build = new Preference(getContext());
|
||||
final Preference telegram = new Preference(getContext());
|
||||
final Preference github = new Preference(getContext());
|
||||
final Preference changelog = new Preference(getContext());
|
||||
final Preference about = new Preference(getContext());
|
||||
|
||||
general.setKey(KEY_GENERAL);
|
||||
general.setIconSpaceReserved(false);
|
||||
general.setFragment(getGeneralFrag().getClass().getName());
|
||||
general.setTitle(R.string.title_general);
|
||||
|
||||
editor.setKey(KEY_EDITOR);
|
||||
editor.setIconSpaceReserved(false);
|
||||
editor.setFragment(getEditorFrag().getClass().getName());
|
||||
editor.setTitle(R.string.idepref_editor_title);
|
||||
|
||||
build.setKey(KEY_BUILD);
|
||||
build.setIconSpaceReserved(false);
|
||||
build.setFragment(getBuildFrag().getClass().getName());
|
||||
build.setTitle(R.string.idepref_build_title);
|
||||
|
||||
github.setKey(KEY_ISSUES);
|
||||
github.setIconSpaceReserved(false);
|
||||
github.setTitle(R.string.title_github);
|
||||
|
||||
telegram.setKey(KEY_TELEGRAM);
|
||||
telegram.setIconSpaceReserved(false);
|
||||
telegram.setTitle(R.string.discussions_on_telegram);
|
||||
|
||||
changelog.setKey(KEY_CHANGELOG);
|
||||
changelog.setIconSpaceReserved(false);
|
||||
changelog.setTitle(R.string.pref_changelog);
|
||||
|
||||
final Preference editor = new Preference(getContext());
|
||||
final Preference build = new Preference(getContext());
|
||||
final Preference telegram = new Preference(getContext());
|
||||
final Preference github = new Preference(getContext());
|
||||
final Preference changelog = new Preference(getContext());
|
||||
final Preference about = new Preference(getContext());
|
||||
|
||||
general.setKey(KEY_GENERAL);
|
||||
general.setIconSpaceReserved(false);
|
||||
general.setFragment(getGeneralFrag().getClass().getName());
|
||||
general.setTitle(R.string.title_general);
|
||||
|
||||
editor.setKey(KEY_EDITOR);
|
||||
editor.setIconSpaceReserved(false);
|
||||
editor.setFragment(getEditorFrag().getClass().getName());
|
||||
editor.setTitle(R.string.idepref_editor_title);
|
||||
|
||||
build.setKey(KEY_BUILD);
|
||||
build.setIconSpaceReserved(false);
|
||||
build.setFragment(getBuildFrag().getClass().getName());
|
||||
build.setTitle(R.string.idepref_build_title);
|
||||
|
||||
github.setKey(KEY_ISSUES);
|
||||
github.setIconSpaceReserved(false);
|
||||
github.setTitle(R.string.title_github);
|
||||
|
||||
telegram.setKey(KEY_TELEGRAM);
|
||||
telegram.setIconSpaceReserved(false);
|
||||
telegram.setTitle(R.string.discussions_on_telegram);
|
||||
|
||||
changelog.setKey(KEY_CHANGELOG);
|
||||
changelog.setIconSpaceReserved(false);
|
||||
changelog.setTitle(R.string.pref_changelog);
|
||||
|
||||
about.setKey(KEY_ABOUT);
|
||||
about.setIconSpaceReserved(false);
|
||||
about.setTitle(R.string.idepref_about_title);
|
||||
|
||||
|
||||
screen.addPreference(general);
|
||||
screen.addPreference(editor);
|
||||
screen.addPreference(build);
|
||||
screen.addPreference(github);
|
||||
screen.addPreference(telegram);
|
||||
screen.addPreference(changelog);
|
||||
screen.addPreference(editor);
|
||||
screen.addPreference(build);
|
||||
screen.addPreference(github);
|
||||
screen.addPreference(telegram);
|
||||
screen.addPreference(changelog);
|
||||
screen.addPreference(about);
|
||||
|
||||
final Preference.OnPreferenceClickListener listener = getListener();
|
||||
github.setOnPreferenceClickListener(listener);
|
||||
telegram.setOnPreferenceClickListener(listener);
|
||||
changelog.setOnPreferenceClickListener(listener);
|
||||
|
||||
final Preference.OnPreferenceClickListener listener = getListener();
|
||||
github.setOnPreferenceClickListener(listener);
|
||||
telegram.setOnPreferenceClickListener(listener);
|
||||
changelog.setOnPreferenceClickListener(listener);
|
||||
about.setOnPreferenceClickListener(listener);
|
||||
}
|
||||
|
||||
private GeneralPreferences getGeneralFrag() {
|
||||
return mGeneralPrefs == null ? mGeneralPrefs = new GeneralPreferences() : mGeneralPrefs;
|
||||
}
|
||||
|
||||
private EditorPreferences getEditorFrag() {
|
||||
return mEditorPreferences == null ? mEditorPreferences = new EditorPreferences() : mEditorPreferences;
|
||||
}
|
||||
|
||||
private BuildPreferences getBuildFrag() {
|
||||
return mBuildPreferences == null ? mBuildPreferences = new BuildPreferences() : mBuildPreferences;
|
||||
}
|
||||
|
||||
private void showChangelog() {
|
||||
final int dp8 = SizeUtils.dp2px(8);
|
||||
final int dp16 = SizeUtils.dp2px(16);
|
||||
}
|
||||
|
||||
private GeneralPreferences getGeneralFrag() {
|
||||
return mGeneralPrefs == null ? mGeneralPrefs = new GeneralPreferences() : mGeneralPrefs;
|
||||
}
|
||||
|
||||
private EditorPreferences getEditorFrag() {
|
||||
return mEditorPreferences == null
|
||||
? mEditorPreferences = new EditorPreferences()
|
||||
: mEditorPreferences;
|
||||
}
|
||||
|
||||
private BuildPreferences getBuildFrag() {
|
||||
return mBuildPreferences == null
|
||||
? mBuildPreferences = new BuildPreferences()
|
||||
: mBuildPreferences;
|
||||
}
|
||||
|
||||
private void showChangelog() {
|
||||
final int dp8 = SizeUtils.dp2px(8);
|
||||
final int dp16 = SizeUtils.dp2px(16);
|
||||
final ScrollView vScroll = new ScrollView(getContext());
|
||||
vScroll.setFillViewport(true);
|
||||
vScroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
|
||||
|
||||
final HorizontalScrollView scroll = new HorizontalScrollView(getContext());
|
||||
scroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
|
||||
scroll.setFillViewport(true);
|
||||
scroll.setPadding(dp16, dp8, dp16, dp8);
|
||||
scroll.setPaddingRelative(dp16, dp8, dp16, dp8);
|
||||
|
||||
final TextView text = new TextView(getContext());
|
||||
text.setText(CHANGELOG == null ? CHANGELOG = changelogAsString() : CHANGELOG);
|
||||
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
text.setTextColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
|
||||
scroll.removeAllViews();
|
||||
vScroll.removeAllViews();
|
||||
scroll.addView(text, new ViewGroup.LayoutParams(-1, -1));
|
||||
vScroll.addView(scroll);
|
||||
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (getContext ());
|
||||
builder.setTitle(R.string.pref_changelog);
|
||||
builder.setView(vScroll);
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private String changelogAsString() {
|
||||
try {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
JSONArray arr = new JSONArray(ResourceUtils.readAssets2String("changelog.json"));
|
||||
for(int i =0;i<arr.length();i++) {
|
||||
JSONObject version = arr.getJSONObject(i);
|
||||
sb.append("v".concat(version.getString("version")), new StyleSpan(Typeface.BOLD), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
JSONArray changes = version.getJSONArray("changes");
|
||||
for(int j=0;j<changes.length();j++) {
|
||||
sb.append("\n ");
|
||||
sb.append("\u2022 ".concat(changes.getString(j)));
|
||||
}
|
||||
sb.append("\n\n");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Throwable th) {
|
||||
return getString(R.string.msg_failed_get_changelog);
|
||||
}
|
||||
}
|
||||
|
||||
private Preference.OnPreferenceClickListener getListener() {
|
||||
return new Preference.OnPreferenceClickListener(){
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p1) {
|
||||
final String key = p1.getKey();
|
||||
if(key.equals(KEY_CHANGELOG)) {
|
||||
showChangelog();
|
||||
} else if(key.equals(KEY_TELEGRAM)) {
|
||||
StudioApp.getInstance().openTelegramGroup();
|
||||
} else if(key.equals(KEY_ISSUES)) {
|
||||
StudioApp.getInstance().openGitHub ();
|
||||
} else if(key.equals(KEY_ABOUT)) {
|
||||
final HorizontalScrollView scroll = new HorizontalScrollView(getContext());
|
||||
scroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
|
||||
scroll.setFillViewport(true);
|
||||
scroll.setPadding(dp16, dp8, dp16, dp8);
|
||||
scroll.setPaddingRelative(dp16, dp8, dp16, dp8);
|
||||
|
||||
final TextView text = new TextView(getContext());
|
||||
text.setText(CHANGELOG == null ? CHANGELOG = changelogAsString() : CHANGELOG);
|
||||
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
text.setTextColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
|
||||
scroll.removeAllViews();
|
||||
vScroll.removeAllViews();
|
||||
scroll.addView(text, new ViewGroup.LayoutParams(-1, -1));
|
||||
vScroll.addView(scroll);
|
||||
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(getContext());
|
||||
builder.setTitle(R.string.pref_changelog);
|
||||
builder.setView(vScroll);
|
||||
builder.setPositiveButton(android.R.string.ok, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private String changelogAsString() {
|
||||
try {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
JSONArray arr = new JSONArray(ResourceUtils.readAssets2String("changelog.json"));
|
||||
for (int i = 0; i < arr.length(); i++) {
|
||||
JSONObject version = arr.getJSONObject(i);
|
||||
sb.append(
|
||||
"v".concat(version.getString("version")),
|
||||
new StyleSpan(Typeface.BOLD),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
JSONArray changes = version.getJSONArray("changes");
|
||||
for (int j = 0; j < changes.length(); j++) {
|
||||
sb.append("\n ");
|
||||
sb.append("\u2022 ".concat(changes.getString(j)));
|
||||
}
|
||||
sb.append("\n\n");
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Throwable th) {
|
||||
return getString(R.string.msg_failed_get_changelog);
|
||||
}
|
||||
}
|
||||
|
||||
private Preference.OnPreferenceClickListener getListener() {
|
||||
return new Preference.OnPreferenceClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference p1) {
|
||||
final String key = p1.getKey();
|
||||
if (key.equals(KEY_CHANGELOG)) {
|
||||
showChangelog();
|
||||
} else if (key.equals(KEY_TELEGRAM)) {
|
||||
StudioApp.getInstance().openTelegramGroup();
|
||||
} else if (key.equals(KEY_ISSUES)) {
|
||||
StudioApp.getInstance().openGitHub();
|
||||
} else if (key.equals(KEY_ABOUT)) {
|
||||
startActivity(new Intent(getContext(), AboutActivity.class));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ import java.util.Objects;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AttrEditorSheet extends BottomSheetDialogFragment implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
public class AttrEditorSheet extends BottomSheetDialogFragment
|
||||
implements SimpleIconTextAdapter.OnBindListener<IconTextListItem> {
|
||||
|
||||
private static final List<IconTextListItem> VIEW_ACTIONS = new ArrayList<> ();
|
||||
private static final Logger LOG = Logger.instance ("AttrBottomSheet");
|
||||
@@ -81,8 +82,13 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = LayoutAttrEditorSheetBinding.inflate (LayoutInflater.from (getContext ()), container, false);
|
||||
public View onCreateView (
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
this.binding =
|
||||
LayoutAttrEditorSheetBinding.inflate (
|
||||
LayoutInflater.from (getContext ()), container, false);
|
||||
return binding.getRoot ();
|
||||
}
|
||||
|
||||
@@ -91,11 +97,15 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (VIEW_ACTIONS.isEmpty ()) {
|
||||
Collections.addAll (VIEW_ACTIONS,
|
||||
IconTextListItem.create (getString (R.string.msg_viewaction_add_attr), R.drawable.ic_add),
|
||||
IconTextListItem.create (getString (R.string.title_viewaction_delete), R.drawable.ic_delete),
|
||||
IconTextListItem.create (getString (R.string.msg_viewaction_select_parent), R.drawable.ic_view_select_parent)
|
||||
);
|
||||
Collections.addAll (
|
||||
VIEW_ACTIONS,
|
||||
IconTextListItem.create (
|
||||
getString (R.string.msg_viewaction_add_attr), R.drawable.ic_add),
|
||||
IconTextListItem.create (
|
||||
getString (R.string.title_viewaction_delete), R.drawable.ic_delete),
|
||||
IconTextListItem.create (
|
||||
getString (R.string.msg_viewaction_select_parent),
|
||||
R.drawable.ic_view_select_parent));
|
||||
}
|
||||
|
||||
binding.valueEditorLayout.closeButton.setOnClickListener (v -> hideValueEditorLayout ());
|
||||
@@ -124,7 +134,8 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
}
|
||||
|
||||
private void setupViewData () {
|
||||
binding.actionsList.setAdapter (new SimpleIconTextAdapter (VIEW_ACTIONS).setOnBindListener (this));
|
||||
binding.actionsList.setAdapter (
|
||||
new SimpleIconTextAdapter (VIEW_ACTIONS).setOnBindListener (this));
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
@@ -132,19 +143,20 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
}
|
||||
|
||||
binding.widgetName.setText (this.selectedView.getXmlTag ());
|
||||
binding.attrList.setAdapter (new XMLAttributeListAdapter (
|
||||
this.selectedView.getAttributes ()
|
||||
.stream ()
|
||||
.map (XMLAttribute::new)
|
||||
.collect (Collectors.toList ()), this::onAttrClick)
|
||||
);
|
||||
binding.attrList.setAdapter (
|
||||
new XMLAttributeListAdapter (
|
||||
this.selectedView.getAttributes ().stream ()
|
||||
.map (XMLAttribute::new)
|
||||
.collect (Collectors.toList ()),
|
||||
this::onAttrClick));
|
||||
}
|
||||
|
||||
public void setSelectedView (IView view) {
|
||||
this.selectedView = view;
|
||||
}
|
||||
|
||||
private void onAttrClick (LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
||||
private void onAttrClick (
|
||||
LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
@@ -153,7 +165,8 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
|
||||
final var format = attribute.findFormat ();
|
||||
if (format == -1) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_attr_format), Toaster.Type.ERROR);
|
||||
StudioApp.getInstance ()
|
||||
.toast (getString (R.string.msg_no_attr_format), Toaster.Type.ERROR);
|
||||
LOG.error (getString (R.string.msg_no_attr_format), attribute);
|
||||
return;
|
||||
}
|
||||
@@ -169,9 +182,11 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
private void showEditorDialog (@NonNull XMLAttribute attribute) {
|
||||
final var attr = attribute.getAttr ();
|
||||
final var values = attr.possibleValues.toArray (new String[0]);
|
||||
final AttributeDialogs.OnClickListener onDone = (dialog, which, newValue) -> applyNewValue (attribute, newValue);
|
||||
final AttributeDialogs.OnClickListener onDone =
|
||||
(dialog, which, newValue) -> applyNewValue (attribute, newValue);
|
||||
|
||||
final ColorPickerView.OnPickListener mPickListener = (color, hexCode) -> applyNewValue (attribute, hexCode);
|
||||
final ColorPickerView.OnPickListener mPickListener =
|
||||
(color, hexCode) -> applyNewValue (attribute, hexCode);
|
||||
|
||||
AlertDialog dialog = null;
|
||||
if (attribute.hasFormat (DIMENSION)) {
|
||||
@@ -197,16 +212,21 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
}
|
||||
|
||||
private void applyNewValue (@NonNull IAttribute attribute, String newValue) {
|
||||
if (this.selectedView.hasAttribute (attribute.getNamespace (), attribute.getAttributeName ())) {
|
||||
if (!this.selectedView.updateAttribute (attribute.getNamespace (), attribute.getAttributeName (), newValue)) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_attr_not_updated), Toaster.Type.ERROR);
|
||||
if (this.selectedView.hasAttribute (
|
||||
attribute.getNamespace (), attribute.getAttributeName ())) {
|
||||
if (!this.selectedView.updateAttribute (
|
||||
attribute.getNamespace (), attribute.getAttributeName (), newValue)) {
|
||||
StudioApp.getInstance ()
|
||||
.toast (getString (R.string.msg_attr_not_updated), Toaster.Type.ERROR);
|
||||
} else {
|
||||
// Update the view data
|
||||
// This will make sure that the attributes list has been updated
|
||||
setupViewData ();
|
||||
}
|
||||
} else {
|
||||
addAttribute (new UiAttribute (attribute.getNamespace (), attribute.getAttributeName (), newValue));
|
||||
addAttribute (
|
||||
new UiAttribute (
|
||||
attribute.getNamespace (), attribute.getAttributeName (), newValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +237,8 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBind (IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
||||
public void postBind (
|
||||
IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
binding.getRoot ().setOnClickListener (v -> onViewActionClick (position));
|
||||
}
|
||||
@@ -269,7 +290,8 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
attributes.addAll (parentLayoutParams.attributes);
|
||||
}
|
||||
|
||||
final var paramSuperclasses = widgetInfo.getLayoutParamSuperClasses (parentWidget.name);
|
||||
final var paramSuperclasses =
|
||||
widgetInfo.getLayoutParamSuperClasses (parentWidget.name);
|
||||
if (paramSuperclasses != null) {
|
||||
for (var superclass : paramSuperclasses) {
|
||||
if ("java.lang.Object".equals (superclass)) {
|
||||
@@ -279,8 +301,10 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
final var split = superclass.split ("\\.");
|
||||
final var simpleClassName = split[split.length - 2];
|
||||
var paramName = split[split.length - 1];
|
||||
paramName = paramName.substring (0, paramName.length () - "Params".length ());
|
||||
final var superParamEntry = attrs.getStyle (simpleClassName + "_" + paramName);
|
||||
paramName =
|
||||
paramName.substring (0, paramName.length () - "Params".length ());
|
||||
final var superParamEntry =
|
||||
attrs.getStyle (simpleClassName + "_" + paramName);
|
||||
if (superParamEntry != null) {
|
||||
attributes.addAll (superParamEntry.attributes);
|
||||
}
|
||||
@@ -294,20 +318,31 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
sheet.show (getChildFragmentManager (), "attr_list_sheet");
|
||||
|
||||
} else if (position == 1) { // Delete
|
||||
DialogUtils.newYesNoDialog (getContext (), (dialog, which) -> {
|
||||
var handled = selectedView.removeFromParent ();
|
||||
if (!handled) {
|
||||
handled = mDeletionFailedListener != null && mDeletionFailedListener.onDeletionFailed (this.selectedView);
|
||||
}
|
||||
if (!handled) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_view_deletion_failed), Toaster.Type.ERROR);
|
||||
} else {
|
||||
dismiss ();
|
||||
}
|
||||
}, (dialog, which) -> dialog.dismiss ()).show ();
|
||||
DialogUtils.newYesNoDialog (
|
||||
getContext (),
|
||||
(dialog, which) -> {
|
||||
var handled = selectedView.removeFromParent ();
|
||||
if (!handled) {
|
||||
handled =
|
||||
mDeletionFailedListener != null
|
||||
&& mDeletionFailedListener.onDeletionFailed (
|
||||
this.selectedView);
|
||||
}
|
||||
if (!handled) {
|
||||
StudioApp.getInstance ()
|
||||
.toast (
|
||||
getString (R.string.msg_view_deletion_failed),
|
||||
Toaster.Type.ERROR);
|
||||
} else {
|
||||
dismiss ();
|
||||
}
|
||||
},
|
||||
(dialog, which) -> dialog.dismiss ())
|
||||
.show ();
|
||||
} else if (position == 2) { // Select parent
|
||||
if (this.selectedView.getParent () == null) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_view_parent), Toaster.Type.ERROR);
|
||||
StudioApp.getInstance ()
|
||||
.toast (getString (R.string.msg_no_view_parent), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -343,13 +378,14 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener can be used to get notified when we fail to remove
|
||||
* a view from its parent. This is used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
|
||||
* <p>
|
||||
* When the user tries to remove the outermost view of the inflated layout,
|
||||
* AttrEditorSheet fails to remove the view from its parent ({@link IView#getParent()} is null for root XML layout).
|
||||
* In this case, DesignerActivity check if the view that we were trying to delete is the root layout or not.
|
||||
* If it is the root layout, then it deletes the layout from the layout container.
|
||||
* A listener can be used to get notified when we fail to remove a view from its parent. This is
|
||||
* used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
|
||||
*
|
||||
* <p>When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet
|
||||
* fails to remove the view from its parent ({@link IView#getParent()} is null for root XML
|
||||
* layout). In this case, DesignerActivity check if the view that we were trying to delete is
|
||||
* the root layout or not. If it is the root layout, then it deletes the layout from the layout
|
||||
* container.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@@ -361,4 +397,4 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
|
||||
*/
|
||||
boolean onDeletionFailed (IView view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,84 +41,91 @@ import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A bottom sheet dialog for showing a simple list of
|
||||
* text icon pair.
|
||||
* A bottom sheet dialog for showing a simple list of text icon pair.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
* @see com.itsaky.androidide.models.IconTextListItem
|
||||
*/
|
||||
public class AttributeListSheet extends BottomSheetDialogFragment {
|
||||
|
||||
|
||||
private LayoutAddAttrSheetBinding binding;
|
||||
private Consumer<Attr> clickConsumer;
|
||||
private List<Attr> mItems;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate (@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
setStyle (DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = LayoutAddAttrSheetBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
this.binding = LayoutAddAttrSheetBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
binding.searchBar.setOnQueryTextListener (new SearchView.OnQueryTextListener () {
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit (String query) {
|
||||
return filterAttributes (query.trim ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange (String newText) {
|
||||
return filterAttributes (newText.trim ());
|
||||
}
|
||||
});
|
||||
binding.attrList.setAdapter (new AttrListAdapter (clickConsumer, mItems));
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.searchBar.setOnQueryTextListener(
|
||||
new SearchView.OnQueryTextListener() {
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return filterAttributes(query.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
return filterAttributes(newText.trim());
|
||||
}
|
||||
});
|
||||
binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, mItems));
|
||||
}
|
||||
|
||||
private boolean filterAttributes (@NonNull String query) {
|
||||
if (mItems == null || TextUtils.isEmpty (query)) {
|
||||
update ();
|
||||
|
||||
private boolean filterAttributes(@NonNull String query) {
|
||||
if (mItems == null || TextUtils.isEmpty(query)) {
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
final var filtered = this.mItems
|
||||
.stream ()
|
||||
.filter (attr -> attr.name.toLowerCase (Locale.ROOT).contains (query.toLowerCase (Locale.ROOT)))
|
||||
.collect (Collectors.toList ());
|
||||
|
||||
update (filtered);
|
||||
|
||||
final var filtered =
|
||||
this.mItems.stream()
|
||||
.filter(
|
||||
attr ->
|
||||
attr.name
|
||||
.toLowerCase(Locale.ROOT)
|
||||
.contains(query.toLowerCase(Locale.ROOT)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
update(filtered);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setItems (@Nullable List<Attr> items) {
|
||||
|
||||
public void setItems(@Nullable List<Attr> items) {
|
||||
if (items == null) {
|
||||
items = new ArrayList<> ();
|
||||
items = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
mItems = items;
|
||||
update ();
|
||||
update();
|
||||
}
|
||||
|
||||
public void onItemClick (Consumer<Attr> onClick) {
|
||||
|
||||
public void onItemClick(Consumer<Attr> onClick) {
|
||||
this.clickConsumer = onClick;
|
||||
}
|
||||
|
||||
public void update () {
|
||||
update (mItems);
|
||||
|
||||
public void update() {
|
||||
update(mItems);
|
||||
}
|
||||
|
||||
public void update (List<Attr> items) {
|
||||
|
||||
public void update(List<Attr> items) {
|
||||
if (binding != null) {
|
||||
binding.attrList.setAdapter (new AttrListAdapter (clickConsumer, items));
|
||||
binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-46
@@ -30,84 +30,84 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.itsaky.androidide.databinding.LayoutSheetBinding;
|
||||
|
||||
public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment {
|
||||
|
||||
|
||||
protected Dialog mDialog;
|
||||
protected boolean shadowEnabled = true;
|
||||
protected boolean titleEnabled = true;
|
||||
private LayoutSheetBinding binding;
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog (Bundle savedInstanceState) {
|
||||
mDialog = super.onCreateDialog (savedInstanceState);
|
||||
mDialog.setOnShowListener (p1 -> onShow ());
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
mDialog = super.onCreateDialog(savedInstanceState);
|
||||
mDialog.setOnShowListener(p1 -> onShow());
|
||||
return mDialog;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutSheetBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = LayoutSheetBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
bind (binding.container);
|
||||
binding.title.setText (getTitle ());
|
||||
binding.title.setOnClickListener (v -> handleTitleClick ());
|
||||
|
||||
if (shouldHideTitle ()) {
|
||||
binding.getRoot ().removeView (binding.title);
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
bind(binding.container);
|
||||
binding.title.setText(getTitle());
|
||||
binding.title.setOnClickListener(v -> handleTitleClick());
|
||||
|
||||
if (shouldHideTitle()) {
|
||||
binding.getRoot().removeView(binding.title);
|
||||
}
|
||||
|
||||
|
||||
if (!shadowEnabled) {
|
||||
binding.shadow.setVisibility (View.GONE);
|
||||
binding.shadow.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTitleClick () {
|
||||
if (isCancelable ()) {
|
||||
dismiss ();
|
||||
|
||||
private void handleTitleClick() {
|
||||
if (isCancelable()) {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShowing () {
|
||||
return mDialog != null && mDialog.isShowing ();
|
||||
|
||||
public boolean isShowing() {
|
||||
return mDialog != null && mDialog.isShowing();
|
||||
}
|
||||
|
||||
protected boolean shouldHideTitle () {
|
||||
|
||||
protected boolean shouldHideTitle() {
|
||||
return !titleEnabled;
|
||||
}
|
||||
|
||||
protected String getTitle () {
|
||||
|
||||
protected String getTitle() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public BaseBottomSheetFragment setTitle (int title) {
|
||||
|
||||
public BaseBottomSheetFragment setTitle(int title) {
|
||||
if (binding != null) {
|
||||
binding.title.setText (title);
|
||||
binding.title.setText(title);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseBottomSheetFragment setTitle (String title) {
|
||||
|
||||
public BaseBottomSheetFragment setTitle(String title) {
|
||||
if (binding != null) {
|
||||
binding.title.setText (title);
|
||||
binding.title.setText(title);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setShowShadow (boolean enabled) {
|
||||
|
||||
public void setShowShadow(boolean enabled) {
|
||||
this.shadowEnabled = enabled;
|
||||
}
|
||||
|
||||
public void setShowTitle (boolean enabled) {
|
||||
|
||||
public void setShowTitle(boolean enabled) {
|
||||
this.titleEnabled = enabled;
|
||||
}
|
||||
|
||||
protected void onShow () {
|
||||
}
|
||||
|
||||
protected abstract void bind (LinearLayout container);
|
||||
|
||||
protected void onShow() {}
|
||||
|
||||
protected abstract void bind(LinearLayout container);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments.sheets;
|
||||
|
||||
@@ -26,74 +25,76 @@ import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.adapters.OptionsSheetAdapter;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OptionsListFragment extends BaseBottomSheetFragment {
|
||||
|
||||
private RecyclerView mList;
|
||||
private OnOptionsClickListener listener;
|
||||
private final List<SheetOption> mOptions = new ArrayList<>();
|
||||
|
||||
protected boolean dismissOnItemClick = true;
|
||||
|
||||
@Override
|
||||
protected void bind(LinearLayout container) {
|
||||
mList = new RecyclerView(getContext());
|
||||
container.removeAllViews();
|
||||
container.addView(mList, new LinearLayout.LayoutParams(-1, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mList.setAdapter(new OptionsSheetAdapter(mOptions, __ -> {
|
||||
if(dismissOnItemClick)
|
||||
dismiss();
|
||||
if(listener != null)
|
||||
listener.onOptionsClick(__);
|
||||
}));
|
||||
}
|
||||
|
||||
public OptionsListFragment setOnOptionsClickListener(OnOptionsClickListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsListFragment addOption(SheetOption option) {
|
||||
if(!mOptions.contains(option)) {
|
||||
mOptions.add(option);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsListFragment removeOption(int optionIndex) {
|
||||
return removeOption(mOptions.get(optionIndex));
|
||||
}
|
||||
|
||||
public OptionsListFragment removeOption(SheetOption option) {
|
||||
mOptions.remove(option);
|
||||
return this;
|
||||
}
|
||||
|
||||
private RecyclerView mList;
|
||||
private OnOptionsClickListener listener;
|
||||
private final List<SheetOption> mOptions = new ArrayList<>();
|
||||
|
||||
protected boolean dismissOnItemClick = true;
|
||||
|
||||
@Override
|
||||
protected void bind(LinearLayout container) {
|
||||
mList = new RecyclerView(getContext());
|
||||
container.removeAllViews();
|
||||
container.addView(mList, new LinearLayout.LayoutParams(-1, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mList.setAdapter(
|
||||
new OptionsSheetAdapter(
|
||||
mOptions,
|
||||
__ -> {
|
||||
if (dismissOnItemClick) dismiss();
|
||||
if (listener != null) listener.onOptionsClick(__);
|
||||
}));
|
||||
}
|
||||
|
||||
public OptionsListFragment setOnOptionsClickListener(OnOptionsClickListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsListFragment addOption(SheetOption option) {
|
||||
if (!mOptions.contains(option)) {
|
||||
mOptions.add(option);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsListFragment removeOption(int optionIndex) {
|
||||
return removeOption(mOptions.get(optionIndex));
|
||||
}
|
||||
|
||||
public OptionsListFragment removeOption(SheetOption option) {
|
||||
mOptions.remove(option);
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsListFragment setDismissOnItemClick(boolean dissmiss) {
|
||||
this.dismissOnItemClick = dissmiss;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return getString(com.itsaky.androidide.R.string.file_options);
|
||||
}
|
||||
|
||||
public static interface OnOptionsClickListener {
|
||||
void onOptionsClick(SheetOption option);
|
||||
}
|
||||
@Override
|
||||
protected String getTitle() {
|
||||
return getString(com.itsaky.androidide.R.string.file_options);
|
||||
}
|
||||
|
||||
public static interface OnOptionsClickListener {
|
||||
void onOptionsClick(SheetOption option);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.fragments.sheets;
|
||||
|
||||
@@ -26,106 +25,106 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import com.itsaky.androidide.databinding.LayoutProgressSheetBinding;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.databinding.LayoutProgressSheetBinding;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class ProgressSheet extends BaseBottomSheetFragment {
|
||||
|
||||
private LayoutProgressSheetBinding binding;
|
||||
private Drawable drawable = null;
|
||||
private String message = "";
|
||||
private String subMessage = "";
|
||||
private boolean subMessageEnabled = false;
|
||||
private boolean welcomTextEnabled = false;
|
||||
|
||||
private final Logger LOG = Logger.instance ("ProgressSheet");
|
||||
|
||||
@Override
|
||||
protected void bind(LinearLayout container) {
|
||||
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
|
||||
container.addView(binding.getRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
binding.message.setText(message);
|
||||
if(subMessageEnabled) {
|
||||
binding.subMessage.setText(subMessage);
|
||||
binding.subMessage.setVisibility(View.VISIBLE);
|
||||
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.removeRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
} else {
|
||||
binding.subMessage.setVisibility(View.GONE);
|
||||
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
}
|
||||
|
||||
if(!welcomTextEnabled) {
|
||||
binding.welcomeText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
private LayoutProgressSheetBinding binding;
|
||||
private Drawable drawable = null;
|
||||
private String message = "";
|
||||
private String subMessage = "";
|
||||
private boolean subMessageEnabled = false;
|
||||
private boolean welcomTextEnabled = false;
|
||||
|
||||
@Override
|
||||
protected boolean shouldHideTitle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
|
||||
this.welcomTextEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setSubMessageEnabled(boolean enabled) {
|
||||
this.subMessageEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setSubMessage(String msg) {
|
||||
this.subMessage = msg;
|
||||
if(isShowing()) {
|
||||
binding.subMessage.setText(msg);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setMessage(String message) {
|
||||
this.message = message;
|
||||
if(isShowing()) {
|
||||
binding.message.setText(message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setProgressDrawable(Drawable drawable) {
|
||||
this.drawable = drawable;
|
||||
if(isShowing()) {
|
||||
binding.progress.setIndeterminateDrawable(drawable);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
private final Logger LOG = Logger.instance("ProgressSheet");
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if(isShowing())
|
||||
super.dismiss();
|
||||
}
|
||||
@Override
|
||||
protected void bind(LinearLayout container) {
|
||||
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
|
||||
container.addView(binding.getRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
binding.message.setText(message);
|
||||
if (subMessageEnabled) {
|
||||
binding.subMessage.setText(subMessage);
|
||||
binding.subMessage.setVisibility(View.VISIBLE);
|
||||
RelativeLayout.LayoutParams p =
|
||||
(RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.removeRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
} else {
|
||||
binding.subMessage.setVisibility(View.GONE);
|
||||
RelativeLayout.LayoutParams p =
|
||||
(RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
}
|
||||
|
||||
if (!welcomTextEnabled) {
|
||||
binding.welcomeText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldHideTitle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
|
||||
this.welcomTextEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setSubMessageEnabled(boolean enabled) {
|
||||
this.subMessageEnabled = enabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setSubMessage(String msg) {
|
||||
this.subMessage = msg;
|
||||
if (isShowing()) {
|
||||
binding.subMessage.setText(msg);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setMessage(String message) {
|
||||
this.message = message;
|
||||
if (isShowing()) {
|
||||
binding.message.setText(message);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProgressSheet setProgressDrawable(Drawable drawable) {
|
||||
this.drawable = drawable;
|
||||
if (isShowing()) {
|
||||
binding.progress.setIndeterminateDrawable(drawable);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if (isShowing()) super.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -32,7 +32,7 @@ import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import io.github.rosemoe.sora.lang.EmptyLanguage;
|
||||
|
||||
public class TextSheetFragment extends BaseBottomSheetFragment {
|
||||
|
||||
|
||||
private SpannableStringBuilder outputBuilder;
|
||||
private IDEEditor editor;
|
||||
private boolean textSelectable;
|
||||
@@ -49,7 +49,7 @@ public class TextSheetFragment extends BaseBottomSheetFragment {
|
||||
container.setPaddingRelative(0, 0, 0, 0);
|
||||
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(-1, 0);
|
||||
p.weight = 1.0f;
|
||||
if(e.getParent() != null && e.getParent() instanceof ViewGroup) {
|
||||
if (e.getParent() != null && e.getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) e.getParent()).removeView(e);
|
||||
}
|
||||
container.addView(getEditor(), p);
|
||||
@@ -65,10 +65,9 @@ public class TextSheetFragment extends BaseBottomSheetFragment {
|
||||
super.onShow();
|
||||
editor.setText(getOutputBuilder());
|
||||
}
|
||||
|
||||
|
||||
private void scrollToBottom() {
|
||||
if(getActivity() != null)
|
||||
getActivity().runOnUiThread(() -> editor.goToEnd());
|
||||
if (getActivity() != null) getActivity().runOnUiThread(() -> editor.goToEnd());
|
||||
}
|
||||
|
||||
public TextSheetFragment setTitleText(int res) {
|
||||
@@ -86,26 +85,33 @@ public class TextSheetFragment extends BaseBottomSheetFragment {
|
||||
}
|
||||
|
||||
public void append(String text, int spanColor, boolean appendLine) {
|
||||
if(text != null && text.trim().length() > 0) {
|
||||
appendInternal(appendLine && !text.endsWith("\n") ? text.concat("\n") : text, spanColor);
|
||||
if (text != null && text.trim().length() > 0) {
|
||||
appendInternal(
|
||||
appendLine && !text.endsWith("\n") ? text.concat("\n") : text, spanColor);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendInternal(String text, int spanColor) {
|
||||
SpannableString str = new SpannableString(text);
|
||||
str.setSpan(new ForegroundColorSpan(spanColor == -1 ? Color.WHITE : spanColor), 0, text.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
str.setSpan(
|
||||
new ForegroundColorSpan(spanColor == -1 ? Color.WHITE : spanColor),
|
||||
0,
|
||||
text.length(),
|
||||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
getOutputBuilder().append(str);
|
||||
if(mDialog != null && mDialog.isShowing()) {
|
||||
if (mDialog != null && mDialog.isShowing()) {
|
||||
setToEditor(str);
|
||||
}
|
||||
}
|
||||
|
||||
private void setToEditor(SpannableString line) {
|
||||
if(getActivity() != null)
|
||||
getActivity().runOnUiThread(() -> {
|
||||
editor.append(line);
|
||||
scrollToBottom();
|
||||
});
|
||||
if (getActivity() != null)
|
||||
getActivity()
|
||||
.runOnUiThread(
|
||||
() -> {
|
||||
editor.append(line);
|
||||
scrollToBottom();
|
||||
});
|
||||
}
|
||||
|
||||
private IDEEditor getEditor() {
|
||||
@@ -113,10 +119,10 @@ public class TextSheetFragment extends BaseBottomSheetFragment {
|
||||
}
|
||||
|
||||
private IDEEditor createEditor() {
|
||||
editor = new IDEEditor (getContext());
|
||||
editor = new IDEEditor(getContext());
|
||||
editor.setEditable(false);
|
||||
editor.setDividerWidth(0);
|
||||
editor.setEditorLanguage(new EmptyLanguage ());
|
||||
editor.setEditorLanguage(new EmptyLanguage());
|
||||
editor.setWordwrap(false);
|
||||
editor.setUndoEnabled(false);
|
||||
editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
/************************************************************************************
|
||||
* 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.handlers;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
@@ -30,6 +31,7 @@ import com.itsaky.androidide.tasks.GradleTask;
|
||||
import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -39,33 +41,33 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BuildServiceHandler extends IDEHandler implements BuildListener {
|
||||
|
||||
|
||||
private IDEService service;
|
||||
private boolean shouldInstallApk = false;
|
||||
|
||||
public BuildServiceHandler(Provider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
|
||||
public IDEService getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if(this.activity() == null) {
|
||||
if (this.activity() == null) {
|
||||
throwNPE();
|
||||
}
|
||||
|
||||
|
||||
AndroidProject project = provider.provideAndroidProject();
|
||||
if(project == null) {
|
||||
if (project == null) {
|
||||
throwNPE();
|
||||
}
|
||||
|
||||
if(project.getProjectPath() == null) {
|
||||
|
||||
if (project.getProjectPath() == null) {
|
||||
throwNPE();
|
||||
}
|
||||
|
||||
|
||||
this.service = new IDEService(new File(project.getProjectPath()));
|
||||
this.service.setListener(this);
|
||||
}
|
||||
@@ -75,159 +77,182 @@ public class BuildServiceHandler extends IDEHandler implements BuildListener {
|
||||
this.service.stopAllDaemons();
|
||||
this.service.exit();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBuildModified() {
|
||||
// Does nothing currently
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule) {
|
||||
project.iconPath = provider.provideAndroidProject().getIconPath();
|
||||
activity().setIDEProject(project);
|
||||
|
||||
if(appModule.isPresent()) {
|
||||
|
||||
if (appModule.isPresent()) {
|
||||
IDEModule app = appModule.get();
|
||||
activity().setStatus(activity().getString(R.string.msg_starting_completion));
|
||||
|
||||
final List<String> paths = app.dependencies.stream().filter(this::isClasspathValid).collect(Collectors.toList());
|
||||
final List<String> paths =
|
||||
app.dependencies.stream()
|
||||
.filter(this::isClasspathValid)
|
||||
.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()) {
|
||||
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().updateServices ();
|
||||
activity().updateServices();
|
||||
} 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));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRunTask(GradleTask task, String name) {
|
||||
if(task != null && task.canOutput()) {
|
||||
if (task != null && task.canOutput()) {
|
||||
activity().setStatus(service.typeString(task.getTaskID()) + " " + name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBuildSuccessful(GradleTask task, String msg) {
|
||||
appendOutput(task, msg);
|
||||
if(task == null || activity() == null) return;
|
||||
if(task.canOutput()) {
|
||||
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 (shouldInstallApk && task instanceof ApkGeneratingTask) {
|
||||
final IDEProject project = activity().provideIDEProject();
|
||||
final Optional<IDEModule> app = project.getModuleByPath(":app"); // TODO Handle multiple application modules
|
||||
if(app.isPresent()) {
|
||||
final Optional<IDEModule> 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));
|
||||
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()
|
||||
.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) {
|
||||
appendOutput(task, msg);
|
||||
if(task == null) return;
|
||||
if(task.canOutput()) {
|
||||
if (task == null) return;
|
||||
if (task.canOutput()) {
|
||||
activity().setStatus(msg);
|
||||
}
|
||||
|
||||
activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
|
||||
|
||||
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()) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
public void setShouldInstallApk (boolean install) {
|
||||
public void setShouldInstallApk(boolean install) {
|
||||
this.shouldInstallApk = install;
|
||||
}
|
||||
|
||||
public void assembleDebug (boolean shouldInstallApk) {
|
||||
public void assembleDebug(boolean shouldInstallApk) {
|
||||
setShouldInstallApk(shouldInstallApk);
|
||||
getService().assembleDebug(shouldInstallApk);
|
||||
}
|
||||
|
||||
|
||||
private boolean isClasspathValid(String path) {
|
||||
if(path == null || path.trim().length() <= 0 || path.trim().equals("/")) return false;
|
||||
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");
|
||||
&& file.getName().endsWith(".jar")
|
||||
/** Release R.jar shouldn't be included in classpath */
|
||||
&& !file.getAbsolutePath().endsWith("/release/R.jar");
|
||||
}
|
||||
|
||||
private void installApks (final Set<File> apks) {
|
||||
if (activity () == null || apks == null || apks.isEmpty()) {
|
||||
|
||||
private void installApks(final Set<File> apks) {
|
||||
if (activity() == null || apks == null || apks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(apks.size() == 1) {
|
||||
|
||||
if (apks.size() == 1) {
|
||||
activity().install(apks.iterator().next());
|
||||
} else {
|
||||
final List<File> files = new ArrayList<File> (apks);
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final List<File> files = new ArrayList<File>(apks);
|
||||
final MaterialAlertDialogBuilder builder =
|
||||
DialogUtils.newMaterialDialogBuilder(activity());
|
||||
builder.setTitle(activity().getString(R.string.title_install_apks));
|
||||
builder.setItems(getNames(files), (d, w) -> {
|
||||
d.dismiss();
|
||||
activity().install(files.get(w));
|
||||
});
|
||||
builder.setItems(
|
||||
getNames(files),
|
||||
(d, w) -> {
|
||||
d.dismiss();
|
||||
activity().install(files.get(w));
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence[] getNames (final Collection<File> apks) {
|
||||
|
||||
private CharSequence[] getNames(final Collection<File> apks) {
|
||||
final String[] names = new String[apks.size()];
|
||||
|
||||
|
||||
int i = 0;
|
||||
for (File apk : apks) {
|
||||
names[i] = apk.getName();
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.handlers;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
@@ -39,76 +39,101 @@ import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileOptionsHandler extends IDEHandler implements OptionsListFragment.OnOptionsClickListener {
|
||||
|
||||
public class FileOptionsHandler extends IDEHandler
|
||||
implements OptionsListFragment.OnOptionsClickListener {
|
||||
|
||||
public static final String RES_PATH_REGEX = "/.*/src/.*/res";
|
||||
public static final String LAYOUT_RES_PATH_REGEX = "/.*/src/.*/res/layout";
|
||||
public static final String MENU_RES_PATH_REGEX = "/.*/src/.*/res/menu";
|
||||
public static final String DRAWABLE_RES_PATH_REGEX = "/.*/src/.*/res/drawable";
|
||||
public static final String JAVA_PATH_REGEX = "/.*/src/.*/java";
|
||||
|
||||
|
||||
public FileOptionsHandler(Provider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if(activity() == null) {
|
||||
if (activity() == null) {
|
||||
throwNPE();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
public void stop() {}
|
||||
|
||||
@Override
|
||||
public void onOptionsClick(SheetOption option) {
|
||||
if(option.extra instanceof File) {
|
||||
if (option.extra instanceof File) {
|
||||
final File f = (File) option.extra;
|
||||
switch(option.id) {
|
||||
case 0 :
|
||||
switch (option.id) {
|
||||
case 0:
|
||||
ClipboardUtils.copyText("[AndroidIDE] Copied File Path", f.getAbsolutePath());
|
||||
activity().getApp().toast(R.string.copied, Toaster.Type.SUCCESS);
|
||||
break;
|
||||
case 1 :
|
||||
case 1:
|
||||
renameFile(f);
|
||||
break;
|
||||
case 2 :
|
||||
case 2:
|
||||
delete(f);
|
||||
break;
|
||||
case 3 :
|
||||
case 3:
|
||||
createNewFile(f);
|
||||
break;
|
||||
case 4 :
|
||||
case 4:
|
||||
createNewFolder(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createNewFile(File f) {
|
||||
createNewFile(f, false);
|
||||
}
|
||||
|
||||
private void createNewFile(final File f, boolean forceUnknownType) {
|
||||
if(forceUnknownType) {
|
||||
if (forceUnknownType) {
|
||||
createNewEmptyFile(f);
|
||||
} else {
|
||||
final boolean isJava = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + JAVA_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isLayoutRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + LAYOUT_RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isMenuRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + MENU_RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isDrawableRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + DRAWABLE_RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
if(isJava) {
|
||||
final boolean isJava =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ JAVA_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isLayoutRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ LAYOUT_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isMenuRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ MENU_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isDrawableRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ DRAWABLE_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
if (isJava) {
|
||||
createJavaClass(f);
|
||||
} else if(isLayoutRes && f.getName().equals("layout")) {
|
||||
} else if (isLayoutRes && f.getName().equals("layout")) {
|
||||
createLayoutRes(f);
|
||||
} else if(isMenuRes && f.getName().equals("menu")) {
|
||||
} else if (isMenuRes && f.getName().equals("menu")) {
|
||||
createMenuRes(f);
|
||||
} else if(isDrawableRes && f.getName().equals("drawable")) {
|
||||
} else if (isDrawableRes && f.getName().equals("drawable")) {
|
||||
createDrawableRes(f);
|
||||
} else if(isRes && f.getName().equals("res")) {
|
||||
} else if (isRes && f.getName().equals("res")) {
|
||||
createNewResource(f);
|
||||
} else {
|
||||
createNewEmptyFile(f);
|
||||
@@ -117,71 +142,93 @@ public class FileOptionsHandler extends IDEHandler implements OptionsListFragmen
|
||||
}
|
||||
|
||||
private void createJavaClass(final File f) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutCreateFileJavaBinding binding = LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
final LayoutCreateFileJavaBinding binding =
|
||||
LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setTitle(R.string.new_java_class);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = Objects.requireNonNull (binding.name.getEditText ()).getText().toString().trim();
|
||||
final String pkgName = ProjectWriter.getPackageName(f);
|
||||
if(pkgName == null || pkgName.trim().length() <= 0) {
|
||||
activity().getApp().toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
|
||||
} else {
|
||||
final int id = binding.typeGroup.getCheckedButtonId();
|
||||
final var javaName = name.endsWith (".java") ? name : name.concat (".java");
|
||||
final var className = !name.contains (".") ? name : name.substring (0, name.lastIndexOf ("."));
|
||||
if(id == binding.typeClass.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaClass(pkgName, className));
|
||||
} else if(id == binding.typeInterface.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaInterface(pkgName, className));
|
||||
} else if(id == binding.typeEnum.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
|
||||
} else if(id == binding.typeActivity.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createActivity(pkgName, className));
|
||||
} else {
|
||||
createFile(f, name, "");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(
|
||||
R.string.text_create,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name =
|
||||
Objects.requireNonNull(binding.name.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final String pkgName = ProjectWriter.getPackageName(f);
|
||||
if (pkgName == null || pkgName.trim().length() <= 0) {
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
|
||||
} else {
|
||||
final int id = binding.typeGroup.getCheckedButtonId();
|
||||
final var javaName = name.endsWith(".java") ? name : name.concat(".java");
|
||||
final var className =
|
||||
!name.contains(".")
|
||||
? name
|
||||
: name.substring(0, name.lastIndexOf("."));
|
||||
if (id == binding.typeClass.getId()) {
|
||||
createFile(
|
||||
f, javaName, ProjectWriter.createJavaClass(pkgName, className));
|
||||
} else if (id == binding.typeInterface.getId()) {
|
||||
createFile(
|
||||
f,
|
||||
javaName,
|
||||
ProjectWriter.createJavaInterface(pkgName, className));
|
||||
} else if (id == binding.typeEnum.getId()) {
|
||||
createFile(
|
||||
f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
|
||||
} else if (id == binding.typeActivity.getId()) {
|
||||
createFile(
|
||||
f, javaName, ProjectWriter.createActivity(pkgName, className));
|
||||
} else {
|
||||
createFile(f, name, "");
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setCancelable(false);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createLayoutRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
|
||||
createNewFileWithContent(
|
||||
Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
|
||||
}
|
||||
|
||||
private void createMenuRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
|
||||
createNewFileWithContent(
|
||||
Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
|
||||
}
|
||||
|
||||
private void createDrawableRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
|
||||
createNewFileWithContent(
|
||||
Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
|
||||
}
|
||||
|
||||
private void createNewResource(File f) {
|
||||
final String[] labels = {
|
||||
activity(). getString(R.string.restype_drawable),
|
||||
activity(). getString(R.string.restype_layout),
|
||||
activity(). getString(R.string.restype_menu),
|
||||
activity(). getString(R.string.restype_other)
|
||||
activity().getString(R.string.restype_drawable),
|
||||
activity().getString(R.string.restype_layout),
|
||||
activity().getString(R.string.restype_menu),
|
||||
activity().getString(R.string.restype_other)
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
builder.setTitle(R.string.new_xml_resource);
|
||||
builder.setItems(labels, (dialogInterface, position) -> {
|
||||
if(position == 0) {
|
||||
createDrawableRes(new File(f, "drawable"));
|
||||
} else if(position == 1) {
|
||||
createLayoutRes(new File(f, "layout"));
|
||||
} else if(position == 2) {
|
||||
createMenuRes(new File(f, "menu"));
|
||||
} else if(position == 3) {
|
||||
createNewFile(f, true);
|
||||
}
|
||||
});
|
||||
builder.setItems(
|
||||
labels,
|
||||
(dialogInterface, position) -> {
|
||||
if (position == 0) {
|
||||
createDrawableRes(new File(f, "drawable"));
|
||||
} else if (position == 1) {
|
||||
createLayoutRes(new File(f, "layout"));
|
||||
} else if (position == 2) {
|
||||
createMenuRes(new File(f, "menu"));
|
||||
} else if (position == 3) {
|
||||
createNewFile(f, true);
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
@@ -194,153 +241,204 @@ public class FileOptionsHandler extends IDEHandler implements OptionsListFragmen
|
||||
}
|
||||
|
||||
private void createNewFileWithContent(File folder, String content, String extension) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutDialogTextInputBinding binding =
|
||||
LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.file_name);
|
||||
Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.file_name);
|
||||
|
||||
builder.setTitle(R.string.new_file);
|
||||
builder.setMessage(activity(). getString(R.string.msg_can_contain_slashes).concat("\n\n").concat(activity(). getString(R.string.msg_newfile_dest, folder.getAbsolutePath())));
|
||||
builder.setMessage(
|
||||
activity()
|
||||
.getString(R.string.msg_can_contain_slashes)
|
||||
.concat("\n\n")
|
||||
.concat(
|
||||
activity()
|
||||
.getString(
|
||||
R.string.msg_newfile_dest,
|
||||
folder.getAbsolutePath())));
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(extension != null && extension.trim().length() > 0) {
|
||||
name = name.endsWith(extension) ? name : name.concat(extension);
|
||||
}
|
||||
createFile(folder, name, content);
|
||||
});
|
||||
builder.setPositiveButton(
|
||||
R.string.text_create,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
if (extension != null && extension.trim().length() > 0) {
|
||||
name = name.endsWith(extension) ? name : name.concat(extension);
|
||||
}
|
||||
createFile(folder, name, content);
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createFile(File f, String name, String content) {
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
if (file.exists()) {
|
||||
activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(FileIOUtils.writeFileFromString(file, content)) {
|
||||
// TODO Notify language servers about file created event
|
||||
activity(). getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
if (FileIOUtils.writeFileFromString(file, content)) {
|
||||
// TODO Notify language servers about file created event
|
||||
activity().getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
|
||||
if (activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
activity()
|
||||
.getFileTreeFragment()
|
||||
.expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
activity().getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFolder(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutDialogTextInputBinding binding =
|
||||
LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.folder_name);
|
||||
Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.folder_name);
|
||||
|
||||
builder.setTitle(R.string.new_folder);
|
||||
builder.setMessage(R.string.msg_can_contain_slashes);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(file.mkdirs()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
builder.setPositiveButton(
|
||||
R.string.text_create,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = binding.name.getEditText().getText().toString().trim();
|
||||
if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if (file.exists()) {
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
if (file.mkdirs()) {
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
|
||||
if (activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity()
|
||||
.getFileTreeFragment()
|
||||
.expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(
|
||||
R.string.msg_folder_creation_failed,
|
||||
Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity().getApp().toast(R.string.msg_folder_creation_failed, Toaster.Type.ERROR);
|
||||
activity().getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
});
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void delete(final File f) {
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.setPositiveButton(R.string.yes, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final boolean deleted = FileUtils.delete(f);
|
||||
activity(). getApp().toast(deleted ? R.string.deleted : R.string.delete_failed, deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(deleted) {
|
||||
// TODO Notify language servers about file delete event
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
|
||||
final var frag = activity().getEditorForFile (f);
|
||||
if(frag != null) {
|
||||
activity().closeFile(activity().findIndexOfEditorByFile (frag.getFile ()));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.title_confirm_delete)
|
||||
.setMessage(activity().getString(R.string.msg_confirm_delete, String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
|
||||
.setCancelable(false)
|
||||
.create().show();
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
builder.setNegativeButton(R.string.no, null)
|
||||
.setPositiveButton(
|
||||
R.string.yes,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final boolean deleted = FileUtils.delete(f);
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(
|
||||
deleted ? R.string.deleted : R.string.delete_failed,
|
||||
deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if (deleted) {
|
||||
// TODO Notify language servers about file delete event
|
||||
if (activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
|
||||
final var frag = activity().getEditorForFile(f);
|
||||
if (frag != null) {
|
||||
activity()
|
||||
.closeFile(
|
||||
activity()
|
||||
.findIndexOfEditorByFile(
|
||||
frag.getFile()));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.title_confirm_delete)
|
||||
.setMessage(
|
||||
activity()
|
||||
.getString(
|
||||
R.string.msg_confirm_delete,
|
||||
String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void renameFile(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutDialogTextInputBinding binding =
|
||||
LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(activity(). getString(R.string.new_name));
|
||||
Objects.requireNonNull(binding.name.getEditText())
|
||||
.setHint(activity().getString(R.string.new_name));
|
||||
binding.name.getEditText().setText(f.getName());
|
||||
|
||||
builder.setTitle(R.string.rename_file);
|
||||
builder.setMessage(R.string.msg_rename_file);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setPositiveButton(R.string.rename_file, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
boolean renamed = name.length () > 0 && name.length () <= 40 && FileUtils.rename (f, name);
|
||||
activity(). getApp().toast(renamed ? R.string.renamed : R.string.rename_failed, renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(renamed) {
|
||||
// TODO Notify language servers about file rename event
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
TreeNode node = new TreeNode(new File(f.getParentFile(), name));
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
parent.addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(
|
||||
R.string.rename_file,
|
||||
(p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
boolean renamed =
|
||||
name.length() > 0 && name.length() <= 40 && FileUtils.rename(f, name);
|
||||
activity()
|
||||
.getApp()
|
||||
.toast(
|
||||
renamed ? R.string.renamed : R.string.rename_failed,
|
||||
renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if (renamed) {
|
||||
// TODO Notify language servers about file rename event
|
||||
if (activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
TreeNode node = new TreeNode(new File(f.getParentFile(), name));
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
parent.addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
* 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.handlers;
|
||||
|
||||
import com.itsaky.androidide.EditorActivity;
|
||||
@@ -23,57 +23,56 @@ import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
/**
|
||||
* A handler is an implementation that handles a specific feature, data or anything else in AndroidIDE
|
||||
* A handler is an implementation that handles a specific feature, data or anything else in
|
||||
* AndroidIDE
|
||||
*/
|
||||
public abstract class IDEHandler {
|
||||
|
||||
|
||||
protected static final Logger LOG = Logger.instance();
|
||||
|
||||
|
||||
protected Provider provider;
|
||||
|
||||
public IDEHandler(Provider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
protected void throwNPE () {
|
||||
|
||||
protected void throwNPE() {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
|
||||
protected EditorActivity activity() {
|
||||
return provider.provideEditorActivity();
|
||||
}
|
||||
|
||||
|
||||
protected AndroidProject androidProject() {
|
||||
return provider.provideAndroidProject();
|
||||
}
|
||||
|
||||
|
||||
protected IDEProject ideProject() {
|
||||
return provider.provideIDEProject();
|
||||
}
|
||||
|
||||
|
||||
public abstract void start();
|
||||
|
||||
public abstract void stop ();
|
||||
|
||||
/**
|
||||
* An interface to communicate between a handler and its client
|
||||
*/
|
||||
|
||||
public abstract void stop();
|
||||
|
||||
/** An interface to communicate between a handler and its client */
|
||||
public interface Provider {
|
||||
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference to {@link EditorActivity}
|
||||
*
|
||||
* @throws NullPointerException is this is required
|
||||
*/
|
||||
EditorActivity provideEditorActivity();
|
||||
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference of the current {@link AndroidProject}
|
||||
*
|
||||
* @throws NullPointerException is this is required
|
||||
*/
|
||||
AndroidProject provideAndroidProject();
|
||||
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference of the current {@link IDEProject}
|
||||
*
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.interfaces;
|
||||
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
@@ -24,5 +24,6 @@ import java.io.File;
|
||||
|
||||
public interface DiagnosticClickListener {
|
||||
void onGroupClick(DiagnosticGroup group);
|
||||
|
||||
void onDiagnosticClick(File file, DiagnosticItem diagnostic);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
/************************************************************************************
|
||||
* 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.interfaces;
|
||||
|
||||
import com.itsaky.androidide.EditorActivity;
|
||||
|
||||
public interface EditorActivityProvider {
|
||||
|
||||
|
||||
/**
|
||||
* Provides a reference to {@link com.itsaky.androidide.EditorActivity EditorActivity}.
|
||||
* This is used by LanguageClients to get reference to EditorActivity without keeping a strong reference.
|
||||
* Provides a reference to {@link com.itsaky.androidide.EditorActivity EditorActivity}. This is
|
||||
* used by LanguageClients to get reference to EditorActivity without keeping a strong
|
||||
* reference.
|
||||
*/
|
||||
EditorActivity provide();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ import com.itsaky.lsp.models.CompletionParams;
|
||||
import com.itsaky.lsp.models.CompletionResult;
|
||||
import com.itsaky.lsp.models.Position;
|
||||
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionHelper;
|
||||
import io.github.rosemoe.sora.text.CharPosition;
|
||||
import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.util.MyCharacter;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -38,94 +43,103 @@ import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionHelper;
|
||||
import io.github.rosemoe.sora.text.CharPosition;
|
||||
import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.util.MyCharacter;
|
||||
|
||||
/**
|
||||
* Common implementation of completion provider which requests completions
|
||||
* to provided language server.
|
||||
* Common implementation of completion provider which requests completions to provided language
|
||||
* server.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CommonCompletionProvider {
|
||||
|
||||
|
||||
private CompletableFuture<CompletionResult> future;
|
||||
|
||||
|
||||
private final ILanguageServer server;
|
||||
|
||||
public CommonCompletionProvider (ILanguageServer server) {
|
||||
|
||||
public CommonCompletionProvider(ILanguageServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes completion items using the provided language server instance.
|
||||
*
|
||||
* @param content The reference to the content of the editor.
|
||||
* @param file The file to compute completions for.
|
||||
* @param content The reference to the content of the editor.
|
||||
* @param file The file to compute completions for.
|
||||
* @param position The position of the cursor in the content.
|
||||
* @return The computed completion items. May return an empty list if the there was an error computing the completion items.
|
||||
* @return The computed completion items. May return an empty list if the there was an error
|
||||
* computing the completion items.
|
||||
*/
|
||||
public List<CompletionItem> complete (ContentReference content, Path file, CharPosition position, Predicate<Character> prefixMatcher) {
|
||||
if (this.future != null && !this.future.isDone ()) {
|
||||
public List<CompletionItem> complete(
|
||||
ContentReference content,
|
||||
Path file,
|
||||
CharPosition position,
|
||||
Predicate<Character> prefixMatcher) {
|
||||
if (this.future != null && !this.future.isDone()) {
|
||||
try {
|
||||
this.future.cancel (true);
|
||||
this.future.cancel(true);
|
||||
} catch (CancellationException e) {
|
||||
return new ArrayList<> ();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
this.future = CompletableFuture.supplyAsync (() -> {
|
||||
final var prefix = CompletionHelper.computePrefix (content, position, prefixMatcher::test);
|
||||
final var completer = server.getCompletionProvider ();
|
||||
|
||||
if (!completer.canComplete (file)) {
|
||||
return ICompletionProvider.EMPTY;
|
||||
}
|
||||
|
||||
final var params = new CompletionParams (new Position (position.line, position.column, position.index), file);
|
||||
params.setContent (content);
|
||||
params.setPrefix (prefix);
|
||||
return completer.complete (params);
|
||||
});
|
||||
|
||||
|
||||
this.future =
|
||||
CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final var prefix =
|
||||
CompletionHelper.computePrefix(
|
||||
content, position, prefixMatcher::test);
|
||||
final var completer = server.getCompletionProvider();
|
||||
|
||||
if (!completer.canComplete(file)) {
|
||||
return ICompletionProvider.EMPTY;
|
||||
}
|
||||
|
||||
final var params =
|
||||
new CompletionParams(
|
||||
new Position(
|
||||
position.line, position.column, position.index),
|
||||
file);
|
||||
params.setContent(content);
|
||||
params.setPrefix(prefix);
|
||||
return completer.complete(params);
|
||||
});
|
||||
|
||||
try {
|
||||
return finalizeResults (future.get ().getItems ());
|
||||
return finalizeResults(future.get().getItems());
|
||||
} catch (Throwable e) {
|
||||
LOG.error ("Unable to compute completions", e);
|
||||
return Collections.emptyList ();
|
||||
LOG.error("Unable to compute completions", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkJavaCompletionChar (char c) {
|
||||
return MyCharacter.isJavaIdentifierPart (c) || c == '.';
|
||||
|
||||
public static boolean checkJavaCompletionChar(char c) {
|
||||
return MyCharacter.isJavaIdentifierPart(c) || c == '.';
|
||||
}
|
||||
|
||||
public static boolean checkXMLCompletionChar (char c) {
|
||||
return MyCharacter.isJavaIdentifierPart (c) || c == '<' || c == '/';
|
||||
|
||||
public static boolean checkXMLCompletionChar(char c) {
|
||||
return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/';
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Contract("_ -> param1")
|
||||
private List<CompletionItem> finalizeResults (@NonNull List<CompletionItem> items) {
|
||||
items.sort (RESULT_SORTER);
|
||||
private List<CompletionItem> finalizeResults(@NonNull List<CompletionItem> items) {
|
||||
items.sort(RESULT_SORTER);
|
||||
return items;
|
||||
}
|
||||
|
||||
private static final Comparator<CompletionItem> RESULT_SORTER = (p1, p2) -> {
|
||||
if (p1 == null && p2 == null) {
|
||||
return 0;
|
||||
} else if (p1 == null) {
|
||||
return -1;
|
||||
} else if (p2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
String s1 = p1.getSortText () == null ? p1.getLabel () : p1.getSortText ();
|
||||
String s2 = p2.getSortText () == null ? p2.getLabel () : p2.getSortText ();
|
||||
return s1.compareTo (s2);
|
||||
};
|
||||
|
||||
private static final Logger LOG = Logger.instance ("CommonCompletionProvider");
|
||||
|
||||
private static final Comparator<CompletionItem> RESULT_SORTER =
|
||||
(p1, p2) -> {
|
||||
if (p1 == null && p2 == null) {
|
||||
return 0;
|
||||
} else if (p1 == null) {
|
||||
return -1;
|
||||
} else if (p2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
String s1 = p1.getSortText() == null ? p1.getLabel() : p1.getSortText();
|
||||
String s2 = p2.getSortText() == null ? p2.getLabel() : p2.getSortText();
|
||||
return s1.compareTo(s2);
|
||||
};
|
||||
|
||||
private static final Logger LOG = Logger.instance("CommonCompletionProvider");
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -21,33 +21,34 @@ import androidx.annotation.NonNull;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
|
||||
import io.github.rosemoe.sora.lang.Language;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.rosemoe.sora.lang.Language;
|
||||
|
||||
/**
|
||||
* Base class for language implementations in the IDE.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class IDELanguage implements Language {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public int getTabSize() {
|
||||
return StudioApp.getInstance().getPrefManager().getEditorTabSize();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the diagnostics from the last analyze.
|
||||
*
|
||||
* @return The diagnostics. Must not be null.
|
||||
*/
|
||||
@NonNull
|
||||
public List<DiagnosticItem> getDiagnostics () {
|
||||
return Collections.emptyList ();
|
||||
public List<DiagnosticItem> getDiagnostics() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -32,6 +32,11 @@ import com.itsaky.androidide.lexers.groovy.GroovyLexer;
|
||||
import com.itsaky.androidide.utils.CharSequenceReader;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -39,53 +44,48 @@ import org.antlr.v4.runtime.Token;
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
public class GroovyAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
|
||||
private static final Logger LOG = Logger.instance ("GroovyAnalyzer");
|
||||
|
||||
|
||||
private static final Logger LOG = Logger.instance("GroovyAnalyzer");
|
||||
|
||||
@Override
|
||||
protected Styles analyze (StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles ();
|
||||
final var colors = new MappedSpans.Builder ();
|
||||
protected Styles analyze(StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles();
|
||||
final var colors = new MappedSpans.Builder();
|
||||
final CodePointCharStream stream;
|
||||
try {
|
||||
stream = CharStreams.fromReader (new CharSequenceReader (text));
|
||||
stream = CharStreams.fromReader(new CharSequenceReader(text));
|
||||
} catch (IOException e) {
|
||||
LOG.error ("Unable to read text to analyze", e);
|
||||
LOG.error("Unable to read text to analyze", e);
|
||||
return styles;
|
||||
}
|
||||
|
||||
final var lexer = new GroovyLexer (stream);
|
||||
final var stack = new Stack<CodeBlock> ();
|
||||
|
||||
|
||||
final var lexer = new GroovyLexer(stream);
|
||||
final var stack = new Stack<CodeBlock>();
|
||||
|
||||
Token token;
|
||||
int line, column, lastLine = 1, currSwitch = 0, maxSwitch = 0, type, previous = 0;
|
||||
boolean isFirst = true;
|
||||
|
||||
while (!delegate.isCancelled ()) {
|
||||
token = lexer.nextToken ();
|
||||
|
||||
|
||||
while (!delegate.isCancelled()) {
|
||||
token = lexer.nextToken();
|
||||
|
||||
if (token == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
line = token.getLine () - 1;
|
||||
column = token.getCharPositionInLine ();
|
||||
type = token.getType ();
|
||||
|
||||
|
||||
line = token.getLine() - 1;
|
||||
column = token.getCharPositionInLine();
|
||||
type = token.getType();
|
||||
|
||||
if (type == GroovyLexer.EOF) {
|
||||
lastLine = line;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case GroovyLexer.WS :
|
||||
if(isFirst) {
|
||||
|
||||
switch (type) {
|
||||
case GroovyLexer.WS:
|
||||
if (isFirst) {
|
||||
colors.addNormalIfNull();
|
||||
}
|
||||
break;
|
||||
@@ -130,7 +130,7 @@ public class GroovyAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case GroovyLexer.VOID:
|
||||
case GroovyLexer.VOLATILE:
|
||||
case GroovyLexer.WHILE:
|
||||
colors.addIfNeeded(line, column, forKeyword ());
|
||||
colors.addIfNeeded(line, column, forKeyword());
|
||||
break;
|
||||
case GroovyLexer.DECIMAL_LITERAL:
|
||||
case GroovyLexer.HEX_LITERAL:
|
||||
@@ -141,126 +141,124 @@ public class GroovyAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case GroovyLexer.BOOL_LITERAL:
|
||||
case GroovyLexer.CHAR_LITERAL:
|
||||
case GroovyLexer.NULL_LITERAL:
|
||||
colors.addIfNeeded(line, column, get (LITERAL));
|
||||
colors.addIfNeeded(line, column, get(LITERAL));
|
||||
break;
|
||||
case GroovyLexer.STRING_LITERAL :
|
||||
case GroovyLexer.SINGLE_QUOTE_STRING :
|
||||
colors.addIfNeeded (line, column, forString ());
|
||||
case GroovyLexer.STRING_LITERAL:
|
||||
case GroovyLexer.SINGLE_QUOTE_STRING:
|
||||
colors.addIfNeeded(line, column, forString());
|
||||
break;
|
||||
case GroovyLexer.LPAREN :
|
||||
case GroovyLexer.RPAREN :
|
||||
case GroovyLexer.LBRACK :
|
||||
case GroovyLexer.RBRACK :
|
||||
case GroovyLexer.SEMI :
|
||||
case GroovyLexer.COMMA :
|
||||
case GroovyLexer.ASSIGN :
|
||||
case GroovyLexer.GT :
|
||||
case GroovyLexer.LT :
|
||||
case GroovyLexer.BANG :
|
||||
case GroovyLexer.TILDE :
|
||||
case GroovyLexer.QUESTION :
|
||||
case GroovyLexer.COLON :
|
||||
case GroovyLexer.EQUAL :
|
||||
case GroovyLexer.GE :
|
||||
case GroovyLexer.LE :
|
||||
case GroovyLexer.NOTEQUAL :
|
||||
case GroovyLexer.AND :
|
||||
case GroovyLexer.OR :
|
||||
case GroovyLexer.INC :
|
||||
case GroovyLexer.DEC :
|
||||
case GroovyLexer.ADD :
|
||||
case GroovyLexer.SUB :
|
||||
case GroovyLexer.MUL :
|
||||
case GroovyLexer.DIV :
|
||||
case GroovyLexer.BITAND :
|
||||
case GroovyLexer.BITOR :
|
||||
case GroovyLexer.CARET :
|
||||
case GroovyLexer.MOD :
|
||||
case GroovyLexer.ADD_ASSIGN :
|
||||
case GroovyLexer.SUB_ASSIGN :
|
||||
case GroovyLexer.MUL_ASSIGN :
|
||||
case GroovyLexer.DIV_ASSIGN :
|
||||
case GroovyLexer.AND_ASSIGN :
|
||||
case GroovyLexer.OR_ASSIGN :
|
||||
case GroovyLexer.XOR_ASSIGN :
|
||||
case GroovyLexer.MOD_ASSIGN :
|
||||
case GroovyLexer.LSHIFT_ASSIGN :
|
||||
case GroovyLexer.RSHIFT_ASSIGN :
|
||||
case GroovyLexer.URSHIFT_ASSIGN :
|
||||
case GroovyLexer.ARROW :
|
||||
case GroovyLexer.COLONCOLON :
|
||||
case GroovyLexer.ELLIPSIS :
|
||||
case GroovyLexer.DOT :
|
||||
colors.addIfNeeded(line, column, get (OPERATOR));
|
||||
case GroovyLexer.LPAREN:
|
||||
case GroovyLexer.RPAREN:
|
||||
case GroovyLexer.LBRACK:
|
||||
case GroovyLexer.RBRACK:
|
||||
case GroovyLexer.SEMI:
|
||||
case GroovyLexer.COMMA:
|
||||
case GroovyLexer.ASSIGN:
|
||||
case GroovyLexer.GT:
|
||||
case GroovyLexer.LT:
|
||||
case GroovyLexer.BANG:
|
||||
case GroovyLexer.TILDE:
|
||||
case GroovyLexer.QUESTION:
|
||||
case GroovyLexer.COLON:
|
||||
case GroovyLexer.EQUAL:
|
||||
case GroovyLexer.GE:
|
||||
case GroovyLexer.LE:
|
||||
case GroovyLexer.NOTEQUAL:
|
||||
case GroovyLexer.AND:
|
||||
case GroovyLexer.OR:
|
||||
case GroovyLexer.INC:
|
||||
case GroovyLexer.DEC:
|
||||
case GroovyLexer.ADD:
|
||||
case GroovyLexer.SUB:
|
||||
case GroovyLexer.MUL:
|
||||
case GroovyLexer.DIV:
|
||||
case GroovyLexer.BITAND:
|
||||
case GroovyLexer.BITOR:
|
||||
case GroovyLexer.CARET:
|
||||
case GroovyLexer.MOD:
|
||||
case GroovyLexer.ADD_ASSIGN:
|
||||
case GroovyLexer.SUB_ASSIGN:
|
||||
case GroovyLexer.MUL_ASSIGN:
|
||||
case GroovyLexer.DIV_ASSIGN:
|
||||
case GroovyLexer.AND_ASSIGN:
|
||||
case GroovyLexer.OR_ASSIGN:
|
||||
case GroovyLexer.XOR_ASSIGN:
|
||||
case GroovyLexer.MOD_ASSIGN:
|
||||
case GroovyLexer.LSHIFT_ASSIGN:
|
||||
case GroovyLexer.RSHIFT_ASSIGN:
|
||||
case GroovyLexer.URSHIFT_ASSIGN:
|
||||
case GroovyLexer.ARROW:
|
||||
case GroovyLexer.COLONCOLON:
|
||||
case GroovyLexer.ELLIPSIS:
|
||||
case GroovyLexer.DOT:
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
break;
|
||||
case GroovyLexer.BOOLEAN:
|
||||
case GroovyLexer.BYTE :
|
||||
case GroovyLexer.BYTE:
|
||||
case GroovyLexer.CHAR:
|
||||
case GroovyLexer.DOUBLE:
|
||||
case GroovyLexer.ENUM :
|
||||
case GroovyLexer.ENUM:
|
||||
case GroovyLexer.FLOAT:
|
||||
case GroovyLexer.INT :
|
||||
case GroovyLexer.INT:
|
||||
case GroovyLexer.LONG:
|
||||
case GroovyLexer.SHORT:
|
||||
colors.addIfNeeded(line, column, get (TYPE_NAME));
|
||||
colors.addIfNeeded(line, column, get(TYPE_NAME));
|
||||
break;
|
||||
case GroovyLexer.COMMENT :
|
||||
case GroovyLexer.LINE_COMMENT :
|
||||
colors.addIfNeeded(line, column, forComment ());
|
||||
case GroovyLexer.COMMENT:
|
||||
case GroovyLexer.LINE_COMMENT:
|
||||
colors.addIfNeeded(line, column, forComment());
|
||||
break;
|
||||
case GroovyLexer.AT :
|
||||
colors.addIfNeeded(line, column, get (ANNOTATION));
|
||||
case GroovyLexer.AT:
|
||||
colors.addIfNeeded(line, column, get(ANNOTATION));
|
||||
break;
|
||||
case GroovyLexer.IDENTIFIER :
|
||||
case GroovyLexer.IDENTIFIER:
|
||||
if (previous == GroovyLexer.AT) {
|
||||
colors.addIfNeeded(line, column, get (ANNOTATION));
|
||||
colors.addIfNeeded(line, column, get(ANNOTATION));
|
||||
break;
|
||||
}
|
||||
|
||||
colors.addIfNeeded(line, column, get (TEXT_NORMAL));
|
||||
|
||||
colors.addIfNeeded(line, column, get(TEXT_NORMAL));
|
||||
break;
|
||||
case GroovyLexer.LBRACE :
|
||||
case GroovyLexer.LBRACE:
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
if (stack.isEmpty()) {
|
||||
if (currSwitch > maxSwitch)
|
||||
maxSwitch = currSwitch;
|
||||
if (currSwitch > maxSwitch) maxSwitch = currSwitch;
|
||||
currSwitch = 0;
|
||||
}
|
||||
currSwitch++;
|
||||
var block = styles.obtainNewBlock ();
|
||||
var block = styles.obtainNewBlock();
|
||||
block.startLine = line;
|
||||
block.startColumn = column;
|
||||
stack.push(block);
|
||||
break;
|
||||
case GroovyLexer.RBRACE :
|
||||
colors.addIfNeeded(line, column, get (OPERATOR));
|
||||
case GroovyLexer.RBRACE:
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
if (!stack.isEmpty()) {
|
||||
var block2 = stack.pop();
|
||||
block2.endLine = line;
|
||||
block2.endColumn = column;
|
||||
if (block2.startLine != block2.endLine)
|
||||
styles.addCodeBlock (block2);
|
||||
if (block2.startLine != block2.endLine) styles.addCodeBlock(block2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
colors.addIfNeeded(line, column, get (TEXT_NORMAL));
|
||||
colors.addIfNeeded(line, column, get(TEXT_NORMAL));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
isFirst = false;
|
||||
if (type != GroovyLexer.WS) {
|
||||
previous = type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (stack.isEmpty() && currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
|
||||
|
||||
colors.determine(lastLine);
|
||||
styles.setSuppressSwitch(maxSwitch + 10);
|
||||
styles.spans = colors.build ();
|
||||
|
||||
styles.spans = colors.build();
|
||||
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.language.groovy;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -36,31 +36,40 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GroovyAutoComplete {
|
||||
|
||||
private static final List<String> ANDROIDX_ARTIFACTS = createArtifacts();
|
||||
private static final List<String> CONFIGURATIONS = createConfigs();
|
||||
private static final List<String> OTHERS = createOtherCompletions();
|
||||
|
||||
public void complete (ContentReference content, CharPosition position, CompletionPublisher publisher, Bundle extraArguments) {
|
||||
publisher.setUpdateThreshold (0);
|
||||
final var prefix = CompletionHelper.computePrefix (content, position, MyCharacter::isJavaIdentifierPart);
|
||||
for(String artifact : ANDROIDX_ARTIFACTS) {
|
||||
if(!artifact.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
|
||||
publisher.addItem (createCompletionItem(artifact));
|
||||
}
|
||||
|
||||
for(String config : CONFIGURATIONS) {
|
||||
if(!config.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
|
||||
publisher.addItem (createCompletionItem(config));
|
||||
}
|
||||
|
||||
for(String other : OTHERS) {
|
||||
if(!other.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
|
||||
publisher.addItem (createCompletionItem(other));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionItem createCompletionItem (String itemLabel) {
|
||||
|
||||
private static final List<String> ANDROIDX_ARTIFACTS = createArtifacts();
|
||||
private static final List<String> CONFIGURATIONS = createConfigs();
|
||||
private static final List<String> OTHERS = createOtherCompletions();
|
||||
|
||||
public void complete(
|
||||
ContentReference content,
|
||||
CharPosition position,
|
||||
CompletionPublisher publisher,
|
||||
Bundle extraArguments) {
|
||||
publisher.setUpdateThreshold(0);
|
||||
final var prefix =
|
||||
CompletionHelper.computePrefix(
|
||||
content, position, MyCharacter::isJavaIdentifierPart);
|
||||
for (String artifact : ANDROIDX_ARTIFACTS) {
|
||||
if (!artifact.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
|
||||
continue;
|
||||
publisher.addItem(createCompletionItem(artifact));
|
||||
}
|
||||
|
||||
for (String config : CONFIGURATIONS) {
|
||||
if (!config.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
|
||||
continue;
|
||||
publisher.addItem(createCompletionItem(config));
|
||||
}
|
||||
|
||||
for (String other : OTHERS) {
|
||||
if (!other.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
|
||||
continue;
|
||||
publisher.addItem(createCompletionItem(other));
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionItem createCompletionItem(String itemLabel) {
|
||||
CompletionItem item = new CompletionItem();
|
||||
item.setLabel(itemLabel);
|
||||
item.setDetail("");
|
||||
@@ -68,311 +77,311 @@ public class GroovyAutoComplete {
|
||||
item.setSortText("0" + itemLabel);
|
||||
item.setInsertTextFormat(InsertTextFormat.PLAIN_TEXT);
|
||||
item.setKind(CompletionItemKind.SNIPPET);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static List<String> createArtifacts() {
|
||||
var artifacts = new ArrayList<String>();
|
||||
artifacts.add("androidx.arch.core:core-common");
|
||||
artifacts.add("androidx.arch.core:core");
|
||||
artifacts.add("androidx.arch.core:core-testing");
|
||||
artifacts.add("androidx.arch.core:core-runtime");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-common");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-common-java8");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-compiler");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-extensions");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-livedata");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-livedata-core");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-reactivestreams");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-runtime");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-viewmodel");
|
||||
artifacts.add("androidx.paging:paging-common");
|
||||
artifacts.add("androidx.paging:paging-runtime");
|
||||
artifacts.add("androidx.paging:paging-rxjava2");
|
||||
artifacts.add("androidx.room:room-common");
|
||||
artifacts.add("androidx.room:room-compiler");
|
||||
artifacts.add("androidx.room:room-guava");
|
||||
artifacts.add("androidx.room:room-migration");
|
||||
artifacts.add("androidx.room:room-runtime");
|
||||
artifacts.add("androidx.room:room-rxjava2");
|
||||
artifacts.add("androidx.room:room-testing");
|
||||
artifacts.add("androidx.sqlite:sqlite");
|
||||
artifacts.add("androidx.sqlite:sqlite-framework");
|
||||
artifacts.add("androidx.constraintlayout:constraintlayout");
|
||||
artifacts.add("androidx.constraintlayout:constraintlayout-solver");
|
||||
artifacts.add("androidx.test.espresso.idling:idling-concurrent");
|
||||
artifacts.add("androidx.test.espresso.idling:idling-net");
|
||||
artifacts.add("androidx.test.espresso:espresso-accessibility");
|
||||
artifacts.add("androidx.test.espresso:espresso-contrib");
|
||||
artifacts.add("androidx.test.espresso:espresso-core");
|
||||
artifacts.add("androidx.test.espresso:espresso-idling-resource");
|
||||
artifacts.add("androidx.test.espresso:espresso-intents");
|
||||
artifacts.add("androidx.test.espresso:espresso-remote");
|
||||
artifacts.add("androidx.test.espresso:espresso-web");
|
||||
artifacts.add("androidx.test.jank:janktesthelper");
|
||||
artifacts.add("androidx.test:test-services");
|
||||
artifacts.add("androidx.test.uiautomator:uiautomator");
|
||||
artifacts.add("androidx.test:monitor");
|
||||
artifacts.add("androidx.test:orchestrator");
|
||||
artifacts.add("androidx.test:rules");
|
||||
artifacts.add("androidx.test:runner");
|
||||
artifacts.add("androidx.vectordrawable:vectordrawable-animated");
|
||||
artifacts.add("androidx.appcompat:appcompat");
|
||||
artifacts.add("androidx.asynclayoutinflater:asynclayoutinflater");
|
||||
artifacts.add("androidx.car:car");
|
||||
artifacts.add("androidx.cardview:cardview");
|
||||
artifacts.add("androidx.collection:collection");
|
||||
artifacts.add("androidx.coordinatorlayout:coordinatorlayout");
|
||||
artifacts.add("androidx.cursoradapter:cursoradapter");
|
||||
artifacts.add("androidx.browser:browser");
|
||||
artifacts.add("androidx.customview:customview");
|
||||
artifacts.add("com.google.android.material:material");
|
||||
artifacts.add("androidx.documentfile:documentfile");
|
||||
artifacts.add("androidx.drawerlayout:drawerlayout");
|
||||
artifacts.add("androidx.exifinterface:exifinterface");
|
||||
artifacts.add("androidx.gridlayout:gridlayout");
|
||||
artifacts.add("androidx.heifwriter:heifwriter");
|
||||
artifacts.add("androidx.interpolator:interpolator");
|
||||
artifacts.add("androidx.leanback:leanback");
|
||||
artifacts.add("androidx.loader:loader");
|
||||
artifacts.add("androidx.localbroadcastmanager:localbroadcastmanager");
|
||||
artifacts.add("androidx.media2:media2");
|
||||
artifacts.add("androidx.media2:media2-exoplayer");
|
||||
artifacts.add("androidx.mediarouter:mediarouter");
|
||||
artifacts.add("androidx.multidex:multidex");
|
||||
artifacts.add("androidx.multidex:multidex-instrumentation");
|
||||
artifacts.add("androidx.palette:palette");
|
||||
artifacts.add("androidx.percentlayout:percentlayout");
|
||||
artifacts.add("androidx.leanback:leanback-preference");
|
||||
artifacts.add("androidx.legacy:legacy-preference-v14");
|
||||
artifacts.add("androidx.preference:preference");
|
||||
artifacts.add("androidx.print:print");
|
||||
artifacts.add("androidx.recommendation:recommendation");
|
||||
artifacts.add("androidx.recyclerview:recyclerview-selection");
|
||||
artifacts.add("androidx.recyclerview:recyclerview");
|
||||
artifacts.add("androidx.slice:slice-builders");
|
||||
artifacts.add("androidx.slice:slice-core");
|
||||
artifacts.add("androidx.slice:slice-view");
|
||||
artifacts.add("androidx.slidingpanelayout:slidingpanelayout");
|
||||
artifacts.add("androidx.annotation:annotation");
|
||||
artifacts.add("androidx.core:core");
|
||||
artifacts.add("androidx.contentpager:contentpager");
|
||||
artifacts.add("androidx.legacy:legacy-support-core-ui");
|
||||
artifacts.add("androidx.legacy:legacy-support-core-utils");
|
||||
artifacts.add("androidx.dynamicanimation:dynamicanimation");
|
||||
artifacts.add("androidx.emoji:emoji");
|
||||
artifacts.add("androidx.emoji:emoji-appcompat");
|
||||
artifacts.add("androidx.emoji:emoji-bundled");
|
||||
artifacts.add("androidx.fragment:fragment");
|
||||
artifacts.add("androidx.media:media");
|
||||
artifacts.add("androidx.tvprovider:tvprovider");
|
||||
artifacts.add("androidx.legacy:legacy-support-v13");
|
||||
artifacts.add("androidx.legacy:legacy-support-v4");
|
||||
artifacts.add("androidx.vectordrawable:vectordrawable");
|
||||
artifacts.add("androidx.swiperefreshlayout:swiperefreshlayout");
|
||||
artifacts.add("androidx.textclassifier:textclassifier");
|
||||
artifacts.add("androidx.transition:transition");
|
||||
artifacts.add("androidx.versionedparcelable:versionedparcelable");
|
||||
artifacts.add("androidx.viewpager:viewpager");
|
||||
artifacts.add("androidx.wear:wear");
|
||||
artifacts.add("androidx.webkit:webkit");
|
||||
|
||||
artifacts.add("fileTree(dir: 'libs', include: ['*.jar'])");
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
private static List<String> createConfigs() {
|
||||
var configs = new ArrayList<String>();
|
||||
configs.add("androidApis");
|
||||
configs.add("androidTestAnnotationProcessor");
|
||||
configs.add("androidTestApi");
|
||||
configs.add("androidTestApk");
|
||||
configs.add("androidTestCompile");
|
||||
configs.add("androidTestCompileOnly");
|
||||
configs.add("androidTestDebugAnnotationProcessor");
|
||||
configs.add("androidTestDebugApi");
|
||||
configs.add("androidTestDebugApk");
|
||||
configs.add("androidTestDebugCompile");
|
||||
configs.add("androidTestDebugCompileOnly");
|
||||
configs.add("androidTestDebugImplementation");
|
||||
configs.add("androidTestDebugProvided");
|
||||
configs.add("androidTestDebugRuntimeOnly");
|
||||
configs.add("androidTestDebugWearApp");
|
||||
configs.add("androidTestImplementation");
|
||||
configs.add("androidTestProvided");
|
||||
configs.add("androidTestRuntimeOnly");
|
||||
configs.add("androidTestUtil");
|
||||
configs.add("androidTestWearApp");
|
||||
configs.add("annotationProcessor");
|
||||
configs.add("api");
|
||||
configs.add("apk");
|
||||
configs.add("archives");
|
||||
configs.add("compile");
|
||||
configs.add("compileOnly");
|
||||
configs.add("coreLibraryDesugaring");
|
||||
configs.add("debugAnnotationProcessor");
|
||||
configs.add("debugApi");
|
||||
configs.add("debugApk");
|
||||
configs.add("debugCompile");
|
||||
configs.add("debugCompileOnly");
|
||||
configs.add("debugImplementation");
|
||||
configs.add("debugProvided");
|
||||
configs.add("debugRuntimeOnly");
|
||||
configs.add("debugWearApp");
|
||||
configs.add("default");
|
||||
configs.add("implementation");
|
||||
configs.add("implementation platform()");
|
||||
configs.add("lintChecks");
|
||||
configs.add("lintClassPath");
|
||||
configs.add("lintPublish");
|
||||
configs.add("provided");
|
||||
configs.add("releaseAnnotationProcessor");
|
||||
configs.add("releaseApi");
|
||||
configs.add("releaseApk");
|
||||
configs.add("releaseCompile");
|
||||
configs.add("releaseCompileOnly");
|
||||
configs.add("releaseImplementation");
|
||||
configs.add("releaseProvided");
|
||||
configs.add("releaseRuntimeOnly");
|
||||
configs.add("releaseWearApp");
|
||||
configs.add("runtimeOnly");
|
||||
configs.add("testAnnotationProcessor");
|
||||
configs.add("testApi");
|
||||
configs.add("testApk");
|
||||
configs.add("testCompile");
|
||||
configs.add("testCompileOnly");
|
||||
configs.add("testDebugAnnotationProcessor");
|
||||
configs.add("testDebugApi");
|
||||
configs.add("testDebugApk");
|
||||
configs.add("testDebugCompile");
|
||||
configs.add("testDebugCompileOnly");
|
||||
configs.add("testDebugImplementation");
|
||||
configs.add("testDebugProvided");
|
||||
configs.add("testDebugRuntimeOnly");
|
||||
configs.add("testDebugWearApp");
|
||||
configs.add("testImplementation");
|
||||
configs.add("testProvided");
|
||||
configs.add("testReleaseAnnotationProcessor");
|
||||
configs.add("testReleaseApi");
|
||||
configs.add("testReleaseApk");
|
||||
configs.add("testReleaseCompile");
|
||||
configs.add("testReleaseCompileOnly");
|
||||
configs.add("testReleaseImplementation");
|
||||
configs.add("testReleaseProvided");
|
||||
configs.add("testReleaseRuntimeOnly");
|
||||
configs.add("testReleaseWearApp");
|
||||
configs.add("testRuntimeOnly");
|
||||
configs.add("testWearApp");
|
||||
configs.add("wearApp");
|
||||
return configs;
|
||||
}
|
||||
|
||||
private static List<String> createOtherCompletions() {
|
||||
var others = new ArrayList<String> ();
|
||||
|
||||
// Common words that user may use
|
||||
others.add ("android");
|
||||
others.add ("applicationVariants");
|
||||
others.add ("compileSdkVersion");
|
||||
others.add ("buildToolsVersion");
|
||||
others.add ("defaultConfig");
|
||||
others.add ("applicationId");
|
||||
others.add ("minSdkVersion");
|
||||
others.add ("targetSdkVersion");
|
||||
others.add ("versionCode");
|
||||
others.add ("versionName");
|
||||
others.add ("multiDexEnabled");
|
||||
others.add ("vectorDrawables.useSupportLibrary");
|
||||
others.add ("compileOptions");
|
||||
others.add ("coreLibraryDesugaringEnabled");
|
||||
others.add ("sourceCompatibility");
|
||||
others.add ("targetCompatibility");
|
||||
others.add ("JavaVersion.VERSION_1_8");
|
||||
others.add ("JavaVersion.VERSION_1_7");
|
||||
|
||||
// Lint options
|
||||
others.add ("lintOptions");
|
||||
others.add ("quiet");
|
||||
others.add ("abortOnError");
|
||||
others.add ("checkReleaseBuilds");
|
||||
others.add ("ignoreWarnings");
|
||||
others.add ("checkAllWarnings");
|
||||
others.add ("warningsAsErrors");
|
||||
others.add ("disable");
|
||||
others.add ("enable");
|
||||
others.add ("check");
|
||||
others.add ("noLines");
|
||||
others.add ("showAll");
|
||||
others.add ("explainIssues");
|
||||
others.add ("lintConfig");
|
||||
others.add ("textReport");
|
||||
others.add ("textOutput");
|
||||
others.add ("xmlReport");
|
||||
others.add ("xmlOutput");
|
||||
others.add ("htmlReport");
|
||||
others.add ("htmlOutput");
|
||||
others.add ("fatal");
|
||||
others.add ("error");
|
||||
others.add ("warning");
|
||||
others.add ("ignore");
|
||||
others.add ("informational");
|
||||
others.add ("baseline");
|
||||
others.add ("checkTestSources");
|
||||
others.add ("ignoreTestSources");
|
||||
others.add ("checkGeneratedSources");
|
||||
others.add ("checkDependencies");
|
||||
|
||||
// buildFeatures options
|
||||
others.add ("buildFeatures");
|
||||
others.add ("aidl");
|
||||
others.add ("buildConfig");
|
||||
others.add ("compose");
|
||||
others.add ("prefab");
|
||||
others.add ("renderScript");
|
||||
others.add ("resValues");
|
||||
others.add ("shaders");
|
||||
others.add ("viewBinding");
|
||||
|
||||
// Signing configs and all
|
||||
others.add ("signingConfigs");
|
||||
others.add ("debug");
|
||||
others.add ("storeFile");
|
||||
others.add ("storePassword");
|
||||
others.add ("keyAlias");
|
||||
others.add ("keyPassword");
|
||||
others.add ("release");
|
||||
others.add ("storeFile");
|
||||
others.add ("storePassword");
|
||||
others.add ("keyAlias");
|
||||
others.add ("keyPassword");
|
||||
others.add ("buildTypes");
|
||||
others.add ("debug");
|
||||
others.add ("minifyEnabled");
|
||||
others.add ("proguardFiles");
|
||||
others.add ("signingConfig");
|
||||
others.add ("stringfog.enable");
|
||||
others.add ("release");
|
||||
others.add ("minifyEnabled");
|
||||
others.add ("proguardFiles");
|
||||
others.add ("signingConfig");
|
||||
others.add ("stringfog.enable");
|
||||
others.add ("packagingOptions");
|
||||
others.add ("exclude");
|
||||
|
||||
// Maybe used in root project's build.gradle
|
||||
others.add ("buildscript");
|
||||
others.add ("ext");
|
||||
others.add ("project.ext");
|
||||
others.add ("repositories");
|
||||
others.add ("maven");
|
||||
others.add ("url");
|
||||
others.add ("google()");
|
||||
others.add ("mavenLocal()");
|
||||
others.add ("mavenCentral()");
|
||||
others.add ("jcenter()");
|
||||
others.add ("classpath");
|
||||
others.add ("allprojects");
|
||||
others.add ("subprojects");
|
||||
return others;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private static List<String> createArtifacts() {
|
||||
var artifacts = new ArrayList<String>();
|
||||
artifacts.add("androidx.arch.core:core-common");
|
||||
artifacts.add("androidx.arch.core:core");
|
||||
artifacts.add("androidx.arch.core:core-testing");
|
||||
artifacts.add("androidx.arch.core:core-runtime");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-common");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-common-java8");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-compiler");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-extensions");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-livedata");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-livedata-core");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-reactivestreams");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-runtime");
|
||||
artifacts.add("androidx.lifecycle:lifecycle-viewmodel");
|
||||
artifacts.add("androidx.paging:paging-common");
|
||||
artifacts.add("androidx.paging:paging-runtime");
|
||||
artifacts.add("androidx.paging:paging-rxjava2");
|
||||
artifacts.add("androidx.room:room-common");
|
||||
artifacts.add("androidx.room:room-compiler");
|
||||
artifacts.add("androidx.room:room-guava");
|
||||
artifacts.add("androidx.room:room-migration");
|
||||
artifacts.add("androidx.room:room-runtime");
|
||||
artifacts.add("androidx.room:room-rxjava2");
|
||||
artifacts.add("androidx.room:room-testing");
|
||||
artifacts.add("androidx.sqlite:sqlite");
|
||||
artifacts.add("androidx.sqlite:sqlite-framework");
|
||||
artifacts.add("androidx.constraintlayout:constraintlayout");
|
||||
artifacts.add("androidx.constraintlayout:constraintlayout-solver");
|
||||
artifacts.add("androidx.test.espresso.idling:idling-concurrent");
|
||||
artifacts.add("androidx.test.espresso.idling:idling-net");
|
||||
artifacts.add("androidx.test.espresso:espresso-accessibility");
|
||||
artifacts.add("androidx.test.espresso:espresso-contrib");
|
||||
artifacts.add("androidx.test.espresso:espresso-core");
|
||||
artifacts.add("androidx.test.espresso:espresso-idling-resource");
|
||||
artifacts.add("androidx.test.espresso:espresso-intents");
|
||||
artifacts.add("androidx.test.espresso:espresso-remote");
|
||||
artifacts.add("androidx.test.espresso:espresso-web");
|
||||
artifacts.add("androidx.test.jank:janktesthelper");
|
||||
artifacts.add("androidx.test:test-services");
|
||||
artifacts.add("androidx.test.uiautomator:uiautomator");
|
||||
artifacts.add("androidx.test:monitor");
|
||||
artifacts.add("androidx.test:orchestrator");
|
||||
artifacts.add("androidx.test:rules");
|
||||
artifacts.add("androidx.test:runner");
|
||||
artifacts.add("androidx.vectordrawable:vectordrawable-animated");
|
||||
artifacts.add("androidx.appcompat:appcompat");
|
||||
artifacts.add("androidx.asynclayoutinflater:asynclayoutinflater");
|
||||
artifacts.add("androidx.car:car");
|
||||
artifacts.add("androidx.cardview:cardview");
|
||||
artifacts.add("androidx.collection:collection");
|
||||
artifacts.add("androidx.coordinatorlayout:coordinatorlayout");
|
||||
artifacts.add("androidx.cursoradapter:cursoradapter");
|
||||
artifacts.add("androidx.browser:browser");
|
||||
artifacts.add("androidx.customview:customview");
|
||||
artifacts.add("com.google.android.material:material");
|
||||
artifacts.add("androidx.documentfile:documentfile");
|
||||
artifacts.add("androidx.drawerlayout:drawerlayout");
|
||||
artifacts.add("androidx.exifinterface:exifinterface");
|
||||
artifacts.add("androidx.gridlayout:gridlayout");
|
||||
artifacts.add("androidx.heifwriter:heifwriter");
|
||||
artifacts.add("androidx.interpolator:interpolator");
|
||||
artifacts.add("androidx.leanback:leanback");
|
||||
artifacts.add("androidx.loader:loader");
|
||||
artifacts.add("androidx.localbroadcastmanager:localbroadcastmanager");
|
||||
artifacts.add("androidx.media2:media2");
|
||||
artifacts.add("androidx.media2:media2-exoplayer");
|
||||
artifacts.add("androidx.mediarouter:mediarouter");
|
||||
artifacts.add("androidx.multidex:multidex");
|
||||
artifacts.add("androidx.multidex:multidex-instrumentation");
|
||||
artifacts.add("androidx.palette:palette");
|
||||
artifacts.add("androidx.percentlayout:percentlayout");
|
||||
artifacts.add("androidx.leanback:leanback-preference");
|
||||
artifacts.add("androidx.legacy:legacy-preference-v14");
|
||||
artifacts.add("androidx.preference:preference");
|
||||
artifacts.add("androidx.print:print");
|
||||
artifacts.add("androidx.recommendation:recommendation");
|
||||
artifacts.add("androidx.recyclerview:recyclerview-selection");
|
||||
artifacts.add("androidx.recyclerview:recyclerview");
|
||||
artifacts.add("androidx.slice:slice-builders");
|
||||
artifacts.add("androidx.slice:slice-core");
|
||||
artifacts.add("androidx.slice:slice-view");
|
||||
artifacts.add("androidx.slidingpanelayout:slidingpanelayout");
|
||||
artifacts.add("androidx.annotation:annotation");
|
||||
artifacts.add("androidx.core:core");
|
||||
artifacts.add("androidx.contentpager:contentpager");
|
||||
artifacts.add("androidx.legacy:legacy-support-core-ui");
|
||||
artifacts.add("androidx.legacy:legacy-support-core-utils");
|
||||
artifacts.add("androidx.dynamicanimation:dynamicanimation");
|
||||
artifacts.add("androidx.emoji:emoji");
|
||||
artifacts.add("androidx.emoji:emoji-appcompat");
|
||||
artifacts.add("androidx.emoji:emoji-bundled");
|
||||
artifacts.add("androidx.fragment:fragment");
|
||||
artifacts.add("androidx.media:media");
|
||||
artifacts.add("androidx.tvprovider:tvprovider");
|
||||
artifacts.add("androidx.legacy:legacy-support-v13");
|
||||
artifacts.add("androidx.legacy:legacy-support-v4");
|
||||
artifacts.add("androidx.vectordrawable:vectordrawable");
|
||||
artifacts.add("androidx.swiperefreshlayout:swiperefreshlayout");
|
||||
artifacts.add("androidx.textclassifier:textclassifier");
|
||||
artifacts.add("androidx.transition:transition");
|
||||
artifacts.add("androidx.versionedparcelable:versionedparcelable");
|
||||
artifacts.add("androidx.viewpager:viewpager");
|
||||
artifacts.add("androidx.wear:wear");
|
||||
artifacts.add("androidx.webkit:webkit");
|
||||
|
||||
artifacts.add("fileTree(dir: 'libs', include: ['*.jar'])");
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
private static List<String> createConfigs() {
|
||||
var configs = new ArrayList<String>();
|
||||
configs.add("androidApis");
|
||||
configs.add("androidTestAnnotationProcessor");
|
||||
configs.add("androidTestApi");
|
||||
configs.add("androidTestApk");
|
||||
configs.add("androidTestCompile");
|
||||
configs.add("androidTestCompileOnly");
|
||||
configs.add("androidTestDebugAnnotationProcessor");
|
||||
configs.add("androidTestDebugApi");
|
||||
configs.add("androidTestDebugApk");
|
||||
configs.add("androidTestDebugCompile");
|
||||
configs.add("androidTestDebugCompileOnly");
|
||||
configs.add("androidTestDebugImplementation");
|
||||
configs.add("androidTestDebugProvided");
|
||||
configs.add("androidTestDebugRuntimeOnly");
|
||||
configs.add("androidTestDebugWearApp");
|
||||
configs.add("androidTestImplementation");
|
||||
configs.add("androidTestProvided");
|
||||
configs.add("androidTestRuntimeOnly");
|
||||
configs.add("androidTestUtil");
|
||||
configs.add("androidTestWearApp");
|
||||
configs.add("annotationProcessor");
|
||||
configs.add("api");
|
||||
configs.add("apk");
|
||||
configs.add("archives");
|
||||
configs.add("compile");
|
||||
configs.add("compileOnly");
|
||||
configs.add("coreLibraryDesugaring");
|
||||
configs.add("debugAnnotationProcessor");
|
||||
configs.add("debugApi");
|
||||
configs.add("debugApk");
|
||||
configs.add("debugCompile");
|
||||
configs.add("debugCompileOnly");
|
||||
configs.add("debugImplementation");
|
||||
configs.add("debugProvided");
|
||||
configs.add("debugRuntimeOnly");
|
||||
configs.add("debugWearApp");
|
||||
configs.add("default");
|
||||
configs.add("implementation");
|
||||
configs.add("implementation platform()");
|
||||
configs.add("lintChecks");
|
||||
configs.add("lintClassPath");
|
||||
configs.add("lintPublish");
|
||||
configs.add("provided");
|
||||
configs.add("releaseAnnotationProcessor");
|
||||
configs.add("releaseApi");
|
||||
configs.add("releaseApk");
|
||||
configs.add("releaseCompile");
|
||||
configs.add("releaseCompileOnly");
|
||||
configs.add("releaseImplementation");
|
||||
configs.add("releaseProvided");
|
||||
configs.add("releaseRuntimeOnly");
|
||||
configs.add("releaseWearApp");
|
||||
configs.add("runtimeOnly");
|
||||
configs.add("testAnnotationProcessor");
|
||||
configs.add("testApi");
|
||||
configs.add("testApk");
|
||||
configs.add("testCompile");
|
||||
configs.add("testCompileOnly");
|
||||
configs.add("testDebugAnnotationProcessor");
|
||||
configs.add("testDebugApi");
|
||||
configs.add("testDebugApk");
|
||||
configs.add("testDebugCompile");
|
||||
configs.add("testDebugCompileOnly");
|
||||
configs.add("testDebugImplementation");
|
||||
configs.add("testDebugProvided");
|
||||
configs.add("testDebugRuntimeOnly");
|
||||
configs.add("testDebugWearApp");
|
||||
configs.add("testImplementation");
|
||||
configs.add("testProvided");
|
||||
configs.add("testReleaseAnnotationProcessor");
|
||||
configs.add("testReleaseApi");
|
||||
configs.add("testReleaseApk");
|
||||
configs.add("testReleaseCompile");
|
||||
configs.add("testReleaseCompileOnly");
|
||||
configs.add("testReleaseImplementation");
|
||||
configs.add("testReleaseProvided");
|
||||
configs.add("testReleaseRuntimeOnly");
|
||||
configs.add("testReleaseWearApp");
|
||||
configs.add("testRuntimeOnly");
|
||||
configs.add("testWearApp");
|
||||
configs.add("wearApp");
|
||||
return configs;
|
||||
}
|
||||
|
||||
private static List<String> createOtherCompletions() {
|
||||
var others = new ArrayList<String>();
|
||||
|
||||
// Common words that user may use
|
||||
others.add("android");
|
||||
others.add("applicationVariants");
|
||||
others.add("compileSdkVersion");
|
||||
others.add("buildToolsVersion");
|
||||
others.add("defaultConfig");
|
||||
others.add("applicationId");
|
||||
others.add("minSdkVersion");
|
||||
others.add("targetSdkVersion");
|
||||
others.add("versionCode");
|
||||
others.add("versionName");
|
||||
others.add("multiDexEnabled");
|
||||
others.add("vectorDrawables.useSupportLibrary");
|
||||
others.add("compileOptions");
|
||||
others.add("coreLibraryDesugaringEnabled");
|
||||
others.add("sourceCompatibility");
|
||||
others.add("targetCompatibility");
|
||||
others.add("JavaVersion.VERSION_1_8");
|
||||
others.add("JavaVersion.VERSION_1_7");
|
||||
|
||||
// Lint options
|
||||
others.add("lintOptions");
|
||||
others.add("quiet");
|
||||
others.add("abortOnError");
|
||||
others.add("checkReleaseBuilds");
|
||||
others.add("ignoreWarnings");
|
||||
others.add("checkAllWarnings");
|
||||
others.add("warningsAsErrors");
|
||||
others.add("disable");
|
||||
others.add("enable");
|
||||
others.add("check");
|
||||
others.add("noLines");
|
||||
others.add("showAll");
|
||||
others.add("explainIssues");
|
||||
others.add("lintConfig");
|
||||
others.add("textReport");
|
||||
others.add("textOutput");
|
||||
others.add("xmlReport");
|
||||
others.add("xmlOutput");
|
||||
others.add("htmlReport");
|
||||
others.add("htmlOutput");
|
||||
others.add("fatal");
|
||||
others.add("error");
|
||||
others.add("warning");
|
||||
others.add("ignore");
|
||||
others.add("informational");
|
||||
others.add("baseline");
|
||||
others.add("checkTestSources");
|
||||
others.add("ignoreTestSources");
|
||||
others.add("checkGeneratedSources");
|
||||
others.add("checkDependencies");
|
||||
|
||||
// buildFeatures options
|
||||
others.add("buildFeatures");
|
||||
others.add("aidl");
|
||||
others.add("buildConfig");
|
||||
others.add("compose");
|
||||
others.add("prefab");
|
||||
others.add("renderScript");
|
||||
others.add("resValues");
|
||||
others.add("shaders");
|
||||
others.add("viewBinding");
|
||||
|
||||
// Signing configs and all
|
||||
others.add("signingConfigs");
|
||||
others.add("debug");
|
||||
others.add("storeFile");
|
||||
others.add("storePassword");
|
||||
others.add("keyAlias");
|
||||
others.add("keyPassword");
|
||||
others.add("release");
|
||||
others.add("storeFile");
|
||||
others.add("storePassword");
|
||||
others.add("keyAlias");
|
||||
others.add("keyPassword");
|
||||
others.add("buildTypes");
|
||||
others.add("debug");
|
||||
others.add("minifyEnabled");
|
||||
others.add("proguardFiles");
|
||||
others.add("signingConfig");
|
||||
others.add("stringfog.enable");
|
||||
others.add("release");
|
||||
others.add("minifyEnabled");
|
||||
others.add("proguardFiles");
|
||||
others.add("signingConfig");
|
||||
others.add("stringfog.enable");
|
||||
others.add("packagingOptions");
|
||||
others.add("exclude");
|
||||
|
||||
// Maybe used in root project's build.gradle
|
||||
others.add("buildscript");
|
||||
others.add("ext");
|
||||
others.add("project.ext");
|
||||
others.add("repositories");
|
||||
others.add("maven");
|
||||
others.add("url");
|
||||
others.add("google()");
|
||||
others.add("mavenLocal()");
|
||||
others.add("mavenCentral()");
|
||||
others.add("jcenter()");
|
||||
others.add("classpath");
|
||||
others.add("allprojects");
|
||||
others.add("subprojects");
|
||||
return others;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,6 @@ import com.itsaky.androidide.lexers.groovy.GroovyLexer;
|
||||
import com.itsaky.androidide.utils.CharSequenceReader;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
|
||||
@@ -41,25 +36,30 @@ import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.text.TextUtils;
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
public class GroovyLanguage extends IDELanguage {
|
||||
|
||||
|
||||
private final GroovyAnalyzer analyzer;
|
||||
private final GroovyAutoComplete completer;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("GroovyLanguage");
|
||||
|
||||
public GroovyLanguage () {
|
||||
analyzer = new GroovyAnalyzer ();
|
||||
completer = new GroovyAutoComplete ();
|
||||
|
||||
private static final Logger LOG = Logger.instance("GroovyLanguage");
|
||||
|
||||
public GroovyLanguage() {
|
||||
analyzer = new GroovyAnalyzer();
|
||||
completer = new GroovyAutoComplete();
|
||||
}
|
||||
|
||||
public int getIndentAdvance (String p1) {
|
||||
|
||||
public int getIndentAdvance(String p1) {
|
||||
try {
|
||||
GroovyLexer lexer = new GroovyLexer (CharStreams.fromReader (new StringReader (p1)));
|
||||
GroovyLexer lexer = new GroovyLexer(CharStreams.fromReader(new StringReader(p1)));
|
||||
Token token = null;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken ()) != null && token.getType () != token.EOF)) {
|
||||
switch (token.getType ()) {
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case GroovyLexer.LBRACE:
|
||||
advance++;
|
||||
break;
|
||||
@@ -68,48 +68,50 @@ public class GroovyLanguage extends IDELanguage {
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max (0, advance);
|
||||
return advance * getTabSize ();
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error ("Failed to calculate indent advance", e);
|
||||
LOG.error("Failed to calculate indent advance", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs () {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs ();
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager () {
|
||||
public AnalyzeManager getAnalyzeManager() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel () {
|
||||
public int getInterruptionLevel() {
|
||||
return INTERRUPTION_LEVEL_STRONG;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete (
|
||||
public void requireAutoComplete(
|
||||
@NonNull ContentReference content,
|
||||
@NonNull CharPosition position,
|
||||
@NonNull CompletionPublisher publisher,
|
||||
@NonNull Bundle extraArguments) throws CompletionCancelledException {
|
||||
|
||||
completer.complete (content, position, publisher, extraArguments);
|
||||
@NonNull Bundle extraArguments)
|
||||
throws CompletionCancelledException {
|
||||
|
||||
completer.complete(content, position, publisher, extraArguments);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance (@NonNull ContentReference content, int line, int column) {
|
||||
public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
|
||||
try {
|
||||
GroovyLexer lexer = new GroovyLexer (CharStreams.fromReader (new CharSequenceReader (content)));
|
||||
GroovyLexer lexer =
|
||||
new GroovyLexer(CharStreams.fromReader(new CharSequenceReader(content)));
|
||||
Token token;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken ()) != null && token.getType () != token.EOF)) {
|
||||
switch (token.getType ()) {
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case GroovyLexer.LBRACE:
|
||||
advance++;
|
||||
break;
|
||||
@@ -118,49 +120,56 @@ public class GroovyLanguage extends IDELanguage {
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max (0, advance);
|
||||
return advance * getTabSize ();
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error ("Failed to calculate indent advance", e);
|
||||
LOG.error("Failed to calculate indent advance", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence format (CharSequence content) {
|
||||
public CharSequence format(CharSequence content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
private final NewlineHandler[] newlineHandlers = new NewlineHandler[]{new BraceHandler ()};
|
||||
|
||||
|
||||
private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
|
||||
|
||||
@Override
|
||||
public NewlineHandler[] getNewlineHandlers () {
|
||||
public NewlineHandler[] getNewlineHandlers() {
|
||||
return newlineHandlers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy () {
|
||||
|
||||
}
|
||||
|
||||
public void destroy() {}
|
||||
|
||||
class BraceHandler implements NewlineHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean matchesRequirement (String beforeText, String afterText) {
|
||||
beforeText = beforeText.trim ();
|
||||
afterText = afterText.trim ();
|
||||
return beforeText.endsWith ("{") && afterText.startsWith ("}");
|
||||
public boolean matchesRequirement(String beforeText, String afterText) {
|
||||
beforeText = beforeText.trim();
|
||||
afterText = afterText.trim();
|
||||
return beforeText.endsWith("{") && afterText.startsWith("}");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NewlineHandleResult handleNewline (String beforeText, String afterText, int tabSize) {
|
||||
int count = TextUtils.countLeadingSpaceCount (beforeText, tabSize);
|
||||
int advanceBefore = getIndentAdvance (beforeText);
|
||||
int advanceAfter = getIndentAdvance (afterText);
|
||||
public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
|
||||
int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
|
||||
int advanceBefore = getIndentAdvance(beforeText);
|
||||
int advanceAfter = getIndentAdvance(afterText);
|
||||
String text;
|
||||
StringBuilder sb = new StringBuilder ("\n").append (TextUtils.createIndent (count + advanceBefore, tabSize, useTab ())).append ('\n').append (text = TextUtils.createIndent (count + advanceAfter, tabSize, useTab ()));
|
||||
int shiftLeft = text.length () + 1;
|
||||
return new NewlineHandleResult (sb, shiftLeft);
|
||||
StringBuilder sb =
|
||||
new StringBuilder("\n")
|
||||
.append(
|
||||
TextUtils.createIndent(
|
||||
count + advanceBefore, tabSize, useTab()))
|
||||
.append('\n')
|
||||
.append(
|
||||
text =
|
||||
TextUtils.createIndent(
|
||||
count + advanceAfter, tabSize, useTab()));
|
||||
int shiftLeft = text.length() + 1;
|
||||
return new NewlineHandleResult(sb, shiftLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,12 @@ import com.itsaky.lsp.models.DiagnosticSeverity;
|
||||
import com.itsaky.lsp.models.Position;
|
||||
import com.itsaky.lsp.models.Range;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Span;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -53,95 +59,92 @@ import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Span;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
/**
|
||||
* Code analyzer for the Java Language
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
|
||||
private List<DiagnosticItem> diagnostics = new ArrayList<> ();
|
||||
private final List<DiagnosticItem> ideDiagnostics = new ArrayList<> ();
|
||||
|
||||
private List<DiagnosticItem> diagnostics = new ArrayList<>();
|
||||
private final List<DiagnosticItem> ideDiagnostics = new ArrayList<>();
|
||||
private final ILanguageServer languageServer;
|
||||
|
||||
public JavaAnalyzer (ILanguageServer languageServer) {
|
||||
|
||||
public JavaAnalyzer(ILanguageServer languageServer) {
|
||||
this.languageServer = languageServer;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public List<DiagnosticItem> getDiagnostics () {
|
||||
final var result = new ArrayList<> (ideDiagnostics);
|
||||
if (diagnostics != null && !diagnostics.isEmpty ()) {
|
||||
result.addAll (diagnostics);
|
||||
public List<DiagnosticItem> getDiagnostics() {
|
||||
final var result = new ArrayList<>(ideDiagnostics);
|
||||
if (diagnostics != null && !diagnostics.isEmpty()) {
|
||||
result.addAll(diagnostics);
|
||||
}
|
||||
result.sort (DiagnosticItem.START_COMPARATOR);
|
||||
result.sort(DiagnosticItem.START_COMPARATOR);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Styles analyze (StringBuilder text, Delegate<Void> delegate) {
|
||||
final var args = getExtraArguments ();
|
||||
if (args.containsKey (IDEEditor.KEY_FILE)) {
|
||||
final var file = new File (args.getString (IDEEditor.KEY_FILE));
|
||||
protected Styles analyze(StringBuilder text, Delegate<Void> delegate) {
|
||||
final var args = getExtraArguments();
|
||||
if (args.containsKey(IDEEditor.KEY_FILE)) {
|
||||
final var file = new File(args.getString(IDEEditor.KEY_FILE));
|
||||
if (languageServer != null && file.exists()) {
|
||||
CompletableFuture.runAsync (() -> {
|
||||
final var analyzer = languageServer.getCodeAnalyzer ();
|
||||
|
||||
diagnostics = analyzer.analyze (file.toPath ());
|
||||
|
||||
if (languageServer.getClient () != null) {
|
||||
languageServer.getClient ().publishDiagnostics (new DiagnosticResult (file.toPath (), diagnostics));
|
||||
}
|
||||
});
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
final var analyzer = languageServer.getCodeAnalyzer();
|
||||
|
||||
diagnostics = analyzer.analyze(file.toPath());
|
||||
|
||||
if (languageServer.getClient() != null) {
|
||||
languageServer
|
||||
.getClient()
|
||||
.publishDiagnostics(
|
||||
new DiagnosticResult(file.toPath(), diagnostics));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
final CodePointCharStream stream;
|
||||
final var styles = new Styles ();
|
||||
final var colors = new MappedSpans.Builder ();
|
||||
final var styles = new Styles();
|
||||
final var colors = new MappedSpans.Builder();
|
||||
try {
|
||||
stream = CharStreams.fromReader (new CharSequenceReader (text));
|
||||
stream = CharStreams.fromReader(new CharSequenceReader(text));
|
||||
} catch (IOException e) {
|
||||
LOG.error ("Unable to create char stream for text", e);
|
||||
LOG.error("Unable to create char stream for text", e);
|
||||
return styles;
|
||||
}
|
||||
final var lexer = new JavaLexer (stream);
|
||||
final var stack = new Stack<CodeBlock> ();
|
||||
|
||||
final var lexer = new JavaLexer(stream);
|
||||
final var stack = new Stack<CodeBlock>();
|
||||
|
||||
Token token;
|
||||
int line, column, lastLine = 1, type, currSwitch = 0, maxSwitch = 0, previous = -1;
|
||||
boolean isFirst = true;
|
||||
|
||||
ideDiagnostics.clear ();
|
||||
|
||||
while (!delegate.isCancelled ()) {
|
||||
token = lexer.nextToken ();
|
||||
|
||||
|
||||
ideDiagnostics.clear();
|
||||
|
||||
while (!delegate.isCancelled()) {
|
||||
token = lexer.nextToken();
|
||||
|
||||
if (token == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
type = token.getType ();
|
||||
line = token.getLine () - 1;
|
||||
column = token.getCharPositionInLine ();
|
||||
|
||||
|
||||
type = token.getType();
|
||||
line = token.getLine() - 1;
|
||||
column = token.getCharPositionInLine();
|
||||
|
||||
if (type == JavaLexer.EOF) {
|
||||
lastLine = line;
|
||||
break;
|
||||
}
|
||||
|
||||
final var tokenLength = token.getText ().length ();
|
||||
|
||||
final var tokenLength = token.getText().length();
|
||||
switch (type) {
|
||||
case JavaLexer.WS:
|
||||
if (isFirst) {
|
||||
colors.addNormalIfNull ();
|
||||
colors.addNormalIfNull();
|
||||
}
|
||||
break;
|
||||
case JavaLexer.ABSTRACT:
|
||||
@@ -186,7 +189,7 @@ public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case JavaLexer.VOLATILE:
|
||||
case JavaLexer.WHILE:
|
||||
case JavaLexer.VAR:
|
||||
colors.addIfNeeded (line, column, forKeyword ());
|
||||
colors.addIfNeeded(line, column, forKeyword());
|
||||
break;
|
||||
case JavaLexer.DECIMAL_LITERAL:
|
||||
case JavaLexer.HEX_LITERAL:
|
||||
@@ -197,10 +200,10 @@ public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case JavaLexer.BOOL_LITERAL:
|
||||
case JavaLexer.CHAR_LITERAL:
|
||||
case JavaLexer.NULL_LITERAL:
|
||||
colors.addIfNeeded (line, column, get (LITERAL));
|
||||
colors.addIfNeeded(line, column, get(LITERAL));
|
||||
break;
|
||||
case JavaLexer.STRING_LITERAL:
|
||||
colors.addIfNeeded (line, column, forString ());
|
||||
colors.addIfNeeded(line, column, forString());
|
||||
break;
|
||||
case JavaLexer.LPAREN:
|
||||
case JavaLexer.RPAREN:
|
||||
@@ -246,7 +249,7 @@ public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case JavaLexer.COLONCOLON:
|
||||
case JavaLexer.ELLIPSIS:
|
||||
case JavaLexer.DOT:
|
||||
colors.addIfNeeded (line, column, get (OPERATOR));
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
break;
|
||||
case JavaLexer.BOOLEAN:
|
||||
case JavaLexer.BYTE:
|
||||
@@ -257,129 +260,133 @@ public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case JavaLexer.INT:
|
||||
case JavaLexer.LONG:
|
||||
case JavaLexer.SHORT:
|
||||
colors.addIfNeeded (line, column, get (TYPE_NAME));
|
||||
colors.addIfNeeded(line, column, get(TYPE_NAME));
|
||||
break;
|
||||
case JavaLexer.BLOCK_COMMENT:
|
||||
colors.addIfNeeded (line, column, forComment ());
|
||||
colors.addIfNeeded(line, column, forComment());
|
||||
break;
|
||||
case JavaLexer.LINE_COMMENT:
|
||||
var commentType = SchemeAndroidIDE.COMMENT;
|
||||
|
||||
|
||||
// highlight special line comments
|
||||
var commentText = token.getText ();
|
||||
if (commentText.length () > 2) {
|
||||
commentText = commentText.substring (2);
|
||||
commentText = commentText.trim ();
|
||||
|
||||
var commentText = token.getText();
|
||||
if (commentText.length() > 2) {
|
||||
commentText = commentText.substring(2);
|
||||
commentText = commentText.trim();
|
||||
|
||||
var mark = true;
|
||||
if ("todo".equalsIgnoreCase (commentText.substring (0, 4))) {
|
||||
if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) {
|
||||
commentType = SchemeAndroidIDE.TODO_COMMENT;
|
||||
} else if ("fixme".equalsIgnoreCase (commentText.substring (0, 5))) {
|
||||
} else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) {
|
||||
commentType = SchemeAndroidIDE.FIXME_COMMENT;
|
||||
} else {
|
||||
mark = false;
|
||||
}
|
||||
|
||||
|
||||
if (mark) {
|
||||
if (diagnostics == null) {
|
||||
diagnostics = new ArrayList<> ();
|
||||
diagnostics = new ArrayList<>();
|
||||
}
|
||||
final var diagnostic = new DiagnosticItem ();
|
||||
diagnostic.setSeverity (DiagnosticSeverity.WARNING);
|
||||
diagnostic.setMessage (commentText);
|
||||
diagnostic.setCode ("special.comment");
|
||||
diagnostic.setRange (new Range (new Position (line, column), new Position (line, column + tokenLength)));
|
||||
diagnostic.setSource (commentText);
|
||||
ideDiagnostics.add (diagnostic);
|
||||
final var diagnostic = new DiagnosticItem();
|
||||
diagnostic.setSeverity(DiagnosticSeverity.WARNING);
|
||||
diagnostic.setMessage(commentText);
|
||||
diagnostic.setCode("special.comment");
|
||||
diagnostic.setRange(
|
||||
new Range(
|
||||
new Position(line, column),
|
||||
new Position(line, column + tokenLength)));
|
||||
diagnostic.setSource(commentText);
|
||||
ideDiagnostics.add(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
colors.addIfNeeded (line, column, withoutCompletion (commentType));
|
||||
|
||||
|
||||
colors.addIfNeeded(line, column, withoutCompletion(commentType));
|
||||
|
||||
break;
|
||||
case JavaLexer.AT:
|
||||
colors.addIfNeeded (line, column, get (ANNOTATION));
|
||||
colors.addIfNeeded(line, column, get(ANNOTATION));
|
||||
break;
|
||||
case JavaLexer.IDENTIFIER:
|
||||
int colorId = SchemeAndroidIDE.TEXT_NORMAL;
|
||||
if (previous == JavaLexer.AT) {
|
||||
colorId = SchemeAndroidIDE.ANNOTATION;
|
||||
}
|
||||
colors.addIfNeeded (line, column, get (colorId));
|
||||
colors.addIfNeeded(line, column, get(colorId));
|
||||
break;
|
||||
case JavaLexer.LBRACE:
|
||||
colors.addIfNeeded (line, column, get (OPERATOR));
|
||||
if (stack.isEmpty ()) {
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
if (stack.isEmpty()) {
|
||||
if (currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
currSwitch = 0;
|
||||
}
|
||||
currSwitch++;
|
||||
var block = styles.obtainNewBlock ();
|
||||
var block = styles.obtainNewBlock();
|
||||
block.startLine = line;
|
||||
block.startColumn = column;
|
||||
stack.push (block);
|
||||
stack.push(block);
|
||||
break;
|
||||
case JavaLexer.RBRACE:
|
||||
colors.addIfNeeded (line, column, get (OPERATOR));
|
||||
if (!stack.isEmpty ()) {
|
||||
var block2 = stack.pop ();
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
if (!stack.isEmpty()) {
|
||||
var block2 = stack.pop();
|
||||
block2.endLine = line;
|
||||
block2.endColumn = column;
|
||||
if (block2.startLine != block2.endLine) {
|
||||
styles.addCodeBlock (block2);
|
||||
styles.addCodeBlock(block2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
colors.addIfNeeded (line, column, get (TEXT_NORMAL));
|
||||
colors.addIfNeeded(line, column, get(TEXT_NORMAL));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
isFirst = false;
|
||||
|
||||
|
||||
if (type != JavaLexer.WS) {
|
||||
previous = type;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.isEmpty () && currSwitch > maxSwitch) {
|
||||
|
||||
if (stack.isEmpty() && currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
|
||||
colors.determine (lastLine);
|
||||
styles.setSuppressSwitch (maxSwitch + 10);
|
||||
|
||||
if (diagnostics != null && !diagnostics.isEmpty ()) {
|
||||
markDiagnostics (colors, diagnostics);
|
||||
|
||||
colors.determine(lastLine);
|
||||
styles.setSuppressSwitch(maxSwitch + 10);
|
||||
|
||||
if (diagnostics != null && !diagnostics.isEmpty()) {
|
||||
markDiagnostics(colors, diagnostics);
|
||||
}
|
||||
|
||||
if (!ideDiagnostics.isEmpty ()) {
|
||||
markDiagnostics (colors, ideDiagnostics);
|
||||
|
||||
if (!ideDiagnostics.isEmpty()) {
|
||||
markDiagnostics(colors, ideDiagnostics);
|
||||
}
|
||||
|
||||
styles.spans = colors.build ();
|
||||
|
||||
|
||||
styles.spans = colors.build();
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
private void markDiagnostics (MappedSpans.Builder colors, List<DiagnosticItem> diagnostics) {
|
||||
diagnostics.sort (DiagnosticItem.START_COMPARATOR);
|
||||
|
||||
private void markDiagnostics(MappedSpans.Builder colors, List<DiagnosticItem> diagnostics) {
|
||||
diagnostics.sort(DiagnosticItem.START_COMPARATOR);
|
||||
for (var d : diagnostics) {
|
||||
var start = d.getRange ().getStart ();
|
||||
var end = d.getRange ().getEnd ();
|
||||
|
||||
colors.markProblemRegion (convertToSpanFlag(d.getSeverity ()),
|
||||
start.getLine (),
|
||||
start.getColumn (),
|
||||
end.getLine (),
|
||||
end.getColumn ());
|
||||
var start = d.getRange().getStart();
|
||||
var end = d.getRange().getEnd();
|
||||
|
||||
colors.markProblemRegion(
|
||||
convertToSpanFlag(d.getSeverity()),
|
||||
start.getLine(),
|
||||
start.getColumn(),
|
||||
end.getLine(),
|
||||
end.getColumn());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
private int convertToSpanFlag (@NonNull DiagnosticSeverity severity) {
|
||||
private int convertToSpanFlag(@NonNull DiagnosticSeverity severity) {
|
||||
switch (severity) {
|
||||
case WARNING:
|
||||
return Span.FLAG_WARNING;
|
||||
@@ -391,6 +398,6 @@ public class JavaAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
return Span.FLAG_TYPO;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = Logger.instance ("JavaAnalyzer");
|
||||
|
||||
private static final Logger LOG = Logger.instance("JavaAnalyzer");
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -30,16 +30,6 @@ import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
|
||||
@@ -50,128 +40,154 @@ import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.text.TextUtils;
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaLanguage extends IDELanguage {
|
||||
|
||||
|
||||
private JavaAnalyzer analyzer;
|
||||
private CommonCompletionProvider completer;
|
||||
|
||||
private CommonCompletionProvider completer;
|
||||
|
||||
private final NewlineHandler[] newlineHandlers;
|
||||
|
||||
|
||||
private static final Logger LOG = Logger.instance("JavaLanguage");
|
||||
|
||||
public JavaLanguage() {
|
||||
final var server = StudioApp.getInstance ().getJavaLanguageServer ();
|
||||
this.analyzer = new JavaAnalyzer (server);
|
||||
this.completer = new CommonCompletionProvider (server);
|
||||
|
||||
|
||||
public JavaLanguage() {
|
||||
final var server = StudioApp.getInstance().getJavaLanguageServer();
|
||||
this.analyzer = new JavaAnalyzer(server);
|
||||
this.completer = new CommonCompletionProvider(server);
|
||||
|
||||
this.newlineHandlers = new NewlineHandler[1];
|
||||
this.newlineHandlers[0] = new BraceHandler();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<DiagnosticItem> getDiagnostics () {
|
||||
return analyzer == null ? Collections.emptyList () : analyzer.getDiagnostics ();
|
||||
}
|
||||
|
||||
public int getIndentAdvance(String p1) {
|
||||
try {
|
||||
JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
|
||||
Token token;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case JavaLexer.LBRACE:
|
||||
advance++;
|
||||
break;
|
||||
case JavaParser.RBRACE:
|
||||
advance --;
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error ("Error calculating indent advance", e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new JavaSymbolPairs ();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager () {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete (@NonNull ContentReference content, @NonNull CharPosition position, @NonNull CompletionPublisher publisher, @NonNull Bundle extraArguments) throws CompletionCancelledException {
|
||||
if (!extraArguments.containsKey (IDEEditor.KEY_FILE)) {
|
||||
return;
|
||||
}
|
||||
final var file = Paths.get (extraArguments.getString (IDEEditor.KEY_FILE));
|
||||
publisher.setUpdateThreshold (0);
|
||||
publisher.addItems (new ArrayList<> (completer.complete (content, file, position, CommonCompletionProvider::checkJavaCompletionChar)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance (@NonNull ContentReference content, int line, int column) {
|
||||
return getIndentAdvance (content.getLine (line).substring (0, column));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<DiagnosticItem> getDiagnostics() {
|
||||
return analyzer == null ? Collections.emptyList() : analyzer.getDiagnostics();
|
||||
}
|
||||
|
||||
public int getIndentAdvance(String p1) {
|
||||
try {
|
||||
JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
|
||||
Token token;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case JavaLexer.LBRACE:
|
||||
advance++;
|
||||
break;
|
||||
case JavaParser.RBRACE:
|
||||
advance--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Error calculating indent advance", e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new JavaSymbolPairs();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete(
|
||||
@NonNull ContentReference content,
|
||||
@NonNull CharPosition position,
|
||||
@NonNull CompletionPublisher publisher,
|
||||
@NonNull Bundle extraArguments)
|
||||
throws CompletionCancelledException {
|
||||
if (!extraArguments.containsKey(IDEEditor.KEY_FILE)) {
|
||||
return;
|
||||
}
|
||||
final var file = Paths.get(extraArguments.getString(IDEEditor.KEY_FILE));
|
||||
publisher.setUpdateThreshold(0);
|
||||
publisher.addItems(
|
||||
new ArrayList<>(
|
||||
completer.complete(
|
||||
content,
|
||||
file,
|
||||
position,
|
||||
CommonCompletionProvider::checkJavaCompletionChar)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
|
||||
return getIndentAdvance(content.getLine(line).substring(0, column));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewlineHandler[] getNewlineHandlers() {
|
||||
return newlineHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy () {
|
||||
analyzer = null;
|
||||
completer = null;
|
||||
}
|
||||
|
||||
class BraceHandler implements NewlineHandler {
|
||||
|
||||
@Override
|
||||
public boolean matchesRequirement (String beforeText, String afterText) {
|
||||
return beforeText.endsWith("{") && afterText.startsWith("}");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
analyzer = null;
|
||||
completer = null;
|
||||
}
|
||||
|
||||
class BraceHandler implements NewlineHandler {
|
||||
|
||||
@Override
|
||||
public boolean matchesRequirement(String beforeText, String afterText) {
|
||||
return beforeText.endsWith("{") && afterText.startsWith("}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
|
||||
int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
|
||||
int advanceBefore = getIndentAdvance(beforeText);
|
||||
int advanceAfter = getIndentAdvance(afterText);
|
||||
String text;
|
||||
StringBuilder sb = new StringBuilder("\n")
|
||||
.append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab()))
|
||||
.append('\n')
|
||||
.append(text = TextUtils.createIndent(count + advanceAfter, tabSize, useTab()));
|
||||
StringBuilder sb =
|
||||
new StringBuilder("\n")
|
||||
.append(
|
||||
TextUtils.createIndent(
|
||||
count + advanceBefore, tabSize, useTab()))
|
||||
.append('\n')
|
||||
.append(
|
||||
text =
|
||||
TextUtils.createIndent(
|
||||
count + advanceAfter, tabSize, useTab()));
|
||||
int shiftLeft = text.length() + 1;
|
||||
return new NewlineHandleResult (sb, shiftLeft);
|
||||
return new NewlineHandleResult(sb, shiftLeft);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class JavaSymbolPairs extends SymbolPairMatch {
|
||||
public JavaSymbolPairs() {
|
||||
super.putPair('{', new Replacement("{}", 1));
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -25,9 +25,6 @@ import com.itsaky.androidide.language.IDELanguage;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||
@@ -39,86 +36,90 @@ import io.github.rosemoe.sora.text.CharPosition;
|
||||
import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LogLanguageImpl extends IDELanguage {
|
||||
|
||||
private static final LogAnalyzer analyzer = new LogAnalyzer();
|
||||
|
||||
public void addLine(LogLine line) {
|
||||
analyzer.addLine (line);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager () {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete (@NonNull ContentReference content, @NonNull CharPosition position, @NonNull CompletionPublisher publisher, @NonNull Bundle extraArguments) throws CompletionCancelledException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance (@NonNull ContentReference content, int line, int column) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewlineHandler[] getNewlineHandlers() {
|
||||
return new NewlineHandler[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy () {
|
||||
|
||||
}
|
||||
|
||||
private static class LogAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
|
||||
private static final List<LogLine> lines = new ArrayList<>();
|
||||
|
||||
public LogAnalyzer addLine(LogLine line) {
|
||||
if(line != null)
|
||||
lines.add(line);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Styles analyze (StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles ();
|
||||
final var colors = new MappedSpans.Builder ();
|
||||
int lastLine = 0;
|
||||
for(int i=0;i<lines.size() && !delegate.isCancelled ();i++) {
|
||||
if(i==0) colors.addNormalIfNull();
|
||||
LogLine line = lines.get(i);
|
||||
colors.addIfNeeded(i, 0, SchemeAndroidIDE.get (line.priority));
|
||||
lastLine = i;
|
||||
}
|
||||
colors.determine(lastLine);
|
||||
|
||||
styles.spans = colors.build ();
|
||||
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
|
||||
private static final LogAnalyzer analyzer = new LogAnalyzer();
|
||||
|
||||
public void addLine(LogLine line) {
|
||||
analyzer.addLine(line);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete(
|
||||
@NonNull ContentReference content,
|
||||
@NonNull CharPosition position,
|
||||
@NonNull CompletionPublisher publisher,
|
||||
@NonNull Bundle extraArguments)
|
||||
throws CompletionCancelledException {}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewlineHandler[] getNewlineHandlers() {
|
||||
return new NewlineHandler[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
|
||||
private static class LogAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
|
||||
private static final List<LogLine> lines = new ArrayList<>();
|
||||
|
||||
public LogAnalyzer addLine(LogLine line) {
|
||||
if (line != null) lines.add(line);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Styles analyze(StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles();
|
||||
final var colors = new MappedSpans.Builder();
|
||||
int lastLine = 0;
|
||||
for (int i = 0; i < lines.size() && !delegate.isCancelled(); i++) {
|
||||
if (i == 0) colors.addNormalIfNull();
|
||||
LogLine line = lines.get(i);
|
||||
colors.addIfNeeded(i, 0, SchemeAndroidIDE.get(line.priority));
|
||||
lastLine = i;
|
||||
}
|
||||
colors.determine(lastLine);
|
||||
|
||||
styles.spans = colors.build();
|
||||
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATO
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_SELECTED;
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment;
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
|
||||
|
||||
import static io.github.rosemoe.sora.widget.schemes.EditorColorScheme.LITERAL;
|
||||
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
@@ -28,6 +29,11 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.CharSequenceReader;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CodePointCharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -35,65 +41,61 @@ import org.antlr.v4.runtime.Token;
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.styling.CodeBlock;
|
||||
import io.github.rosemoe.sora.lang.styling.MappedSpans;
|
||||
import io.github.rosemoe.sora.lang.styling.Styles;
|
||||
|
||||
/**
|
||||
* Simple analyzer implementation for the XML files.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class XMLAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
|
||||
private static final Logger LOG = Logger.instance ("XMLAnalyzer");
|
||||
|
||||
|
||||
private static final Logger LOG = Logger.instance("XMLAnalyzer");
|
||||
|
||||
@Override
|
||||
protected Styles analyze (StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles ();
|
||||
final var colors = new MappedSpans.Builder ();
|
||||
|
||||
protected Styles analyze(StringBuilder text, Delegate<Void> delegate) {
|
||||
final var styles = new Styles();
|
||||
final var colors = new MappedSpans.Builder();
|
||||
|
||||
CodePointCharStream stream;
|
||||
try {
|
||||
stream = CharStreams.fromReader (new CharSequenceReader (text));
|
||||
stream = CharStreams.fromReader(new CharSequenceReader(text));
|
||||
} catch (IOException e) {
|
||||
LOG.error ("Unable to create stream for analyze", e);
|
||||
LOG.error("Unable to create stream for analyze", e);
|
||||
return styles;
|
||||
}
|
||||
|
||||
XMLLexer lexer = new XMLLexer (stream);
|
||||
|
||||
XMLLexer lexer = new XMLLexer(stream);
|
||||
Token token;
|
||||
int previous = 0;
|
||||
int line, column, lastLine = 1;
|
||||
var first = true;
|
||||
|
||||
final var stack = new Stack<CodeBlock> ();
|
||||
|
||||
while (!delegate.isCancelled ()) {
|
||||
token = lexer.nextToken ();
|
||||
|
||||
final var stack = new Stack<CodeBlock>();
|
||||
|
||||
while (!delegate.isCancelled()) {
|
||||
token = lexer.nextToken();
|
||||
if (token == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.getType () == XMLLexer.EOF) {
|
||||
lastLine = token.getLine () - 1;
|
||||
|
||||
if (token.getType() == XMLLexer.EOF) {
|
||||
lastLine = token.getLine() - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
line = token.getLine () - 1;
|
||||
column = token.getCharPositionInLine ();
|
||||
|
||||
line = token.getLine() - 1;
|
||||
column = token.getCharPositionInLine();
|
||||
lastLine = line;
|
||||
|
||||
switch (token.getType ()) {
|
||||
|
||||
switch (token.getType()) {
|
||||
case XMLLexer.S:
|
||||
case XMLLexer.SEA_WS:
|
||||
if (first) {
|
||||
colors.addNormalIfNull ();
|
||||
colors.addNormalIfNull();
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case XMLLexer.COMMENT:
|
||||
colors.addIfNeeded (line, column, forComment ());
|
||||
colors.addIfNeeded(line, column, forComment());
|
||||
break;
|
||||
case XMLLexer.OPEN:
|
||||
case XMLLexer.OPEN_SLASH:
|
||||
@@ -103,52 +105,52 @@ public class XMLAnalyzer extends SimpleAnalyzeManager<Void> {
|
||||
case XMLLexer.EQUALS:
|
||||
case XMLLexer.COLON:
|
||||
case XMLLexer.XMLDeclOpen:
|
||||
colors.addIfNeeded (line, column, get (OPERATOR));
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
break;
|
||||
case XMLLexer.SLASH_CLOSE:
|
||||
colors.addIfNeeded (line, column, get (OPERATOR));
|
||||
final var closeBlock = stack.pop ();
|
||||
colors.addIfNeeded(line, column, get(OPERATOR));
|
||||
final var closeBlock = stack.pop();
|
||||
closeBlock.endLine = line;
|
||||
closeBlock.endColumn = column;
|
||||
styles.addCodeBlock (closeBlock);
|
||||
styles.addCodeBlock(closeBlock);
|
||||
break;
|
||||
case XMLLexer.STRING:
|
||||
colors.addIfNeeded (line, column, LITERAL);
|
||||
colors.addIfNeeded(line, column, LITERAL);
|
||||
break;
|
||||
case XMLLexer.Name:
|
||||
var type = SchemeAndroidIDE.TEXT_NORMAL;
|
||||
if (previous == XMLLexer.OPEN) {
|
||||
type = SchemeAndroidIDE.XML_TAG;
|
||||
final var block = styles.obtainNewBlock ();
|
||||
final var block = styles.obtainNewBlock();
|
||||
block.startLine = line;
|
||||
block.startColumn = column;
|
||||
stack.push (block);
|
||||
stack.push(block);
|
||||
}
|
||||
|
||||
|
||||
if (previous == XMLLexer.OPEN_SLASH) {
|
||||
type = SchemeAndroidIDE.XML_TAG;
|
||||
final var block = stack.pop ();
|
||||
final var block = stack.pop();
|
||||
block.endLine = line;
|
||||
block.endColumn = column;
|
||||
styles.addCodeBlock (block);
|
||||
styles.addCodeBlock(block);
|
||||
}
|
||||
|
||||
colors.addIfNeeded (line, column, get (type));
|
||||
|
||||
colors.addIfNeeded(line, column, get(type));
|
||||
break;
|
||||
case XMLLexer.TEXT:
|
||||
default:
|
||||
colors.addIfNeeded (line, column, get (TEXT_SELECTED));
|
||||
colors.addIfNeeded(line, column, get(TEXT_SELECTED));
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
if (token.getType () != XMLLexer.SEA_WS) {
|
||||
previous = token.getType ();
|
||||
if (token.getType() != XMLLexer.SEA_WS) {
|
||||
previous = token.getType();
|
||||
}
|
||||
}
|
||||
colors.determine (lastLine);
|
||||
|
||||
styles.spans = colors.build ();
|
||||
|
||||
colors.determine(lastLine);
|
||||
|
||||
styles.spans = colors.build();
|
||||
|
||||
return styles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*
|
||||
@@ -29,13 +29,6 @@ import com.itsaky.androidide.utils.JavaCharacter;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
|
||||
@@ -44,105 +37,122 @@ import io.github.rosemoe.sora.text.CharPosition;
|
||||
import io.github.rosemoe.sora.text.ContentReference;
|
||||
import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class XMLLanguage extends IDELanguage {
|
||||
|
||||
private XMLAnalyzer analyzer;
|
||||
private final CommonCompletionProvider completer;
|
||||
private final NewlineHandler[] newlineHandlers;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("XMLLanguage");
|
||||
|
||||
public XMLLanguage() {
|
||||
this.completer = new CommonCompletionProvider (StudioApp.getInstance ().getXMLLanguageServer ());
|
||||
this.analyzer = new XMLAnalyzer();
|
||||
this.newlineHandlers = new NewlineHandler[0];
|
||||
}
|
||||
|
||||
public boolean isAutoCompleteChar(char ch) {
|
||||
return JavaCharacter.isJavaIdentifierPart(ch)
|
||||
|| ch == '<'
|
||||
|| ch == '/';
|
||||
}
|
||||
|
||||
public int getIndentAdvance(String content) {
|
||||
try {
|
||||
XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(content)));
|
||||
Token token;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case XMLLexer.OPEN:
|
||||
case XMLLexer.OPEN_SLASH :
|
||||
case XMLLexer.XMLDeclOpen :
|
||||
advance++;
|
||||
break;
|
||||
case XMLLexer.CLOSE:
|
||||
case XMLLexer.SLASH_CLOSE :
|
||||
case XMLLexer.SPECIAL_CLOSE :
|
||||
advance --;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error ("Failed to compute indent advance", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
private XMLAnalyzer analyzer;
|
||||
private final CommonCompletionProvider completer;
|
||||
private final NewlineHandler[] newlineHandlers;
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager () {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel () {
|
||||
return INTERRUPTION_LEVEL_STRONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete (@NonNull ContentReference content, @NonNull CharPosition position, @NonNull CompletionPublisher publisher, @NonNull Bundle extraArguments) throws CompletionCancelledException {
|
||||
if (!extraArguments.containsKey (IDEEditor.KEY_FILE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var file = Paths.get (extraArguments.getString (IDEEditor.KEY_FILE));
|
||||
publisher.setUpdateThreshold (0);
|
||||
publisher.addItems (new ArrayList<> (completer.complete (content, file, position, CommonCompletionProvider::checkXMLCompletionChar)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance (@NonNull ContentReference content, int line, int column) {
|
||||
final var text = content.getLine (line).substring (0, column);
|
||||
return getIndentAdvance (text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
private static final Logger LOG = Logger.instance("XMLLanguage");
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence content) {
|
||||
return content;
|
||||
}
|
||||
public XMLLanguage() {
|
||||
this.completer =
|
||||
new CommonCompletionProvider(StudioApp.getInstance().getXMLLanguageServer());
|
||||
this.analyzer = new XMLAnalyzer();
|
||||
this.newlineHandlers = new NewlineHandler[0];
|
||||
}
|
||||
|
||||
public boolean isAutoCompleteChar(char ch) {
|
||||
return JavaCharacter.isJavaIdentifierPart(ch) || ch == '<' || ch == '/';
|
||||
}
|
||||
|
||||
public int getIndentAdvance(String content) {
|
||||
try {
|
||||
XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(content)));
|
||||
Token token;
|
||||
int advance = 0;
|
||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||
switch (token.getType()) {
|
||||
case XMLLexer.OPEN:
|
||||
case XMLLexer.OPEN_SLASH:
|
||||
case XMLLexer.XMLDeclOpen:
|
||||
advance++;
|
||||
break;
|
||||
case XMLLexer.CLOSE:
|
||||
case XMLLexer.SLASH_CLOSE:
|
||||
case XMLLexer.SPECIAL_CLOSE:
|
||||
advance--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
advance = Math.max(0, advance);
|
||||
return advance * getTabSize();
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Failed to compute indent advance", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolPairMatch getSymbolPairs() {
|
||||
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AnalyzeManager getAnalyzeManager() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInterruptionLevel() {
|
||||
return INTERRUPTION_LEVEL_STRONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requireAutoComplete(
|
||||
@NonNull ContentReference content,
|
||||
@NonNull CharPosition position,
|
||||
@NonNull CompletionPublisher publisher,
|
||||
@NonNull Bundle extraArguments)
|
||||
throws CompletionCancelledException {
|
||||
if (!extraArguments.containsKey(IDEEditor.KEY_FILE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var file = Paths.get(extraArguments.getString(IDEEditor.KEY_FILE));
|
||||
publisher.setUpdateThreshold(0);
|
||||
publisher.addItems(
|
||||
new ArrayList<>(
|
||||
completer.complete(
|
||||
content,
|
||||
file,
|
||||
position,
|
||||
CommonCompletionProvider::checkXMLCompletionChar)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
|
||||
final var text = content.getLine(line).substring(0, column);
|
||||
return getIndentAdvance(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence format(CharSequence content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewlineHandler[] getNewlineHandlers() {
|
||||
return newlineHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy () {
|
||||
analyzer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
analyzer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*
|
||||
@@ -49,6 +49,8 @@ import com.itsaky.lsp.models.Range;
|
||||
import com.itsaky.lsp.models.TextEdit;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import io.github.rosemoe.sora.text.Content;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -56,255 +58,253 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.rosemoe.sora.text.Content;
|
||||
|
||||
/**
|
||||
* AndroidIDE specific implementation of the LanguageClient
|
||||
*/
|
||||
/** AndroidIDE specific implementation of the LanguageClient */
|
||||
public class IDELanguageClientImpl implements ILanguageClient {
|
||||
|
||||
|
||||
protected static final Gson gson = new Gson();
|
||||
protected static final Logger LOG = Logger.instance("AbstractLanguageClient");
|
||||
|
||||
|
||||
private final Map<File, List<DiagnosticItem>> diagnostics = new HashMap<>();
|
||||
private static IDELanguageClientImpl mInstance;
|
||||
|
||||
|
||||
protected EditorActivityProvider activityProvider;
|
||||
|
||||
private IDELanguageClientImpl (EditorActivityProvider provider) {
|
||||
|
||||
private IDELanguageClientImpl(EditorActivityProvider provider) {
|
||||
setActivityProvider(provider);
|
||||
}
|
||||
|
||||
public static IDELanguageClientImpl getInstance () {
|
||||
|
||||
public static IDELanguageClientImpl getInstance() {
|
||||
if (mInstance == null) {
|
||||
throw new IllegalStateException ("Client not initialized");
|
||||
throw new IllegalStateException("Client not initialized");
|
||||
}
|
||||
|
||||
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public static IDELanguageClientImpl initialize (EditorActivityProvider provider) {
|
||||
|
||||
public static IDELanguageClientImpl initialize(EditorActivityProvider provider) {
|
||||
if (mInstance != null) {
|
||||
throw new IllegalStateException ("Client is already initialized");
|
||||
throw new IllegalStateException("Client is already initialized");
|
||||
}
|
||||
|
||||
mInstance = new IDELanguageClientImpl (provider);
|
||||
|
||||
|
||||
mInstance = new IDELanguageClientImpl(provider);
|
||||
|
||||
return getInstance();
|
||||
}
|
||||
|
||||
public static void shutdown () {
|
||||
|
||||
public static void shutdown() {
|
||||
mInstance = null;
|
||||
}
|
||||
|
||||
public static boolean isInitialized () {
|
||||
|
||||
public static boolean isInitialized() {
|
||||
return mInstance != null;
|
||||
}
|
||||
|
||||
public void setActivityProvider(EditorActivityProvider provider) {
|
||||
this.activityProvider = provider;
|
||||
}
|
||||
|
||||
|
||||
protected EditorActivity activity() {
|
||||
if (activityProvider == null) return null;
|
||||
return activityProvider.provide();
|
||||
}
|
||||
|
||||
private CodeEditorView findEditorByFile (File file) {
|
||||
return activity ().getEditorForFile (file);
|
||||
|
||||
private CodeEditorView findEditorByFile(File file) {
|
||||
return activity().getEditorForFile(file);
|
||||
}
|
||||
|
||||
|
||||
public void hideDiagnostics() {
|
||||
if(activity() == null || activity().getDiagnosticBinding() == null) {
|
||||
if (activity() == null || activity().getDiagnosticBinding() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
activity().getDiagnosticBinding().getRoot().setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void hideBottomDiagnosticView(final File file) {
|
||||
if(activity() == null || file == null) {
|
||||
if (activity() == null || file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final var frag = findEditorByFile(file);
|
||||
if(frag == null || frag.getBinding() == null) {
|
||||
if (frag == null || frag.getBinding() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
frag.getBinding().diagnosticTextContainer.setVisibility(View.GONE);
|
||||
frag.getBinding().diagnosticText.setVisibility(View.GONE);
|
||||
frag.getBinding().diagnosticText.setClickable(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void publishDiagnostics(DiagnosticResult result) {
|
||||
boolean error = result == null;
|
||||
activity().handleDiagnosticsResultVisibility(error || result.getDiagnostics().isEmpty());
|
||||
|
||||
if(error) {
|
||||
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = result.getFile ().toFile ();
|
||||
if(!file.exists() || !file.isFile()) return;
|
||||
|
||||
|
||||
File file = result.getFile().toFile();
|
||||
if (!file.exists() || !file.isFile()) return;
|
||||
|
||||
diagnostics.put(file, result.getDiagnostics());
|
||||
activity().setDiagnosticsAdapter(newDiagnosticsAdapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity
|
||||
*/
|
||||
|
||||
/** Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity */
|
||||
public void showLocations(List<Location> locations) {
|
||||
|
||||
|
||||
// Cannot show anything if the activity() is null
|
||||
if (activity () == null) {
|
||||
if (activity() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean error = locations == null || locations.isEmpty ();
|
||||
activity ().handleSearchResultVisibility (error);
|
||||
|
||||
|
||||
|
||||
boolean error = locations == null || locations.isEmpty();
|
||||
activity().handleSearchResultVisibility(error);
|
||||
|
||||
if (error) {
|
||||
activity ().setSearchResultAdapter (new SearchListAdapter (null, null, null));
|
||||
activity().setSearchResultAdapter(new SearchListAdapter(null, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<File, List<SearchResult>> results = new HashMap<> ();
|
||||
for (int i = 0; i < locations.size (); i++) {
|
||||
|
||||
final Map<File, List<SearchResult>> results = new HashMap<>();
|
||||
for (int i = 0; i < locations.size(); i++) {
|
||||
try {
|
||||
final Location loc = locations.get (i);
|
||||
final Location loc = locations.get(i);
|
||||
if (loc == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final File file = loc.getFile ().toFile ();
|
||||
if (!file.exists () || !file.isFile ()) continue;
|
||||
var frag = findEditorByFile (file);
|
||||
|
||||
final File file = loc.getFile().toFile();
|
||||
if (!file.exists() || !file.isFile()) continue;
|
||||
var frag = findEditorByFile(file);
|
||||
Content content;
|
||||
if (frag != null && frag.getEditor () != null)
|
||||
content = frag.getEditor ().getText ();
|
||||
else content = new Content (FileIOUtils.readFile2String (file));
|
||||
final List<SearchResult> matches = results.containsKey (file) ? results.get (file) : new ArrayList<> ();
|
||||
Objects.requireNonNull (matches).add (
|
||||
new SearchResult (
|
||||
loc.getRange (),
|
||||
file,
|
||||
content.getLineString (loc.getRange ().getStart ().getLine ()),
|
||||
content.subContent (
|
||||
loc.getRange ().getStart ().getLine (),
|
||||
loc.getRange ().getStart ().getColumn (),
|
||||
loc.getRange ().getEnd ().getLine (),
|
||||
loc.getRange ().getEnd ().getColumn ()
|
||||
).toString ()
|
||||
)
|
||||
);
|
||||
results.put (file, matches);
|
||||
if (frag != null && frag.getEditor() != null) content = frag.getEditor().getText();
|
||||
else content = new Content(FileIOUtils.readFile2String(file));
|
||||
final List<SearchResult> matches =
|
||||
results.containsKey(file) ? results.get(file) : new ArrayList<>();
|
||||
Objects.requireNonNull(matches)
|
||||
.add(
|
||||
new SearchResult(
|
||||
loc.getRange(),
|
||||
file,
|
||||
content.getLineString(loc.getRange().getStart().getLine()),
|
||||
content.subContent(
|
||||
loc.getRange().getStart().getLine(),
|
||||
loc.getRange().getStart().getColumn(),
|
||||
loc.getRange().getEnd().getLine(),
|
||||
loc.getRange().getEnd().getColumn())
|
||||
.toString()));
|
||||
results.put(file, matches);
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Failed to show file location", th);
|
||||
LOG.error("Failed to show file location", th);
|
||||
}
|
||||
}
|
||||
|
||||
activity ().handleSearchResults (results);
|
||||
|
||||
activity().handleSearchResults(results);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform the given {@link CodeActionItem}
|
||||
*
|
||||
* @param editor The {@link IDEEditor} that invoked the code action request.
|
||||
* This is required to reduce the time finding the code action from the edits.
|
||||
* @param editor The {@link IDEEditor} that invoked the code action request. This is required to
|
||||
* reduce the time finding the code action from the edits.
|
||||
* @param action The action to perform
|
||||
*/
|
||||
public void performCodeAction(IDEEditor editor, CodeActionItem action) {
|
||||
if(activity() == null || editor == null || action == null) {
|
||||
if (activity() == null || editor == null || action == null) {
|
||||
StudioApp.getInstance().toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final ProgressSheet progress = new ProgressSheet();
|
||||
progress.setSubMessageEnabled(false);
|
||||
progress.setWelcomeTextEnabled(false);
|
||||
progress.setCancelable(false);
|
||||
progress.setMessage(activity().getString(R.string.msg_performing_fixes));
|
||||
progress.show(activity().getSupportFragmentManager(), "quick_fix_progress");
|
||||
|
||||
new TaskExecutor().executeAsyncProvideError(() -> performCodeActionAsync(editor, action), (a, b) -> {
|
||||
progress.dismiss();
|
||||
if(a == null || b != null || !a) {
|
||||
StudioApp.getInstance().toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
new TaskExecutor()
|
||||
.executeAsyncProvideError(
|
||||
() -> performCodeActionAsync(editor, action),
|
||||
(a, b) -> {
|
||||
progress.dismiss();
|
||||
if (a == null || b != null || !a) {
|
||||
StudioApp.getInstance()
|
||||
.toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<DiagnosticGroup> mapAsGroup(Map<File, List<DiagnosticItem>> diags) {
|
||||
List<DiagnosticGroup> groups = new ArrayList<>();
|
||||
if(diags == null || diags.size() <= 0)
|
||||
return groups;
|
||||
for(File file : diags.keySet()) {
|
||||
if (diags == null || diags.size() <= 0) return groups;
|
||||
for (File file : diags.keySet()) {
|
||||
var fileDiags = diags.get(file);
|
||||
if(fileDiags == null || fileDiags.size() <= 0)
|
||||
continue;
|
||||
DiagnosticGroup group = new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
|
||||
if (fileDiags == null || fileDiags.size() <= 0) continue;
|
||||
DiagnosticGroup group =
|
||||
new DiagnosticGroup(R.drawable.ic_language_java, file, fileDiags);
|
||||
groups.add(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
private void showAvailableQuickfixes (IDEEditor editor, List<CodeActionItem> actions) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
private void showAvailableQuickfixes(IDEEditor editor, List<CodeActionItem> actions) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||
builder.setTitle(R.string.msg_code_actions);
|
||||
builder.setItems(asArray(actions), (d, w) -> {
|
||||
d.dismiss();
|
||||
hideDiagnostics();
|
||||
hideBottomDiagnosticView(editor.getFile());
|
||||
performCodeAction(editor, actions.get(w));
|
||||
});
|
||||
builder.setItems(
|
||||
asArray(actions),
|
||||
(d, w) -> {
|
||||
d.dismiss();
|
||||
hideDiagnostics();
|
||||
hideBottomDiagnosticView(editor.getFile());
|
||||
performCodeAction(editor, actions.get(w));
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private CharSequence[] asArray(List<CodeActionItem> actions) {
|
||||
final String[] arr = new String[actions.size()];
|
||||
for(int i=0;i<actions.size();i++) {
|
||||
for (int i = 0; i < actions.size(); i++) {
|
||||
arr[i] = actions.get(i).getTitle();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
private Boolean performCodeActionAsync(final IDEEditor editor, final CodeActionItem action) {
|
||||
final var changes = action.getChanges();
|
||||
if(changes.isEmpty()) {
|
||||
if (changes.isEmpty()) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
for(var change : changes) {
|
||||
final var path = change.getFile ();
|
||||
|
||||
for (var change : changes) {
|
||||
final var path = change.getFile();
|
||||
if (path == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final File file = path.toFile ();
|
||||
if(!file.exists()) {
|
||||
|
||||
final File file = path.toFile();
|
||||
if (!file.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for(TextEdit edit : change.getEdits ()) {
|
||||
final String editorFilepath = editor.getFile() == null ? "" : editor.getFile().getAbsolutePath();
|
||||
if(file.getAbsolutePath().equals(editorFilepath)) {
|
||||
for (TextEdit edit : change.getEdits()) {
|
||||
final String editorFilepath =
|
||||
editor.getFile() == null ? "" : editor.getFile().getAbsolutePath();
|
||||
if (file.getAbsolutePath().equals(editorFilepath)) {
|
||||
// Edit is in the same editor which requested the code action
|
||||
editInEditor(editor, edit);
|
||||
} else {
|
||||
var openedFrag = findEditorByFile(file);
|
||||
|
||||
if(openedFrag != null && openedFrag.getEditor() != null) {
|
||||
if (openedFrag != null && openedFrag.getEditor() != null) {
|
||||
// Edit is in another 'opened' file
|
||||
editInEditor(openedFrag.getEditor(), edit);
|
||||
} else {
|
||||
// Edit is in some other file which is not opened
|
||||
// We should open that file and perform the edit
|
||||
openedFrag = activity().openFile(file);
|
||||
if(openedFrag != null && openedFrag.getEditor() != null) {
|
||||
if (openedFrag != null && openedFrag.getEditor() != null) {
|
||||
editInEditor(openedFrag.getEditor(), edit);
|
||||
}
|
||||
}
|
||||
@@ -314,60 +314,75 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
private void editInEditor (final IDEEditor editor, final TextEdit edit) {
|
||||
|
||||
private void editInEditor(final IDEEditor editor, final TextEdit edit) {
|
||||
final Range range = edit.getRange();
|
||||
final int startLine = range.getStart().getLine();
|
||||
final int startCol = range.getStart().getColumn ();
|
||||
final int startCol = range.getStart().getColumn();
|
||||
final int endLine = range.getEnd().getLine();
|
||||
final int endCol = range.getEnd().getColumn ();
|
||||
|
||||
activity().runOnUiThread(() -> {
|
||||
if(startLine == endLine && startCol == endCol) {
|
||||
editor.getText().insert(startLine, startCol, edit.getNewText());
|
||||
} else {
|
||||
editor.getText().replace(startLine, startCol, endLine, endCol, edit.getNewText());
|
||||
}
|
||||
});
|
||||
final int endCol = range.getEnd().getColumn();
|
||||
|
||||
activity()
|
||||
.runOnUiThread(
|
||||
() -> {
|
||||
if (startLine == endLine && startCol == endCol) {
|
||||
editor.getText().insert(startLine, startCol, edit.getNewText());
|
||||
} else {
|
||||
editor.getText()
|
||||
.replace(
|
||||
startLine,
|
||||
startCol,
|
||||
endLine,
|
||||
endCol,
|
||||
edit.getNewText());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public DiagnosticsAdapter newDiagnosticsAdapter() {
|
||||
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public com.itsaky.lsp.models.ShowDocumentResult showDocument (com.itsaky.lsp.models.ShowDocumentParams params) {
|
||||
public com.itsaky.lsp.models.ShowDocumentResult showDocument(
|
||||
com.itsaky.lsp.models.ShowDocumentParams params) {
|
||||
boolean success = false;
|
||||
final var result = new com.itsaky.lsp.models.ShowDocumentResult (false);
|
||||
if(activity() == null) {
|
||||
final var result = new com.itsaky.lsp.models.ShowDocumentResult(false);
|
||||
if (activity() == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if(params != null) {
|
||||
File file = params.getFile ().toFile ();
|
||||
if (file.exists () && file.isFile () && FileUtils.isUtf8 (file)) {
|
||||
final var range = params.getSelection ();
|
||||
var frag = activity ().getEditorAtIndex (
|
||||
activity ().getBinding ().tabs.getSelectedTabPosition ());
|
||||
|
||||
if (params != null) {
|
||||
File file = params.getFile().toFile();
|
||||
if (file.exists() && file.isFile() && FileUtils.isUtf8(file)) {
|
||||
final var range = params.getSelection();
|
||||
var frag =
|
||||
activity()
|
||||
.getEditorAtIndex(
|
||||
activity().getBinding().tabs.getSelectedTabPosition());
|
||||
if (frag != null
|
||||
&& frag.getFile () != null
|
||||
&& frag.getEditor () != null
|
||||
&& frag.getFile ().getAbsolutePath ().equals (file.getAbsolutePath ())) {
|
||||
if (LSPUtils.isEqual (range.getStart (), range.getEnd ())) {
|
||||
frag.getEditor ().setSelection (range.getStart ().getLine (), range.getStart ().getColumn ());
|
||||
&& frag.getFile() != null
|
||||
&& frag.getEditor() != null
|
||||
&& frag.getFile().getAbsolutePath().equals(file.getAbsolutePath())) {
|
||||
if (LSPUtils.isEqual(range.getStart(), range.getEnd())) {
|
||||
frag.getEditor()
|
||||
.setSelection(
|
||||
range.getStart().getLine(), range.getStart().getColumn());
|
||||
} else {
|
||||
frag.getEditor ().setSelectionRegion (range.getStart ().getLine (),
|
||||
range.getStart ().getColumn (),
|
||||
range.getEnd ().getLine (),
|
||||
range.getEnd ().getColumn ());
|
||||
frag.getEditor()
|
||||
.setSelectionRegion(
|
||||
range.getStart().getLine(),
|
||||
range.getStart().getColumn(),
|
||||
range.getEnd().getLine(),
|
||||
range.getEnd().getColumn());
|
||||
}
|
||||
} else {
|
||||
activity ().openFileAndSelect (file, range);
|
||||
activity().openFileAndSelect(file, range);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result.setSuccess(success);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
import java.util.List;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ApkMetadata {
|
||||
|
||||
|
||||
@SerializedName("version")
|
||||
private int version;
|
||||
|
||||
|
||||
@SerializedName("artifactType")
|
||||
private ArtifactType artifactType;
|
||||
|
||||
|
||||
@SerializedName("applicationId")
|
||||
private String applicationId;
|
||||
|
||||
|
||||
@SerializedName("variantName")
|
||||
private String variantName;
|
||||
|
||||
|
||||
@SerializedName("elements")
|
||||
private List<Element> elements;
|
||||
|
||||
|
||||
@SerializedName("elementType")
|
||||
private String elementType;
|
||||
|
||||
@@ -89,128 +90,141 @@ public class ApkMetadata {
|
||||
}
|
||||
|
||||
public static class ArtifactType {
|
||||
|
||||
|
||||
public static String TYPE_APK = "APK";
|
||||
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
|
||||
@SerializedName("kind")
|
||||
private String kind;
|
||||
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public void setKind(String kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public String getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+getClass().getCanonicalName()+"]"
|
||||
+ "\ntype=" + type
|
||||
+ ",\nkind=" + kind;
|
||||
return "[" + getClass().getCanonicalName() + "]" + "\ntype=" + type + ",\nkind=" + kind;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Element {
|
||||
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
|
||||
@SerializedName("filters")
|
||||
private List<Object> filters;
|
||||
|
||||
|
||||
@SerializedName("attributes")
|
||||
private List<Object> attributes;
|
||||
|
||||
|
||||
@SerializedName("versionCode")
|
||||
private int versionCode;
|
||||
|
||||
|
||||
@SerializedName("versionName")
|
||||
private String versionName;
|
||||
|
||||
|
||||
@SerializedName("outputFile")
|
||||
private String outputFile;
|
||||
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public void setFilters(List<Object> filters) {
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
|
||||
public void setAttributes(List<Object> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
|
||||
public void setVersionCode(int versionCode) {
|
||||
this.versionCode = versionCode;
|
||||
}
|
||||
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
this.versionName = versionName;
|
||||
}
|
||||
|
||||
|
||||
public void setOutputFile(String outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public List<Object> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
public List<Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
||||
public int getVersionCode() {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
|
||||
public String getVersionName() {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
|
||||
public String getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+getClass().getCanonicalName()+"]"
|
||||
+ "\ntype=" + type
|
||||
+ ",\nfilters=" + filters
|
||||
+ ",\nattributes=" + attributes
|
||||
+ ",\nversionCode=" + versionCode
|
||||
+ ",\nversionName=" + versionName
|
||||
+ ",\noutputFile=" + outputFile;
|
||||
return "["
|
||||
+ getClass().getCanonicalName()
|
||||
+ "]"
|
||||
+ "\ntype="
|
||||
+ type
|
||||
+ ",\nfilters="
|
||||
+ filters
|
||||
+ ",\nattributes="
|
||||
+ attributes
|
||||
+ ",\nversionCode="
|
||||
+ versionCode
|
||||
+ ",\nversionName="
|
||||
+ versionName
|
||||
+ ",\noutputFile="
|
||||
+ outputFile;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+getClass().getCanonicalName()+"]"
|
||||
+ "\nversion=" + version
|
||||
+ ",\nartifactType=" + artifactType
|
||||
+ ",\napplicationId=" + applicationId
|
||||
+ ",\nvariantName=" + variantName
|
||||
+ ",\nelements=" + elements
|
||||
+ ",\nelementType=" + elementType;
|
||||
return "["
|
||||
+ getClass().getCanonicalName()
|
||||
+ "]"
|
||||
+ "\nversion="
|
||||
+ version
|
||||
+ ",\nartifactType="
|
||||
+ artifactType
|
||||
+ ",\napplicationId="
|
||||
+ applicationId
|
||||
+ ",\nvariantName="
|
||||
+ variantName
|
||||
+ ",\nelements="
|
||||
+ elements
|
||||
+ ",\nelementType="
|
||||
+ elementType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
import com.itsaky.lsp.models.DiagnosticItem;
|
||||
|
||||
@@ -21,22 +21,21 @@ import androidx.annotation.DrawableRes;
|
||||
|
||||
public interface IconTextListItem {
|
||||
|
||||
String getText ();
|
||||
String getText();
|
||||
|
||||
int getIconResource ();
|
||||
|
||||
public static IconTextListItem create (String name, @DrawableRes int icon) {
|
||||
return new IconTextListItem () {
|
||||
int getIconResource();
|
||||
|
||||
public static IconTextListItem create(String name, @DrawableRes int icon) {
|
||||
return new IconTextListItem() {
|
||||
@Override
|
||||
public String getText () {
|
||||
public String getText() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getIconResource () {
|
||||
public int getIconResource() {
|
||||
return icon;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
@@ -22,10 +22,10 @@ import com.google.gson.annotations.SerializedName;
|
||||
public class License {
|
||||
@SerializedName("name")
|
||||
public String name;
|
||||
|
||||
|
||||
@SerializedName("license")
|
||||
public String license;
|
||||
|
||||
|
||||
@SerializedName("url")
|
||||
public String url;
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*
|
||||
@@ -28,101 +28,120 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LogLine {
|
||||
|
||||
public String unformatted;
|
||||
|
||||
public String date;
|
||||
public String time;
|
||||
public String pid;
|
||||
public String tid;
|
||||
public String priorityChar;
|
||||
public String tag;
|
||||
public String message;
|
||||
|
||||
public int priority;
|
||||
|
||||
public boolean formatted;
|
||||
|
||||
// It makes things easier in LogLanguageImpl
|
||||
public static final int INFO = SchemeAndroidIDE.LOG_INFO;
|
||||
public static final int DEBUG = SchemeAndroidIDE.LOG_DEBUG;
|
||||
public static final int ERROR = SchemeAndroidIDE.LOG_ERROR;
|
||||
public static final int WARNING = SchemeAndroidIDE.LOG_WARNING;
|
||||
|
||||
public LogLine (String date, String time, String pid, String tid, String priorityChar, String tag, String message) {
|
||||
this (date, time, pid, tid, priorityChar, tag, message, true);
|
||||
}
|
||||
|
||||
public LogLine (String unformatted) {
|
||||
this.unformatted = unformatted;
|
||||
this.formatted = false;
|
||||
}
|
||||
|
||||
public LogLine(String date, String time, String pid, String tid, String priorityChar, @NonNull String tag, String message, boolean formatted) {
|
||||
this.date = date;
|
||||
this.time = time;
|
||||
this.pid = pid;
|
||||
this.tid = tid;
|
||||
this.priorityChar = priorityChar;
|
||||
this.tag = tag;
|
||||
this.message = message;
|
||||
this.priority = parsePriority(priorityChar);
|
||||
this.formatted = formatted;
|
||||
|
||||
if(tag.length() > 25) {
|
||||
|
||||
public String unformatted;
|
||||
|
||||
public String date;
|
||||
public String time;
|
||||
public String pid;
|
||||
public String tid;
|
||||
public String priorityChar;
|
||||
public String tag;
|
||||
public String message;
|
||||
|
||||
public int priority;
|
||||
|
||||
public boolean formatted;
|
||||
|
||||
// It makes things easier in LogLanguageImpl
|
||||
public static final int INFO = SchemeAndroidIDE.LOG_INFO;
|
||||
public static final int DEBUG = SchemeAndroidIDE.LOG_DEBUG;
|
||||
public static final int ERROR = SchemeAndroidIDE.LOG_ERROR;
|
||||
public static final int WARNING = SchemeAndroidIDE.LOG_WARNING;
|
||||
|
||||
public LogLine(
|
||||
String date,
|
||||
String time,
|
||||
String pid,
|
||||
String tid,
|
||||
String priorityChar,
|
||||
String tag,
|
||||
String message) {
|
||||
this(date, time, pid, tid, priorityChar, tag, message, true);
|
||||
}
|
||||
|
||||
public LogLine(String unformatted) {
|
||||
this.unformatted = unformatted;
|
||||
this.formatted = false;
|
||||
}
|
||||
|
||||
public LogLine(
|
||||
String date,
|
||||
String time,
|
||||
String pid,
|
||||
String tid,
|
||||
String priorityChar,
|
||||
@NonNull String tag,
|
||||
String message,
|
||||
boolean formatted) {
|
||||
this.date = date;
|
||||
this.time = time;
|
||||
this.pid = pid;
|
||||
this.tid = tid;
|
||||
this.priorityChar = priorityChar;
|
||||
this.tag = tag;
|
||||
this.message = message;
|
||||
this.priority = parsePriority(priorityChar);
|
||||
this.formatted = formatted;
|
||||
|
||||
if (tag.length() > 25) {
|
||||
this.tag = "...".concat(tag.substring(tag.length() - 25));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static LogLine forLogString (@NonNull final String log) {
|
||||
try {
|
||||
final var split = log.split ("\\s", 7);
|
||||
return new LogLine(
|
||||
split[0], // date
|
||||
split[1], // time
|
||||
split[2], // process id
|
||||
split[3], // thread id
|
||||
split[4], // priority
|
||||
split[5], // tag
|
||||
split[6] // message
|
||||
);
|
||||
} catch (Throwable th) {
|
||||
return new LogLine (log);
|
||||
}
|
||||
}
|
||||
|
||||
private int parsePriority(String s) {
|
||||
if(s == null || s.trim().length() > 1) {
|
||||
return DEBUG;
|
||||
}
|
||||
char c = s.toLowerCase(Locale.US).charAt(0);
|
||||
if(c == 'w') {
|
||||
return WARNING;
|
||||
} else if(c == 'e') {
|
||||
return ERROR;
|
||||
} else if(c == 'i') {
|
||||
return INFO;
|
||||
} else {
|
||||
return DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
public int getColor(Context ctx) {
|
||||
int id = R.color.log_normal;
|
||||
if(priority == WARNING) {
|
||||
id = R.color.log_warning;
|
||||
} else if(priority == ERROR) {
|
||||
id = R.color.log_error;
|
||||
} else if(priority == INFO) {
|
||||
id = R.color.log_info;
|
||||
}
|
||||
return ContextCompat.getColor(ctx, id);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.formatted ? String.format("%-6s %-13s %-6s %-6s %-2s %-40s %s", date, time, pid, tid, priorityChar, tag, message) : this.unformatted;
|
||||
}
|
||||
@NonNull
|
||||
public static LogLine forLogString(@NonNull final String log) {
|
||||
try {
|
||||
final var split = log.split("\\s", 7);
|
||||
return new LogLine(
|
||||
split[0], // date
|
||||
split[1], // time
|
||||
split[2], // process id
|
||||
split[3], // thread id
|
||||
split[4], // priority
|
||||
split[5], // tag
|
||||
split[6] // message
|
||||
);
|
||||
} catch (Throwable th) {
|
||||
return new LogLine(log);
|
||||
}
|
||||
}
|
||||
|
||||
private int parsePriority(String s) {
|
||||
if (s == null || s.trim().length() > 1) {
|
||||
return DEBUG;
|
||||
}
|
||||
char c = s.toLowerCase(Locale.US).charAt(0);
|
||||
if (c == 'w') {
|
||||
return WARNING;
|
||||
} else if (c == 'e') {
|
||||
return ERROR;
|
||||
} else if (c == 'i') {
|
||||
return INFO;
|
||||
} else {
|
||||
return DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
public int getColor(Context ctx) {
|
||||
int id = R.color.log_normal;
|
||||
if (priority == WARNING) {
|
||||
id = R.color.log_warning;
|
||||
} else if (priority == ERROR) {
|
||||
id = R.color.log_error;
|
||||
} else if (priority == INFO) {
|
||||
id = R.color.log_info;
|
||||
}
|
||||
return ContextCompat.getColor(ctx, id);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.formatted
|
||||
? String.format(
|
||||
"%-6s %-13s %-6s %-6s %-2s %-40s %s",
|
||||
date, time, pid, tid, priorityChar, tag, message)
|
||||
: this.unformatted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
public class NewProjectDetails {
|
||||
|
||||
public String name;
|
||||
public String packageName;
|
||||
public int minSdk;
|
||||
public int targetSdk;
|
||||
|
||||
public NewProjectDetails(){}
|
||||
|
||||
public NewProjectDetails(String name, String packageName, int minSdk, int targetSdk) {
|
||||
this.name = name;
|
||||
this.packageName = packageName;
|
||||
this.minSdk = minSdk;
|
||||
this.targetSdk = targetSdk;
|
||||
}
|
||||
public String name;
|
||||
public String packageName;
|
||||
public int minSdk;
|
||||
public int targetSdk;
|
||||
|
||||
public NewProjectDetails setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public NewProjectDetails() {}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public NewProjectDetails(String name, String packageName, int minSdk, int targetSdk) {
|
||||
this.name = name;
|
||||
this.packageName = packageName;
|
||||
this.minSdk = minSdk;
|
||||
this.targetSdk = targetSdk;
|
||||
}
|
||||
|
||||
public NewProjectDetails setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
return this;
|
||||
}
|
||||
public NewProjectDetails setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public NewProjectDetails setMinSdk(int minSdk) {
|
||||
this.minSdk = minSdk;
|
||||
return this;
|
||||
}
|
||||
public NewProjectDetails setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMinSdk() {
|
||||
return minSdk;
|
||||
}
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public NewProjectDetails setTargetSdk(int targetSdk) {
|
||||
this.targetSdk = targetSdk;
|
||||
return this;
|
||||
}
|
||||
public NewProjectDetails setMinSdk(int minSdk) {
|
||||
this.minSdk = minSdk;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTargetSdk() {
|
||||
return targetSdk;
|
||||
}
|
||||
public int getMinSdk() {
|
||||
return minSdk;
|
||||
}
|
||||
|
||||
public NewProjectDetails setTargetSdk(int targetSdk) {
|
||||
this.targetSdk = targetSdk;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getTargetSdk() {
|
||||
return targetSdk;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,31 +25,32 @@ import com.itsaky.lsp.java.models.DefaultJavaServerSettings;
|
||||
|
||||
/**
|
||||
* Server settings for the java language server.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class PrefBasedJavaServerSettings extends DefaultJavaServerSettings {
|
||||
|
||||
|
||||
public static final String KEY_JAVA_PREF_MATCH_LOWER = "idepref_editor_java_matchLower";
|
||||
|
||||
|
||||
private static PrefBasedJavaServerSettings instance;
|
||||
|
||||
|
||||
private final PreferenceManager prefs;
|
||||
|
||||
public PrefBasedJavaServerSettings () {
|
||||
this.prefs = StudioApp.getInstance ().getPrefManager ();
|
||||
|
||||
public PrefBasedJavaServerSettings() {
|
||||
this.prefs = StudioApp.getInstance().getPrefManager();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public static PrefBasedJavaServerSettings getInstance () {
|
||||
public static PrefBasedJavaServerSettings getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new PrefBasedJavaServerSettings ();
|
||||
instance = new PrefBasedJavaServerSettings();
|
||||
}
|
||||
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean shouldMatchAllLowerCase () {
|
||||
return prefs.getBoolean (KEY_JAVA_PREF_MATCH_LOWER, false);
|
||||
public boolean shouldMatchAllLowerCase() {
|
||||
return prefs.getBoolean(KEY_JAVA_PREF_MATCH_LOWER, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,85 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.StringRes;
|
||||
import android.content.Context;
|
||||
|
||||
public class ProjectTemplate {
|
||||
|
||||
private int id;
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
@DrawableRes
|
||||
private int imageId;
|
||||
|
||||
public ProjectTemplate() {
|
||||
}
|
||||
|
||||
public ProjectTemplate(int id, String name, String description, int imageId) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.imageId = imageId;
|
||||
}
|
||||
|
||||
public ProjectTemplate setId(int id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
private int id;
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ProjectTemplate setName(Context ctx, @StringRes int name) {
|
||||
return setName(ctx.getString(name));
|
||||
}
|
||||
@DrawableRes private int imageId;
|
||||
|
||||
public ProjectTemplate setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public ProjectTemplate() {}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ProjectTemplate setDescription(Context ctx, @StringRes int desc) {
|
||||
return setDescription(ctx.getString(desc));
|
||||
}
|
||||
public ProjectTemplate(int id, String name, String description, int imageId) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.imageId = imageId;
|
||||
}
|
||||
|
||||
public ProjectTemplate setDescription(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
public ProjectTemplate setId(int id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ProjectTemplate setImageId(@DrawableRes int imageId) {
|
||||
this.imageId = imageId;
|
||||
return this;
|
||||
}
|
||||
public ProjectTemplate setName(Context ctx, @StringRes int name) {
|
||||
return setName(ctx.getString(name));
|
||||
}
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
public ProjectTemplate setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ProjectTemplate setDescription(Context ctx, @StringRes int desc) {
|
||||
return setDescription(ctx.getString(desc));
|
||||
}
|
||||
|
||||
public ProjectTemplate setDescription(String description) {
|
||||
this.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public ProjectTemplate setImageId(@DrawableRes int imageId) {
|
||||
this.imageId = imageId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.models;
|
||||
|
||||
import com.itsaky.lsp.models.Range;
|
||||
@@ -25,9 +25,9 @@ public class SearchResult extends Range {
|
||||
public File file;
|
||||
public String line;
|
||||
public String match;
|
||||
|
||||
|
||||
public SearchResult(Range src, File file, String line, String match) {
|
||||
super(src.getStart (), src.getEnd ());
|
||||
super(src.getStart(), src.getEnd());
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
this.match = match;
|
||||
|
||||
@@ -26,8 +26,7 @@ import androidx.annotation.DrawableRes;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Holds data about the widget item in widgets list shown in
|
||||
* UI Designer screen.
|
||||
* Holds data about the widget item in widgets list shown in UI Designer screen.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@@ -38,7 +37,7 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
||||
|
||||
private final String clazz;
|
||||
|
||||
private UIWidget (Parcel in) {
|
||||
private UIWidget(Parcel in) {
|
||||
this.name = in.readString();
|
||||
this.icon = in.readInt();
|
||||
this.clazz = in.readString();
|
||||
@@ -50,17 +49,18 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
||||
this.clazz = clazz.getName();
|
||||
}
|
||||
|
||||
public static final Creator<UIWidget> CREATOR = new Creator<UIWidget>() {
|
||||
@Override
|
||||
public UIWidget createFromParcel(Parcel in) {
|
||||
return new UIWidget(in);
|
||||
}
|
||||
public static final Creator<UIWidget> CREATOR =
|
||||
new Creator<UIWidget>() {
|
||||
@Override
|
||||
public UIWidget createFromParcel(Parcel in) {
|
||||
return new UIWidget(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIWidget[] newArray(int size) {
|
||||
return new UIWidget[size];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public UIWidget[] newArray(int size) {
|
||||
return new UIWidget[size];
|
||||
}
|
||||
};
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -75,7 +75,9 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UIWidget widget = (UIWidget) o;
|
||||
return getIcon() == widget.getIcon() && getName().equals(widget.getName()) && clazz.equals(widget.clazz);
|
||||
return getIcon() == widget.getIcon()
|
||||
&& getName().equals(widget.getName())
|
||||
&& clazz.equals(widget.clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,15 +87,16 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
||||
|
||||
/**
|
||||
* Get the class of this widget.
|
||||
*
|
||||
* @return The class that this widget represents
|
||||
* @throws ClassNotFoundException If there is no class with this widget's class name.
|
||||
* However, this will never be thrown.
|
||||
* @throws ClassNotFoundException If there is no class with this widget's class name. However,
|
||||
* this will never be thrown.
|
||||
*/
|
||||
public Class<? extends View> asClass () throws ClassNotFoundException {
|
||||
public Class<? extends View> asClass() throws ClassNotFoundException {
|
||||
return getClass().getClassLoader().loadClass(this.clazz).asSubclass(View.class);
|
||||
}
|
||||
|
||||
public String getWidgetClassName () {
|
||||
public String getWidgetClassName() {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ package com.itsaky.androidide.models;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -30,28 +29,22 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Model class for holding data about a group that will be displayed
|
||||
* in Widgets list in UI Designer screen.
|
||||
* Model class for holding data about a group that will be displayed in Widgets list in UI Designer
|
||||
* screen.
|
||||
*
|
||||
* A UIWidgetGroup contains multiple {@link UIWidget}.
|
||||
* <p>A UIWidgetGroup contains multiple {@link UIWidget}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
|
||||
/**
|
||||
* Name of this group.
|
||||
*/
|
||||
/** Name of this group. */
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Children of this group.
|
||||
*/
|
||||
/** Children of this group. */
|
||||
private final List<UIWidget> children;
|
||||
|
||||
/**
|
||||
* Is this group currently selected in the group list?
|
||||
*/
|
||||
/** Is this group currently selected in the group list? */
|
||||
private boolean selected;
|
||||
|
||||
public UIWidgetGroup(String name) {
|
||||
@@ -66,25 +59,27 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
this.selected = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Creator<UIWidgetGroup> CREATOR = new Creator<UIWidgetGroup>() {
|
||||
public static final Creator<UIWidgetGroup> CREATOR =
|
||||
new Creator<UIWidgetGroup>() {
|
||||
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public UIWidgetGroup createFromParcel(Parcel in) {
|
||||
return new UIWidgetGroup(in);
|
||||
}
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public UIWidgetGroup createFromParcel(Parcel in) {
|
||||
return new UIWidgetGroup(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public UIWidgetGroup[] newArray(int size) {
|
||||
return new UIWidgetGroup[size];
|
||||
}
|
||||
};
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public UIWidgetGroup[] newArray(int size) {
|
||||
return new UIWidgetGroup[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of this group.
|
||||
*
|
||||
* @return The name of this group.
|
||||
*/
|
||||
public String getName() {
|
||||
@@ -93,6 +88,7 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
|
||||
/**
|
||||
* Get the children of this group.
|
||||
*
|
||||
* @return The children.
|
||||
*/
|
||||
@NonNull
|
||||
@@ -110,12 +106,13 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
|
||||
/**
|
||||
* Add this widget to this group. Does nothing if the child is already present.
|
||||
*
|
||||
* @param child The child to add.
|
||||
* @return This instance for chained method calls.
|
||||
*/
|
||||
public UIWidgetGroup addChild (UIWidget child) {
|
||||
public UIWidgetGroup addChild(UIWidget child) {
|
||||
if (children.contains(child)) {
|
||||
return this;
|
||||
return this;
|
||||
}
|
||||
this.children.add(child);
|
||||
return this;
|
||||
@@ -123,11 +120,12 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
|
||||
/**
|
||||
* Get the child at the given index.
|
||||
*
|
||||
* @param index The index of the child.
|
||||
* @return The child at the index.
|
||||
* @throws IndexOutOfBoundsException If the given index is not valid.
|
||||
*/
|
||||
public UIWidget getChild (int index ){
|
||||
public UIWidget getChild(int index) {
|
||||
return children.get(index);
|
||||
}
|
||||
|
||||
@@ -136,7 +134,9 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UIWidgetGroup group = (UIWidgetGroup) o;
|
||||
return isSelected() == group.isSelected() && getName().equals(group.getName()) && getChildren().equals(group.getChildren());
|
||||
return isSelected() == group.isSelected()
|
||||
&& getName().equals(group.getName())
|
||||
&& getChildren().equals(group.getChildren());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -38,135 +38,141 @@ import java.util.Objects;
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class XMLAttribute extends UiAttribute implements Parcelable {
|
||||
|
||||
public static final Creator<XMLAttribute> CREATOR = new Creator<> () {
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public XMLAttribute createFromParcel (Parcel in) {
|
||||
return new XMLAttribute (in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public XMLAttribute[] newArray (int size) {
|
||||
return new XMLAttribute[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final Creator<XMLAttribute> CREATOR =
|
||||
new Creator<>() {
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public XMLAttribute createFromParcel(Parcel in) {
|
||||
return new XMLAttribute(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public XMLAttribute[] newArray(int size) {
|
||||
return new XMLAttribute[size];
|
||||
}
|
||||
};
|
||||
public Attr attr;
|
||||
/**
|
||||
* Is this attribute applied to any view?
|
||||
*/
|
||||
/** Is this attribute applied to any view? */
|
||||
private boolean isApplied;
|
||||
/**
|
||||
* Format of this attribute. May be stored here so we don't
|
||||
* have to look in {@link com.itsaky.attrinfo.AttrInfo AttrInfo}
|
||||
* again and again.
|
||||
* Format of this attribute. May be stored here so we don't have to look in {@link
|
||||
* com.itsaky.attrinfo.AttrInfo AttrInfo} again and again.
|
||||
*/
|
||||
private int format = -1;
|
||||
|
||||
public XMLAttribute (IAttribute attribute) {
|
||||
this (attribute, true);
|
||||
|
||||
public XMLAttribute(IAttribute attribute) {
|
||||
this(attribute, true);
|
||||
}
|
||||
|
||||
public XMLAttribute (@NonNull IAttribute attribute, boolean isApplied) {
|
||||
this (attribute.getNamespace (), attribute.getAttributeName (), attribute.getValue (), isApplied);
|
||||
|
||||
public XMLAttribute(@NonNull IAttribute attribute, boolean isApplied) {
|
||||
this(
|
||||
attribute.getNamespace(),
|
||||
attribute.getAttributeName(),
|
||||
attribute.getValue(),
|
||||
isApplied);
|
||||
}
|
||||
|
||||
public XMLAttribute (INamespace namespace, String name, String value, boolean isApplied) {
|
||||
super (namespace, name, value);
|
||||
|
||||
public XMLAttribute(INamespace namespace, String name, String value, boolean isApplied) {
|
||||
super(namespace, name, value);
|
||||
this.isApplied = isApplied;
|
||||
}
|
||||
|
||||
private XMLAttribute (@NonNull Parcel in) {
|
||||
this (in.readParcelable (INamespace.class.getClassLoader ()), in.readString (), in.readString (), in.readByte () != 0);
|
||||
|
||||
private XMLAttribute(@NonNull Parcel in) {
|
||||
this(
|
||||
in.readParcelable(INamespace.class.getClassLoader()),
|
||||
in.readString(),
|
||||
in.readString(),
|
||||
in.readByte() != 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* {@link #isApplied()} will return true by default.
|
||||
* Create a new attribute instance with the android namespace. {@link #isApplied()} will return
|
||||
* true by default.
|
||||
*
|
||||
* @param name The name of the attribute.
|
||||
* @param name The name of the attribute.
|
||||
* @param value The value of the attribute.
|
||||
* @return A new instance of this class with the provided data.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_, _ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name, String value) {
|
||||
return new XMLAttribute (INamespace.ANDROID, name, value, true);
|
||||
public static XMLAttribute newAndroidAttribute(String name, String value) {
|
||||
return new XMLAttribute(INamespace.ANDROID, name, value, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* The {@link #isApplied()} will return {@code false} by default.
|
||||
* Create a new attribute instance with the android namespace. The {@link #isApplied()} will
|
||||
* return {@code false} by default.
|
||||
*
|
||||
* @param name The name of the attribute.
|
||||
* @return The new attribute instance.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name) {
|
||||
return new XMLAttribute (INamespace.ANDROID, name, "", false);
|
||||
public static XMLAttribute newAndroidAttribute(String name) {
|
||||
return new XMLAttribute(INamespace.ANDROID, name, "", false);
|
||||
}
|
||||
|
||||
public int getFormat () {
|
||||
|
||||
public int getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat (int format) {
|
||||
|
||||
public void setFormat(int format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public int findFormat () {
|
||||
|
||||
if (getFormat () > 0) {
|
||||
return getFormat ();
|
||||
|
||||
public int findFormat() {
|
||||
|
||||
if (getFormat() > 0) {
|
||||
return getFormat();
|
||||
}
|
||||
|
||||
final var attrs = StudioApp.getInstance ().attrInfo ();
|
||||
this.attr = attrs.getAttribute (getAttributeName ());
|
||||
|
||||
final var attrs = StudioApp.getInstance().attrInfo();
|
||||
this.attr = attrs.getAttribute(getAttributeName());
|
||||
if (this.attr != null) {
|
||||
this.format = this.attr.format;
|
||||
}
|
||||
|
||||
return getFormat ();
|
||||
|
||||
return getFormat();
|
||||
}
|
||||
|
||||
public boolean hasFormat (int format) {
|
||||
|
||||
public boolean hasFormat(int format) {
|
||||
return (this.format & format) != 0;
|
||||
}
|
||||
|
||||
public Attr getAttr () {
|
||||
|
||||
public Attr getAttr() {
|
||||
return attr;
|
||||
}
|
||||
|
||||
public void setAttr (Attr attr) {
|
||||
Objects.requireNonNull (attr);
|
||||
|
||||
public void setAttr(Attr attr) {
|
||||
Objects.requireNonNull(attr);
|
||||
this.attr = attr;
|
||||
setFormat (attr.format);
|
||||
setFormat(attr.format);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void apply (String value) {
|
||||
super.apply (value);
|
||||
public void apply(String value) {
|
||||
super.apply(value);
|
||||
isApplied = true;
|
||||
}
|
||||
|
||||
public boolean isApplied () {
|
||||
|
||||
public boolean isApplied() {
|
||||
return isApplied;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int describeContents () {
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeToParcel (@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelable (getNamespace (), flags);
|
||||
dest.writeString (getAttributeName ());
|
||||
dest.writeString (getValue ());
|
||||
dest.writeByte ((byte) (isApplied ? 1 : 0));
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelable(getNamespace(), flags);
|
||||
dest.writeString(getAttributeName());
|
||||
dest.writeString(getValue());
|
||||
dest.writeByte((byte) (isApplied ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,88 @@
|
||||
/************************************************************************************
|
||||
* 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.project;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidProject implements Parcelable {
|
||||
|
||||
private String appName;
|
||||
private String packageName;
|
||||
private String iconPath;
|
||||
private String projectPath;
|
||||
private String mainModule;
|
||||
private String mainSourcePath;
|
||||
|
||||
|
||||
private String appName;
|
||||
private String packageName;
|
||||
private String iconPath;
|
||||
private String projectPath;
|
||||
private String mainModule;
|
||||
private String mainSourcePath;
|
||||
|
||||
private List<String> modulePaths = new ArrayList<>();
|
||||
private List<String> sourcePaths = new ArrayList<>();
|
||||
private List<String> classPaths = new ArrayList<>();
|
||||
|
||||
public AndroidProject() {
|
||||
addSource(StudioApp.getInstance().getLogSenderDir().getAbsolutePath());
|
||||
}
|
||||
public AndroidProject() {
|
||||
addSource(StudioApp.getInstance().getLogSenderDir().getAbsolutePath());
|
||||
}
|
||||
|
||||
protected AndroidProject(Parcel in) {
|
||||
appName = in.readString();
|
||||
packageName = in.readString();
|
||||
iconPath = in.readString();
|
||||
projectPath = in.readString();
|
||||
mainModule = in.readString();
|
||||
mainSourcePath = in.readString();
|
||||
protected AndroidProject(Parcel in) {
|
||||
appName = in.readString();
|
||||
packageName = in.readString();
|
||||
iconPath = in.readString();
|
||||
projectPath = in.readString();
|
||||
mainModule = in.readString();
|
||||
mainSourcePath = in.readString();
|
||||
in.readList(modulePaths, String.class.getClassLoader());
|
||||
in.readList(sourcePaths, String.class.getClassLoader());
|
||||
in.readList(classPaths, String.class.getClassLoader());
|
||||
}
|
||||
|
||||
public File getBuildDir() {
|
||||
return new File(getMainModulePath(), "build");
|
||||
}
|
||||
}
|
||||
|
||||
public AndroidProject setMainModule(String mainModule) {
|
||||
this.mainModule = mainModule;
|
||||
return this;
|
||||
}
|
||||
public File getBuildDir() {
|
||||
return new File(getMainModulePath(), "build");
|
||||
}
|
||||
|
||||
public String getMainModule() {
|
||||
return mainModule;
|
||||
}
|
||||
|
||||
public String getMainModulePath() {
|
||||
return new File(getProjectPath(), getMainModule()).getAbsolutePath();
|
||||
}
|
||||
public AndroidProject setMainModule(String mainModule) {
|
||||
this.mainModule = mainModule;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidProject setMainSourcePath(String mainModulePath) {
|
||||
this.mainSourcePath = mainModulePath;
|
||||
return this;
|
||||
}
|
||||
public String getMainModule() {
|
||||
return mainModule;
|
||||
}
|
||||
|
||||
public String getMainSourcePath() {
|
||||
return mainSourcePath;
|
||||
}
|
||||
public String getMainModulePath() {
|
||||
return new File(getProjectPath(), getMainModule()).getAbsolutePath();
|
||||
}
|
||||
|
||||
public AndroidProject setMainSourcePath(String mainModulePath) {
|
||||
this.mainSourcePath = mainModulePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMainSourcePath() {
|
||||
return mainSourcePath;
|
||||
}
|
||||
|
||||
public AndroidProject setModulePaths(List<String> modulePaths) {
|
||||
this.modulePaths = modulePaths;
|
||||
@@ -94,8 +95,7 @@ public class AndroidProject implements Parcelable {
|
||||
}
|
||||
|
||||
public AndroidProject addModule(String path) {
|
||||
if (!getModulePaths().contains(path))
|
||||
getModulePaths().add(path);
|
||||
if (!getModulePaths().contains(path)) getModulePaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -110,8 +110,7 @@ public class AndroidProject implements Parcelable {
|
||||
}
|
||||
|
||||
public AndroidProject addSource(String path) {
|
||||
if (!getSourcePaths().contains(path))
|
||||
getSourcePaths().add(path);
|
||||
if (!getSourcePaths().contains(path)) getSourcePaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -126,91 +125,101 @@ public class AndroidProject implements Parcelable {
|
||||
}
|
||||
|
||||
public AndroidProject addClasspath(String path) {
|
||||
if (!getClassPaths().contains(path))
|
||||
getClassPaths().add(path);
|
||||
if (!getClassPaths().contains(path)) getClassPaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidProject setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
public AndroidProject setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public AndroidProject setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
public AndroidProject setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public AndroidProject setIconPath(String iconPath) {
|
||||
this.iconPath = iconPath;
|
||||
public AndroidProject setIconPath(String iconPath) {
|
||||
this.iconPath = iconPath;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public String getIconPath() {
|
||||
return iconPath;
|
||||
}
|
||||
public String getIconPath() {
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
public AndroidProject setProjectPath(String projectPath) {
|
||||
this.projectPath = projectPath;
|
||||
public AndroidProject setProjectPath(String projectPath) {
|
||||
this.projectPath = projectPath;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public String getProjectPath() {
|
||||
return projectPath;
|
||||
}
|
||||
public String getProjectPath() {
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel p1, int p2) {
|
||||
p1.writeString(appName);
|
||||
p1.writeString(packageName);
|
||||
p1.writeString(iconPath);
|
||||
p1.writeString(projectPath);
|
||||
p1.writeString(mainModule);
|
||||
p1.writeString(mainSourcePath);
|
||||
@Override
|
||||
public void writeToParcel(Parcel p1, int p2) {
|
||||
p1.writeString(appName);
|
||||
p1.writeString(packageName);
|
||||
p1.writeString(iconPath);
|
||||
p1.writeString(projectPath);
|
||||
p1.writeString(mainModule);
|
||||
p1.writeString(mainSourcePath);
|
||||
p1.writeList(modulePaths);
|
||||
p1.writeList(sourcePaths);
|
||||
p1.writeList(classPaths);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["
|
||||
+ "\nappName=" + getAppName()
|
||||
+ "\npackageName=" + getPackageName()
|
||||
+ "\niconPath=" + getIconPath()
|
||||
+ "\nprojectPath=" + getProjectPath()
|
||||
+ "\nmainModule=" + getMainModule()
|
||||
+ "\nmainModulePath=" + getMainModulePath()
|
||||
+ "\nmainModuleSourcePath=" + getMainSourcePath()
|
||||
+ "\nmodules=" + getModulePaths()
|
||||
+ "\nsources=" + getSourcePaths()
|
||||
+ "\nclasspaths=" + getClassPaths()
|
||||
+ "\n]";
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["
|
||||
+ "\nappName="
|
||||
+ getAppName()
|
||||
+ "\npackageName="
|
||||
+ getPackageName()
|
||||
+ "\niconPath="
|
||||
+ getIconPath()
|
||||
+ "\nprojectPath="
|
||||
+ getProjectPath()
|
||||
+ "\nmainModule="
|
||||
+ getMainModule()
|
||||
+ "\nmainModulePath="
|
||||
+ getMainModulePath()
|
||||
+ "\nmainModuleSourcePath="
|
||||
+ getMainSourcePath()
|
||||
+ "\nmodules="
|
||||
+ getModulePaths()
|
||||
+ "\nsources="
|
||||
+ getSourcePaths()
|
||||
+ "\nclasspaths="
|
||||
+ getClassPaths()
|
||||
+ "\n]";
|
||||
}
|
||||
|
||||
public static final Creator<AndroidProject> CREATOR = new Creator<AndroidProject>(){
|
||||
public static final Creator<AndroidProject> CREATOR =
|
||||
new Creator<AndroidProject>() {
|
||||
|
||||
@Override
|
||||
public AndroidProject createFromParcel(Parcel p1) {
|
||||
return new AndroidProject(p1);
|
||||
}
|
||||
@Override
|
||||
public AndroidProject createFromParcel(Parcel p1) {
|
||||
return new AndroidProject(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidProject[] newArray(int p1) {
|
||||
return new AndroidProject[p1];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public AndroidProject[] newArray(int p1) {
|
||||
return new AndroidProject[p1];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/************************************************************************************
|
||||
* 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.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
@@ -22,65 +22,47 @@ import com.google.gson.annotations.SerializedName;
|
||||
/**
|
||||
* Represents a module (subproject) of a root project.
|
||||
*
|
||||
* In Android projects, a module can either be an application project,
|
||||
* or a library project.
|
||||
* <p>In Android projects, a module can either be an application project, or a library project.
|
||||
*/
|
||||
public class IDEModule extends IDEProject {
|
||||
|
||||
/**
|
||||
* Build tools version of this project
|
||||
*/
|
||||
|
||||
/** Build tools version of this project */
|
||||
@SerializedName("buildToolsVersion")
|
||||
public String buildToolsVersion;
|
||||
|
||||
|
||||
/**
|
||||
* Compile SDK version of this project
|
||||
*
|
||||
* Representation: 'android-31', 'android-30', 'android-29', etc
|
||||
* <p>Representation: 'android-31', 'android-30', 'android-29', etc
|
||||
*/
|
||||
@SerializedName("compileSdkVersion")
|
||||
public String compileSdkVersion;
|
||||
|
||||
/**
|
||||
* Minimum SDK version of this project
|
||||
*/
|
||||
|
||||
/** Minimum SDK version of this project */
|
||||
@SerializedName("minSdk")
|
||||
public SDK minSdk;
|
||||
|
||||
/**
|
||||
* Target SDK version of this project
|
||||
*/
|
||||
|
||||
/** Target SDK version of this project */
|
||||
@SerializedName("targetSdk")
|
||||
public SDK targetSdk;
|
||||
|
||||
/**
|
||||
* Version code of this project
|
||||
*/
|
||||
|
||||
/** Version code of this project */
|
||||
@SerializedName("versionCode")
|
||||
public int versionCode = 0;
|
||||
|
||||
/**
|
||||
* Version name of this project
|
||||
*/
|
||||
|
||||
/** Version name of this project */
|
||||
@SerializedName("versionName")
|
||||
public String versionName = "Not defined";
|
||||
|
||||
/**
|
||||
* Is this project an Android library project?
|
||||
*/
|
||||
|
||||
/** Is this project an Android library project? */
|
||||
@SerializedName("isLibrary")
|
||||
public boolean isLibrary = true;
|
||||
|
||||
/**
|
||||
* Does this module has viewBinding enabled?
|
||||
*/
|
||||
|
||||
/** Does this module has viewBinding enabled? */
|
||||
@SerializedName("viewBindingEnabled")
|
||||
public boolean viewBindingEnabled;
|
||||
|
||||
/**
|
||||
* Library projects don't have applicationId, application projects do.
|
||||
*/
|
||||
|
||||
/** Library projects don't have applicationId, application projects do. */
|
||||
@SerializedName("applicationId")
|
||||
public String applicationId;
|
||||
}
|
||||
|
||||
@@ -5,84 +5,66 @@
|
||||
* 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.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A model that contains data of a project currently opened in AndroidIDE
|
||||
*/
|
||||
/** A model that contains data of a project currently opened in AndroidIDE */
|
||||
public class IDEProject {
|
||||
|
||||
/**
|
||||
* Name of this project
|
||||
*/
|
||||
|
||||
/** Name of this project */
|
||||
@SerializedName("name")
|
||||
public String name = "";
|
||||
|
||||
/**
|
||||
* Name of this project which should be displayed to user
|
||||
*/
|
||||
|
||||
/** Name of this project which should be displayed to user */
|
||||
@SerializedName("displayName")
|
||||
public String displayName = "";
|
||||
|
||||
/**
|
||||
* Description of this project. Maybe null.
|
||||
*/
|
||||
|
||||
/** Description of this project. Maybe null. */
|
||||
@SerializedName("description")
|
||||
public String description = "";
|
||||
|
||||
|
||||
/**
|
||||
* Path of this project. This is NOT the directory path of this project.
|
||||
*
|
||||
* For example, ':app or ':module:subModule'
|
||||
*
|
||||
* <p>For example, ':app or ':module:subModule'
|
||||
*/
|
||||
@SerializedName("path")
|
||||
public String path = "";
|
||||
|
||||
/**
|
||||
* Path of the directory containing this project
|
||||
*/
|
||||
|
||||
/** Path of the directory containing this project */
|
||||
@SerializedName("projectDir")
|
||||
public String projectDir;
|
||||
|
||||
/**
|
||||
* File path of the application's icon
|
||||
* We get this manually by parsing AndroidManifest.xml
|
||||
*/
|
||||
|
||||
/** File path of the application's icon We get this manually by parsing AndroidManifest.xml */
|
||||
public String iconPath;
|
||||
|
||||
/**
|
||||
* Subprojects of this project
|
||||
*/
|
||||
|
||||
/** Subprojects of this project */
|
||||
@SerializedName("modules")
|
||||
public List<IDEModule> modules = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Tasks included in this project
|
||||
*/
|
||||
|
||||
/** Tasks included in this project */
|
||||
@SerializedName("tasks")
|
||||
public List<IDETask> tasks = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Path of jar file of dependencies of this project
|
||||
*/
|
||||
|
||||
/** Path of jar file of dependencies of this project */
|
||||
@SerializedName("dependencies")
|
||||
public List<String> dependencies = new ArrayList<>();
|
||||
|
||||
|
||||
/**
|
||||
* Get the module by its path
|
||||
*
|
||||
@@ -90,29 +72,21 @@ public class IDEProject {
|
||||
* @return An Optional containing the IDEModule or Optional.empty()
|
||||
*/
|
||||
public Optional<IDEModule> getModuleByPath(String path) {
|
||||
if(modules != null && modules.size() > 0) {
|
||||
for(IDEModule module : modules) {
|
||||
if(module == null) continue;
|
||||
if(module.path.trim().equals(path.trim()))
|
||||
return Optional.of(module);
|
||||
if (modules != null && modules.size() > 0) {
|
||||
for (IDEModule module : modules) {
|
||||
if (module == null) continue;
|
||||
if (module.path.trim().equals(path.trim())) return Optional.of(module);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores information about a SDK. Like,
|
||||
* • Codename
|
||||
* • API string
|
||||
* • API level
|
||||
*/
|
||||
|
||||
/** Stores information about a SDK. Like, • Codename • API string • API level */
|
||||
public static class SDK {
|
||||
|
||||
@SerializedName("codename")
|
||||
public String codename;
|
||||
|
||||
|
||||
@SerializedName("apiString")
|
||||
public String apiString;
|
||||
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
/************************************************************************************
|
||||
* 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.project;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Represents a Gradle task
|
||||
*/
|
||||
/** Represents a Gradle task */
|
||||
public class IDETask {
|
||||
|
||||
/**
|
||||
* Name of this task
|
||||
*/
|
||||
|
||||
/** Name of this task */
|
||||
@SerializedName("name")
|
||||
public String name;
|
||||
|
||||
/**
|
||||
* Description of this task
|
||||
*/
|
||||
|
||||
/** Description of this task */
|
||||
@SerializedName("description")
|
||||
public String description;
|
||||
|
||||
/**
|
||||
* Name of the group that this task belongs to
|
||||
*/
|
||||
|
||||
/** Name of the group that this task belongs to */
|
||||
@SerializedName("group")
|
||||
public String group;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Path of this task
|
||||
*
|
||||
* For example, ':app:assembleDebug', ':app:build'
|
||||
*
|
||||
* <p>For example, ':app:assembleDebug', ':app:build'
|
||||
*/
|
||||
@SerializedName("path")
|
||||
public String path;
|
||||
|
||||
}
|
||||
|
||||
@@ -17,30 +17,22 @@
|
||||
|
||||
package com.itsaky.androidide.project;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.FileObserver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A file observer that observer files in project directories.
|
||||
* Sub classes need to provide the file extensions that they want to get
|
||||
* notified about.
|
||||
* A file observer that observer files in project directories. Sub classes need to provide the file
|
||||
* extensions that they want to get notified about.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class ProjectFileObserver extends FileObserver {
|
||||
// Cannot use constructors that accept java.io.File
|
||||
// They were added in API 29
|
||||
public ProjectFileObserver (String path) {
|
||||
this (path, ALL_EVENTS);
|
||||
public ProjectFileObserver(String path) {
|
||||
this(path, ALL_EVENTS);
|
||||
}
|
||||
|
||||
public ProjectFileObserver (String path, int mask) {
|
||||
super (path, mask);
|
||||
|
||||
public ProjectFileObserver(String path, int mask) {
|
||||
super(path, mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,226 +27,227 @@ import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public class ProjectResourceTable implements IResourceTable {
|
||||
|
||||
|
||||
private File resDir;
|
||||
private File color;
|
||||
private File layout;
|
||||
|
||||
|
||||
private File[] drawables;
|
||||
private File[] mipmaps;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("ProjectResourceFinder");
|
||||
|
||||
|
||||
private static final Logger LOG = Logger.instance("ProjectResourceFinder");
|
||||
|
||||
@Override
|
||||
public File findDrawable (@NonNull String name) {
|
||||
public File findDrawable(@NonNull String name) {
|
||||
for (File drawable : drawables) {
|
||||
final File file = findFileWithName (drawable.listFiles (), name);
|
||||
final File file = findFileWithName(drawable.listFiles(), name);
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public File findLayout (@NonNull String name) {
|
||||
return findFileWithName (this.layout.listFiles (), name);
|
||||
public File findLayout(@NonNull String name) {
|
||||
return findFileWithName(this.layout.listFiles(), name);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String findString (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
|
||||
public String findString(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
|
||||
if (table == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var resource = table.findString (name);
|
||||
|
||||
final var resource = table.findString(name);
|
||||
if (resource == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var value = resource.getValue ();
|
||||
if (value.startsWith ("@string/")) {
|
||||
var ref = value.substring ("@string/".length ());
|
||||
if (name.equals (ref)) {
|
||||
|
||||
final var value = resource.getValue();
|
||||
if (value.startsWith("@string/")) {
|
||||
var ref = value.substring("@string/".length());
|
||||
if (name.equals(ref)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String findBoolean (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
|
||||
public String findBoolean(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
|
||||
if (table == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var resource = table.findBoolean (name);
|
||||
|
||||
final var resource = table.findBoolean(name);
|
||||
if (resource == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var val = resource.getValue ();
|
||||
if (val.startsWith ("@bool/")) {
|
||||
final var ref = val.substring ("@bool/".length ());
|
||||
if (ref.equals (name)) {
|
||||
|
||||
final var val = resource.getValue();
|
||||
if (val.startsWith("@bool/")) {
|
||||
final var ref = val.substring("@bool/".length());
|
||||
if (ref.equals(name)) {
|
||||
// recursive reference
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String findInteger (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
|
||||
public String findInteger(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
|
||||
if (table == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var resource = table.findInteger (name);
|
||||
|
||||
final var resource = table.findInteger(name);
|
||||
if (resource == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var val = resource.getValue ();
|
||||
if (val.startsWith ("@integer/")) {
|
||||
final var ref = val.substring ("@integer/".length ());
|
||||
if (ref.equals (name)) {
|
||||
|
||||
final var val = resource.getValue();
|
||||
if (val.startsWith("@integer/")) {
|
||||
final var ref = val.substring("@integer/".length());
|
||||
if (ref.equals(name)) {
|
||||
// recursive reference
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String findColor (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
|
||||
public String findColor(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
|
||||
if (table == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var resource = table.findColor (name);
|
||||
|
||||
final var resource = table.findColor(name);
|
||||
if (resource == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var value = resource.getValue ();
|
||||
if (value.startsWith ("@color/")) {
|
||||
var ref = value.substring ("@color/".length ());
|
||||
if (name.equals (ref)) {
|
||||
|
||||
final var value = resource.getValue();
|
||||
if (value.startsWith("@color/")) {
|
||||
var ref = value.substring("@color/".length());
|
||||
if (name.equals(ref)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] findArray (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
public String[] findArray(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
if (table == null) {
|
||||
return new String[]{name};
|
||||
return new String[] {name};
|
||||
}
|
||||
|
||||
final var resource = table.findArray (name);
|
||||
|
||||
final var resource = table.findArray(name);
|
||||
if (resource == null) {
|
||||
return new String[]{name};
|
||||
return new String[] {name};
|
||||
}
|
||||
|
||||
return resource.getValues ();
|
||||
|
||||
return resource.getValues();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String findDimension (@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable (resDir);
|
||||
|
||||
public String findDimension(@NonNull String name) {
|
||||
final var table = ValuesTableFactory.getTable(resDir);
|
||||
|
||||
if (table == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var resource = table.findDimension (name);
|
||||
|
||||
final var resource = table.findDimension(name);
|
||||
if (resource == null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final var value = resource.getValue ();
|
||||
if (value.startsWith ("@dimen/")) {
|
||||
var ref = value.substring ("@dimen/".length ());
|
||||
if (name.equals (ref)) {
|
||||
|
||||
final var value = resource.getValue();
|
||||
if (value.startsWith("@dimen/")) {
|
||||
var ref = value.substring("@dimen/".length());
|
||||
if (name.equals(ref)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setInflatingFile (@NonNull File file) {
|
||||
if (file.getParentFile () == null) {
|
||||
throw new IllegalArgumentException ("Invalid inflating file");
|
||||
public void setInflatingFile(@NonNull File file) {
|
||||
if (file.getParentFile() == null) {
|
||||
throw new IllegalArgumentException("Invalid inflating file");
|
||||
}
|
||||
|
||||
setupDirectories (file // layout file
|
||||
.getParentFile () // layout dir
|
||||
.getParentFile ()); // res dir
|
||||
|
||||
setupDirectories(
|
||||
file // layout file
|
||||
.getParentFile() // layout dir
|
||||
.getParentFile()); // res dir
|
||||
}
|
||||
|
||||
private void setupDirectories (File resDir) {
|
||||
|
||||
|
||||
private void setupDirectories(File resDir) {
|
||||
|
||||
if (resDir == null) {
|
||||
LOG.error ("Null resource directory passed to resource finder. Ignoring.");
|
||||
LOG.error("Null resource directory passed to resource finder. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.resDir = resDir;
|
||||
this.color = new File (resDir, "color");
|
||||
this.layout = new File (resDir, "layout");
|
||||
|
||||
final File[] drawables = resDir.listFiles (new NameStartsWith ("drawable"));
|
||||
this.color = new File(resDir, "color");
|
||||
this.layout = new File(resDir, "layout");
|
||||
|
||||
final File[] drawables = resDir.listFiles(new NameStartsWith("drawable"));
|
||||
this.drawables = drawables == null ? new File[0] : drawables;
|
||||
|
||||
final File[] mipmaps = resDir.listFiles (new NameStartsWith ("mipmap"));
|
||||
|
||||
final File[] mipmaps = resDir.listFiles(new NameStartsWith("mipmap"));
|
||||
this.mipmaps = mipmaps == null ? new File[0] : mipmaps;
|
||||
}
|
||||
|
||||
private File findFileWithName (File[] files, String name) {
|
||||
|
||||
private File findFileWithName(File[] files, String name) {
|
||||
if (files == null || name == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
for (File file : files) {
|
||||
var simpleName = file.getName ();
|
||||
|
||||
if (simpleName.startsWith (name)) {
|
||||
var simpleName = file.getName();
|
||||
|
||||
if (simpleName.startsWith(name)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static class NameStartsWith implements FileFilter {
|
||||
|
||||
|
||||
private final String name;
|
||||
|
||||
public NameStartsWith (String name) {
|
||||
|
||||
public NameStartsWith(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept (@NonNull File file) {
|
||||
return file.getName ().startsWith (name);
|
||||
public boolean accept(@NonNull File file) {
|
||||
return file.getName().startsWith(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,67 @@
|
||||
/************************************************************************************
|
||||
* 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 android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LogReceiver extends BroadcastReceiver {
|
||||
|
||||
private LogListener listener;
|
||||
|
||||
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
|
||||
public static final String EXTRA_LINE = "log_line";
|
||||
|
||||
private final Logger LOG = Logger.instance ("LogReceiver");
|
||||
|
||||
public LogReceiver setLogListener(LogListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if(intent.getAction().equals(APPEND_LOG) && intent.hasExtra(EXTRA_LINE)) {
|
||||
String line = intent.getStringExtra(EXTRA_LINE);
|
||||
if(line == null) return;
|
||||
try {
|
||||
sendLogLine(line);
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to parse log line from app.", th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLogLine(String line) {
|
||||
final var log = LogLine.forLogString (line);
|
||||
if(listener != null) {
|
||||
private LogListener listener;
|
||||
|
||||
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
|
||||
public static final String EXTRA_LINE = "log_line";
|
||||
|
||||
private final Logger LOG = Logger.instance("LogReceiver");
|
||||
|
||||
public LogReceiver setLogListener(LogListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(APPEND_LOG) && intent.hasExtra(EXTRA_LINE)) {
|
||||
String line = intent.getStringExtra(EXTRA_LINE);
|
||||
if (line == null) return;
|
||||
try {
|
||||
sendLogLine(line);
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Unable to parse log line from app.", th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLogLine(String line) {
|
||||
final var log = LogLine.forLogString(line);
|
||||
if (listener != null) {
|
||||
listener.appendLogLine(log);
|
||||
}
|
||||
}
|
||||
|
||||
public interface LogListener {
|
||||
void appendLogLine (LogLine line);
|
||||
}
|
||||
}
|
||||
|
||||
public interface LogListener {
|
||||
void appendLogLine(LogLine line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +1,140 @@
|
||||
/************************************************************************************
|
||||
* 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 android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.BuildConfig;
|
||||
import com.itsaky.androidide.SplashActivity;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
|
||||
import java.util.Map;
|
||||
import android.provider.Settings;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
public class MessagingService extends FirebaseMessagingService {
|
||||
|
||||
public static final String TOPIC_UPDATE= "app_update";
|
||||
public static final String TOPIC_DEV_MSGS = "developer_notifications";
|
||||
|
||||
private final String KEY_TITLE = "title";
|
||||
private final String KEY_MESSAGE = "message";
|
||||
private final String KEY_VERSION = "version";
|
||||
private final String KEY_CLICK_ACTION = "click_action";
|
||||
|
||||
private final String ACTION_OPEN_APP = "open_app";
|
||||
private final String ACTION_OPEN_URL = "open_url:";
|
||||
|
||||
|
||||
public static final String TOPIC_UPDATE = "app_update";
|
||||
public static final String TOPIC_DEV_MSGS = "developer_notifications";
|
||||
|
||||
private final String KEY_TITLE = "title";
|
||||
private final String KEY_MESSAGE = "message";
|
||||
private final String KEY_VERSION = "version";
|
||||
private final String KEY_CLICK_ACTION = "click_action";
|
||||
|
||||
private final String ACTION_OPEN_APP = "open_app";
|
||||
private final String ACTION_OPEN_URL = "open_url:";
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage message) {
|
||||
if(message != null && message.getData() != null)
|
||||
processNotification(message.getFrom(), message.getData());
|
||||
}
|
||||
public void onMessageReceived(RemoteMessage message) {
|
||||
if (message != null && message.getData() != null)
|
||||
processNotification(message.getFrom(), message.getData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewToken(String token) {
|
||||
super.onNewToken(token);
|
||||
}
|
||||
|
||||
private void processNotification(String topic, Map<String, String> data) {
|
||||
if(topic != null && topic.contains("/")) {
|
||||
topic = topic.substring(topic.lastIndexOf("/") + 1);
|
||||
if(topic.equals(TOPIC_UPDATE) && checkHasUpdateData(data)) {
|
||||
final String title = data.get(KEY_TITLE);
|
||||
final String msg = data.get(KEY_MESSAGE);
|
||||
final String version = data.get(KEY_VERSION);
|
||||
final String action = data.get(KEY_CLICK_ACTION);
|
||||
final int l = stringAsInt(version);
|
||||
if(l > BuildConfig.VERSION_CODE) {
|
||||
showNotificationWithAction(action, title, msg);
|
||||
}
|
||||
} else if(topic.equals(TOPIC_DEV_MSGS) && checkHasTitleAndMessage(data)) {
|
||||
final String title = data.get(KEY_TITLE);
|
||||
final String msg = data.get(KEY_MESSAGE);
|
||||
final String action = data.get(KEY_CLICK_ACTION);
|
||||
showNotificationWithAction(action, title, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotificationWithAction(String action, String title, String msg) {
|
||||
Intent i = null;
|
||||
int flag = PendingIntent.FLAG_IMMUTABLE;
|
||||
if (action.equals(ACTION_OPEN_APP)) {
|
||||
i = StudioApp.getInstance().getPackageManager().getLaunchIntentForPackage(StudioApp.getInstance().getPackageName());
|
||||
} else if (action.startsWith(ACTION_OPEN_URL)) {
|
||||
i = createOpenUrlIntent(action.substring(ACTION_OPEN_URL.length()));
|
||||
flag |= Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
}
|
||||
@Override
|
||||
public void onNewToken(String token) {
|
||||
super.onNewToken(token);
|
||||
}
|
||||
|
||||
if (i != null) {
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0, i, flag);
|
||||
showNotification(title, msg, intent, StudioApp.NOTIFICATION_ID_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotification(String title, String msg, PendingIntent i, String channelId) {
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(this, channelId);
|
||||
b.setContentTitle(title)
|
||||
.setContentText(msg)
|
||||
.setAutoCancel(true)
|
||||
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
|
||||
.setContentIntent(i);
|
||||
|
||||
NotificationManagerCompat.from(StudioApp.getInstance()).notify(0, b.build());
|
||||
}
|
||||
|
||||
private Intent createOpenAppIntent() {
|
||||
Intent i = new Intent(this, SplashActivity.class);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return i;
|
||||
}
|
||||
|
||||
private Intent createOpenUrlIntent(String url) {
|
||||
return new Intent(Intent.ACTION_VIEW, android.net.Uri.parse(url));
|
||||
}
|
||||
|
||||
private int stringAsInt(String str) {
|
||||
try {
|
||||
return Integer.valueOf(str);
|
||||
} catch (Throwable th) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkHasUpdateData(Map<String, String> data) {
|
||||
return checkHasTitleAndMessage(data)
|
||||
&& data.containsKey(KEY_VERSION)
|
||||
&& data.containsKey(KEY_CLICK_ACTION);
|
||||
}
|
||||
|
||||
private boolean checkHasTitleAndMessage(Map<String, String> data) {
|
||||
return data.containsKey(KEY_TITLE)
|
||||
&& data.containsKey(KEY_MESSAGE);
|
||||
}
|
||||
private void processNotification(String topic, Map<String, String> data) {
|
||||
if (topic != null && topic.contains("/")) {
|
||||
topic = topic.substring(topic.lastIndexOf("/") + 1);
|
||||
if (topic.equals(TOPIC_UPDATE) && checkHasUpdateData(data)) {
|
||||
final String title = data.get(KEY_TITLE);
|
||||
final String msg = data.get(KEY_MESSAGE);
|
||||
final String version = data.get(KEY_VERSION);
|
||||
final String action = data.get(KEY_CLICK_ACTION);
|
||||
final int l = stringAsInt(version);
|
||||
if (l > BuildConfig.VERSION_CODE) {
|
||||
showNotificationWithAction(action, title, msg);
|
||||
}
|
||||
} else if (topic.equals(TOPIC_DEV_MSGS) && checkHasTitleAndMessage(data)) {
|
||||
final String title = data.get(KEY_TITLE);
|
||||
final String msg = data.get(KEY_MESSAGE);
|
||||
final String action = data.get(KEY_CLICK_ACTION);
|
||||
showNotificationWithAction(action, title, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotificationWithAction(String action, String title, String msg) {
|
||||
Intent i = null;
|
||||
int flag = PendingIntent.FLAG_IMMUTABLE;
|
||||
if (action.equals(ACTION_OPEN_APP)) {
|
||||
i =
|
||||
StudioApp.getInstance()
|
||||
.getPackageManager()
|
||||
.getLaunchIntentForPackage(StudioApp.getInstance().getPackageName());
|
||||
} else if (action.startsWith(ACTION_OPEN_URL)) {
|
||||
i = createOpenUrlIntent(action.substring(ACTION_OPEN_URL.length()));
|
||||
flag |= Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
}
|
||||
|
||||
if (i != null) {
|
||||
PendingIntent intent = PendingIntent.getActivity(this, 0, i, flag);
|
||||
showNotification(title, msg, intent, StudioApp.NOTIFICATION_ID_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
private void showNotification(String title, String msg, PendingIntent i, String channelId) {
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(this, channelId);
|
||||
b.setContentTitle(title)
|
||||
.setContentText(msg)
|
||||
.setAutoCancel(true)
|
||||
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
|
||||
.setContentIntent(i);
|
||||
|
||||
NotificationManagerCompat.from(StudioApp.getInstance()).notify(0, b.build());
|
||||
}
|
||||
|
||||
private Intent createOpenAppIntent() {
|
||||
Intent i = new Intent(this, SplashActivity.class);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return i;
|
||||
}
|
||||
|
||||
private Intent createOpenUrlIntent(String url) {
|
||||
return new Intent(Intent.ACTION_VIEW, android.net.Uri.parse(url));
|
||||
}
|
||||
|
||||
private int stringAsInt(String str) {
|
||||
try {
|
||||
return Integer.valueOf(str);
|
||||
} catch (Throwable th) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkHasUpdateData(Map<String, String> data) {
|
||||
return checkHasTitleAndMessage(data)
|
||||
&& data.containsKey(KEY_VERSION)
|
||||
&& data.containsKey(KEY_CLICK_ACTION);
|
||||
}
|
||||
|
||||
private boolean checkHasTitleAndMessage(Map<String, String> data) {
|
||||
return data.containsKey(KEY_TITLE) && data.containsKey(KEY_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
/************************************************************************************
|
||||
* 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.builder;
|
||||
|
||||
import com.itsaky.androidide.project.IDEModule;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A client should implement this interface to get callbacks about a build
|
||||
*/
|
||||
/** A client should implement this interface to get callbacks about a build */
|
||||
public interface BuildListener {
|
||||
/**
|
||||
* Called to notify the client about the modification of build/generated files
|
||||
*/
|
||||
/** Called to notify the client about the modification of build/generated files */
|
||||
void onBuildModified();
|
||||
|
||||
/**
|
||||
@@ -70,9 +67,7 @@ public interface BuildListener {
|
||||
*/
|
||||
void onBuildFailed(GradleTask task, String msg);
|
||||
|
||||
/**
|
||||
* Called to notify client to save any opened files
|
||||
*/
|
||||
/** Called to notify client to save any opened files */
|
||||
void saveFiles();
|
||||
|
||||
/**
|
||||
@@ -83,8 +78,6 @@ public interface BuildListener {
|
||||
*/
|
||||
void appendOutput(GradleTask task, CharSequence text);
|
||||
|
||||
/**
|
||||
* Called to notify the client to prepare for a build
|
||||
*/
|
||||
/** Called to notify the client to prepare for a build */
|
||||
void prepareBuild();
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*
|
||||
@@ -64,104 +64,105 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IDEService {
|
||||
|
||||
|
||||
private BuildListener listener;
|
||||
private GradleTask currentTask;
|
||||
|
||||
private IDEProject mIDEProject;
|
||||
private IDEModule mAppModule;
|
||||
|
||||
|
||||
private final File projectRoot;
|
||||
private final IProcessExecutor processExecutor;
|
||||
|
||||
|
||||
private boolean isBuilding = false;
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = GradleTask.TASK_SHOW_DEPENDENCIES;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = GradleTask.TASK_ASSEMBLE_DEBUG;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = GradleTask.TASK_ASSEMBLE_RELEASE;
|
||||
public static final int TASK_BUILD = GradleTask.TASK_BUILD;
|
||||
public static final int TASK_BUNDLE = GradleTask.TASK_BUNDLE;
|
||||
public static final int TASK_CLEAN = GradleTask.TASK_CLEAN;
|
||||
public static final int TASK_CLEAN_BUILD = GradleTask.TASK_CLEAN_BUILD;
|
||||
public static final int TASK_DEX = GradleTask.TASK_DEX;
|
||||
public static final int TASK_LINT = GradleTask.TASK_LINT;
|
||||
public static final int TASK_LINT_DEBUG = GradleTask.TASK_LINT_DEBUG;
|
||||
public static final int TASK_LINT_RELEASE = GradleTask.TASK_LINT_RELEASE;
|
||||
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = GradleTask.TASK_SHOW_DEPENDENCIES;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = GradleTask.TASK_ASSEMBLE_DEBUG;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = GradleTask.TASK_ASSEMBLE_RELEASE;
|
||||
public static final int TASK_BUILD = GradleTask.TASK_BUILD;
|
||||
public static final int TASK_BUNDLE = GradleTask.TASK_BUNDLE;
|
||||
public static final int TASK_CLEAN = GradleTask.TASK_CLEAN;
|
||||
public static final int TASK_CLEAN_BUILD = GradleTask.TASK_CLEAN_BUILD;
|
||||
public static final int TASK_DEX = GradleTask.TASK_DEX;
|
||||
public static final int TASK_LINT = GradleTask.TASK_LINT;
|
||||
public static final int TASK_LINT_DEBUG = GradleTask.TASK_LINT_DEBUG;
|
||||
public static final int TASK_LINT_RELEASE = GradleTask.TASK_LINT_RELEASE;
|
||||
|
||||
public static final Logger LOG = Logger.instance("IDEService");
|
||||
|
||||
|
||||
private final InputStreamLineReader.OnReadListener mOutputReadListener = this::onBuildOutput;
|
||||
|
||||
private final IProcessExitListener mProcessExitListener = new IProcessExitListener() {
|
||||
@Override
|
||||
public void onExit(int code) {
|
||||
isBuilding = false;
|
||||
if (listener != null) {
|
||||
final String msg = getString(R.string.msg_gradle_terminated, code);
|
||||
if (code == 0) {
|
||||
onBuildSuccessful(currentTask, msg);
|
||||
} else {
|
||||
onBuildFailed(currentTask, msg);
|
||||
|
||||
private final IProcessExitListener mProcessExitListener =
|
||||
new IProcessExitListener() {
|
||||
@Override
|
||||
public void onExit(int code) {
|
||||
isBuilding = false;
|
||||
if (listener != null) {
|
||||
final String msg = getString(R.string.msg_gradle_terminated, code);
|
||||
if (code == 0) {
|
||||
onBuildSuccessful(currentTask, msg);
|
||||
} else {
|
||||
onBuildFailed(currentTask, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
public IDEService(File projectRoot) {
|
||||
this.projectRoot = projectRoot;
|
||||
this.processExecutor = ProcessExecutorFactory.commonExecutor();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public IDEService setListener(final BuildListener listener) {
|
||||
this.listener = listener == null ? null : new MainThreadBuildListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
protected void onBuildOutput(String line) {
|
||||
if(listener == null || line == null) return;
|
||||
if (listener == null || line == null) return;
|
||||
line = line.trim();
|
||||
|
||||
|
||||
String PROJECT_INITIALIZED = ">>> PROJECT INITIALIZED <<<";
|
||||
if(line.contains(PROJECT_INITIALIZED)) {
|
||||
if (line.contains(PROJECT_INITIALIZED)) {
|
||||
readIdeProject();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
String text = line;
|
||||
|
||||
final var app = StudioApp.getInstance ();
|
||||
|
||||
final var app = StudioApp.getInstance();
|
||||
// Try to shorten common file paths
|
||||
if (line.contains(app.getRootDir().getAbsolutePath())) {
|
||||
text = line.replace(app.getRootDir().getAbsolutePath(), "HOME");
|
||||
}
|
||||
if (line.contains(Objects.requireNonNull (app.getRootDir ().getParentFile ()).getAbsolutePath())) {
|
||||
if (line.contains(
|
||||
Objects.requireNonNull(app.getRootDir().getParentFile()).getAbsolutePath())) {
|
||||
text = line.replace(app.getRootDir().getParentFile().getAbsolutePath(), "ROOT");
|
||||
}
|
||||
|
||||
|
||||
listener.appendOutput(currentTask, text);
|
||||
|
||||
|
||||
String RUN_TASK = "> Task";
|
||||
String STARTING_DAEMON = "Starting a ";
|
||||
if(line.startsWith(RUN_TASK)) {
|
||||
if (line.startsWith(RUN_TASK)) {
|
||||
listener.onRunTask(currentTask, line.trim());
|
||||
} else if(line.startsWith(STARTING_DAEMON)) {
|
||||
} else if (line.startsWith(STARTING_DAEMON)) {
|
||||
listener.onStartingGradleDaemon(currentTask);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBuildSuccessful (GradleTask task, String msg) {
|
||||
if(currentTask != null && currentTask.getTaskID() != TASK_SHOW_DEPENDENCIES) {
|
||||
}
|
||||
|
||||
protected void onBuildSuccessful(GradleTask task, String msg) {
|
||||
if (currentTask != null && currentTask.getTaskID() != TASK_SHOW_DEPENDENCIES) {
|
||||
listener.onBuildSuccessful(task, msg.trim());
|
||||
}
|
||||
appendOutputSeparator();
|
||||
|
||||
if(currentTask != null && currentTask.affectsGeneratedSources()) {
|
||||
if (currentTask != null && currentTask.affectsGeneratedSources()) {
|
||||
notifyExternalSourceChange();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBuildFailed (GradleTask task, @NonNull String msg) {
|
||||
|
||||
protected void onBuildFailed(GradleTask task, @NonNull String msg) {
|
||||
isBuilding = false;
|
||||
listener.onBuildFailed(task, msg.trim());
|
||||
appendOutputSeparator();
|
||||
@@ -169,25 +170,32 @@ public class IDEService {
|
||||
|
||||
private void readIdeProject() {
|
||||
// Parsing project data is meaningless if there is no one listening...
|
||||
if(listener == null) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final BuildListener local = listener;
|
||||
new Thread(() -> {
|
||||
mIDEProject = new Gson()
|
||||
.fromJson(
|
||||
FileIOUtils.readFile2String(Environment.PROJECT_DATA_FILE),
|
||||
IDEProject.class
|
||||
);
|
||||
if(mIDEProject != null) {
|
||||
Optional<IDEModule> appModule = mIDEProject.getModuleByPath(":app");
|
||||
appModule.ifPresent (ideModule -> mAppModule = ideModule);
|
||||
local.onProjectLoaded(mIDEProject, appModule);
|
||||
} else {
|
||||
StudioApp.getInstance ().toast(com.itsaky.androidide.R.string.msg_init_project_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}).start();
|
||||
new Thread(
|
||||
() -> {
|
||||
mIDEProject =
|
||||
new Gson()
|
||||
.fromJson(
|
||||
FileIOUtils.readFile2String(
|
||||
Environment.PROJECT_DATA_FILE),
|
||||
IDEProject.class);
|
||||
if (mIDEProject != null) {
|
||||
Optional<IDEModule> appModule = mIDEProject.getModuleByPath(":app");
|
||||
appModule.ifPresent(ideModule -> mAppModule = ideModule);
|
||||
local.onProjectLoaded(mIDEProject, appModule);
|
||||
} else {
|
||||
StudioApp.getInstance()
|
||||
.toast(
|
||||
com.itsaky.androidide.R.string
|
||||
.msg_init_project_failed,
|
||||
Toaster.Type.ERROR);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void appendOutputSeparator() {
|
||||
@@ -208,40 +216,40 @@ public class IDEService {
|
||||
}
|
||||
|
||||
private String getString(@StringRes int id) {
|
||||
return StudioApp.getInstance ().getString(id);
|
||||
return StudioApp.getInstance().getString(id);
|
||||
}
|
||||
|
||||
private String getString(@StringRes int id, Object... format) {
|
||||
return StudioApp.getInstance ().getString(id, format);
|
||||
return StudioApp.getInstance().getString(id, format);
|
||||
}
|
||||
|
||||
|
||||
private void notifyExternalSourceChange() {
|
||||
// TODO Watch for file changes using a file observer
|
||||
// instead of depending on the build
|
||||
if(listener != null) {
|
||||
if (listener != null) {
|
||||
listener.onBuildModified();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private String[] getArguments(List<String> tasks) {
|
||||
final PreferenceManager prefs = StudioApp.getInstance ().getPrefManager();
|
||||
final PreferenceManager prefs = StudioApp.getInstance().getPrefManager();
|
||||
final List<String> args = new ArrayList<>();
|
||||
|
||||
|
||||
// 'bash' shell is always preferred
|
||||
if (Environment.SHELL.exists ()) {
|
||||
if (Environment.SHELL.exists()) {
|
||||
args.add(Environment.SHELL.getAbsolutePath());
|
||||
} else {
|
||||
args.add (Environment.BUSYBOX.getAbsolutePath ());
|
||||
args.add ("sh");
|
||||
args.add(Environment.BUSYBOX.getAbsolutePath());
|
||||
args.add("sh");
|
||||
}
|
||||
|
||||
args.add(new File (projectRoot, "gradlew").getAbsolutePath());
|
||||
|
||||
args.add(new File(projectRoot, "gradlew").getAbsolutePath());
|
||||
args.addAll(asAppTasks(tasks));
|
||||
args.add("--init-script");
|
||||
args.add(Environment.INIT_SCRIPT.getAbsolutePath());
|
||||
|
||||
if (prefs.isStackTraceEnabled ()) {
|
||||
if (prefs.isStackTraceEnabled()) {
|
||||
args.add("--stacktrace");
|
||||
}
|
||||
if (prefs.isGradleInfoEnabled()) {
|
||||
@@ -263,16 +271,16 @@ public class IDEService {
|
||||
|
||||
private Collection<? extends String> asAppTasks(@NonNull List<String> tasks) {
|
||||
return tasks.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::asAppModuleTask)
|
||||
.collect(Collectors.toList());
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::asAppModuleTask)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
private String asAppModuleTask (@NonNull String name) {
|
||||
private String asAppModuleTask(@NonNull String name) {
|
||||
return name.startsWith(":app:") ? name : ":app:" + name;
|
||||
}
|
||||
|
||||
|
||||
public void execTask(GradleTask task) {
|
||||
execTask(task, false);
|
||||
}
|
||||
@@ -283,103 +291,126 @@ public class IDEService {
|
||||
} else if (isBuilding()) {
|
||||
return;
|
||||
}
|
||||
|
||||
execTask(task, isGradleWrapperAvailable() ? CompletableFuture.completedFuture(Boolean.TRUE) : installWrapper());
|
||||
}
|
||||
|
||||
private void execTask (GradleTask task, CompletableFuture<Boolean> installing) {
|
||||
|
||||
final Runnable taskRunner = () -> {
|
||||
if(installing != null) {
|
||||
Boolean result;
|
||||
String failure = "";
|
||||
try {
|
||||
result = installing.get();
|
||||
} catch (Throwable th) {
|
||||
result = Boolean.FALSE;
|
||||
failure = th.getMessage();
|
||||
}
|
||||
|
||||
if(result.equals(Boolean.FALSE)) {
|
||||
if(listener != null) {
|
||||
listener.appendOutput(task, "-------------------- ERROR --------------------");
|
||||
listener.appendOutput(task, getString(R.string.msg_gradlew_installation_failed, failure));
|
||||
listener.appendOutput(task, "-----------------------------------------------");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
|
||||
final ProcessStreamsHolder holder = new ProcessStreamsHolder();
|
||||
|
||||
listener.saveFiles();
|
||||
|
||||
currentTask = task;
|
||||
listener.appendOutput(task, getString(R.string.msg_task_begin, currentTime(), task.getName()));
|
||||
try {
|
||||
|
||||
Environment.mkdirIfNotExits (Environment.TMP_DIR);
|
||||
|
||||
processExecutor.execAsync(holder, mProcessExitListener, projectRoot.getAbsolutePath(), true, getArguments(task.getTasks()));
|
||||
isBuilding = true;
|
||||
|
||||
startReading (holder.in);
|
||||
|
||||
listener.prepareBuild();
|
||||
} catch (Throwable e) {
|
||||
listener.onBuildFailed(task, e.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
execTask(
|
||||
task,
|
||||
isGradleWrapperAvailable()
|
||||
? CompletableFuture.completedFuture(Boolean.TRUE)
|
||||
: installWrapper());
|
||||
}
|
||||
|
||||
private void execTask(GradleTask task, CompletableFuture<Boolean> installing) {
|
||||
|
||||
final Runnable taskRunner =
|
||||
() -> {
|
||||
if (installing != null) {
|
||||
Boolean result;
|
||||
String failure = "";
|
||||
try {
|
||||
result = installing.get();
|
||||
} catch (Throwable th) {
|
||||
result = Boolean.FALSE;
|
||||
failure = th.getMessage();
|
||||
}
|
||||
|
||||
if (result.equals(Boolean.FALSE)) {
|
||||
if (listener != null) {
|
||||
listener.appendOutput(
|
||||
task, "-------------------- ERROR --------------------");
|
||||
listener.appendOutput(
|
||||
task,
|
||||
getString(
|
||||
R.string.msg_gradlew_installation_failed, failure));
|
||||
listener.appendOutput(
|
||||
task, "-----------------------------------------------");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
|
||||
final ProcessStreamsHolder holder = new ProcessStreamsHolder();
|
||||
|
||||
listener.saveFiles();
|
||||
|
||||
currentTask = task;
|
||||
listener.appendOutput(
|
||||
task,
|
||||
getString(R.string.msg_task_begin, currentTime(), task.getName()));
|
||||
try {
|
||||
|
||||
Environment.mkdirIfNotExits(Environment.TMP_DIR);
|
||||
|
||||
processExecutor.execAsync(
|
||||
holder,
|
||||
mProcessExitListener,
|
||||
projectRoot.getAbsolutePath(),
|
||||
true,
|
||||
getArguments(task.getTasks()));
|
||||
isBuilding = true;
|
||||
|
||||
startReading(holder.in);
|
||||
|
||||
listener.prepareBuild();
|
||||
} catch (Throwable e) {
|
||||
listener.onBuildFailed(task, e.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final Thread thread = new Thread(taskRunner, "GradleTaskExecutor");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void startReading(InputStream in) {
|
||||
final Thread reader = new Thread (new InputStreamLineReader (in, mOutputReadListener), "GradleProcessOutputReader");
|
||||
final Thread reader =
|
||||
new Thread(
|
||||
new InputStreamLineReader(in, mOutputReadListener),
|
||||
"GradleProcessOutputReader");
|
||||
reader.setDaemon(true);
|
||||
reader.start();
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> installWrapper() {
|
||||
if(listener != null) {
|
||||
if (listener != null) {
|
||||
listener.appendOutput(null, "-------------------- NOTE --------------------");
|
||||
listener.appendOutput(null, getString(R.string.msg_installing_gradlew));
|
||||
listener.appendOutput(null, "----------------------------------------------");
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync (() -> {
|
||||
final File extracted = new File(Environment.TMP_DIR, "gradle-wrapper.zip");
|
||||
if(ResourceUtils.copyFileFromAssets(getCommonAsset("gradle-wrapper.zip"), extracted.getAbsolutePath())) {
|
||||
try {
|
||||
final List<File> files = ZipUtils.unzipFile(extracted, projectRoot);
|
||||
if(files != null && !files.isEmpty()) {
|
||||
return Boolean.TRUE;
|
||||
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final File extracted = new File(Environment.TMP_DIR, "gradle-wrapper.zip");
|
||||
if (ResourceUtils.copyFileFromAssets(
|
||||
getCommonAsset("gradle-wrapper.zip"), extracted.getAbsolutePath())) {
|
||||
try {
|
||||
final List<File> files = ZipUtils.unzipFile(extracted, projectRoot);
|
||||
if (files != null && !files.isEmpty()) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean.FALSE;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isGradleWrapperAvailable () {
|
||||
|
||||
public boolean isGradleWrapperAvailable() {
|
||||
if (projectRoot == null || !projectRoot.exists()) {
|
||||
return false;
|
||||
}
|
||||
final File gradlew = new File(projectRoot, "gradlew");
|
||||
final File gradleWrapperJar = new File(projectRoot, "gradle/wrapper/gradle-wrapper.jar");
|
||||
final File gradleWrapperProps = new File(projectRoot, "gradle/wrapper/gradle-wrapper.properties");
|
||||
|
||||
return gradlew.exists () && gradleWrapperJar.exists () && gradleWrapperProps.exists ();
|
||||
final File gradleWrapperProps =
|
||||
new File(projectRoot, "gradle/wrapper/gradle-wrapper.properties");
|
||||
|
||||
return gradlew.exists() && gradleWrapperJar.exists() && gradleWrapperProps.exists();
|
||||
}
|
||||
|
||||
|
||||
public void assembleDebug(boolean installApk) {
|
||||
execTask(((AssembleDebug) BaseGradleTasks.ASSEMBLE_DEBUG).setInstallApk(installApk));
|
||||
}
|
||||
@@ -395,7 +426,7 @@ public class IDEService {
|
||||
public void bundle() {
|
||||
execTask(BaseGradleTasks.BUNDLE);
|
||||
}
|
||||
|
||||
|
||||
public void clean() {
|
||||
execTask(BaseGradleTasks.CLEAN);
|
||||
}
|
||||
@@ -405,9 +436,15 @@ public class IDEService {
|
||||
}
|
||||
|
||||
public void stopAllDaemons() {
|
||||
StudioApp.getInstance ().newShell(null).bgAppend(String.format(Locale.US, "cd '%s' && sh gradlew --stop", projectRoot.getAbsolutePath()));
|
||||
if(isBuilding()) {
|
||||
if(listener != null)
|
||||
StudioApp.getInstance()
|
||||
.newShell(null)
|
||||
.bgAppend(
|
||||
String.format(
|
||||
Locale.US,
|
||||
"cd '%s' && sh gradlew --stop",
|
||||
projectRoot.getAbsolutePath()));
|
||||
if (isBuilding()) {
|
||||
if (listener != null)
|
||||
listener.onBuildFailed(currentTask, getString(R.string.msg_daemons_stopped));
|
||||
isBuilding = false;
|
||||
}
|
||||
@@ -424,15 +461,16 @@ public class IDEService {
|
||||
public void lintRelease() {
|
||||
execTask(BaseGradleTasks.LINT_RELEASE);
|
||||
}
|
||||
|
||||
|
||||
public void updateResourceClasses() {
|
||||
execTask(new UpdateResourceClassesTask(mAppModule != null && mAppModule.viewBindingEnabled));
|
||||
execTask(
|
||||
new UpdateResourceClassesTask(mAppModule != null && mAppModule.viewBindingEnabled));
|
||||
}
|
||||
|
||||
|
||||
public boolean isBuilding() {
|
||||
return isBuilding;
|
||||
}
|
||||
|
||||
|
||||
public String typeString(int type) {
|
||||
switch (type) {
|
||||
case TASK_ASSEMBLE_DEBUG:
|
||||
@@ -445,11 +483,12 @@ public class IDEService {
|
||||
return getString(R.string.clean_amp_build);
|
||||
case TASK_CLEAN:
|
||||
return getString(R.string.clean_project);
|
||||
case TASK_BUNDLE :
|
||||
case TASK_BUNDLE:
|
||||
return getString(R.string.create_aab);
|
||||
case TASK_DEX:
|
||||
return getString(R.string.compiling);
|
||||
default: return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*
|
||||
@@ -27,12 +27,12 @@ import com.itsaky.androidide.tasks.GradleTask;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
class MainThreadBuildListener implements BuildListener {
|
||||
class MainThreadBuildListener implements BuildListener {
|
||||
|
||||
private final BuildListener listener;
|
||||
|
||||
public MainThreadBuildListener(@NonNull final BuildListener listener) {
|
||||
Objects.requireNonNull (listener, "Build listener cannot be null");
|
||||
Objects.requireNonNull(listener, "Build listener cannot be null");
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,16 +23,15 @@ import io.github.rosemoe.sora.lang.styling.TextStyle;
|
||||
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme;
|
||||
|
||||
/**
|
||||
* Base class for editor color schemes in AndroidIDE.
|
||||
* If you're implementing a color scheme for the IDE,
|
||||
* this should be the base class instead of {@link EditorColorScheme}.
|
||||
* Base class for editor color schemes in AndroidIDE. If you're implementing a color scheme for the
|
||||
* IDE, this should be the base class instead of {@link EditorColorScheme}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SchemeAndroidIDE extends EditorColorScheme {
|
||||
|
||||
|
||||
private static int current = EditorColorScheme.END_COLOR_ID;
|
||||
|
||||
|
||||
public static final int LOG_INFO = current++;
|
||||
public static final int LOG_DEBUG = current++;
|
||||
public static final int LOG_ERROR = current++;
|
||||
@@ -63,115 +62,117 @@ public class SchemeAndroidIDE extends EditorColorScheme {
|
||||
public static final int LOCAL_VARIABLE = current++;
|
||||
public static final int TODO_COMMENT = current++;
|
||||
public static final int FIXME_COMMENT = current++;
|
||||
|
||||
|
||||
@Override
|
||||
public void applyDefault () {
|
||||
public void applyDefault() {
|
||||
// Apply default colors
|
||||
super.applyDefault ();
|
||||
|
||||
super.applyDefault();
|
||||
|
||||
// Apply customized colors
|
||||
setColor (WHOLE_BACKGROUND, 0xff212121);
|
||||
setColor (LINE_NUMBER_BACKGROUND, 0xff212121);
|
||||
setColor (LINE_DIVIDER, Color.TRANSPARENT);
|
||||
setColor (LINE_NUMBER, 0xffaaaaaa );
|
||||
setColor (LINE_NUMBER_PANEL, 0xff000000);
|
||||
setColor (LINE_NUMBER_PANEL_TEXT, 0xffffffff);
|
||||
setColor (TEXT_NORMAL, 0xfff5f5f5);
|
||||
setColor (SELECTED_TEXT_BACKGROUND, 0xff757575);
|
||||
setColor (TEXT_SELECTED, 0xff424242);
|
||||
setColor (SELECTION_INSERT, 0xffff5252);
|
||||
setColor (SELECTION_HANDLE, 0xffff5252);
|
||||
setColor (CURRENT_LINE, 0xff313131);
|
||||
setColor (UNDERLINE, 0xffffffff);
|
||||
setColor (SCROLL_BAR_THUMB, 0xff757575);
|
||||
setColor (SCROLL_BAR_THUMB_PRESSED, 0xffff5252);
|
||||
setColor (SCROLL_BAR_TRACK, 0xffbdbdbd);
|
||||
setColor (BLOCK_LINE, 0xff424242);
|
||||
setColor (BLOCK_LINE_CURRENT, 0xffffffff);
|
||||
setColor (AUTO_COMP_PANEL_BG, 0xff757575);
|
||||
setColor (AUTO_COMP_PANEL_CORNER, 0xff9e9e9e);
|
||||
setColor (MATCHED_TEXT_BACKGROUND, 0xffFF8F00);
|
||||
setColor (NON_PRINTABLE_CHAR, 0xffdddddd);
|
||||
setColor (KEYWORD, 0xffff6060);
|
||||
setColor (OPERATOR, 0xff4fc3f7);
|
||||
setColor (LITERAL, 0xff8bc34a);
|
||||
setColor (TYPE_NAME, 0xff4fc3f7);
|
||||
setColor (LOCAL_VARIABLE, 0xfff5f5f5);
|
||||
setColor (RESOURCE_VARIABLE, 0xfff5f5f5);
|
||||
setColor (PARAMETER, 0xfff5f5f5);
|
||||
setColor (EXCEPTION_PARAM, 0xfff5f5f5);
|
||||
setColor (METHOD_DECLARATION, 0xfff5f5f5);
|
||||
setColor (METHOD_INVOCATION, 0xfff5f5f5);
|
||||
setColor (CONSTRUCTOR, 0xfff5f5f5);
|
||||
setColor (ANNOTATION, 0xff4fc3f7);
|
||||
setColor (PACKAGE_NAME, 0xfff0be4b);
|
||||
setColor (FIELD, 0xfff0be4b);
|
||||
setColor (STATIC_INIT, 0xfff0be4b);
|
||||
setColor (INSTANCE_INIT, 0xfff0be4b);
|
||||
setColor (TYPE_PARAM, 0xfff0be4b);
|
||||
setColor (STATIC_FIELD, 0xffffb300);
|
||||
setColor (ENUM_TYPE, 0xff4fc3f7);
|
||||
setColor (INTERFACE, 0xff4fc3f7);
|
||||
setColor (ENUM, 0xffffb300);
|
||||
setColor (XML_TAG, 0xffff6060);
|
||||
|
||||
setColor (DIAGNOSTIC_ERROR, 0xfff44336);
|
||||
setColor (DIAGNOSTIC_WARNING, 0xffFFEB3B);
|
||||
setColor (DIAGNOSTIC_INFO, 0xff4CAF50);
|
||||
setColor (DIAGNOSTIC_HINT, 0xffffffff);
|
||||
|
||||
setColor (STDERR, 0xfff44336);
|
||||
setColor (STDOUT, 0xff4CAF50);
|
||||
|
||||
setColor (TODO_COMMENT, 0xffffc400);
|
||||
setColor (FIXME_COMMENT, 0xffffab00);
|
||||
setColor (COMMENT, 0xffbdbdbd);
|
||||
setColor(WHOLE_BACKGROUND, 0xff212121);
|
||||
setColor(LINE_NUMBER_BACKGROUND, 0xff212121);
|
||||
setColor(LINE_DIVIDER, Color.TRANSPARENT);
|
||||
setColor(LINE_NUMBER, 0xffaaaaaa);
|
||||
setColor(LINE_NUMBER_PANEL, 0xff000000);
|
||||
setColor(LINE_NUMBER_PANEL_TEXT, 0xffffffff);
|
||||
setColor(TEXT_NORMAL, 0xfff5f5f5);
|
||||
setColor(SELECTED_TEXT_BACKGROUND, 0xff757575);
|
||||
setColor(TEXT_SELECTED, 0xff424242);
|
||||
setColor(SELECTION_INSERT, 0xffff5252);
|
||||
setColor(SELECTION_HANDLE, 0xffff5252);
|
||||
setColor(CURRENT_LINE, 0xff313131);
|
||||
setColor(UNDERLINE, 0xffffffff);
|
||||
setColor(SCROLL_BAR_THUMB, 0xff757575);
|
||||
setColor(SCROLL_BAR_THUMB_PRESSED, 0xffff5252);
|
||||
setColor(SCROLL_BAR_TRACK, 0xffbdbdbd);
|
||||
setColor(BLOCK_LINE, 0xff424242);
|
||||
setColor(BLOCK_LINE_CURRENT, 0xffffffff);
|
||||
setColor(AUTO_COMP_PANEL_BG, 0xff757575);
|
||||
setColor(AUTO_COMP_PANEL_CORNER, 0xff9e9e9e);
|
||||
setColor(MATCHED_TEXT_BACKGROUND, 0xffFF8F00);
|
||||
setColor(NON_PRINTABLE_CHAR, 0xffdddddd);
|
||||
setColor(KEYWORD, 0xffff6060);
|
||||
setColor(OPERATOR, 0xff4fc3f7);
|
||||
setColor(LITERAL, 0xff8bc34a);
|
||||
setColor(TYPE_NAME, 0xff4fc3f7);
|
||||
setColor(LOCAL_VARIABLE, 0xfff5f5f5);
|
||||
setColor(RESOURCE_VARIABLE, 0xfff5f5f5);
|
||||
setColor(PARAMETER, 0xfff5f5f5);
|
||||
setColor(EXCEPTION_PARAM, 0xfff5f5f5);
|
||||
setColor(METHOD_DECLARATION, 0xfff5f5f5);
|
||||
setColor(METHOD_INVOCATION, 0xfff5f5f5);
|
||||
setColor(CONSTRUCTOR, 0xfff5f5f5);
|
||||
setColor(ANNOTATION, 0xff4fc3f7);
|
||||
setColor(PACKAGE_NAME, 0xfff0be4b);
|
||||
setColor(FIELD, 0xfff0be4b);
|
||||
setColor(STATIC_INIT, 0xfff0be4b);
|
||||
setColor(INSTANCE_INIT, 0xfff0be4b);
|
||||
setColor(TYPE_PARAM, 0xfff0be4b);
|
||||
setColor(STATIC_FIELD, 0xffffb300);
|
||||
setColor(ENUM_TYPE, 0xff4fc3f7);
|
||||
setColor(INTERFACE, 0xff4fc3f7);
|
||||
setColor(ENUM, 0xffffb300);
|
||||
setColor(XML_TAG, 0xffff6060);
|
||||
|
||||
setColor(DIAGNOSTIC_ERROR, 0xfff44336);
|
||||
setColor(DIAGNOSTIC_WARNING, 0xffFFEB3B);
|
||||
setColor(DIAGNOSTIC_INFO, 0xff4CAF50);
|
||||
setColor(DIAGNOSTIC_HINT, 0xffffffff);
|
||||
|
||||
setColor(STDERR, 0xfff44336);
|
||||
setColor(STDOUT, 0xff4CAF50);
|
||||
|
||||
setColor(TODO_COMMENT, 0xffffc400);
|
||||
setColor(FIXME_COMMENT, 0xffffab00);
|
||||
setColor(COMMENT, 0xffbdbdbd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link TextStyle#makeStyle(int)}
|
||||
*
|
||||
* @param id The color id.
|
||||
* @return The style flags.
|
||||
*/
|
||||
public static long get (int id) {
|
||||
return TextStyle.makeStyle (id);
|
||||
public static long get(int id) {
|
||||
return TextStyle.makeStyle(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create style for keywords. Convenient method to avoid calling {@link TextStyle#makeStyle(int, int, boolean, boolean, boolean)}
|
||||
* Create style for keywords. Convenient method to avoid calling {@link TextStyle#makeStyle(int,
|
||||
* int, boolean, boolean, boolean)}
|
||||
*
|
||||
* @return The default style for keywords.
|
||||
*/
|
||||
public static long forKeyword () {
|
||||
return TextStyle.makeStyle (KEYWORD, 0, true, false, false);
|
||||
public static long forKeyword() {
|
||||
return TextStyle.makeStyle(KEYWORD, 0, true, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create style for string literals. The returned style sets the {@link TextStyle#NO_COMPLETION_BIT}.
|
||||
* Create style for string literals. The returned style sets the {@link
|
||||
* TextStyle#NO_COMPLETION_BIT}.
|
||||
*
|
||||
* @return The style for string literals.
|
||||
*/
|
||||
public static long forString () {
|
||||
return TextStyle.makeStyle (LITERAL, true);
|
||||
public static long forString() {
|
||||
return TextStyle.makeStyle(LITERAL, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create color style for default comments.
|
||||
*
|
||||
* @return The style for {@link #COMMENT}.
|
||||
*/
|
||||
public static long forComment () {
|
||||
return TextStyle.makeStyle (COMMENT, true);
|
||||
public static long forComment() {
|
||||
return TextStyle.makeStyle(COMMENT, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create style for the given id without completions.
|
||||
*
|
||||
* @param id The id to create style for.
|
||||
* @return The style for the id.
|
||||
*/
|
||||
public static long withoutCompletion (int id) {
|
||||
return TextStyle.makeStyle (id, true);
|
||||
public static long withoutCompletion(int id) {
|
||||
return TextStyle.makeStyle(id, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
/************************************************************************************
|
||||
* 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.syntax.highlighters;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
/**
|
||||
* A highlighter is used to highlight a small piece of code
|
||||
*
|
||||
* Implementations of these class shouldn't be used in CodeEditor
|
||||
* For CodeEditor usage, implement an EditorLanguage instead
|
||||
*
|
||||
* <p>Implementations of these class shouldn't be used in CodeEditor For CodeEditor usage, implement
|
||||
* an EditorLanguage instead
|
||||
*/
|
||||
public interface Highlighter {
|
||||
SpannableStringBuilder highlight(String code, String match) throws Exception;
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* 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/>.
|
||||
*/
|
||||
@@ -19,15 +19,18 @@ package com.itsaky.androidide.syntax.highlighters;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import io.github.rosemoe.sora.widget.schemes.EditorColorScheme;
|
||||
import java.io.StringReader;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JavaHighlighter implements Highlighter {
|
||||
|
||||
@@ -37,11 +40,10 @@ public class JavaHighlighter implements Highlighter {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
final EditorColorScheme scheme = new SchemeAndroidIDE();
|
||||
Token token;
|
||||
while((token = lexer.nextToken()) != null) {
|
||||
if(token.getType() == JavaLexer.EOF)
|
||||
break;
|
||||
switch(token.getType()) {
|
||||
case JavaLexer.WS :
|
||||
while ((token = lexer.nextToken()) != null) {
|
||||
if (token.getType() == JavaLexer.EOF) break;
|
||||
switch (token.getType()) {
|
||||
case JavaLexer.WS:
|
||||
sb.append(token.getText());
|
||||
break;
|
||||
case JavaLexer.ABSTRACT:
|
||||
@@ -85,7 +87,10 @@ public class JavaHighlighter implements Highlighter {
|
||||
case JavaLexer.VOID:
|
||||
case JavaLexer.VOLATILE:
|
||||
case JavaLexer.WHILE:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.KEYWORD)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(EditorColorScheme.KEYWORD)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.DECIMAL_LITERAL:
|
||||
case JavaLexer.HEX_LITERAL:
|
||||
@@ -96,75 +101,90 @@ public class JavaHighlighter implements Highlighter {
|
||||
case JavaLexer.BOOL_LITERAL:
|
||||
case JavaLexer.CHAR_LITERAL:
|
||||
case JavaLexer.NULL_LITERAL:
|
||||
case JavaLexer.STRING_LITERAL :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.LITERAL)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
case JavaLexer.STRING_LITERAL:
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(EditorColorScheme.LITERAL)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.LPAREN :
|
||||
case JavaLexer.RPAREN :
|
||||
case JavaLexer.LBRACK :
|
||||
case JavaLexer.RBRACK :
|
||||
case JavaLexer.SEMI :
|
||||
case JavaLexer.COMMA :
|
||||
case JavaLexer.ASSIGN :
|
||||
case JavaLexer.GT :
|
||||
case JavaLexer.LT :
|
||||
case JavaLexer.BANG :
|
||||
case JavaLexer.TILDE :
|
||||
case JavaLexer.QUESTION :
|
||||
case JavaLexer.COLON :
|
||||
case JavaLexer.EQUAL :
|
||||
case JavaLexer.GE :
|
||||
case JavaLexer.LE :
|
||||
case JavaLexer.NOTEQUAL :
|
||||
case JavaLexer.AND :
|
||||
case JavaLexer.OR :
|
||||
case JavaLexer.INC :
|
||||
case JavaLexer.DEC :
|
||||
case JavaLexer.ADD :
|
||||
case JavaLexer.SUB :
|
||||
case JavaLexer.MUL :
|
||||
case JavaLexer.DIV :
|
||||
case JavaLexer.BITAND :
|
||||
case JavaLexer.BITOR :
|
||||
case JavaLexer.CARET :
|
||||
case JavaLexer.MOD :
|
||||
case JavaLexer.ADD_ASSIGN :
|
||||
case JavaLexer.SUB_ASSIGN :
|
||||
case JavaLexer.MUL_ASSIGN :
|
||||
case JavaLexer.DIV_ASSIGN :
|
||||
case JavaLexer.AND_ASSIGN :
|
||||
case JavaLexer.OR_ASSIGN :
|
||||
case JavaLexer.XOR_ASSIGN :
|
||||
case JavaLexer.MOD_ASSIGN :
|
||||
case JavaLexer.LSHIFT_ASSIGN :
|
||||
case JavaLexer.RSHIFT_ASSIGN :
|
||||
case JavaLexer.URSHIFT_ASSIGN :
|
||||
case JavaLexer.ARROW :
|
||||
case JavaLexer.COLONCOLON :
|
||||
case JavaLexer.ELLIPSIS :
|
||||
case JavaLexer.DOT :
|
||||
case JavaLexer.LBRACE :
|
||||
case JavaLexer.RBRACE :
|
||||
case JavaLexer.AT :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.OPERATOR)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
case JavaLexer.LPAREN:
|
||||
case JavaLexer.RPAREN:
|
||||
case JavaLexer.LBRACK:
|
||||
case JavaLexer.RBRACK:
|
||||
case JavaLexer.SEMI:
|
||||
case JavaLexer.COMMA:
|
||||
case JavaLexer.ASSIGN:
|
||||
case JavaLexer.GT:
|
||||
case JavaLexer.LT:
|
||||
case JavaLexer.BANG:
|
||||
case JavaLexer.TILDE:
|
||||
case JavaLexer.QUESTION:
|
||||
case JavaLexer.COLON:
|
||||
case JavaLexer.EQUAL:
|
||||
case JavaLexer.GE:
|
||||
case JavaLexer.LE:
|
||||
case JavaLexer.NOTEQUAL:
|
||||
case JavaLexer.AND:
|
||||
case JavaLexer.OR:
|
||||
case JavaLexer.INC:
|
||||
case JavaLexer.DEC:
|
||||
case JavaLexer.ADD:
|
||||
case JavaLexer.SUB:
|
||||
case JavaLexer.MUL:
|
||||
case JavaLexer.DIV:
|
||||
case JavaLexer.BITAND:
|
||||
case JavaLexer.BITOR:
|
||||
case JavaLexer.CARET:
|
||||
case JavaLexer.MOD:
|
||||
case JavaLexer.ADD_ASSIGN:
|
||||
case JavaLexer.SUB_ASSIGN:
|
||||
case JavaLexer.MUL_ASSIGN:
|
||||
case JavaLexer.DIV_ASSIGN:
|
||||
case JavaLexer.AND_ASSIGN:
|
||||
case JavaLexer.OR_ASSIGN:
|
||||
case JavaLexer.XOR_ASSIGN:
|
||||
case JavaLexer.MOD_ASSIGN:
|
||||
case JavaLexer.LSHIFT_ASSIGN:
|
||||
case JavaLexer.RSHIFT_ASSIGN:
|
||||
case JavaLexer.URSHIFT_ASSIGN:
|
||||
case JavaLexer.ARROW:
|
||||
case JavaLexer.COLONCOLON:
|
||||
case JavaLexer.ELLIPSIS:
|
||||
case JavaLexer.DOT:
|
||||
case JavaLexer.LBRACE:
|
||||
case JavaLexer.RBRACE:
|
||||
case JavaLexer.AT:
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(EditorColorScheme.OPERATOR)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.BOOLEAN:
|
||||
case JavaLexer.BYTE :
|
||||
case JavaLexer.BYTE:
|
||||
case JavaLexer.CHAR:
|
||||
case JavaLexer.DOUBLE:
|
||||
case JavaLexer.ENUM :
|
||||
case JavaLexer.ENUM:
|
||||
case JavaLexer.FLOAT:
|
||||
case JavaLexer.INT :
|
||||
case JavaLexer.INT:
|
||||
case JavaLexer.LONG:
|
||||
case JavaLexer.SHORT:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(SchemeAndroidIDE.TYPE_NAME)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(SchemeAndroidIDE.TYPE_NAME)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
case JavaLexer.BLOCK_COMMENT :
|
||||
case JavaLexer.LINE_COMMENT :
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.COMMENT)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
case JavaLexer.BLOCK_COMMENT:
|
||||
case JavaLexer.LINE_COMMENT:
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(EditorColorScheme.COMMENT)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
default:
|
||||
sb.append(token.getText(), new ForegroundColorSpan(scheme.getColor(EditorColorScheme.TEXT_NORMAL)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.append(
|
||||
token.getText(),
|
||||
new ForegroundColorSpan(scheme.getColor(EditorColorScheme.TEXT_NORMAL)),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -173,9 +193,17 @@ public class JavaHighlighter implements Highlighter {
|
||||
|
||||
Pattern pattern = Pattern.compile(Pattern.quote(match));
|
||||
Matcher matcher = pattern.matcher(code);
|
||||
while(matcher.find()) {
|
||||
sb.setSpan(bg, matcher.start(), matcher.end(), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.setSpan(fg, matcher.start(), matcher.end(), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
while (matcher.find()) {
|
||||
sb.setSpan(
|
||||
bg,
|
||||
matcher.start(),
|
||||
matcher.end(),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
sb.setSpan(
|
||||
fg,
|
||||
matcher.start(),
|
||||
matcher.end(),
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
return sb;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks;
|
||||
|
||||
@@ -29,19 +28,19 @@ public abstract class BaseGradleTask implements GradleTask {
|
||||
public boolean affectsGeneratedSources() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSaveFiles() {
|
||||
return getType() == Type.BUILD
|
||||
|| getType() == Type.LINT
|
||||
|| getTaskID() == IDEService.TASK_DEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj != null
|
||||
&& obj instanceof GradleTask
|
||||
&& getTaskID() == ((GradleTask) obj).getTaskID()
|
||||
&& getType() == ((GradleTask) obj).getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSaveFiles() {
|
||||
return getType() == Type.BUILD
|
||||
|| getType() == Type.LINT
|
||||
|| getTaskID() == IDEService.TASK_DEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj != null
|
||||
&& obj instanceof GradleTask
|
||||
&& getTaskID() == ((GradleTask) obj).getTaskID()
|
||||
&& getType() == ((GradleTask) obj).getType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +1,68 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface GradleTask {
|
||||
|
||||
public String getName();
|
||||
|
||||
public String getName();
|
||||
|
||||
public String getCommands();
|
||||
|
||||
public List<String> getTasks();
|
||||
public int getTaskID();
|
||||
|
||||
public int getTaskID();
|
||||
|
||||
public boolean affectsGeneratedSources();
|
||||
public boolean shouldSaveFiles();
|
||||
public boolean canOutput();
|
||||
public Type getType();
|
||||
|
||||
public static enum Type {
|
||||
ANDROIDIDE_PLUGIN,
|
||||
BUILD,
|
||||
LINT,
|
||||
HELP,
|
||||
OTHER
|
||||
|
||||
public boolean shouldSaveFiles();
|
||||
|
||||
public boolean canOutput();
|
||||
|
||||
public Type getType();
|
||||
|
||||
public static enum Type {
|
||||
ANDROIDIDE_PLUGIN,
|
||||
BUILD,
|
||||
LINT,
|
||||
HELP,
|
||||
OTHER
|
||||
}
|
||||
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = 0;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = 1;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = 2;
|
||||
public static final int TASK_BUILD = 3;
|
||||
public static final int TASK_BUNDLE = 4;
|
||||
public static final int TASK_CLEAN = 5;
|
||||
public static final int TASK_CLEAN_BUILD = 6;
|
||||
public static final int TASK_COMPILE_JAVA = 7;
|
||||
public static final int TASK_DEX = 8;
|
||||
public static final int TASK_LINT = 9;
|
||||
public static final int TASK_LINT_DEBUG = 10;
|
||||
public static final int TASK_LINT_RELEASE = 11;
|
||||
public static final int TASK_TASKS = 12;
|
||||
public static final int TASK_INIT_PROJECT = 13;
|
||||
public static final int TASK_PROCESS_DEBUG_RESOURCES = 14;
|
||||
public static final int DATABINDING_GEN_BASE_CLASSES_DEBUG = 15;
|
||||
public static final int TASK_UPDATE_RESOURCE_CLASSES = 16;
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = 0;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = 1;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = 2;
|
||||
public static final int TASK_BUILD = 3;
|
||||
public static final int TASK_BUNDLE = 4;
|
||||
public static final int TASK_CLEAN = 5;
|
||||
public static final int TASK_CLEAN_BUILD = 6;
|
||||
public static final int TASK_COMPILE_JAVA = 7;
|
||||
public static final int TASK_DEX = 8;
|
||||
public static final int TASK_LINT = 9;
|
||||
public static final int TASK_LINT_DEBUG = 10;
|
||||
public static final int TASK_LINT_RELEASE = 11;
|
||||
public static final int TASK_TASKS = 12;
|
||||
public static final int TASK_INIT_PROJECT = 13;
|
||||
public static final int TASK_PROCESS_DEBUG_RESOURCES = 14;
|
||||
public static final int DATABINDING_GEN_BASE_CLASSES_DEBUG = 15;
|
||||
public static final int TASK_UPDATE_RESOURCE_CLASSES = 16;
|
||||
}
|
||||
|
||||
@@ -1,79 +1,83 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class TaskExecutor {
|
||||
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private final Logger LOG = Logger.instance ("TaskExecutor");
|
||||
|
||||
private final Logger LOG = Logger.instance("TaskExecutor");
|
||||
|
||||
public interface Callback<R> {
|
||||
void complete(R result);
|
||||
}
|
||||
|
||||
|
||||
public interface CallbackWithError<R> {
|
||||
void complete(R result, Throwable error);
|
||||
}
|
||||
|
||||
public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final R result = callable.call();
|
||||
handler.post(() -> {
|
||||
callback.complete(result);
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Callable task was not able to finish", th);
|
||||
}
|
||||
});
|
||||
executor.execute(
|
||||
() -> {
|
||||
try {
|
||||
final R result = callable.call();
|
||||
handler.post(
|
||||
() -> {
|
||||
callback.complete(result);
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Callable task was not able to finish", th);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public <R> void executeAsyncProvideError(Callable<R> callable, CallbackWithError<R> callback) {
|
||||
executor.execute(() -> {
|
||||
Throwable error = null;
|
||||
R result = null;
|
||||
|
||||
try {
|
||||
result = callable.call();
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Callable task was not able to finish", th);
|
||||
error = th;
|
||||
}
|
||||
final R resultCopied = result;
|
||||
final Throwable errorCopied = error;
|
||||
|
||||
handler.post(() -> {
|
||||
callback.complete(resultCopied, errorCopied);
|
||||
});
|
||||
});
|
||||
executor.execute(
|
||||
() -> {
|
||||
Throwable error = null;
|
||||
R result = null;
|
||||
|
||||
try {
|
||||
result = callable.call();
|
||||
} catch (Throwable th) {
|
||||
LOG.error("Callable task was not able to finish", th);
|
||||
error = th;
|
||||
}
|
||||
final R resultCopied = result;
|
||||
final Throwable errorCopied = error;
|
||||
|
||||
handler.post(
|
||||
() -> {
|
||||
callback.complete(resultCopied, errorCopied);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.callables;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
@@ -31,56 +32,53 @@ import java.util.Comparator;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class FileTreeCallable implements Callable<Boolean> {
|
||||
private final Context ctx;
|
||||
private TreeNode parent;
|
||||
private File file;
|
||||
private final Context ctx;
|
||||
private TreeNode parent;
|
||||
private File file;
|
||||
|
||||
public FileTreeCallable(Context ctx, TreeNode parent, File file) {
|
||||
this.ctx = ctx;
|
||||
this.parent = parent;
|
||||
this.file = file;
|
||||
}
|
||||
public FileTreeCallable(Context ctx, TreeNode parent, File file) {
|
||||
this.ctx = ctx;
|
||||
this.parent = parent;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
getNodeFromArray(file.listFiles(/*new HiddenFilesFilter()*/), parent);
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
getNodeFromArray(file.listFiles(/*new HiddenFilesFilter()*/ ), parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void getNodeFromArray(File[] files, TreeNode parent) {
|
||||
Arrays.sort(files, new SortFileName());
|
||||
Arrays.sort(files, new SortFolder());
|
||||
for (File file : files) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(ctx));
|
||||
parent.addChild(node);
|
||||
}
|
||||
}
|
||||
private void getNodeFromArray(File[] files, TreeNode parent) {
|
||||
Arrays.sort(files, new SortFileName());
|
||||
Arrays.sort(files, new SortFolder());
|
||||
for (File file : files) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(ctx));
|
||||
parent.addChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HiddenFilesFilter implements FileFilter {
|
||||
public static class HiddenFilesFilter implements FileFilter {
|
||||
|
||||
@Override
|
||||
public boolean accept(File p1) {
|
||||
return !p1.getName().startsWith(".");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean accept(File p1) {
|
||||
return !p1.getName().startsWith(".");
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortFileName implements Comparator<File> {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
return f1.getName().compareTo(f2.getName());
|
||||
}
|
||||
}
|
||||
public static class SortFileName implements Comparator<File> {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
return f1.getName().compareTo(f2.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortFolder implements Comparator<File> {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
if (f1.isDirectory() == f2.isDirectory())
|
||||
return 0;
|
||||
else if (f1.isDirectory() && !f2.isDirectory())
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
public static class SortFolder implements Comparator<File> {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
if (f1.isDirectory() == f2.isDirectory()) return 0;
|
||||
else if (f1.isDirectory() && !f2.isDirectory()) return -1;
|
||||
else return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,54 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.callables;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.itsaky.androidide.models.License;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class LicenseReader implements Callable<List<License>> {
|
||||
|
||||
|
||||
private final Context ctx;
|
||||
|
||||
public LicenseReader(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<License> call() throws Exception {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(ctx.getAssets().open("licenses.json")));
|
||||
BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(ctx.getAssets().open("licenses.json")));
|
||||
String line;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while((line = reader.readLine()) != null) {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
TypeToken<List<License>> token = new TypeToken<List<License>>(){};
|
||||
TypeToken<List<License>> token = new TypeToken<List<License>>() {};
|
||||
return new Gson().fromJson(sb.toString(), token.getType());
|
||||
}
|
||||
}
|
||||
|
||||
+26
-22
@@ -1,49 +1,53 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.callables;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public class ListDirectoryCallable implements java.util.concurrent.Callable<String> {
|
||||
|
||||
private File file;
|
||||
|
||||
public ListDirectoryCallable(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return TextUtils.join("\n", FileUtils.listFilesInDirWithFilter(file, ARCHIVE_FILTER, false));
|
||||
}
|
||||
|
||||
private final FileFilter ARCHIVE_FILTER = new FileFilter(){
|
||||
private File file;
|
||||
|
||||
@Override
|
||||
public boolean accept(File p1) {
|
||||
return p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip"));
|
||||
}
|
||||
};
|
||||
public ListDirectoryCallable(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return TextUtils.join(
|
||||
"\n", FileUtils.listFilesInDirWithFilter(file, ARCHIVE_FILTER, false));
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+24
-23
@@ -1,47 +1,48 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.callables;
|
||||
|
||||
import com.itsaky.androidide.models.NewProjectDetails;
|
||||
import com.itsaky.androidide.utils.ProjectWriter;
|
||||
import java.util.concurrent.Callable;
|
||||
import com.itsaky.androidide.models.ProjectTemplate;
|
||||
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
|
||||
import com.itsaky.androidide.models.NewProjectDetails;
|
||||
import com.itsaky.androidide.models.ProjectTemplate;
|
||||
import com.itsaky.androidide.utils.ProjectWriter;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ProjectCreatorCallable implements Callable<Void> {
|
||||
|
||||
private ProjectTemplate template;
|
||||
private NewProjectDetails details;
|
||||
private ProjectWriterCallback callback;
|
||||
private ProjectTemplate template;
|
||||
private NewProjectDetails details;
|
||||
private ProjectWriterCallback callback;
|
||||
|
||||
public ProjectCreatorCallable(ProjectTemplate template, NewProjectDetails details, ProjectWriterCallback callback) {
|
||||
this.template = template;
|
||||
this.details = details;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
ProjectWriter.write(template, details, callback);
|
||||
return null;
|
||||
}
|
||||
public ProjectCreatorCallable(
|
||||
ProjectTemplate template, NewProjectDetails details, ProjectWriterCallback callback) {
|
||||
this.template = template;
|
||||
this.details = details;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
ProjectWriter.write(template, details, callback);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.callables;
|
||||
|
||||
import com.blankj.utilcode.util.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public final class UnzipCallable implements Callable<List<File>> {
|
||||
|
||||
private File src;
|
||||
private File dest;
|
||||
private File src;
|
||||
private File dest;
|
||||
|
||||
public UnzipCallable(File src, File dest) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
}
|
||||
public UnzipCallable(File src, File dest) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> call() throws Exception {
|
||||
return ZipUtils.unzipFile(src, dest);
|
||||
}
|
||||
@Override
|
||||
public List<File> call() throws Exception {
|
||||
return ZipUtils.unzipFile(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/************************************************************************************
|
||||
* 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.tasks.gradle;
|
||||
|
||||
@@ -35,21 +34,16 @@ import com.itsaky.androidide.tasks.gradle.plugin.InitializeIDEProject;
|
||||
import com.itsaky.androidide.tasks.gradle.resources.ProcessDebugResources;
|
||||
|
||||
public class BaseGradleTasks {
|
||||
|
||||
public static final BaseGradleTask
|
||||
|
||||
BUILD = new Build(),
|
||||
ASSEMBLE_DEBUG = new AssembleDebug(),
|
||||
ASSEMBLE_RELEASE = new AssembleRelease(),
|
||||
BUNDLE = new Bundle(),
|
||||
CLEAN = new Clean(),
|
||||
CLEAN_BUILD = new CleanBuild(),
|
||||
|
||||
LINT = new Lint(),
|
||||
LINT_DEBUG = new LintDebug(),
|
||||
LINT_RELEASE = new LintRelease(),
|
||||
|
||||
PROCESS_DEBUG_RESOURCES = new ProcessDebugResources(),
|
||||
|
||||
INITIALIZE_IDE_PROJECT = new InitializeIDEProject();
|
||||
|
||||
public static final BaseGradleTask BUILD = new Build(),
|
||||
ASSEMBLE_DEBUG = new AssembleDebug(),
|
||||
ASSEMBLE_RELEASE = new AssembleRelease(),
|
||||
BUNDLE = new Bundle(),
|
||||
CLEAN = new Clean(),
|
||||
CLEAN_BUILD = new CleanBuild(),
|
||||
LINT = new Lint(),
|
||||
LINT_DEBUG = new LintDebug(),
|
||||
LINT_RELEASE = new LintRelease(),
|
||||
PROCESS_DEBUG_RESOURCES = new ProcessDebugResources(),
|
||||
INITIALIZE_IDE_PROJECT = new InitializeIDEProject();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user