From 159ddaea80646d2fa18f040e760ea7c5a8031376 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 9 Feb 2023 15:02:31 +0100 Subject: [PATCH] manage tags --- .../mastodon/activities/ComposeActivity.java | 3 + .../mastodon/activities/TagCacheActivity.java | 98 ++++++++++++++++ .../mastodon/client/entities/api/Tag.java | 11 ++ .../client/entities/app/CamelTag.java | 16 ++- .../android/mastodon/jobs/ComposeWorker.java | 1 + .../mastodon/ui/drawer/ComposeAdapter.java | 14 ++- .../mastodon/ui/drawer/TagsEditAdapter.java | 107 ++++++++++++++++++ .../mastodon/layout/activity_camel_tag.xml | 50 ++++++++ .../mastodon/layout/drawer_status_compose.xml | 2 +- .../mastodon/layout/drawer_tag_edit.xml | 41 +++++++ .../res/layouts/mastodon/values/strings.xml | 1 + .../res/menus/mastodon/menu/menu_compose.xml | 6 + app/src/main/res/values/strings.xml | 5 + 13 files changed, 352 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/mastodon/activities/TagCacheActivity.java create mode 100644 app/src/main/java/app/fedilab/android/mastodon/ui/drawer/TagsEditAdapter.java create mode 100644 app/src/main/res/layouts/mastodon/layout/activity_camel_tag.xml create mode 100644 app/src/main/res/layouts/mastodon/layout/drawer_tag_edit.xml diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java index cc57e2645..40f016d34 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java @@ -399,6 +399,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } else { Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show(); } + } else if (item.getItemId() == R.id.action_tags) { + TagCacheActivity tagCacheActivity = new TagCacheActivity(); + tagCacheActivity.show(getSupportFragmentManager(), null); } return true; } diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/TagCacheActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/TagCacheActivity.java new file mode 100644 index 000000000..9f48e0955 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/TagCacheActivity.java @@ -0,0 +1,98 @@ +package app.fedilab.android.mastodon.activities; +/* Copyright 2023 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program 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. + * + * Fedilab 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 Fedilab; if not, + * see . */ + +import android.app.Dialog; +import android.os.Bundle; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.databinding.ActivityCamelTagBinding; +import app.fedilab.android.mastodon.client.entities.app.CamelTag; +import app.fedilab.android.mastodon.exception.DBException; +import app.fedilab.android.mastodon.ui.drawer.TagsEditAdapter; +import es.dmoral.toasty.Toasty; + +public class TagCacheActivity extends DialogFragment { + + private List tags; + private TagsEditAdapter tagsEditAdapter; + + private ActivityCamelTagBinding binding; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + binding = ActivityCamelTagBinding.inflate(getLayoutInflater()); + + MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext()); + materialAlertDialogBuilder.setView(binding.getRoot()); + + Dialog dialog = materialAlertDialogBuilder.create(); + tags = new ArrayList<>(); + + binding.saveTag.setOnClickListener(v -> { + if (binding.tagAdd.getText() != null && (binding.tagAdd.getText().toString().trim().replaceAll("#", "").length() > 0)) { + String tagToInsert = binding.tagAdd.getText().toString().trim().replaceAll("#", ""); + try { + boolean isPresent = new CamelTag(requireActivity()).tagExists(tagToInsert); + if (isPresent) + Toasty.warning(requireActivity(), getString(R.string.tags_already_stored), Toast.LENGTH_LONG).show(); + else { + new CamelTag(requireActivity()).insert(tagToInsert); + int position = tags.size(); + tags.add(tagToInsert); + Toasty.success(requireActivity(), getString(R.string.tags_stored), Toast.LENGTH_LONG).show(); + binding.tagAdd.setText(""); + tagsEditAdapter.notifyItemInserted(position); + } + } catch (DBException e) { + throw new RuntimeException(e); + } + + } + }); + dialog.setTitle(R.string.manage_tags); + + new Thread(() -> { + List tagsTemp = new CamelTag(requireActivity()).getAll(); + requireActivity().runOnUiThread(() -> { + if (tagsTemp != null) + tags = tagsTemp; + if (tags != null) { + tagsEditAdapter = new TagsEditAdapter(tags); + binding.tagList.setAdapter(tagsEditAdapter); + LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); + binding.tagList.setLayoutManager(mLayoutManager); + } + }); + }).start(); + + binding.close.setOnClickListener(v -> requireDialog().dismiss()); + return dialog; + } + + +} diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java index 4bc053f97..a1b9b1b3d 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java @@ -14,6 +14,8 @@ package app.fedilab.android.mastodon.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import androidx.annotation.Nullable; + import com.google.gson.annotations.SerializedName; import java.io.Serializable; @@ -42,4 +44,13 @@ public class Tag implements Serializable { } return weight; } + + @Override + public boolean equals(@Nullable Object obj) { + boolean same = false; + if (obj instanceof Tag) { + same = this.name.equals(((Tag) obj).name); + } + return same; + } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CamelTag.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CamelTag.java index f55e3b619..e535cfc0d 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CamelTag.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/CamelTag.java @@ -73,7 +73,7 @@ public class CamelTag { return cursorToTag(c); } - private boolean tagExists(String name) throws DBException { + public boolean tagExists(String name) throws DBException { Cursor c = db.query(Sqlite.TABLE_CACHE_TAGS, null, Sqlite.COL_TAG + " = \"" + name + "\"", null, null, null, null, null); boolean isPresent = (c != null && c.getCount() > 0); assert c != null; @@ -98,6 +98,20 @@ public class CamelTag { } } + /** + * Returns all tags in db + * + * @return string tags List + */ + public List getAll() { + try { + Cursor c = db.query(Sqlite.TABLE_CACHE_TAGS, null, null, null, null, null, Sqlite.COL_TAG + " ASC", null); + return cursorToTag(c); + } catch (Exception e) { + return null; + } + } + private List cursorToTag(Cursor c) { //No element found if (c.getCount() == 0) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java index 357565e2a..3c0ab65b8 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java +++ b/app/src/main/java/app/fedilab/android/mastodon/jobs/ComposeWorker.java @@ -234,6 +234,7 @@ public class ComposeWorker extends Worker { if (statuses.get(i).local_only) { statuses.get(i).text += " \uD83D\uDC41"; } + //Record tags if (statuses.get(i).text != null && statuses.get(i).text.length() > 0) { Matcher matcher = Helper.hashtagPattern.matcher(statuses.get(i).text); while (matcher.find()) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java index 6c1c2279d..009c1f2f6 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java @@ -119,6 +119,7 @@ import app.fedilab.android.mastodon.client.entities.api.Poll; import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.app.BaseAccount; +import app.fedilab.android.mastodon.client.entities.app.CamelTag; import app.fedilab.android.mastodon.client.entities.app.Languages; import app.fedilab.android.mastodon.client.entities.app.Quotes; import app.fedilab.android.mastodon.client.entities.app.StatusDraft; @@ -818,13 +819,24 @@ public class ComposeAdapter extends RecyclerView.Adapter camelTags = new CamelTag(context).getBy(searchGroup); searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, null, "hashtags", false, true, false, 0, null, null, 10).observe((LifecycleOwner) context, results -> { - if (results == null) { + if (results == null || results.hashtags == null || results.hashtags.size() == 0) { return; } + if (camelTags != null && camelTags.size() > 0) { + for (String camelTag : camelTags) { + Tag tag = new Tag(); + tag.name = camelTag; + if (!results.hashtags.contains(tag)) { + results.hashtags.add(0, tag); + } + } + } + int currentCursorPosition = holder.binding.content.getSelectionStart(); TagsSearchAdapter tagsSearchAdapter = new TagsSearchAdapter(context, results.hashtags); holder.binding.content.setThreshold(1); diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/TagsEditAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/TagsEditAdapter.java new file mode 100644 index 000000000..5bbf2d39e --- /dev/null +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/TagsEditAdapter.java @@ -0,0 +1,107 @@ +package app.fedilab.android.mastodon.ui.drawer; +/* Copyright 2023 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program 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. + * + * Fedilab 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 Fedilab; if not, + * see . */ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.databinding.DrawerTagEditBinding; +import app.fedilab.android.mastodon.client.entities.app.CamelTag; +import app.fedilab.android.mastodon.exception.DBException; +import es.dmoral.toasty.Toasty; + +public class TagsEditAdapter extends RecyclerView.Adapter { + + private final List tags; + private final TagsEditAdapter tagsEditAdapter; + private Context context; + + public TagsEditAdapter(List tags) { + this.tags = tags; + tagsEditAdapter = this; + } + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { + context = parent.getContext(); + DrawerTagEditBinding itemBinding = DrawerTagEditBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new TagCaheViewHolder(itemBinding); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { + final String[] tag = {tags.get(viewHolder.getLayoutPosition())}; + TagCaheViewHolder holder = (TagCaheViewHolder) viewHolder; + holder.binding.tagName.setText(String.format("#%s", tag[0])); + holder.binding.saveTag.setOnClickListener(v -> { + if (holder.binding.tagName.getText() != null && holder.binding.tagName.getText().toString().trim().replaceAll("#", "").length() > 0) { + String tagToInsert = holder.binding.tagName.getText().toString().trim().replaceAll("#", ""); + try { + boolean isPresent = new CamelTag(context).tagExists(tagToInsert); + if (isPresent) + Toasty.warning(context, context.getString(R.string.tags_already_stored), Toast.LENGTH_LONG).show(); + else { + new CamelTag(context).update(tag[0], tagToInsert); + Toasty.success(context, context.getString(R.string.tags_renamed), Toast.LENGTH_LONG).show(); + } + } catch (DBException e) { + throw new RuntimeException(e); + } + + } + }); + + holder.binding.deleteTag.setOnClickListener(v -> { + holder.binding.tagName.clearFocus(); + new CamelTag(context).removeTag(tag[0]); + tags.remove(tag[0]); + tagsEditAdapter.notifyItemRemoved(viewHolder.getLayoutPosition()); + Toasty.success(context, context.getString(R.string.tags_deleted), Toast.LENGTH_LONG).show(); + }); + + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return tags.size(); + } + + + static class TagCaheViewHolder extends RecyclerView.ViewHolder { + DrawerTagEditBinding binding; + + public TagCaheViewHolder(@NonNull DrawerTagEditBinding drawerTagEditBinding) { + super(drawerTagEditBinding.getRoot()); + binding = drawerTagEditBinding; + } + } +} + + diff --git a/app/src/main/res/layouts/mastodon/layout/activity_camel_tag.xml b/app/src/main/res/layouts/mastodon/layout/activity_camel_tag.xml new file mode 100644 index 000000000..43a1d5aa8 --- /dev/null +++ b/app/src/main/res/layouts/mastodon/layout/activity_camel_tag.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml b/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml index 0ad55de14..1c54a62f6 100644 --- a/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml +++ b/app/src/main/res/layouts/mastodon/layout/drawer_status_compose.xml @@ -77,7 +77,7 @@ android:focusable="true" android:gravity="top|start" android:inputType="textMultiLine|textCapSentences" - android:minLines="8" + android:minLines="10" app:layout_constraintEnd_toStartOf="@id/button_emoji" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/content_spoiler" /> diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_tag_edit.xml b/app/src/main/res/layouts/mastodon/layout/drawer_tag_edit.xml new file mode 100644 index 000000000..429180caa --- /dev/null +++ b/app/src/main/res/layouts/mastodon/layout/drawer_tag_edit.xml @@ -0,0 +1,41 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/mastodon/values/strings.xml b/app/src/main/res/layouts/mastodon/values/strings.xml index 0d2c4cc40..78554f9eb 100644 --- a/app/src/main/res/layouts/mastodon/values/strings.xml +++ b/app/src/main/res/layouts/mastodon/values/strings.xml @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/app/src/main/res/menus/mastodon/menu/menu_compose.xml b/app/src/main/res/menus/mastodon/menu/menu_compose.xml index 8fc21461c..74ed80b4d 100644 --- a/app/src/main/res/menus/mastodon/menu/menu_compose.xml +++ b/app/src/main/res/menus/mastodon/menu/menu_compose.xml @@ -21,4 +21,10 @@ android:icon="@drawable/ic_baseline_schedule_send_24" android:title="@string/schedule" app:showAsAction="never" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3dd4479b4..939c34516 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2256,4 +2256,9 @@ Number of media Number of replies Update date + + The tag has been changed! + The tag has been removed! + he tag has been stored! + Manage tags \ No newline at end of file