Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-notes.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorifact <xyyjxl@outlook.com>2020-06-14 17:56:32 +0300
committerifact <xyyjxl@outlook.com>2020-06-14 17:56:32 +0300
commitfb0f03d01118279e4f1bac7b2670d95e540fb0c9 (patch)
tree70a3e18ffb72410dcfc5b443cb52c3d0534a7c68
parentf52f874f6c5f293ee1c03b430544efc879fc639a (diff)
parent85342bbc2ae6edec7536c4cb7d5c5e2a96353a98 (diff)
Merge branch '603-sorting-method' of https://github.com/stefan-niedermann/nextcloud-notes into 603-sorting-method
-rw-r--r--app/src/androidTest/java/it/niedermann/owncloud/notes/persistence/NotesDatabaseTest.java4
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java2
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java40
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/android/fragment/EditTitleDialogFragment.java81
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/model/LocalAccount.java2
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java4
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java8
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java26
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java35
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java4
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/util/NoteUtil.java27
-rw-r--r--app/src/main/res/drawable/ic_title_grey600_24dp.xml5
-rw-r--r--app/src/main/res/layout/dialog_edit_title.xml14
-rw-r--r--app/src/main/res/menu/menu_note_fragment.xml6
-rw-r--r--app/src/main/res/values/strings.xml7
-rw-r--r--app/src/test/java/android/text/TextUtils.java11
-rw-r--r--app/src/test/java/it/niedermann/owncloud/notes/util/NoteUtilTest.java36
-rw-r--r--fastlane/metadata/android/en-US/changelogs/2016000.txt3
18 files changed, 272 insertions, 43 deletions
diff --git a/app/src/androidTest/java/it/niedermann/owncloud/notes/persistence/NotesDatabaseTest.java b/app/src/androidTest/java/it/niedermann/owncloud/notes/persistence/NotesDatabaseTest.java
index ef7e5f65..19c3146b 100644
--- a/app/src/androidTest/java/it/niedermann/owncloud/notes/persistence/NotesDatabaseTest.java
+++ b/app/src/androidTest/java/it/niedermann/owncloud/notes/persistence/NotesDatabaseTest.java
@@ -150,7 +150,7 @@ public class NotesDatabaseTest {
String newContent = getCurDate() + " This is a even greater day my friend.";
DBNote dbNote = new DBNote(newNoteID, 1, Calendar.getInstance(), "A Greater Day",
newContent, true, "Best Friend's Record", null, DBStatus.VOID,
- accountID, NoteUtil.generateNoteExcerpt(newContent), 0);
+ accountID, NoteUtil.generateNoteExcerpt(newContent, "Test-Title"), 0);
// Add a new note
long noteID = db.addNote(accountID, dbNote);
@@ -165,7 +165,7 @@ public class NotesDatabaseTest {
newContent = getCurDate() + " This is a even greater day my friend.";
dbNote = new DBNote(0, 1, Calendar.getInstance(), "An Even Greater Day",
newContent, true, "Sincere Friend's Record", null, DBStatus.VOID,
- accountID, NoteUtil.generateNoteExcerpt(newContent), 0);
+ accountID, NoteUtil.generateNoteExcerpt(newContent, "Test-Title"), 0);
// Add a new note
noteID = db.addNote(accountID, dbNote);
// Check if this note is added successfully
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java
index 2cc72aea..4f5722aa 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AppendToNoteActivity.java
@@ -44,7 +44,7 @@ public class AppendToNoteActivity extends NotesListViewActivity {
} else {
newContent = receivedText;
}
- db.updateNoteAndSync(ssoAccount, localAccount.getId(), note, newContent, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
+ db.updateNoteAndSync(ssoAccount, localAccount, note, newContent, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
} else {
Toast.makeText(this, R.string.shared_text_empty, Toast.LENGTH_SHORT).show();
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java
index ccdfc6ab..69d1b6dc 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java
@@ -20,6 +20,7 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
+import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -31,7 +32,9 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.android.activity.EditNoteActivity;
import it.niedermann.owncloud.notes.android.fragment.CategoryDialogFragment.CategoryDialogListener;
+import it.niedermann.owncloud.notes.android.fragment.EditTitleDialogFragment.EditTitleListener;
import it.niedermann.owncloud.notes.branding.BrandedFragment;
+import it.niedermann.owncloud.notes.model.ApiVersion;
import it.niedermann.owncloud.notes.model.CloudNote;
import it.niedermann.owncloud.notes.model.DBNote;
import it.niedermann.owncloud.notes.model.DBStatus;
@@ -48,7 +51,7 @@ import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon;
import static it.niedermann.owncloud.notes.util.ColorUtil.isColorDark;
import static it.niedermann.owncloud.notes.util.Notes.isDarkThemeActive;
-public abstract class BaseNoteFragment extends BrandedFragment implements CategoryDialogListener {
+public abstract class BaseNoteFragment extends BrandedFragment implements CategoryDialogListener, EditTitleListener {
private static final String TAG = BaseNoteFragment.class.getSimpleName();
@@ -70,6 +73,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
private int originalScrollY;
protected NotesDatabase db;
private NoteFragmentListener listener;
+ private boolean titleModified = false;
protected boolean isNew = true;
@@ -195,6 +199,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
MenuItem itemFavorite = menu.findItem(R.id.menu_favorite);
prepareFavoriteOption(itemFavorite);
+ menu.findItem(R.id.menu_title).setVisible(localAccount.getPreferredApiVersion() != null && localAccount.getPreferredApiVersion().compareTo(new ApiVersion("1.0", 1, 0)) >= 0);
menu.findItem(R.id.menu_delete).setVisible(!isNew);
}
@@ -214,7 +219,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
if (originalNote == null) {
db.deleteNoteAndSync(ssoAccount, note.getId());
} else {
- db.updateNoteAndSync(ssoAccount, localAccount.getId(), originalNote, null, null);
+ db.updateNoteAndSync(ssoAccount, localAccount, originalNote, null, null);
}
listener.close();
return true;
@@ -230,6 +235,9 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
case R.id.menu_category:
showCategorySelector();
return true;
+ case R.id.menu_title:
+ showEditTitleDialog();
+ return true;
case R.id.menu_move:
MoveAccountDialogFragment.newInstance().show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName());
return true;
@@ -275,7 +283,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
}
public void onCloseNote() {
- if (originalNote == null && getContent().isEmpty()) {
+ if (!titleModified && originalNote == null && getContent().isEmpty()) {
db.deleteNoteAndSync(ssoAccount, note.getId());
}
}
@@ -297,8 +305,9 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
Log.v(TAG, "... not saving, since nothing has changed");
}
} else {
- note = db.updateNoteAndSync(ssoAccount, localAccount.getId(), note, newContent, callback);
+ note = db.updateNoteAndSync(ssoAccount, localAccount, note, newContent, callback);
listener.onNoteUpdated(note);
+ requireActivity().invalidateOptionsMenu();
}
} else {
Log.e(TAG, "note is null");
@@ -326,12 +335,35 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
categoryFragment.show(manager, fragmentId);
}
+ /**
+ * Opens a dialog in order to chose a category
+ */
+ private void showEditTitleDialog() {
+ final String fragmentId = "fragment_edit_title";
+ FragmentManager manager = requireActivity().getSupportFragmentManager();
+ Fragment frag = manager.findFragmentByTag(fragmentId);
+ if (frag != null) {
+ manager.beginTransaction().remove(frag).commit();
+ }
+ DialogFragment editTitleFragment = EditTitleDialogFragment.newInstance(note.getTitle());
+ editTitleFragment.setTargetFragment(this, 0);
+ editTitleFragment.show(manager, fragmentId);
+ }
+
@Override
public void onCategoryChosen(String category) {
db.setCategory(ssoAccount, note, category, null);
listener.onNoteUpdated(note);
}
+ @Override
+ public void onTitleEdited(String newTitle) {
+ titleModified = true;
+ note.setTitle(newTitle);
+ note = db.updateNoteAndSync(ssoAccount, localAccount, note, note.getContent(), newTitle, null);
+ listener.onNoteUpdated(note);
+ }
+
public void moveNote(LocalAccount account) {
db.moveNoteToAnotherAccount(ssoAccount, note.getAccountId(), note, account.getId());
listener.close();
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/EditTitleDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/EditTitleDialogFragment.java
new file mode 100644
index 00000000..db1dbd61
--- /dev/null
+++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/EditTitleDialogFragment.java
@@ -0,0 +1,81 @@
+package it.niedermann.owncloud.notes.android.fragment;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+
+import it.niedermann.owncloud.notes.R;
+import it.niedermann.owncloud.notes.databinding.DialogEditTitleBinding;
+
+public class EditTitleDialogFragment extends DialogFragment {
+
+ static final String PARAM_OLD_TITLE = "old_title";
+
+ private String oldTitle;
+ private EditTitleListener listener;
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ final Bundle args = getArguments();
+ if (args == null) {
+ throw new IllegalArgumentException("Provide at least " + PARAM_OLD_TITLE);
+ }
+ oldTitle = args.getString(PARAM_OLD_TITLE);
+
+ if (getTargetFragment() instanceof EditTitleListener) {
+ listener = (EditTitleListener) getTargetFragment();
+ } else if (getActivity() instanceof EditTitleListener) {
+ listener = (EditTitleListener) getActivity();
+ } else {
+ throw new IllegalArgumentException("Calling activity or target fragment must implement " + EditTitleListener.class.getSimpleName());
+ }
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ View dialogView = View.inflate(getContext(), R.layout.dialog_edit_title, null);
+ DialogEditTitleBinding binding = DialogEditTitleBinding.bind(dialogView);
+
+ if (savedInstanceState == null) {
+ if (requireArguments().containsKey(PARAM_OLD_TITLE)) {
+ binding.title.setText(requireArguments().getString(PARAM_OLD_TITLE));
+ }
+ }
+
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.change_note_title)
+ .setView(dialogView)
+ .setCancelable(true)
+ .setPositiveButton(R.string.action_edit_save, (dialog, which) -> listener.onTitleEdited(binding.title.getText().toString()))
+ .setNegativeButton(R.string.simple_cancel, null)
+ .create();
+ }
+
+ public static DialogFragment newInstance(String title) {
+ final DialogFragment fragment = new EditTitleDialogFragment();
+ final Bundle args = new Bundle();
+ args.putString(PARAM_OLD_TITLE, title);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ /**
+ * Interface that must be implemented by the calling Activity.
+ */
+ public interface EditTitleListener {
+ /**
+ * This method is called after the user has changed the title of a note manually.
+ *
+ * @param newTitle the new title that a user submitted
+ */
+ void onTitleEdited(String newTitle);
+ }
+}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/model/LocalAccount.java b/app/src/main/java/it/niedermann/owncloud/notes/model/LocalAccount.java
index 6a6c3a5e..bfcb4d74 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/model/LocalAccount.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/model/LocalAccount.java
@@ -24,6 +24,7 @@ public class LocalAccount {
private String etag;
private String capabilitiesETag;
private long modified;
+ @Nullable
private ApiVersion preferredApiVersion;
@ColorInt
private int color;
@@ -78,6 +79,7 @@ public class LocalAccount {
this.modified = modified;
}
+ @Nullable
public ApiVersion getPreferredApiVersion() {
return preferredApiVersion;
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java
index 23573d24..19d0a903 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java
@@ -98,8 +98,8 @@ public class LoadNotesListTask extends AsyncTask<Void, Void, List<Item>> {
for (int i = 0; i < noteList.size(); i++) {
DBNote currentNote = noteList.get(i);
String initials = currentNote.getTitle().substring(0, 1).toUpperCase();
- if (!initials.matches("[A-Z]")) {
- initials = initials.matches("[\\u0250-\\uFFFF]") ? "Other" : "#";
+ if (!initials.matches("[A-Z\\u00C0-\\u00DF]")) {
+ initials = initials.matches("[\\u0250-\\uFFFF]") ? context.getString(R.string.simple_other) : "#";
}
if (i > 0 && !initials.equals(lastInitials)) {
itemList.add(new SectionItem(initials));
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java
index 69aeae3d..207b4d49 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java
@@ -72,9 +72,9 @@ public abstract class NotesClient {
if (preferredApiVersion == null) {
Log.i(TAG, "apiVersion is null, using " + NotesClientV02.class.getSimpleName());
return new NotesClientV02(appContext);
-// } else if (preferredApiVersion.compareTo(SUPPORTED_API_VERSIONS[0]) == 0) {
-// Log.i(TAG, "Using " + NotesClient_1_0.class.getSimpleName());
-// return new NotesClient_1_0(appContext);
+ } else if (preferredApiVersion.compareTo(SUPPORTED_API_VERSIONS[0]) == 0) {
+ Log.i(TAG, "Using " + NotesClientV1.class.getSimpleName());
+ return new NotesClientV1(appContext);
} else if (preferredApiVersion.compareTo(SUPPORTED_API_VERSIONS[1]) == 0) {
Log.i(TAG, "Using " + NotesClientV02.class.getSimpleName());
return new NotesClientV02(appContext);
@@ -189,7 +189,7 @@ public abstract class NotesClient {
String supportedApiVersions = null;
final AidlNetworkRequest.PlainHeader supportedApiVersionsHeader = response.getPlainHeader(HEADER_KEY_X_NOTES_API_VERSIONS);
if (supportedApiVersionsHeader != null) {
- supportedApiVersions = Objects.requireNonNull(supportedApiVersionsHeader.getValue()).replace("\"", "");
+ supportedApiVersions = "[" + Objects.requireNonNull(supportedApiVersionsHeader.getValue()) + "]";
}
// return these header fields since they should only be saved after successful processing the result!
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java
index 449818c9..75da6a56 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java
@@ -7,6 +7,11 @@ import androidx.annotation.WorkerThread;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
import it.niedermann.owncloud.notes.model.CloudNote;
import it.niedermann.owncloud.notes.util.ServerResponse.NoteResponse;
import it.niedermann.owncloud.notes.util.ServerResponse.NotesResponse;
@@ -18,23 +23,34 @@ public class NotesClientV1 extends NotesClient {
NotesClientV1(@NonNull Context appContext) {
super(appContext);
- throw new UnsupportedOperationException("Not implemented yet.");
}
NotesResponse getNotes(SingleSignOnAccount ssoAccount, long lastModified, String lastETag) throws Exception {
- throw new UnsupportedOperationException("Not implemented yet.");
+ Map<String, String> parameter = new HashMap<>();
+ parameter.put(GET_PARAM_KEY_PRUNE_BEFORE, Long.toString(lastModified));
+ return new NotesResponse(requestServer(ssoAccount, "notes", METHOD_GET, parameter, null, lastETag));
+ }
+
+ private NoteResponse putNote(SingleSignOnAccount ssoAccount, CloudNote note, String path, String method) throws Exception {
+ JSONObject paramObject = new JSONObject();
+ paramObject.accumulate(JSON_TITLE, note.getTitle());
+ paramObject.accumulate(JSON_CONTENT, note.getContent());
+ paramObject.accumulate(JSON_MODIFIED, note.getModified().getTimeInMillis() / 1000);
+ paramObject.accumulate(JSON_FAVORITE, note.isFavorite());
+ paramObject.accumulate(JSON_CATEGORY, note.getCategory());
+ return new NoteResponse(requestServer(ssoAccount, path, method, null, paramObject, null));
}
NoteResponse createNote(SingleSignOnAccount ssoAccount, CloudNote note) throws Exception {
- throw new UnsupportedOperationException("Not implemented yet.");
+ return putNote(ssoAccount, note, "notes", METHOD_POST);
}
NoteResponse editNote(SingleSignOnAccount ssoAccount, CloudNote note) throws Exception {
- throw new UnsupportedOperationException("Not implemented yet.");
+ return putNote(ssoAccount, note, "notes/" + note.getRemoteId(), METHOD_PUT);
}
void deleteNote(SingleSignOnAccount ssoAccount, long noteId) throws Exception {
- throw new UnsupportedOperationException("Not implemented yet.");
+ this.requestServer(ssoAccount, "notes/" + noteId, METHOD_DELETE, null, null, null);
}
@Override
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
index eb608ebe..2e1fe0f0 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
@@ -62,6 +62,7 @@ import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACT
import static it.niedermann.owncloud.notes.android.appwidget.NoteListWidget.updateNoteListWidgets;
import static it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget.updateSingleNoteWidgets;
import static it.niedermann.owncloud.notes.model.NoteListsWidgetData.MODE_DISPLAY_CATEGORY;
+import static it.niedermann.owncloud.notes.util.NoteUtil.generateNoteExcerpt;
/**
* Helps to add, get, update and delete Notes with the option to trigger a Resync with the Server.
@@ -98,7 +99,7 @@ public class NotesDatabase extends AbstractNotesDatabase {
* @param note Note
*/
public long addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, CloudNote note) {
- DBNote dbNote = new DBNote(0, 0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), note.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(note.getContent()), 0);
+ DBNote dbNote = new DBNote(0, 0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), note.getEtag(), DBStatus.LOCAL_EDITED, accountId, generateNoteExcerpt(note.getContent(), note.getTitle()), 0);
long id = addNote(accountId, dbNote);
notifyWidgets();
getNoteServerSyncHelper().scheduleSync(ssoAccount, true);
@@ -125,7 +126,7 @@ public class NotesDatabase extends AbstractNotesDatabase {
} else {
values.put(key_status, DBStatus.VOID.getTitle());
values.put(key_account_id, accountId);
- values.put(key_excerpt, NoteUtil.generateNoteExcerpt(note.getContent()));
+ values.put(key_excerpt, generateNoteExcerpt(note.getContent(), note.getTitle()));
}
if (note.getRemoteId() > 0) {
values.put(key_remote_id, note.getRemoteId());
@@ -518,22 +519,36 @@ public class NotesDatabase extends AbstractNotesDatabase {
return db.insert(table_category, null, values);
}
+ public DBNote updateNoteAndSync(SingleSignOnAccount ssoAccount, @NonNull LocalAccount localAccount, @NonNull DBNote oldNote, @Nullable String newContent, @Nullable ISyncCallback callback) {
+ return updateNoteAndSync(ssoAccount, localAccount, oldNote, newContent, null, callback);
+ }
+
/**
* Updates a single Note with a new content.
* The title is derived from the new content automatically, and modified date as well as DBStatus are updated, too -- if the content differs to the state in the database.
*
* @param oldNote Note to be changed
* @param newContent New content. If this is <code>null</code>, then <code>oldNote</code> is saved again (useful for undoing changes).
+ * @param newTitle New title. If this is <code>null</code>, then either the old title is reused (in case the note has been synced before) or a title is generated (in case it is a new note)
* @param callback When the synchronization is finished, this callback will be invoked (optional).
* @return changed note if differs from database, otherwise the old note.
*/
- public DBNote updateNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, @NonNull DBNote oldNote, @Nullable String newContent, @Nullable ISyncCallback callback) {
- //debugPrintFullDB();
+ public DBNote updateNoteAndSync(SingleSignOnAccount ssoAccount, @NonNull LocalAccount localAccount, @NonNull DBNote oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
DBNote newNote;
if (newContent == null) {
- newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, oldNote.getExcerpt(), oldNote.getScrollY());
+ newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, localAccount.getId(), oldNote.getExcerpt(), oldNote.getScrollY());
} else {
- newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(newContent, getContext()), newContent, oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(newContent), oldNote.getScrollY());
+ final String title;
+ if (newTitle != null) {
+ title = newTitle;
+ } else {
+ if (oldNote.getRemoteId() == 0 || localAccount.getPreferredApiVersion() == null || localAccount.getPreferredApiVersion().compareTo(new ApiVersion("1.0", 0, 0)) < 0) {
+ title = NoteUtil.generateNonEmptyNoteTitle(newContent, getContext());
+ } else {
+ title = oldNote.getTitle();
+ }
+ }
+ newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), Calendar.getInstance(), title, newContent, oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, localAccount.getId(), generateNoteExcerpt(newContent, title), oldNote.getScrollY());
}
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues(7);
@@ -545,7 +560,7 @@ public class NotesDatabase extends AbstractNotesDatabase {
values.put(key_excerpt, newNote.getExcerpt());
values.put(key_scroll_y, newNote.getScrollY());
int rows = db.update(table_notes, values, key_id + " = ? AND (" + key_content + " != ? OR " + key_category + " != ?)", new String[]{String.valueOf(newNote.getId()), newNote.getContent(), newNote.getCategory()});
- removeEmptyCategory(accountId);
+ removeEmptyCategory(localAccount.getId());
// if data was changed, set new status and schedule sync (with callback); otherwise invoke callback directly.
if (rows > 0) {
notifyWidgets();
@@ -597,7 +612,7 @@ public class NotesDatabase extends AbstractNotesDatabase {
values.put(key_favorite, remoteNote.isFavorite());
values.put(key_category, getCategoryIdByTitle(localAccount.getId(), remoteNote.getCategory()));
values.put(key_etag, remoteNote.getEtag());
- values.put(key_excerpt, NoteUtil.generateNoteExcerpt(remoteNote.getContent()));
+ values.put(key_excerpt, generateNoteExcerpt(remoteNote.getContent(), remoteNote.getTitle()));
String whereClause;
String[] whereArgs;
if (forceUnchangedDBNoteState != null) {
@@ -869,9 +884,9 @@ public class NotesDatabase extends AbstractNotesDatabase {
Log.i(TAG, "Given API version is a valid JSON array but does not contain any valid API versions. Do not update database.");
}
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("API version does contain a non-valid version.");
+ throw new IllegalArgumentException("API version does contain a non-valid version: " + apiVersion);
} catch (JSONException e) {
- throw new IllegalArgumentException("API version must contain be a JSON array.");
+ throw new IllegalArgumentException("API version must contain be a JSON array: " + apiVersion);
}
} else {
Log.v(TAG, "Given API version is null. Do not update database");
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java
index 98ddc601..c5bf2c02 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java
@@ -15,10 +15,10 @@ public class Migration_9_10 {
*/
public Migration_9_10(@NonNull SQLiteDatabase db) {
db.execSQL("ALTER TABLE NOTES ADD COLUMN EXCERPT INTEGER NOT NULL DEFAULT ''");
- Cursor cursor = db.query("NOTES", new String[]{"ID", "CONTENT"}, null, null, null, null, null, null);
+ Cursor cursor = db.query("NOTES", new String[]{"ID", "CONTENT", "TITLE"}, null, null, null, null, null, null);
while (cursor.moveToNext()) {
ContentValues values = new ContentValues();
- values.put("EXCERPT", NoteUtil.generateNoteExcerpt(cursor.getString(1)));
+ values.put("EXCERPT", NoteUtil.generateNoteExcerpt(cursor.getString(1), cursor.getString(2)));
db.update("NOTES", values, "ID" + " = ? ", new String[]{cursor.getString(0)});
}
cursor.close();
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/NoteUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/NoteUtil.java
index 26127b44..e79635f2 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/util/NoteUtil.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/util/NoteUtil.java
@@ -2,6 +2,7 @@ package it.niedermann.owncloud.notes.util;
import android.content.Context;
import android.content.SharedPreferences;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -76,22 +77,34 @@ public class NoteUtil {
* @return truncated string
*/
@NonNull
- private static String truncateString(@NonNull String str, int len) {
+ private static String truncateString(@NonNull String str, @SuppressWarnings("SameParameterValue") int len) {
return str.substring(0, Math.min(len, str.length()));
}
/**
- * Generates an excerpt of a content String (reads second line which is not empty)
+ * Generates an excerpt of a content that does <em>not</em> match the given title
*
- * @param content String
+ * @param content {@link String}
+ * @param title {@link String} In case the content starts with the title, the excerpt should be generated starting from this point
* @return excerpt String
*/
@NonNull
- public static String generateNoteExcerpt(@NonNull String content) {
- if (content.contains("\n"))
- return truncateString(removeMarkDown(content.replaceFirst("^.*\n", "")), 200).replace("\n", EXCERPT_LINE_SEPARATOR);
- else
+ public static String generateNoteExcerpt(@NonNull String content, @Nullable String title) {
+ content = removeMarkDown(content.trim());
+ if(TextUtils.isEmpty(content)) {
+ return "";
+ }
+ if (!TextUtils.isEmpty(title)) {
+ final String trimmedTitle = removeMarkDown(title.trim());
+ if (content.startsWith(trimmedTitle)) {
+ content = content.substring(trimmedTitle.length());
+ }
+ }
+ if (content.contains("\n")) {
+ return truncateString(content.trim(), 200).replace("\n", EXCERPT_LINE_SEPARATOR);
+ } else {
return "";
+ }
}
@NonNull
diff --git a/app/src/main/res/drawable/ic_title_grey600_24dp.xml b/app/src/main/res/drawable/ic_title_grey600_24dp.xml
new file mode 100644
index 00000000..a4cec0b8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_title_grey600_24dp.xml
@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="24dp"
+ android:tint="#757575" android:viewportHeight="24.0"
+ android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M5,4v3h5.5v12h3V7H19V4z"/>
+</vector>
diff --git a/app/src/main/res/layout/dialog_edit_title.xml b/app/src/main/res/layout/dialog_edit_title.xml
new file mode 100644
index 00000000..805926bd
--- /dev/null
+++ b/app/src/main/res/layout/dialog_edit_title.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp">
+
+ <EditText
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/change_note_title"
+ android:importantForAutofill="no"
+ android:inputType="text" />
+</com.google.android.material.textfield.TextInputLayout> \ No newline at end of file
diff --git a/app/src/main/res/menu/menu_note_fragment.xml b/app/src/main/res/menu/menu_note_fragment.xml
index 83d82772..97cfad95 100644
--- a/app/src/main/res/menu/menu_note_fragment.xml
+++ b/app/src/main/res/menu/menu_note_fragment.xml
@@ -17,6 +17,12 @@
android:checkable="true"
app:showAsAction="ifRoom" />
<item
+ android:id="@+id/menu_title"
+ android:icon="@drawable/ic_title_grey600_24dp"
+ android:orderInCategory="100"
+ android:title="@string/menu_edit_title"
+ app:showAsAction="ifRoom" />
+ <item
android:id="@+id/menu_category"
android:icon="@drawable/ic_folder_white_24dp"
android:orderInCategory="100"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 91e560b3..ef9e47ff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -192,6 +192,8 @@
<string name="added_content">Added "%1$s"</string>
<string name="shared_text_empty">Shared text was empty</string>
<string name="append_to_note">Append to note</string>
+ <string name="change_note_title">Change note title</string>
+ <string name="menu_edit_title">Edit title</string>
<string name="settings_branding">Branding</string>
<string name="settings_gridview">Grid view 🆕</string>
<string name="simple_security">Security</string>
@@ -245,8 +247,8 @@
<string name="formatting_help_codefence_inline_escaped" translateable="false">\\`%1$s\\`</string>
<string name="formatting_help_codefence" translateable="false">```</string>
<string name="formatting_help_codefence_escaped" translatable="false">\\`\\`\\`</string>
- <string name="formatting_help_javascript_1" translatable="false">if (isAwesome){</string>
- <string name="formatting_help_javascript_2" translatable="false">return true</string>
+ <string name="formatting_help_javascript_1" translatable="false">int getRandom() {</string>
+ <string name="formatting_help_javascript_2" translatable="false">return 4;</string>
<string name="formatting_help_javascript_3" translatable="false">}</string>
<string name="formatting_help_codefence_javascript_escaped" translatable="false">\\`\\`\\`javascript</string>
<string name="formatting_help_codefence_javascript" translateable="false">```javascript</string>
@@ -302,4 +304,5 @@
<string name="formatting_help_unsupported_body_2">Tables</string>
<string name="formatting_help_unsupported_body_3">Images</string>
<string name="formatting_help_unsupported_body_4">If you are interested in contributing support for one of those features, get in contact with us via GitHub or E-Mail.</string>
+ <string name="simple_other">Other</string>
</resources>
diff --git a/app/src/test/java/android/text/TextUtils.java b/app/src/test/java/android/text/TextUtils.java
index 9bc7b5f1..d57433ae 100644
--- a/app/src/test/java/android/text/TextUtils.java
+++ b/app/src/test/java/android/text/TextUtils.java
@@ -1,5 +1,7 @@
package android.text;
+import androidx.annotation.Nullable;
+
import java.util.Iterator;
public class TextUtils {
@@ -26,4 +28,13 @@ public class TextUtils {
}
return sb.toString();
}
+
+ /**
+ * Returns true if the string is null or 0-length.
+ * @param str the string to be examined
+ * @return true if str is null or zero length
+ */
+ public static boolean isEmpty(@Nullable CharSequence str) {
+ return str == null || str.length() == 0;
+ }
}
diff --git a/app/src/test/java/it/niedermann/owncloud/notes/util/NoteUtilTest.java b/app/src/test/java/it/niedermann/owncloud/notes/util/NoteUtilTest.java
index 8f892311..80dd1898 100644
--- a/app/src/test/java/it/niedermann/owncloud/notes/util/NoteUtilTest.java
+++ b/app/src/test/java/it/niedermann/owncloud/notes/util/NoteUtilTest.java
@@ -63,9 +63,37 @@ public class NoteUtilTest extends TestCase {
}
public void testGenerateNoteExcerpt() {
- assertEquals("", NoteUtil.generateNoteExcerpt("Test"));
- assertEquals("Foo", NoteUtil.generateNoteExcerpt("Test\nFoo"));
- assertEquals("Foo Bar", NoteUtil.generateNoteExcerpt("Test\nFoo\nBar"));
- assertEquals("", NoteUtil.generateNoteExcerpt(""));
+ // title is different from content → return max. 200 characters starting with the first line which is not empty
+ assertEquals("", NoteUtil.generateNoteExcerpt("Test", "Title"));
+ assertEquals("Test Foo", NoteUtil.generateNoteExcerpt("Test\nFoo", "Title"));
+ assertEquals("Test Foo Bar", NoteUtil.generateNoteExcerpt("Test\nFoo\nBar", "Title"));
+ assertEquals("", NoteUtil.generateNoteExcerpt("", "Title"));
+
+ // content actually starts with title → return max. 200 characters starting with the first character after the title
+ assertEquals("", NoteUtil.generateNoteExcerpt("Title", "Title"));
+ assertEquals("Foo", NoteUtil.generateNoteExcerpt("Title\nFoo", "Title"));
+ assertEquals("Title Bar", NoteUtil.generateNoteExcerpt("Title\nTitle\nBar", "Title"));
+ assertEquals("", NoteUtil.generateNoteExcerpt("", "Title"));
+
+ // some empty lines between the actual contents → Should be ignored
+ assertEquals("", NoteUtil.generateNoteExcerpt("\nTitle", "Title"));
+ assertEquals("Foo", NoteUtil.generateNoteExcerpt("\n\n\n\nTitle\nFoo", "Title"));
+ assertEquals("Title Bar", NoteUtil.generateNoteExcerpt("\nTitle\n\n\nTitle\nBar", "\n\nTitle"));
+ assertEquals("", NoteUtil.generateNoteExcerpt("\n\n\n", "\nTitle"));
+
+ // content has markdown while titles markdown is already stripped
+ assertEquals("", NoteUtil.generateNoteExcerpt("# Title", "Title"));
+ assertEquals("Foo", NoteUtil.generateNoteExcerpt("Title\n- Foo", "Title"));
+ assertEquals("Title Bar", NoteUtil.generateNoteExcerpt("# Title\n- Title\n- Bar", "Title"));
+
+ // title has markdown while contents markdown is stripped
+ assertEquals("", NoteUtil.generateNoteExcerpt("Title", "# Title"));
+ assertEquals("Foo", NoteUtil.generateNoteExcerpt("Title\nFoo", "- Title"));
+ assertEquals("Title Bar", NoteUtil.generateNoteExcerpt("Title\nTitle\nBar", "- Title"));
+
+ // content and title have markdown
+ assertEquals("", NoteUtil.generateNoteExcerpt("# Title", "# Title"));
+ assertEquals("Foo", NoteUtil.generateNoteExcerpt("# Title\n- Foo", "- Title"));
+ assertEquals("Title Bar", NoteUtil.generateNoteExcerpt("- Title\nTitle\nBar", "- Title"));
}
} \ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/2016000.txt b/fastlane/metadata/android/en-US/changelogs/2016000.txt
new file mode 100644
index 00000000..02089166
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/2016000.txt
@@ -0,0 +1,3 @@
+- 🔧 Support APIv1
+ - Title will only be auto generated when creating a new note
+ - Ability to change title manually \ No newline at end of file