From a8152e272d59d0f77c8ba31c8676cecaab5a0a64 Mon Sep 17 00:00:00 2001 From: stom79 Date: Sat, 1 Dec 2018 15:32:07 +0100 Subject: [PATCH] Issue #597 - Improve the way to manage tags --- app/src/main/AndroidManifest.xml | 5 +- .../mastodon/activities/BaseMainActivity.java | 3 +- .../mastodon/activities/TagCacheActivity.java | 128 ++++++++++++++++++ .../mastodon/activities/TootActivity.java | 5 + .../mastodon/drawers/TagsEditAdapter.java | 120 ++++++++++++++++ .../services/LiveNotificationService.java | 87 ++++++------ .../etalab/mastodon/sqlite/TagsCacheDAO.java | 30 ++++ app/src/main/res/drawable-anydpi/ic_save.xml | 9 ++ .../main/res/layout/activity_tag_cache.xml | 61 +++++++++ app/src/main/res/layout/drawer_tag_edit.xml | 53 ++++++++ app/src/main/res/menu/main_toot.xml | 5 + app/src/main/res/values/strings.xml | 5 + 12 files changed, 463 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/activities/TagCacheActivity.java create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/drawers/TagsEditAdapter.java create mode 100644 app/src/main/res/drawable-anydpi/ic_save.xml create mode 100644 app/src/main/res/layout/activity_tag_cache.xml create mode 100644 app/src/main/res/layout/drawer_tag_edit.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5f97632e6..f34e05368 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -221,7 +221,10 @@ android:windowSoftInputMode="adjustPan" android:theme="@style/Base.V7.Theme.AppCompat.Dialog" android:excludeFromRecents="true"/> - + . */ +package fr.gouv.etalab.mastodon.activities; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; + +import es.dmoral.toasty.Toasty; +import fr.gouv.etalab.mastodon.R; +import fr.gouv.etalab.mastodon.drawers.TagsEditAdapter; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import fr.gouv.etalab.mastodon.sqlite.TagsCacheDAO; + + +/** + * Created by Thomas on 01/12/2018. + * Tag cache activity class + */ + +public class TagCacheActivity extends BaseActivity { + + private RecyclerView tag_list; + private List tags; + private TagsEditAdapter tagsEditAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + requestWindowFeature(Window.FEATURE_NO_TITLE); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + switch (theme){ + case Helper.THEME_LIGHT: + setTheme(R.style.AppTheme_NoActionBar); + getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(TagCacheActivity.this, R.color.mastodonC3__)); + break; + case Helper.THEME_DARK: + setTheme(R.style.AppThemeDark_NoActionBar); + getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(TagCacheActivity.this, R.color.mastodonC1)); + break; + case Helper.THEME_BLACK: + setTheme(R.style.AppThemeBlack_NoActionBar); + getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(TagCacheActivity.this, R.color.black_3)); + break; + default: + setTheme(R.style.AppThemeDark_NoActionBar); + getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(TagCacheActivity.this, R.color.mastodonC1)); + } + getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + tags = new ArrayList<>(); + setContentView(R.layout.activity_tag_cache); + + tag_list = findViewById(R.id.tag_list); + SQLiteDatabase db = Sqlite.getInstance(TagCacheActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + EditText tag_add = findViewById(R.id.tag_add); + ImageButton save_tag = findViewById(R.id.save_tag); + + save_tag.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( tag_add.getText() != null && tag_add.getText().toString().trim().replaceAll("\\#","").length() > 0) { + String tagToInsert = tag_add.getText().toString().trim().replaceAll("\\#",""); + boolean isPresent = new TagsCacheDAO(TagCacheActivity.this, db).isPresent(tagToInsert); + if( isPresent) + Toasty.warning(TagCacheActivity.this, getString(R.string.tags_already_stored), Toast.LENGTH_LONG).show(); + else { + new TagsCacheDAO(TagCacheActivity.this, db).insert(tagToInsert); + int position = tags.size(); + tags.add(tagToInsert); + Toasty.success(TagCacheActivity.this, getString(R.string.tags_stored), Toast.LENGTH_LONG).show(); + tag_add.setText(""); + tagsEditAdapter.notifyItemInserted(position); + } + } + } + }); + + setTitle(R.string.manage_tags); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + + List tagsTemp = new TagsCacheDAO(TagCacheActivity.this, db).getAll(); + if( tagsTemp != null) + tags = tagsTemp; + if( tags != null){ + tagsEditAdapter = new TagsEditAdapter(TagCacheActivity.this, tags); + tag_list.setAdapter(tagsEditAdapter); + LinearLayoutManager mLayoutManager = new LinearLayoutManager(TagCacheActivity.this); + tag_list.setLayoutManager(mLayoutManager); + } + } + }); + + } +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java index 8a40d8524..79b10e5db 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java @@ -1126,6 +1126,11 @@ public class TootActivity extends BaseActivity implements OnRetrieveSearcAccount case R.id.action_store: storeToot(true, true); return true; + case R.id.action_tags: + Intent intentTags = new Intent(TootActivity.this, TagCacheActivity.class); + intentTags.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + startActivity(intentTags); + return true; case R.id.action_restore: try{ final List drafts = new StatusStoredDAO(TootActivity.this, db).getAllDrafts(); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/TagsEditAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/TagsEditAdapter.java new file mode 100644 index 000000000..5f8111d74 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/TagsEditAdapter.java @@ -0,0 +1,120 @@ +package fr.gouv.etalab.mastodon.drawers; +/* Copyright 2018 Thomas Schneider + * + * This file is a part of Mastalab + * + * 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. + * + * Mastalab 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 Mastalab; if not, + * see . */ + + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.List; + +import es.dmoral.toasty.Toasty; +import fr.gouv.etalab.mastodon.R; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import fr.gouv.etalab.mastodon.sqlite.TagsCacheDAO; + + +/** + * Created by Thomas on 01/12/2018. + * Adapter for tags when editing + */ +public class TagsEditAdapter extends RecyclerView.Adapter { + + private Context context; + private List tags; + private LayoutInflater layoutInflater; + private TagsEditAdapter tagsEditAdapter; + + public TagsEditAdapter(Context context, List tags){ + this.tags = tags; + this.layoutInflater = LayoutInflater.from(context); + this.context = context; + tagsEditAdapter = this; + } + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { + return new ViewHolder(layoutInflater.inflate(R.layout.drawer_tag_edit, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { + final String[] tag = {tags.get(viewHolder.getAdapterPosition())}; + ViewHolder holder = (ViewHolder) viewHolder; + holder.tag_name.setText(String.format("#%s", tag[0])); + SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + holder.save_tag.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( holder.tag_name.getText() != null && holder.tag_name.getText().toString().trim().replaceAll("\\#","").length() > 0) { + String tagToInsert = holder.tag_name.getText().toString().trim().replaceAll("\\#",""); + boolean isPresent = new TagsCacheDAO(context, db).isPresent(tagToInsert); + if( isPresent) + Toasty.warning(context, context.getString(R.string.tags_already_stored), Toast.LENGTH_LONG).show(); + else { + new TagsCacheDAO(context, db).update(tag[0], tagToInsert); + Toasty.success(context, context.getString(R.string.tags_renamed), Toast.LENGTH_LONG).show(); + } + } + } + }); + + holder.delete_tag.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new TagsCacheDAO(context, db).removeTag(tag[0]); + tags.remove(tag[0]); + tagsEditAdapter.notifyItemRemoved(viewHolder.getAdapterPosition()); + 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(); + } + + + class ViewHolder extends RecyclerView.ViewHolder{ + TextView tag_name; + ImageButton save_tag, delete_tag; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + tag_name = itemView.findViewById(R.id.tag_name); + delete_tag = itemView.findViewById(R.id.delete_tag); + save_tag = itemView.findViewById(R.id.save_tag); + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java index afdc9b1b2..5af81d446 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/LiveNotificationService.java @@ -261,12 +261,9 @@ public class LiveNotificationService extends Service implements NetworkStateRece event = Helper.EventStreaming.NOTIFICATION; notification = API.parseNotificationResponse(getApplicationContext(), new JSONObject(response.get("payload").toString())); b.putParcelable("data", notification); - - boolean liveNotifications = sharedpreferences.getBoolean(Helper.SET_LIVE_NOTIFICATIONS, true); boolean canNotify = Helper.canNotify(getApplicationContext()); boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true); - String targeted_account = null; Helper.NotifType notifType = Helper.NotifType.MENTION; boolean activityRunning = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("isMainActivityRunning", false); @@ -336,53 +333,51 @@ public class LiveNotificationService extends Service implements NetworkStateRece } long notif_id = Long.parseLong(account.getId()); final int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); - if (notification.getAccount().getAvatar() != null) { - final String finalTitle = title; - Handler mainHandler = new Handler(Looper.getMainLooper()); - Helper.NotifType finalNotifType = notifType; - Runnable myRunnable = new Runnable() { - @Override - public void run() { - if (finalTitle != null) { - Glide.with(getApplicationContext()) - .asBitmap() - .load(notification.getAccount().getAvatar()) - .listener(new RequestListener() { - @Override - public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { - return false; - } + final String finalTitle = title; + Handler mainHandler = new Handler(Looper.getMainLooper()); + Helper.NotifType finalNotifType = notifType; + Runnable myRunnable = new Runnable() { + @Override + public void run() { + if (finalTitle != null) { + Glide.with(getApplicationContext()) + .asBitmap() + .load(notification.getAccount().getAvatar()) + .listener(new RequestListener() { + @Override + public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + return false; + } - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - notify_user(getApplicationContext(), intent, notificationId, BitmapFactory.decodeResource(getResources(), - R.drawable.mastodonlogo), finalNotifType, finalTitle, "@" + account.getAcct() + "@" + account.getInstance()); - String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null); - if (lastNotif == null || Long.parseLong(notification.getId()) > Long.parseLong(lastNotif)) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId()); - editor.apply(); - } - return false; + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + notify_user(getApplicationContext(), intent, notificationId, BitmapFactory.decodeResource(getResources(), + R.drawable.mastodonlogo), finalNotifType, finalTitle, "@" + account.getAcct() + "@" + account.getInstance()); + String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null); + if (lastNotif == null || Long.parseLong(notification.getId()) > Long.parseLong(lastNotif)) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId()); + editor.apply(); } - }) - .into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, Transition transition) { - notify_user(getApplicationContext(), intent, notificationId, resource, finalNotifType, finalTitle, "@" + account.getAcct() + "@" + account.getInstance()); - String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null); - if (lastNotif == null || Long.parseLong(notification.getId()) > Long.parseLong(lastNotif)) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId()); - editor.apply(); - } + return false; + } + }) + .into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, Transition transition) { + notify_user(getApplicationContext(), intent, notificationId, resource, finalNotifType, finalTitle, "@" + account.getAcct() + "@" + account.getInstance()); + String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null); + if (lastNotif == null || Long.parseLong(notification.getId()) > Long.parseLong(lastNotif)) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId()); + editor.apply(); } - }); - } + } + }); } - }; - mainHandler.post(myRunnable); - } + } + }; + mainHandler.post(myRunnable); } } break; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/TagsCacheDAO.java b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/TagsCacheDAO.java index f6dd9ea6c..453e6a9b5 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/TagsCacheDAO.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/TagsCacheDAO.java @@ -52,6 +52,19 @@ public class TagsCacheDAO { }catch (Exception ignored) {} } + /** + * update a tag in database + * @param oldTag String + * @param newTag String + */ + public void update(String oldTag, String newTag) { + ContentValues values = new ContentValues(); + values.put(Sqlite.COL_TAGS, newTag); + try{ + db.update(Sqlite.TABLE_CACHE_TAGS, values, Sqlite.COL_TAGS + " = ?",new String[]{ oldTag}); + }catch (Exception ignored) {} + } + /*** * Remove all tags */ @@ -59,6 +72,11 @@ public class TagsCacheDAO { db.delete(Sqlite.TABLE_CACHE_TAGS, null, null); } + + public void removeTag(String tag) { + db.delete(Sqlite.TABLE_CACHE_TAGS, Sqlite.COL_TAGS + " = ?", new String[]{tag}); + } + /** * Returns all tags in db * @return string tags List @@ -72,6 +90,18 @@ public class TagsCacheDAO { } } + /** + * Returns tags starting by "search" + * @return boolean present + */ + public boolean isPresent(String search){ + Cursor c = db.query(Sqlite.TABLE_CACHE_TAGS, null, Sqlite.COL_TAGS + " = \"" + search + "\"", null, null, null, null, null); + boolean isPresent = (c!= null && c.getCount() > 0); + assert c != null; + c.close(); + return isPresent; + } + /** * Returns tags starting by "search" * @return tags List diff --git a/app/src/main/res/drawable-anydpi/ic_save.xml b/app/src/main/res/drawable-anydpi/ic_save.xml new file mode 100644 index 000000000..a561d632a --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_save.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_tag_cache.xml b/app/src/main/res/layout/activity_tag_cache.xml new file mode 100644 index 000000000..01fc7dcad --- /dev/null +++ b/app/src/main/res/layout/activity_tag_cache.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_tag_edit.xml b/app/src/main/res/layout/drawer_tag_edit.xml new file mode 100644 index 000000000..4020ada19 --- /dev/null +++ b/app/src/main/res/layout/drawer_tag_edit.xml @@ -0,0 +1,53 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main_toot.xml b/app/src/main/res/menu/main_toot.xml index c20fda7a4..4b889bcd8 100644 --- a/app/src/main/res/menu/main_toot.xml +++ b/app/src/main/res/menu/main_toot.xml @@ -41,4 +41,9 @@ android:title="@string/schedule" android:icon="@drawable/ic_schedule" app:showAsAction="never" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index de66370e5..0e30b64f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -680,6 +680,11 @@ Truncate toots over \'x\' lines. Zero means disabled. Display more Display less + Manage tags + The tag already exists! + The tag has been stored! + The tag has been changed! + The tag has been deleted! Never