Made the 'Auto save files' an EXPERIMENTAL feature and disabled by default

Can be enabled from preferences
This commit is contained in:
Akash Yadav
2022-01-27 09:05:10 +05:30
parent 20949aa318
commit 3b92570821
9 changed files with 161 additions and 69 deletions
+1
View File
@@ -12,6 +12,7 @@
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_github.xml" value="0.21837606837606838" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_linear_layout.xml" value="0.175" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_relative_layout.xml" value="0.175" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_save.xml" value="0.26205128205128203" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_signature_help.xml" value="0.19907407407407407" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/drawable/ic_text_lower.xml" value="0.19907407407407407" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/activity_about.xml" value="0.264" />
Binary file not shown.
@@ -28,6 +28,7 @@ import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
@@ -110,7 +111,6 @@ import com.itsaky.androidide.views.CodeEditorView;
import com.itsaky.androidide.views.MaterialBanner;
import com.itsaky.androidide.views.SymbolInputView;
import com.itsaky.inflater.ILayoutInflater;
import com.itsaky.inflater.values.ValuesTable;
import com.itsaky.inflater.values.ValuesTableFactory;
import com.itsaky.lsp.java.models.JavaServerConfiguration;
import com.itsaky.lsp.models.DiagnosticItem;
@@ -122,7 +122,6 @@ import com.unnamed.b.atv.model.TreeNode;
import org.jetbrains.annotations.Contract;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
@@ -133,6 +132,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -827,7 +827,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public void saveResult (int index, SaveResult result) {
if (index >= 0 && index < mViewModel.getOpenedFileCount ()) {
var frag = getEditorAtIndex (index);
if (frag == null) {
if (frag == null || frag.getFile () == null) {
return;
}
@@ -941,8 +941,13 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
var opened = mViewModel.getOpenedFile (index);
LOG.info ("Closing file:", opened);
final var editor = getEditorAtIndex (index);
if (editor != null && editor.isModified ()) {
notifyFilesUnsaved (Collections.singletonList (editor), () -> closeFile (index));
return;
}
if (editor != null && editor.getEditor () != null) {
editor.save ();
editor.getEditor ().close ();
} else {
LOG.error ("Cannot save file before close. Editor instance is null");
@@ -964,40 +969,80 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public void closeAll () {
final var count = mViewModel.getOpenedFileCount ();
final var unsavedFiles = mViewModel.getOpenedFiles ()
.stream ()
.map (this::getEditorForFile)
.filter (editor -> editor != null && editor.isModified ())
.collect (Collectors.toList ());
// Save and close all files one by one
for (int i = 0; i < count; i++) {
final var editor = getEditorAtIndex (i);
if (editor != null && editor.getEditor () != null) {
editor.save ();
editor.getEditor ().close ();
} else {
LOG.error ("Unable to save file at index:", i);
if (unsavedFiles.isEmpty ()) {
// Files were already saved, close all files one by one
for (int i = 0; i < count; i++) {
final var editor = getEditorAtIndex (i);
if (editor != null && editor.getEditor () != null) {
editor.getEditor ().close ();
} else {
LOG.error ("Unable to close file at index:", i);
}
}
mViewModel.removeAllFiles ();
mBinding.tabs.removeAllTabs ();
mBinding.tabs.requestLayout ();
mBinding.editorContainer.removeAllViews ();
invalidateOptionsMenu ();
} else {
// There are unsaved files
notifyFilesUnsaved (unsavedFiles, this::closeAll);
}
mViewModel.removeAllFiles ();
mBinding.tabs.removeAllTabs ();
mBinding.tabs.requestLayout ();
mBinding.editorContainer.removeAllViews ();
invalidateOptionsMenu ();
}
private void notifyFilesUnsaved (List<CodeEditorView> unsavedEditors, Runnable invokeAfter) {
final var builder = DialogUtils.newYesNoDialog (this,
getString (R.string.title_files_unsaved), // title
getString (R.string.msg_files_unsaved, TextUtils.join ("\n", unsavedEditors)), // message
(dialog, which) -> { // 'yes' click
dialog.dismiss ();
saveAll (true);
invokeAfter.run ();
},
(dialog, which) -> { // 'no' click
dialog.dismiss ();
// Mark all the files as saved, then try to close them all
for (var editor : unsavedEditors) {
editor.markAsSaved ();
}
invokeAfter.run ();
});
builder.show ();
}
public void closeOthers () {
final var file = mViewModel.getCurrentFile ();
for (int i = 0; i < mViewModel.getOpenedFileCount (); i++) {
final var editor = getEditorAtIndex (i);
// Index of files changes as we keep close files
// So we compare the files instead of index
if (editor != null) {
if (!file.equals (editor.getFile ())) {
closeFile (i);
final var unsavedFiles = mViewModel.getOpenedFiles ()
.stream ()
.map (this::getEditorForFile)
.filter (editor -> editor != null && editor.isModified ())
.collect (Collectors.toList ());
if (unsavedFiles.isEmpty ()) {
final var file = mViewModel.getCurrentFile ();
for (int i = 0; i < mViewModel.getOpenedFileCount (); i++) {
final var editor = getEditorAtIndex (i);
// Index of files changes as we keep close files
// So we compare the files instead of index
if (editor != null) {
if (!file.equals (editor.getFile ())) {
closeFile (i);
}
} else {
LOG.error ("Unable to save file at index:", i);
}
} else {
LOG.error ("Unable to save file at index:", i);
}
} else {
notifyFilesUnsaved (unsavedFiles, this::closeOthers);
}
}
@@ -1019,7 +1064,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
public CodeEditorView getEditorForFile (@NonNull final File file) {
for (int i = 0; i < mViewModel.getOpenedFileCount (); i++) {
final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt (i);
if (editor.getFile ().equals (file)) {
if (file.equals (editor.getFile ())) {
return editor;
}
}
@@ -1157,7 +1202,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private void previewLayout () {
try {
if (getCurrentEditor () == null) {
if (getCurrentEditor () == null || getCurrentEditor ().getFile () == null) {
LOG.error ("No file is opened. Cannot preview layout.");
return;
}
@@ -1498,14 +1543,16 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mTabCloseAction.setOnActionItemClickListener ((item) -> {
final int id = item.getActionId ();
saveAll ();
if (getApp ().getPrefManager ().getBoolean (PreferenceManager.KEY_EDITOR_AUTO_SAVE, false)) {
saveAll ();
}
if (id == ACTION_ID_CLOSE) {
closeFile (mBinding.tabs.getSelectedTabPosition ());
}
if (id == ACTION_ID_OTHERS) {
closeOthers ();
closeOthers ();
}
if (id == ACTION_ID_ALL) {
@@ -19,6 +19,8 @@ package com.itsaky.androidide.fragments.preferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreference;
@@ -33,12 +35,15 @@ import com.itsaky.androidide.utils.DialogUtils;
import static com.itsaky.androidide.managers.PreferenceManager.*;
import static com.itsaky.androidide.models.PrefBasedJavaServerSettings.*;
public class EditorPreferences extends BasePreferenceFragment implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
public class EditorPreferences extends BasePreferenceFragment {
@Override
public void onCreatePreferences(Bundle p1, String p2) {
super.onCreatePreferences(p1, p2);
if(getContext() == null) return;
if(getContext() == null) {
return;
}
final var screen = getPreferenceScreen();
final var commonCategory = new PreferenceCategory (getContext ());
@@ -48,6 +53,7 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere
final var nonPrintable = new Preference(getContext());
final var tabSize = new Preference(getContext());
final var drawHex = new SwitchPreference(getContext());
final var autoSave = new SwitchPreference (getContext ());
final var javaMatchLower = new SwitchPreference (getContext ());
@@ -74,6 +80,11 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere
drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title);
drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary);
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));
@@ -84,46 +95,55 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere
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);
nonPrintable.setOnPreferenceClickListener(this);
tabSize.setOnPreferenceClickListener(this);
drawHex.setOnPreferenceChangeListener(this);
javaMatchLower.setOnPreferenceChangeListener (this);
fontSize.setOnPreferenceClickListener(this::onPreferenceClick);
nonPrintable.setOnPreferenceClickListener(this::onPreferenceClick);
tabSize.setOnPreferenceClickListener(this::onPreferenceClick);
drawHex.setOnPreferenceChangeListener(this::onPreferenceChange);
autoSave.setOnPreferenceChangeListener (this::onPreferenceChange);
javaMatchLower.setOnPreferenceChangeListener (this::onPreferenceChange);
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));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
final boolean value = (boolean) newValue;
if(preference.getKey().equals(KEY_EDITOR_DRAW_HEX)) {
getPrefManager().putBoolean(KEY_EDITOR_DRAW_HEX, value);
ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true;
} else if (preference.getKey ().equals (KEY_JAVA_PREF_MATCH_LOWER)) {
getPrefManager ().putBoolean (KEY_JAVA_PREF_MATCH_LOWER, value);
final var javaServer = StudioApp.getInstance ().getJavaLanguageServer ();
javaServer.applySettings (PrefBasedJavaServerSettings.getInstance ());
switch (preference.getKey ()) {
case KEY_EDITOR_DRAW_HEX:
ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true;
break;
case KEY_JAVA_PREF_MATCH_LOWER:
final var javaServer = StudioApp.getInstance ().getJavaLanguageServer ();
javaServer.applySettings (PrefBasedJavaServerSettings.getInstance ());
break;
}
getPrefManager ().putBoolean (preference.getKey (), value);
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
public boolean onPreferenceClick(@NonNull Preference preference) {
final String key = preference.getKey();
if(key.equals(KEY_EDITOR_FONT_SIZE)) {
showTextSizeDialog();
} else if(key.equals(KEY_EDITOR_PRINTABLE_CHARS)) {
showPrintableCharsDialog();
} else if(key.equals(KEY_EDITOR_TAB_SIZE)) {
showTabSizeDialog();
}
switch (key) {
case KEY_EDITOR_FONT_SIZE:
showTextSizeDialog ();
break;
case KEY_EDITOR_PRINTABLE_CHARS:
showPrintableCharsDialog ();
break;
case KEY_EDITOR_TAB_SIZE:
showTabSizeDialog ();
break;
}
return true;
}
@@ -170,9 +190,7 @@ public class EditorPreferences extends BasePreferenceFragment implements Prefere
float s = binding.slider.getValue();
changeTextSize(binding, s);
});
builder.setNeutralButton(R.string.reset, (p1, p2) -> {
changeTextSize(binding, 14);
});
builder.setNeutralButton(R.string.reset, (p1, p2) -> changeTextSize(binding, 14));
builder.setCancelable(false);
builder.show();
}
@@ -1,8 +1,6 @@
/************************************************************************************
/*
* This file is part of AndroidIDE.
*
*
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -16,18 +14,24 @@
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*
**************************************************************************************/
*/
package com.itsaky.androidide.utils;
import androidx.annotation.Nullable;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.views.SymbolInputView.Symbol;
import java.io.File;
public class Symbols {
public static Symbol[] forFile(File file) {
public static Symbol[] forFile(@Nullable File file) {
if (file == null) {
return new Symbol[0];
}
if (file.isFile()) {
if (file.getName().endsWith(".java")
|| file.getName().endsWith(".gradle"))
@@ -125,6 +125,15 @@ public class EditorViewModel extends ViewModel {
return Objects.requireNonNull (mFiles.getValue ()).size ();
}
/**
* Get the list of currently opened files.
* @return The list of opened files.
*/
@NonNull
public List<File> getOpenedFiles () {
return mFiles.getValue () == null ? new ArrayList<> () : mFiles.getValue ();
}
/**
* Add an observer to the list of opened files.
* @param lifecycleOwner The lifecycle owner.
@@ -374,4 +374,12 @@ public class CodeEditorView extends FrameLayout {
ValuesTableFactory.syncWithFile (getFile ());
}
}
/**
* Mark this files as saved. Even if it not saved.
*/
public void markAsSaved () {
isModified = false;
notifySaved ();
}
}
+4
View File
@@ -359,5 +359,9 @@
<string name="idepref_java_matchLower_summary">If enabled, then typing a lower case or upper case letters will suggest class names as well as scope members.</string>
<string name="idepref_editor_category_common">Common</string>
<string name="idepref_editor_category_java">Java</string>
<string name="title_files_unsaved">Unsaved files</string>
<string name="msg_files_unsaved">Some files are not saved. Would you like to save them before closing? Unsaved files are:\n%s</string>
<string name="idepref_editor_autoSave_title">Save before closing files</string>
<string name="idepref_editor_autoSave_summary">Save files before closing them. This is an EXPERIMENTAL feature. You are expected to save the files yourself.</string>
</resources>
@@ -42,6 +42,7 @@ public class PreferenceManager {
public static final String KEY_EDITOR_FONT_SIZE = "idepref_editor_fontSize";
public static final String KEY_EDITOR_PRINTABLE_CHARS = "idepref_editor_nonPrintableFlags";
public static final String KEY_EDITOR_TAB_SIZE = "idepref_editor_tabSize";
public static final String KEY_EDITOR_AUTO_SAVE = "idepref_editor_autoSave";
public static final String KEY_GRADLE_CMD_STACK_TRACE = "idepref_gradleCmd_stacktrace";
public static final String KEY_GRADLE_CMD_DEBUG = "idepref_gradleCmd_debug";