diff options
Diffstat (limited to 'app/src/main')
13 files changed, 219 insertions, 33 deletions
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/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 40df4efa..b5beeecf 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 @@ -58,6 +58,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. @@ -94,7 +95,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); @@ -121,7 +122,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()); @@ -495,22 +496,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); @@ -522,7 +537,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(); @@ -574,7 +589,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) { @@ -846,9 +861,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 812c2756..d754524c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -191,6 +191,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> |