diff --git a/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Starred.java b/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Starred.java index 4fbdfc784..45cb763b7 100644 --- a/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Starred.java +++ b/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Starred.java @@ -117,6 +117,9 @@ public void addTag(Tag tag) { * Remove a tag from this item's tag list */ public void removeTag(Tag tag) { - this.tags.remove(tag); + if (tags.contains(tag)) { + this.tags.remove(tag); + } + } } diff --git a/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Tag.java b/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Tag.java index 643125c5b..75f79a0b0 100644 --- a/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Tag.java +++ b/opacclient/libopac/src/main/java/de/geeksfactory/opacclient/objects/Tag.java @@ -8,6 +8,7 @@ public class Tag { private int id; private String tagName; + private int starredItemRefId; @Override public String toString() { @@ -41,4 +42,18 @@ public String getTagName() { public void setTagName(String tagName) { this.tagName = tagName; } + + /** + * Get this tag's reference ID to the starred item it is attached to + */ + public int getStarredItemRefId() { + return starredItemRefId; + } + + /** + * Set this tag's reference ID to the starred item it is attached to + */ + public void setStarredItemRefId(int starredItemRefId) { + this.starredItemRefId = starredItemRefId; + } } diff --git a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/frontend/StarredFragment.java b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/frontend/StarredFragment.java index 32394b594..ab0d38031 100644 --- a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/frontend/StarredFragment.java +++ b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/frontend/StarredFragment.java @@ -25,13 +25,14 @@ import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; @@ -39,16 +40,20 @@ import android.support.v4.content.Loader; import android.support.v4.widget.SimpleCursorAdapter; import android.text.Html; -import android.text.InputType; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; @@ -68,7 +73,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import de.geeksfactory.opacclient.OpacClient; import de.geeksfactory.opacclient.R; @@ -105,7 +109,6 @@ public class StarredFragment extends Fragment implements private int activatedPosition = ListView.INVALID_POSITION; private TextView tvWelcome; private Starred sItem; - private String tagName = ""; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -486,26 +489,22 @@ private void setActivatedPosition(int position) { * @param tagName * @return updated tag list */ - private List addTag(Starred item, String tagName) { + private Tag addTag(Starred item, String tagName) { StarDataSource data = new StarDataSource(getActivity()); sItem = item; data.addTag(item, tagName); - item.addTag(data.getTagByTagName(tagName)); - return data.getAllTags(item); + Tag tagFromDatabase = data.getTagByTagName(tagName); + item.addTag(tagFromDatabase); + return tagFromDatabase; } /** * Removes tag from the database and the starred item. - * @param item - * @param tagName - * @return updated tag list + * @param tag */ - private List removeTag(Starred item, String tagName) { + private void removeTag(Tag tag) { StarDataSource data = new StarDataSource(getActivity()); - sItem = item; - data.removeTag(data.getTagByTagName(tagName)); - item.removeTag(data.getTagByTagName(tagName)); - return data.getAllTags(item); + data.removeTag(data.getTagByTagName(tag.getTagName())); } /** @@ -518,6 +517,16 @@ private List getTags(Starred item) { return data.getAllTags(item); } + private List getTagNames(Starred item) { + StarDataSource data = new StarDataSource(getActivity()); + return data.getAllTagNames(item); + } + +// private List getAllTagNamesExceptThisItem(Starred item) { +// StarDataSource data = new StarDataSource(getActivity()); +// return data.getAllTagNamesExceptThisItem(item); +// } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -559,108 +568,58 @@ public void bindView(View view, Context context, Cursor cursor) { ivType.setImageBitmap(null); } - TextView tagView = (TextView) view.findViewById(R.id.tvTag); - List currentTags = getTags(item); + ImageView ivTagMenu = (ImageView) view.findViewById(R.id.ivTagMenu); + ivTagMenu.setFocusableInTouchMode(false); + ivTagMenu.setFocusable(false); + ivTagMenu.setTag(item); + List currentTagList = getTags(item); + List currentTagListNames = getTagNames(item); - ImageView ivRemoveTag = (ImageView) view.findViewById(R.id.ivRemoveTag); - ivRemoveTag.setFocusableInTouchMode(false); - ivRemoveTag.setFocusable(false); - ivRemoveTag.setTag(item); - ivRemoveTag.setOnClickListener(arg0 -> { + ivTagMenu.setOnClickListener(arg0 -> { // Create alert dialog box for removing of tags AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle("Choose tags to remove from " + item.getTitle()); - - // add a checkbox list from the current tags list - String[] tagsList = new String[currentTags.size()]; - for (int i = 0; i < currentTags.size(); i++) { - tagsList[i] = currentTags.get(i).getTagName(); - } - boolean[] checkedItems = new boolean[tagsList.length]; - - builder.setMultiChoiceItems(tagsList, null, (dialog, which, isChecked) -> { - // user checked or unchecked a box - checkedItems[which] = true; - }); - - // Set up the buttons - builder.setPositiveButton("OK", (dialog, which) -> { - // Iterate through each tag name in the tag list and remove those that have - // been checked - for (int i = 0; i < tagsList.length; i++) { - if (checkedItems[i]) { - List tags = removeTag(item, tagsList[i]); - // Update the tags text view - if (tags.size() > 0) { - StringBuilder tagsBuilder = new StringBuilder(); - for (Tag t : tags) { - tagsBuilder.append(t.getTagName()).append(" | "); - } - System.out.println(tagsBuilder.toString()); - tagView.setText(Html.fromHtml(tagsBuilder.toString())); - } else { - tagView.setText(""); - } - } + builder.setTitle(item.getTitle() + " tags list"); + + // set the custom layout + final View customLayout = getLayoutInflater().inflate(R.layout.tag_menu, null); + builder.setView(customLayout); + + // create list view + ListView tagsListView = (ListView) customLayout.findViewById(R.id.lvTags); + ArrayAdapter tagAdapter = new TagListAdapter(context, currentTagList); + tagsListView.setAdapter(tagAdapter); + + EditText editText = (EditText) customLayout.findViewById(R.id.autoCompleteTextView); +// ArrayAdapter allTagNamesAdapter = new ArrayAdapter<>(context, android.R.layout.select_dialog_item, getAllTagNamesExceptThisItem(item)); +// autocomplete.setThreshold(2); +// autocomplete.setAdapter(allTagNamesAdapter); + + Button addTagButton = (Button) customLayout.findViewById(R.id.addTag); + addTagButton.setOnClickListener(view1 -> { + String tagName = editText.getText().toString(); + // prevent an empty tag or an exisiting tag from being added + if (!tagName.equals("") && !currentTagListNames.contains(tagName)) { + Tag tagToAdd = addTag(item, tagName); + currentTagList.add(tagToAdd); + tagAdapter.notifyDataSetChanged(); + currentTagListNames.add(tagName); + editText.setText(""); + Toast.makeText(context, "Added tag \"" + tagName + "\" to " + item.getTitle(), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(context, "Please enter a nonempty tag name that already isn't on the list", Toast.LENGTH_LONG).show(); } + // hide keyboard once done so as to see toast messages + editText.onEditorAction(EditorInfo.IME_ACTION_DONE); + }); - builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + builder.setNegativeButton("Back", (dialog, which) -> dialog.cancel()); AlertDialog dialog = builder.create(); dialog.show(); }); - ImageView ivAddTag = (ImageView) view.findViewById(R.id.ivAddTag); - ivAddTag.setFocusableInTouchMode(false); - ivAddTag.setFocusable(false); - ivAddTag.setTag(item); - - if (currentTags.size() > 0) { - StringBuilder tagsBuilder = new StringBuilder(); - for (Tag t : currentTags) { - tagsBuilder.append(t.getTagName()).append(" | "); - } - tagView.setText(Html.fromHtml(tagsBuilder.toString())); - } else { - tagView.setText(""); - } - - ivAddTag.setOnClickListener(arg0 -> { - // Create alert dialog box for entering of tag - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle("Add tag to " + item.getTitle()); - - // Set up the input - final EditText input = new EditText(context); - - // Specify the type of input expected - input.setInputType(InputType.TYPE_CLASS_TEXT); - builder.setView(input); - - // Set up the buttons - builder.setPositiveButton("OK", - (dialog, which) -> { - tagName = input.getText().toString(); - List tags = addTag(item, tagName); - // Create a string from updated tag list and update the view - if (tags.size() > 0) { - StringBuilder tagsBuilder = new StringBuilder(); - for (Tag t : tags) { - tagsBuilder.append(t.getTagName()).append(" | "); - } - tagView.setText(Html.fromHtml(tagsBuilder.toString())); - } else { - tagView.setText(""); - } - }); - - builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); - - builder.show(); - - }); ImageView ivDelete = (ImageView) view.findViewById(R.id.ivDelete); ivDelete.setFocusableInTouchMode(false); @@ -676,4 +635,52 @@ public void onClick(View arg0) { }); } } + + private class TagListAdapter extends ArrayAdapter { + + public TagListAdapter(Context context, List tagList) { + super(getActivity(), R.layout.listitem_tag, tagList); + } + + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + View itemView = convertView; + if (itemView == null) { + itemView = getLayoutInflater().inflate(R.layout.listitem_tag, parent, false); + } + + Tag currentTag = getItem(position); + + TextView tv = (TextView) itemView.findViewById(R.id.tvTitle); + tv.setText(currentTag.getTagName()); + + ImageView ivDelete = (ImageView) itemView.findViewById(R.id.ivDelete); + ivDelete.setFocusableInTouchMode(false); + ivDelete.setFocusable(false); + ivDelete.setTag(currentTag); + ivDelete.setOnClickListener(arg0 -> { + Tag item = (Tag) arg0.getTag(); + removeTag(item); + Toast.makeText(getContext(), "Removed tag \"" + item.getTagName() + "\"", Toast.LENGTH_SHORT).show(); + }); + + return itemView; + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + } + + @Override + public void add(Tag object) { + super.add(object); + } + + public void removeTag(Tag object) { + super.remove(object); + StarredFragment.this.removeTag(object); + } + } } diff --git a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDataSource.java b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDataSource.java index f0683457b..167af4e59 100644 --- a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDataSource.java +++ b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDataSource.java @@ -36,7 +36,6 @@ import de.geeksfactory.opacclient.objects.SearchResult; import de.geeksfactory.opacclient.objects.Starred; import de.geeksfactory.opacclient.objects.Tag; -import de.geeksfactory.opacclient.searchfields.SearchField; public class StarDataSource { @@ -216,13 +215,36 @@ public static Tag cursorToTag(Cursor cursor) { return tag; } + public boolean hasTagNameInTagTable(String tagName) { + if (tagName == null) { + return false; + } + String[] selA = {tagName}; + Cursor cursor = database.query(StarDatabase.TAGS_TABLE, null, "tag = ?", + selA, null, null, null); + int c = cursor.getCount(); + cursor.close(); + return (c > 0); + } + + public boolean hasTagIdInStarTagTable(long tagId) { + String[] selA = {"" + tagId}; + Cursor cursor = database.query(StarDatabase.STAR_TAGS_TABLE, null, "tag = ?", + selA, null, null, null); + int c = cursor.getCount(); + cursor.close(); + return (c > 0); + } + /** * Add given tag to the tags database and the starred-tag database */ public long addTag(Starred item, String tagName) { ContentValues values = new ContentValues(); values.put("tag", tagName); - database.insert(StarDatabase.TAGS_TABLE, null, values); + if (!hasTagNameInTagTable(tagName)) { + database.insert(StarDatabase.TAGS_TABLE, null, values); + } values = new ContentValues(); values.put("tag", getTagByTagName(tagName).getId()); @@ -241,10 +263,10 @@ public long addTag(Starred item, String tagName) { public void removeTag(Tag tag) { String[] selA = {"" + tag.getId()}; database.delete(StarDatabase.STAR_TAGS_TABLE, "tag=?", selA); - - selA = new String[]{"" + tag.getTagName()}; - database.delete(StarDatabase.TAGS_TABLE, "tag=?", selA); - + if (!hasTagIdInStarTagTable(tag.getId())) { + selA = new String[]{"" + tag.getTagName()}; + database.delete(StarDatabase.TAGS_TABLE, "tag=?", selA); + } } public Tag getTagByTagName(String tagName) { @@ -299,4 +321,44 @@ public List getAllTags(Starred item) { cursor.close(); return tags; } + + /** + * Get all tag names that belong to this Starred item + */ + public List getAllTagNames(Starred item) { + List tags = new ArrayList<>(); + String[] selA = {Integer.toString(item.getId())}; + Cursor cursor = database.query(StarDatabase.STAR_TAGS_TABLE, StarDatabase.STAR_TAGS_COLUMNS, "item = ?", selA, null, null, null); + + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + int tagId = cursorToStarAndTagId(cursor); + tags.add(getTagById(tagId).getTagName()); + cursor.moveToNext(); + } + // Make sure to close the cursor + cursor.close(); + return tags; + } + +// /** +// * Get all tags that do not belong to this Starred item +// */ +// public List getAllTagNamesExceptThisItem(Starred item) { +// List listOfTagNames = new ArrayList<>(); +// Cursor cursor = database.rawQuery("select * from " + StarDatabase.STAR_TAGS_TABLE, null); +// +// cursor.moveToFirst(); +// while (!cursor.isAfterLast()) { +// if (cursor.getInt(1) != item.getId()) { +// int tagId = cursorToStarAndTagId(cursor); +// listOfTagNames.add(getTagById(tagId).getTagName()); +// cursor.moveToNext(); +// } +// +// } +// // Make sure to close the cursor +// cursor.close(); +// return listOfTagNames; +// } } diff --git a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDatabase.java b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDatabase.java index 3ea971583..22fc2d2bb 100644 --- a/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDatabase.java +++ b/opacclient/opacapp/src/main/java/de/geeksfactory/opacclient/storage/StarDatabase.java @@ -36,7 +36,7 @@ public class StarDatabase extends SQLiteOpenHelper { + " ( id integer primary key autoincrement," + " medianr text," + " bib text," + " title text," + " mediatype text" + ");"; public static final String TAGS_DATABASE_CREATE = "create table " + TAGS_TABLE - + " ( id integer primary key autoincrement," +" tag text);"; + + " ( id integer primary key autoincrement," +" tag text unique);"; public static final String STAR_TAGS_DATABASE_CREATE = "create table " + STAR_TAGS_TABLE + " ( tag integer references tags (id)," + " item integer references starred (id)," + " primary key (tag, item)" diff --git a/opacclient/opacapp/src/main/res/drawable/ic_local_offer_black_24dp.xml b/opacclient/opacapp/src/main/res/drawable/ic_local_offer_black_24dp.xml new file mode 100644 index 000000000..8b19fe422 --- /dev/null +++ b/opacclient/opacapp/src/main/res/drawable/ic_local_offer_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/opacclient/opacapp/src/main/res/layout/listitem_starred.xml b/opacclient/opacapp/src/main/res/layout/listitem_starred.xml index a1a4dfba3..a4854ac35 100644 --- a/opacclient/opacapp/src/main/res/layout/listitem_starred.xml +++ b/opacclient/opacapp/src/main/res/layout/listitem_starred.xml @@ -36,16 +36,8 @@ android:padding="5dp" android:textAppearance="?android:attr/textAppearanceMedium"/> - - + android:contentDescription="@string/tag_menu" + app:srcCompat="@drawable/ic_local_offer_black_24dp" + tools:src="@drawable/ic_local_offer_black_24dp"/> - \ No newline at end of file diff --git a/opacclient/opacapp/src/main/res/layout/listitem_tag.xml b/opacclient/opacapp/src/main/res/layout/listitem_tag.xml new file mode 100644 index 000000000..ca32e27d8 --- /dev/null +++ b/opacclient/opacapp/src/main/res/layout/listitem_tag.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/opacclient/opacapp/src/main/res/layout/tag_menu.xml b/opacclient/opacapp/src/main/res/layout/tag_menu.xml new file mode 100644 index 000000000..7b383208c --- /dev/null +++ b/opacclient/opacapp/src/main/res/layout/tag_menu.xml @@ -0,0 +1,34 @@ + + + + + + + + +