From 6d7eb161917b893b9c32b4187cae418a125673ce Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Fri, 13 May 2022 22:34:49 +0200 Subject: [PATCH] added new endpoint, added reply hide option, renamed database methods, added comments --- .../twidda/backend/api/Twitter.java | 47 +++++- .../twidda/backend/api/impl/TweetV1.java | 7 +- .../twidda/backend/async/TweetAction.java | 40 +++-- .../twidda/backend/async/TweetLoader.java | 8 +- .../twidda/database/AppDatabase.java | 158 ++++++++++-------- .../twidda/database/DatabaseAdapter.java | 2 +- .../twidda/database/impl/TweetImpl.java | 10 +- .../org/nuclearfog/twidda/model/Tweet.java | 5 + .../twidda/ui/activities/TweetActivity.java | 45 ++++- app/src/main/res/menu/tweet.xml | 5 + app/src/main/res/values-de-rDE/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 12 files changed, 240 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java index d7a04263..4a8b179d 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/Twitter.java @@ -70,6 +70,7 @@ public class Twitter implements GlobalSettings.SettingsListener { public static final String SIGNATURE_ALG = "HMAC-SHA256"; private static final String OAUTH = "1.0"; + private static final String JSON = ".json"; private static final String API = "https://api.twitter.com/"; private static final String UPLOAD = "https://upload.twitter.com/"; private static final String DOWNLOAD = "https://ton.twitter.com/"; @@ -106,6 +107,7 @@ public class Twitter implements GlobalSettings.SettingsListener { private static final String TWEET_UNRETWEET = API + "1.1/statuses/unretweet/"; private static final String TWEET_UPLOAD = API + "1.1/statuses/update.json"; private static final String TWEET_DELETE = API + "1.1/statuses/destroy/"; + private static final String TWEET_HIDE = API + "2/tweets/"; private static final String TRENDS = API + "1.1/trends/place.json"; private static final String LOCATIONS = API + "1.1/trends/available.json"; private static final String USERLIST_SHOW = API + "1.1/lists/show.json"; @@ -827,7 +829,7 @@ public class Twitter implements GlobalSettings.SettingsListener { * @return updated tweet */ public Tweet retweetTweet(long tweetId) throws TwitterException { - TweetV1 result = getTweet1(TWEET_RETWEET + tweetId + ".json", new ArrayList<>(2)); + TweetV1 result = getTweet1(TWEET_RETWEET + tweetId + JSON, new ArrayList<>(2)); result.setRetweet(true); return result; } @@ -839,18 +841,44 @@ public class Twitter implements GlobalSettings.SettingsListener { * @return updated tweet */ public Tweet unretweetTweet(long tweetId) throws TwitterException { - TweetV1 result = getTweet1(TWEET_UNRETWEET + tweetId + ".json", new ArrayList<>(2)); + TweetV1 result = getTweet1(TWEET_UNRETWEET + tweetId + JSON, new ArrayList<>(2)); result.setRetweet(false); return result; } + /** + * hides reply of the own tweet + * + * @param tweetId ID of the tweet + * @param hide true to hide reply + */ + public void hideReply(long tweetId, boolean hide) throws TwitterException { + try { + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(mediaType, "{\"hidden\":" + hide + "}"); + Response response = put(TWEET_HIDE + tweetId + "/hidden", new ArrayList<>(2), body); + + if (response.body() != null && response.code() == 200) { + JSONObject json = new JSONObject(response.body().string()); + if (json.getJSONObject("data").getBoolean("hidden") == hide) { + return; // successfull if result equals request + } + } + throw new TwitterException(response); + } catch (IOException e) { + throw new TwitterException(e); + } catch (JSONException e) { + throw new TwitterException(e); + } + } + /** * remove tweet of the authenticating user * * @param tweetId tweet ID */ public void deleteTweet(long tweetId) throws TwitterException { - getTweet1(TWEET_DELETE + tweetId + ".json", new ArrayList<>(2)); + getTweet1(TWEET_DELETE + tweetId + JSON, new ArrayList<>(2)); } /** @@ -1680,6 +1708,19 @@ public class Twitter implements GlobalSettings.SettingsListener { return client.newCall(request).execute(); } + /** + * create and call PUT endpoint + * + * @param endpoint endpoint url + * @return http response + */ + private Response put(String endpoint, List params, RequestBody body)throws IOException { + String authHeader = buildHeader("PUT", endpoint, params); + String url = appendParams(endpoint, params); + Request request = new Request.Builder().url(url).addHeader("Authorization", authHeader).put(body).build(); + return client.newCall(request).execute(); + } + /** * create and call GET endpoint * diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/impl/TweetV1.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/impl/TweetV1.java index 60d604d0..05bb8530 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/impl/TweetV1.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/impl/TweetV1.java @@ -69,7 +69,7 @@ public class TweetV1 implements Tweet { author = new UserV1(json.getJSONObject("user"), twitterId); id = Long.parseLong(json.optString("id_str", "-1")); replyId = json.optLong("in_reply_to_status_id", -1); - replyUserId = json.optLong("in_reply_to_status_id", -1); + replyUserId = json.optLong("in_reply_to_user_id", -1); retweetCount = json.optInt("retweet_count"); favoriteCount = json.optInt("favorite_count"); isFavorited = json.optBoolean("favorited"); @@ -212,6 +212,11 @@ public class TweetV1 implements Tweet { return coordinates; } + @Override + public boolean isHidden() { + return false; + } + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof Tweet)) diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetAction.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetAction.java index a37a146f..9537e21e 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetAction.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetAction.java @@ -49,6 +49,14 @@ public class TweetAction extends AsyncTask { * remove tweet from favorites */ UNFAVORITE, + /** + * hide reply + */ + HIDE, + /** + * unhide reply + */ + UNHIDE, /** * delete own tweet */ @@ -85,7 +93,7 @@ public class TweetAction extends AsyncTask { try { switch (action) { case LD_DB: - Tweet tweet = db.getStatus(tweetId); + Tweet tweet = db.getTweet(tweetId); if (tweet != null) { publishProgress(tweet); } @@ -94,35 +102,35 @@ public class TweetAction extends AsyncTask { tweet = twitter.showTweet(tweetId); //tweet = mTwitter.getStatus(tweetId); publishProgress(tweet); - if (db.containStatus(tweetId)) { + if (db.containsTweet(tweetId)) { // update tweet if there is a database entry - db.updateStatus(tweet); + db.updateTweet(tweet); } break; case DELETE: twitter.deleteTweet(tweetId); - db.removeStatus(tweetId); + db.removeTweet(tweetId); // removing retweet reference to this tweet if (retweetId > 0) - db.removeStatus(retweetId); + db.removeTweet(retweetId); break; case RETWEET: tweet = twitter.retweetTweet(tweetId); publishProgress(tweet); - db.updateStatus(tweet); + db.updateTweet(tweet); break; case UNRETWEET: tweet = twitter.unretweetTweet(tweetId); publishProgress(tweet); - db.updateStatus(tweet); + db.updateTweet(tweet); // removing retweet reference to this tweet if (retweetId > 0) - db.removeStatus(retweetId); + db.removeTweet(retweetId); else - db.removeStatus(tweetId); + db.removeTweet(tweetId); break; case FAVORITE: @@ -136,11 +144,21 @@ public class TweetAction extends AsyncTask { publishProgress(tweet); db.removeFavorite(tweet); break; + + case HIDE: + twitter.hideReply(tweetId, true); + db.hideReply(tweetId, true); + break; + + case UNHIDE: + twitter.hideReply(tweetId, false); + db.hideReply(tweetId, false); + break; } } catch (TwitterException twException) { this.twException = twException; if (twException.getErrorType() == ErrorHandler.TwitterError.RESOURCE_NOT_FOUND) { - db.removeStatus(tweetId); + db.removeTweet(tweetId); } } return null; @@ -161,7 +179,7 @@ public class TweetAction extends AsyncTask { TweetActivity activity = weakRef.get(); if (activity != null) { if (twException == null) { - activity.onAction(action, tweetId); + activity.OnSuccess(action, tweetId); } else { activity.onError(twException, tweetId); } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetLoader.java index 0f068cfa..41c4033e 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/TweetLoader.java @@ -170,21 +170,21 @@ public class TweetLoader extends AsyncTask> { break; case REPLIES_OFFLINE: - tweets = db.getAnswers(id); + tweets = db.getTweetReplies(id); break; case REPLIES: if (sinceId == 0 && maxId == 0) { - tweets = db.getAnswers(id); + tweets = db.getTweetReplies(id); if (tweets.isEmpty()) { tweets = twitter.getTweetReplies(search, id, sinceId, maxId); - if (!tweets.isEmpty() && db.containStatus(id)) { + if (!tweets.isEmpty() && db.containsTweet(id)) { db.storeReplies(tweets); } } } else if (sinceId > 0) { tweets = twitter.getTweetReplies(search, id, sinceId, maxId); - if (!tweets.isEmpty() && db.containStatus(id)) { + if (!tweets.isEmpty() && db.containsTweet(id)) { db.storeReplies(tweets); } } else if (maxId > 1) { diff --git a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java index 5985d6c3..fde83d85 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java @@ -51,6 +51,7 @@ public class AppDatabase { public static final int MEDIA_VIDEO_MASK = 2 << 6; // tweet contains a video public static final int MEDIA_ANGIF_MASK = 3 << 6; // tweet contains an animation public static final int MEDIA_SENS_MASK = 1 << 8; // tweet contains sensitive media + public static final int HIDDEN_MASK = 1 << 9; // tweet is hidden // user status bits public static final int VER_MASK = 1; // user is verified @@ -134,11 +135,12 @@ public class AppDatabase { /** * SQL query to get replies of a tweet specified by a tweet ID */ - static final String ANSWER_QUERY = "SELECT * FROM " + TWEET_TABLE + static final String REPLY_QUERY = "SELECT * FROM " + TWEET_TABLE + " WHERE " + TweetTable.NAME + "." + TweetTable.REPLYTWEET + "=?" + " AND " + TweetRegisterTable.NAME + "." + TweetRegisterTable.OWNER + "=?" + " AND " + UserRegisterTable.NAME + "." + UserRegisterTable.OWNER + "=?" + " AND " + TweetRegisterTable.NAME + "." + TweetRegisterTable.REGISTER + "&" + RPL_MASK + " IS NOT 0" + + " AND " + TweetRegisterTable.NAME + "." + TweetRegisterTable.REGISTER + "&" + HIDDEN_MASK + " IS 0" + " AND " + UserRegisterTable.NAME + "." + UserRegisterTable.REGISTER + "&" + EXCL_USR + " IS 0" + " ORDER BY " + TweetTable.ID + " DESC LIMIT ?"; @@ -259,7 +261,7 @@ public class AppDatabase { public void storeHomeTimeline(List home) { SQLiteDatabase db = getDbWrite(); for (Tweet tweet : home) - storeStatus(tweet, HOM_MASK, db); + storeTweet(tweet, HOM_MASK, db); commit(db); } @@ -271,7 +273,7 @@ public class AppDatabase { public void storeMentions(List mentions) { SQLiteDatabase db = getDbWrite(); for (Tweet tweet : mentions) - storeStatus(tweet, MEN_MASK, db); + storeTweet(tweet, MEN_MASK, db); commit(db); } @@ -283,7 +285,7 @@ public class AppDatabase { public void storeUserTweets(List stats) { SQLiteDatabase db = getDbWrite(); for (Tweet tweet : stats) - storeStatus(tweet, UTW_MASK, db); + storeTweet(tweet, UTW_MASK, db); commit(db); } @@ -297,7 +299,7 @@ public class AppDatabase { SQLiteDatabase db = getDbWrite(); removeOldFavorites(db, ownerId); for (Tweet tweet : fav) { - storeStatus(tweet, 0, db); + storeTweet(tweet, 0, db); storeFavorite(tweet.getId(), ownerId, db); } commit(db); @@ -311,7 +313,7 @@ public class AppDatabase { public void storeReplies(List replies) { SQLiteDatabase db = getDbWrite(); for (Tweet tweet : replies) - storeStatus(tweet, RPL_MASK, db); + storeTweet(tweet, RPL_MASK, db); commit(db); } @@ -345,7 +347,7 @@ public class AppDatabase { if (tweet.getEmbeddedTweet() != null) tweet = tweet.getEmbeddedTweet(); SQLiteDatabase db = getDbWrite(); - storeStatus(tweet, 0, db); + storeTweet(tweet, 0, db); storeFavorite(tweet.getId(), homeId, db); commit(db); } @@ -376,7 +378,7 @@ public class AppDatabase { Cursor cursor = db.rawQuery(HOME_QUERY, args); if (cursor.moveToFirst()) { do { - Tweet tweet = getStatus(cursor); + Tweet tweet = getTweet(cursor); tweetList.add(tweet); } while (cursor.moveToNext()); } @@ -398,7 +400,7 @@ public class AppDatabase { Cursor cursor = db.rawQuery(MENTION_QUERY, args); if (cursor.moveToFirst()) { do { - Tweet tweet = getStatus(cursor); + Tweet tweet = getTweet(cursor); tweetList.add(tweet); } while (cursor.moveToNext()); } @@ -421,7 +423,7 @@ public class AppDatabase { Cursor cursor = db.rawQuery(USERTWEET_QUERY, args); if (cursor.moveToFirst()) { do { - Tweet tweet = getStatus(cursor); + Tweet tweet = getTweet(cursor); tweetList.add(tweet); } while (cursor.moveToNext()); } @@ -444,7 +446,7 @@ public class AppDatabase { Cursor cursor = db.rawQuery(USERFAVORIT_QUERY, args); if (cursor.moveToFirst()) { do { - Tweet tweet = getStatus(cursor); + Tweet tweet = getTweet(cursor); tweetList.add(tweet); } while (cursor.moveToNext()); } @@ -465,13 +467,13 @@ public class AppDatabase { } /** - * load status + * get tweet from database * * @param tweetId tweet ID * @return tweet or null if not found */ @Nullable - public Tweet getStatus(long tweetId) { + public Tweet getTweet(long tweetId) { String homeStr = Long.toString(homeId); String[] args = {Long.toString(tweetId), homeStr, homeStr}; @@ -479,27 +481,27 @@ public class AppDatabase { Tweet result = null; Cursor cursor = db.rawQuery(SINGLE_TWEET_QUERY, args); if (cursor.moveToFirst()) - result = getStatus(cursor); + result = getTweet(cursor); cursor.close(); return result; } /** - * get tweet answers + * get tweet replies * * @param tweetId Tweet ID - * @return list of tweet answers + * @return list of tweets */ - public List getAnswers(long tweetId) { + public List getTweetReplies(long tweetId) { String homeStr = Long.toString(homeId); String[] args = {Long.toString(tweetId), homeStr, homeStr, Integer.toString(limit)}; SQLiteDatabase db = getDbRead(); List tweetList = new LinkedList<>(); - Cursor cursor = db.rawQuery(ANSWER_QUERY, args); + Cursor cursor = db.rawQuery(REPLY_QUERY, args); if (cursor.moveToFirst()) { do { - Tweet tweet = getStatus(cursor); + Tweet tweet = getTweet(cursor); tweetList.add(tweet); } while (cursor.moveToNext()); } @@ -508,24 +510,24 @@ public class AppDatabase { } /** - * update status and author information + * update tweet and author information * * @param tweet Tweet */ - public void updateStatus(Tweet tweet) { + public void updateTweet(Tweet tweet) { SQLiteDatabase db = getDbWrite(); - updateStatus(tweet, db); + updateTweet(tweet, db); if (tweet.getEmbeddedTweet() != null) - updateStatus(tweet.getEmbeddedTweet(), db); + updateTweet(tweet.getEmbeddedTweet(), db); commit(db); } /** - * remove status + * remove tweet from database * * @param tweetId Tweet ID */ - public void removeStatus(long tweetId) { + public void removeTweet(long tweetId) { String[] args = {Long.toString(tweetId)}; SQLiteDatabase db = getDbWrite(); @@ -535,7 +537,29 @@ public class AppDatabase { } /** - * remove status from favorites + * hide or unhide tweet reply + * + * @param replyId ID of the reply + * @param hide true to hide this tweet + */ + public void hideReply(long replyId, boolean hide) { + String[] args = {Long.toString(replyId), Long.toString(homeId)}; + + SQLiteDatabase db = getDbWrite(); + int register = getTweetRegister(db, replyId); + if (hide) + register |= HIDDEN_MASK; + else + register &= ~HIDDEN_MASK; + + ContentValues values = new ContentValues(3); + values.put(TweetRegisterTable.REGISTER, register); + db.update(TweetRegisterTable.NAME, values, TWEET_REG_SELECT, args); + commit(db); + } + + /** + * remove tweet from favorites * * @param tweet Tweet to remove from the favorites */ @@ -633,9 +657,9 @@ public class AppDatabase { * @param id Tweet ID * @return true if found */ - public boolean containStatus(long id) { + public boolean containsTweet(long id) { SQLiteDatabase db = getDbRead(); - return containStatus(id, db); + return tweetExists(id, db); } /** @@ -662,11 +686,11 @@ public class AppDatabase { * @param cursor cursor containing tweet informations * @return tweet instance */ - private Tweet getStatus(Cursor cursor) { + private Tweet getTweet(Cursor cursor) { TweetImpl result = new TweetImpl(cursor, homeId); // check if there is an embedded tweet if (result.getEmbeddedTweetId() > 1) - result.addEmbeddedTweet(getStatus(result.getEmbeddedTweetId())); + result.addEmbeddedTweet(getTweet(result.getEmbeddedTweetId())); return result; } @@ -739,61 +763,61 @@ public class AppDatabase { * save tweet into database * * @param tweet Tweet information - * @param statusRegister predefined status register or zero if there isn't one + * @param tweetFlags predefined tweet status register or zero if there isn't one * @param db SQLite database */ - private void storeStatus(Tweet tweet, int statusRegister, SQLiteDatabase db) { + private void storeTweet(Tweet tweet, int tweetFlags, SQLiteDatabase db) { User user = tweet.getAuthor(); Tweet rtStat = tweet.getEmbeddedTweet(); long rtId = -1L; if (rtStat != null) { - storeStatus(rtStat, 0, db); + storeTweet(rtStat, 0, db); rtId = rtStat.getId(); } - statusRegister |= getTweetRegister(db, tweet.getId()); + tweetFlags |= getTweetRegister(db, tweet.getId()); if (tweet.isFavorited()) { - statusRegister |= FAV_MASK; + tweetFlags |= FAV_MASK; } else { - statusRegister &= ~FAV_MASK; + tweetFlags &= ~FAV_MASK; } if (tweet.isRetweeted()) { - statusRegister |= RTW_MASK; + tweetFlags |= RTW_MASK; } else { - statusRegister &= ~RTW_MASK; + tweetFlags &= ~RTW_MASK; } if (tweet.isSensitive()) { - statusRegister |= MEDIA_SENS_MASK; + tweetFlags |= MEDIA_SENS_MASK; } else { - statusRegister &= ~MEDIA_SENS_MASK; + tweetFlags &= ~MEDIA_SENS_MASK; } if (Tweet.MEDIA_PHOTO.equals(tweet.getMediaType())) { - statusRegister |= MEDIA_IMAGE_MASK; + tweetFlags |= MEDIA_IMAGE_MASK; } else if (Tweet.MEDIA_VIDEO.equals(tweet.getMediaType())) { - statusRegister |= MEDIA_VIDEO_MASK; + tweetFlags |= MEDIA_VIDEO_MASK; } else if (Tweet.MEDIA_GIF.equals(tweet.getMediaType())) { - statusRegister |= MEDIA_ANGIF_MASK; + tweetFlags |= MEDIA_ANGIF_MASK; } - ContentValues status = new ContentValues(16); - status.put(TweetTable.MEDIA, getMediaLinks(tweet)); - status.put(TweetTable.ID, tweet.getId()); - status.put(TweetTable.USER, user.getId()); - status.put(TweetTable.SINCE, tweet.getTimestamp()); - status.put(TweetTable.TWEET, tweet.getText()); - status.put(TweetTable.EMBEDDED, rtId); - status.put(TweetTable.SOURCE, tweet.getSource()); - status.put(TweetTable.REPLYTWEET, tweet.getRepliedTweetId()); - status.put(TweetTable.RETWEET, tweet.getRetweetCount()); - status.put(TweetTable.FAVORITE, tweet.getFavoriteCount()); - status.put(TweetTable.REPLYUSER, tweet.getRepliedUserId()); - status.put(TweetTable.PLACE, tweet.getLocationName()); - status.put(TweetTable.COORDINATE, tweet.getLocationCoordinates()); - status.put(TweetTable.REPLYUSER, tweet.getRepliedUserId()); - status.put(TweetTable.REPLYNAME, tweet.getReplyName()); + ContentValues tweetUpdate = new ContentValues(16); + tweetUpdate.put(TweetTable.MEDIA, getMediaLinks(tweet)); + tweetUpdate.put(TweetTable.ID, tweet.getId()); + tweetUpdate.put(TweetTable.USER, user.getId()); + tweetUpdate.put(TweetTable.SINCE, tweet.getTimestamp()); + tweetUpdate.put(TweetTable.TWEET, tweet.getText()); + tweetUpdate.put(TweetTable.EMBEDDED, rtId); + tweetUpdate.put(TweetTable.SOURCE, tweet.getSource()); + tweetUpdate.put(TweetTable.REPLYTWEET, tweet.getRepliedTweetId()); + tweetUpdate.put(TweetTable.RETWEET, tweet.getRetweetCount()); + tweetUpdate.put(TweetTable.FAVORITE, tweet.getFavoriteCount()); + tweetUpdate.put(TweetTable.REPLYUSER, tweet.getRepliedUserId()); + tweetUpdate.put(TweetTable.PLACE, tweet.getLocationName()); + tweetUpdate.put(TweetTable.COORDINATE, tweet.getLocationCoordinates()); + tweetUpdate.put(TweetTable.REPLYUSER, tweet.getRepliedUserId()); + tweetUpdate.put(TweetTable.REPLYNAME, tweet.getReplyName()); - db.insertWithOnConflict(TweetTable.NAME, "", status, CONFLICT_REPLACE); + db.insertWithOnConflict(TweetTable.NAME, "", tweetUpdate, CONFLICT_REPLACE); storeUser(user, db, CONFLICT_IGNORE); - setTweetRegister(db, tweet, statusRegister); + setTweetRegister(db, tweet, tweetFlags); } /** @@ -802,7 +826,7 @@ public class AppDatabase { * @param tweet update of the tweet * @param db database instance */ - private void updateStatus(Tweet tweet, SQLiteDatabase db) { + private void updateTweet(Tweet tweet, SQLiteDatabase db) { String[] tweetIdArg = {Long.toString(tweet.getId())}; String[] userIdArg = {Long.toString(tweet.getAuthor().getId())}; @@ -888,7 +912,7 @@ public class AppDatabase { } /** - * get status register of a tweet or zero if tweet was not found + * get tweet register of a tweet or zero if tweet was not found * * @param db database instance * @param tweetID ID of the tweet @@ -906,7 +930,7 @@ public class AppDatabase { } /** - * set status register of a tweet. if an entry exists, update it + * set tweet register of a tweet. if an entry exists, update it * * @param db database instance * @param tweet Tweet @@ -915,9 +939,7 @@ public class AppDatabase { public void setTweetRegister(SQLiteDatabase db, Tweet tweet, int register) { String[] args = {Long.toString(tweet.getId()), Long.toString(homeId)}; - ContentValues values = new ContentValues(3); - values.put(TweetRegisterTable.ID, tweet.getId()); - values.put(TweetRegisterTable.OWNER, homeId); + ContentValues values = new ContentValues(2); values.put(TweetRegisterTable.REGISTER, register); values.put(TweetRegisterTable.RETWEETUSER, tweet.getMyRetweetId()); @@ -975,7 +997,7 @@ public class AppDatabase { * @param db database instance * @return true if found */ - private boolean containStatus(long id, SQLiteDatabase db) { + private boolean tweetExists(long id, SQLiteDatabase db) { String[] args = {Long.toString(id)}; Cursor c = db.query(TweetTable.NAME, null, TWEET_SELECT, args, null, null, SINGLE_ITEM); diff --git a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java index 3fa5fb9f..ad90130a 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java @@ -555,7 +555,7 @@ public class DatabaseAdapter { String REGISTER = "tweetRegister"; /** - * + * ID of the retweet of the current user (if exists) */ String RETWEETUSER = "retweeterID"; } diff --git a/app/src/main/java/org/nuclearfog/twidda/database/impl/TweetImpl.java b/app/src/main/java/org/nuclearfog/twidda/database/impl/TweetImpl.java index 4da99947..d618b349 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/impl/TweetImpl.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/impl/TweetImpl.java @@ -1,6 +1,7 @@ package org.nuclearfog.twidda.database.impl; import static org.nuclearfog.twidda.database.AppDatabase.FAV_MASK; +import static org.nuclearfog.twidda.database.AppDatabase.HIDDEN_MASK; import static org.nuclearfog.twidda.database.AppDatabase.MEDIA_ANGIF_MASK; import static org.nuclearfog.twidda.database.AppDatabase.MEDIA_IMAGE_MASK; import static org.nuclearfog.twidda.database.AppDatabase.MEDIA_SENS_MASK; @@ -21,7 +22,7 @@ import org.nuclearfog.twidda.model.User; import java.util.regex.Pattern; /** - * Tweet implementation for database + * Implementation of a database tweet * * @author nuclearfog */ @@ -53,6 +54,7 @@ public class TweetImpl implements Tweet { private boolean retweeted; private boolean favorited; private boolean sensitive; + private boolean isHidden; public TweetImpl(Cursor cursor, long currentUserId) { @@ -75,6 +77,7 @@ public class TweetImpl implements Tweet { favorited = (tweetRegister & FAV_MASK) != 0; retweeted = (tweetRegister & RTW_MASK) != 0; sensitive = (tweetRegister & MEDIA_SENS_MASK) != 0; + isHidden = (tweetRegister & HIDDEN_MASK) != 0; if (!linkStr.isEmpty()) mediaLinks = SEPARATOR.split(linkStr); userMentions = StringTools.getUserMentions(text, author.getScreenname()); @@ -195,6 +198,11 @@ public class TweetImpl implements Tweet { return locationCoordinates; } + @Override + public boolean isHidden() { + return isHidden; + } + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof Tweet)) diff --git a/app/src/main/java/org/nuclearfog/twidda/model/Tweet.java b/app/src/main/java/org/nuclearfog/twidda/model/Tweet.java index 42c48f9e..4c949f2f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/model/Tweet.java +++ b/app/src/main/java/org/nuclearfog/twidda/model/Tweet.java @@ -126,6 +126,11 @@ public interface Tweet extends Serializable { */ boolean isFavorited(); + /** + * @return true if tweet is hidden by current user + */ + boolean isHidden(); + /** * @return name of the location if attached */ diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/TweetActivity.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/TweetActivity.java index d9a44316..2f8bfb22 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/TweetActivity.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/TweetActivity.java @@ -133,6 +133,8 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, @Nullable private Tweet tweet; + private boolean hidden; + @Override protected void attachBaseContext(Context newBase) { @@ -175,6 +177,7 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, } else { username = tweet.getAuthor().getScreenname(); tweetId = tweet.getId(); + hidden = tweet.isHidden(); } } else { username = getIntent().getStringExtra(KEY_TWEET_NAME); @@ -278,17 +281,26 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, if (tweet == null) return super.onPrepareOptionsMenu(m); - MenuItem deleteItem = m.findItem(R.id.menu_tweet_delete); - MenuItem copyItem = m.findItem(R.id.menu_tweet_copy); - SubMenu copyMenu = copyItem.getSubMenu(); + MenuItem optDelete = m.findItem(R.id.menu_tweet_delete); + MenuItem optHide = m.findItem(R.id.menu_tweet_hide); + MenuItem optCopy = m.findItem(R.id.menu_tweet_copy); + SubMenu copyMenu = optCopy.getSubMenu(); Tweet currentTweet = tweet; if (tweet.getEmbeddedTweet() != null) { currentTweet = tweet.getEmbeddedTweet(); } - + if (currentTweet.getRepliedUserId() == settings.getCurrentUserId() + && currentTweet.getAuthor().getId() != settings.getCurrentUserId()) { + optHide.setVisible(true); + if (hidden) { + optHide.setTitle(R.string.menu_tweet_unhide); + } else { + optHide.setTitle(R.string.menu_tweet_hide); + } + } // enable delete option only if current user owns tweets - deleteItem.setVisible(currentTweet.getAuthor().isCurrentUser()); + optDelete.setVisible(currentTweet.getAuthor().isCurrentUser()); // add media link items // check if menu doesn't contain media links already @@ -317,6 +329,15 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, if (item.getItemId() == R.id.menu_tweet_delete) { deleteDialog.show(DialogType.TWEET_DELETE); } + // hide tweet + else if (item.getItemId() == R.id.menu_tweet_hide) { + if (hidden) { + statusAsync = new TweetAction(this, Action.UNHIDE, tweet.getId(), -1L); + } else { + statusAsync = new TweetAction(this, Action.HIDE, tweet.getId(), -1L); + } + statusAsync.execute(); + } // get tweet link else if (item.getItemId() == R.id.menu_tweet_browser) { String username = author.getScreenname().substring(1); @@ -671,7 +692,7 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, * @param action action type * @param tweetId ID of the tweet */ - public void onAction(Action action, long tweetId) { + public void OnSuccess(Action action, long tweetId) { switch (action) { case RETWEET: Toast.makeText(this, R.string.info_tweet_retweeted, LENGTH_SHORT).show(); @@ -695,6 +716,18 @@ public class TweetActivity extends AppCompatActivity implements OnClickListener, Toast.makeText(this, R.string.info_tweet_unfavored, LENGTH_SHORT).show(); break; + case HIDE: + hidden = true; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.info_reply_hidden, LENGTH_SHORT).show(); + break; + + case UNHIDE: + hidden = false; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.info_reply_unhidden, LENGTH_SHORT).show(); + break; + case DELETE: Toast.makeText(this, R.string.info_tweet_removed, LENGTH_SHORT).show(); Intent returnData = new Intent(); diff --git a/app/src/main/res/menu/tweet.xml b/app/src/main/res/menu/tweet.xml index 9cf5be19..86033fee 100644 --- a/app/src/main/res/menu/tweet.xml +++ b/app/src/main/res/menu/tweet.xml @@ -23,6 +23,11 @@ + + Medienlink Tweet Text kopiert Medienlink kopiert + Antwort ausblenden + Antwort einblenden + Antwort wurde ausgeblendet + Antwort wurde eingeblendet \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81dbd397..36c07bd3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,6 +28,8 @@ Tweet removed from favorites Tweet liked Tweet unliked + Reply is hidden + Reply is visible User muted User unmuted Directmessage sent @@ -112,6 +114,8 @@ unmute follow requested delete + hide + unhide open in browser mute edit profile