diff --git a/app/build.gradle b/app/build.gradle index a0f7a236..837f4a5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,14 +33,12 @@ android { } dependencies { - implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.exifinterface:exifinterface:1.0.0' - implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.vectordrawable:vectordrawable:1.1.0' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.google.android.material:material:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' implementation 'org.twitter4j:twitter4j-core:4.0.7' implementation 'com.squareup.picasso:picasso:2.71828' diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/items/Tweet.java b/app/src/main/java/org/nuclearfog/twidda/backend/items/Tweet.java index 5c9e5ff0..9c23fe11 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/items/Tweet.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/items/Tweet.java @@ -2,7 +2,9 @@ package org.nuclearfog.twidda.backend.items; import androidx.annotation.Nullable; +import twitter4j.GeoLocation; import twitter4j.MediaEntity; +import twitter4j.Place; import twitter4j.Status; import twitter4j.URLEntity; @@ -32,12 +34,27 @@ public class Tweet { private final boolean retweeted; private final boolean favored; + private final String locationName; + private final String locationCoordinates; + + /** + * Tweet Constructor + * + * @param status Twitter4J status + */ public Tweet(Status status) { this(status, status.getRetweetCount(), status.isRetweeted(), status.getFavoriteCount(), status.isFavorited()); } - + /** + * Tweet constructor + * @param status twitter4j status + * @param retweetCount set retweet count + * @param retweeted set if tweet is retweeted by current user + * @param favoriteCount set favor count + * @param favored set if tweet is favored by current user + */ public Tweet(Status status, int retweetCount, boolean retweeted, int favoriteCount, boolean favored) { this.retweetCount = retweetCount; this.retweeted = retweeted; @@ -57,6 +74,16 @@ public class Tweet { api = api.substring(0, api.indexOf('<')); source = api; + Place place = status.getPlace(); + GeoLocation geo = status.getGeoLocation(); + if (place != null) + locationName = place.getFullName(); + else + locationName = ""; + if (geo != null) + locationCoordinates = geo.getLatitude() + "," + geo.getLongitude(); + else + locationCoordinates = ""; if (status.getInReplyToScreenName() == null) replyName = ""; else @@ -67,10 +94,29 @@ public class Tweet { embedded = null; } - + /** + * Tweet constructor for database tweets + * @param tweetID unique id of tweet + * @param retweetCount number of retweets + * @param favoriteCount number of favors + * @param user tweet author + * @param tweet tweet text + * @param time time long format + * @param replyName author's name of replied tweet + * @param replyUserId quthor's ID of replied tweet + * @param medias Media links attached to tweet + * @param source used API of the tweet + * @param replyID ID of replied tweet + * @param embedded quoted tweet + * @param myRetweetId ID of the current users retweeted tweet + * @param retweeted tweet is retweeted by current user + * @param favored tweet is favored by current user + * @param coordinates location gps coordinates + * @param place location full place name + */ public Tweet(long tweetID, int retweetCount, int favoriteCount, TwitterUser user, String tweet, long time, String replyName, long replyUserId, String[] medias, String source, long replyID, - Tweet embedded, long myRetweetId, boolean retweeted, boolean favored) { + Tweet embedded, long myRetweetId, boolean retweeted, boolean favored, String place, String coordinates) { this.tweetID = tweetID; this.user = user; this.retweetCount = retweetCount; @@ -86,6 +132,8 @@ public class Tweet { this.favored = favored; this.myRetweetId = myRetweetId; this.replyUserId = replyUserId; + this.locationName = place; + this.locationCoordinates = coordinates; } /** @@ -233,6 +281,24 @@ public class Tweet { return favored; } + /** + * get location of tweet if any + * + * @return full location name + */ + public String getLocationName() { + return locationName; + } + + /** + * get location coordinate + * + * @return latitude and longitude + */ + public String getLocationCoordinates() { + return locationCoordinates; + } + /** * @param status Twitter4J status * @return Array of Medialinks 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 6dc212e9..e29bb6d7 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java @@ -545,6 +545,10 @@ public class AppDatabase { String source = cursor.getString(index); index = cursor.getColumnIndex("media"); String medialinks = cursor.getString(index); + index = cursor.getColumnIndex("place"); + String place = cursor.getString(index); + index = cursor.getColumnIndex("geo"); + String geo = cursor.getString(index); index = cursor.getColumnIndex("replyUserID"); long replyUserId = cursor.getLong(index); index = cursor.getColumnIndex("statusregister"); @@ -559,7 +563,7 @@ public class AppDatabase { if (retweetId > 1) embeddedTweet = getStatus(retweetId); return new Tweet(tweetId, retweet, favorit, user, tweettext, time, replyname, replyUserId, medias, - source, replyStatusId, embeddedTweet, retweeterId, retweeted, favorited); + source, replyStatusId, embeddedTweet, retweeterId, retweeted, favorited, place, geo); } @@ -691,6 +695,9 @@ public class AppDatabase { status.put("favorite", tweet.getFavorCount()); status.put("retweeterID", tweet.getMyRetweetId()); status.put("replyUserID", tweet.getReplyUserId()); + status.put("place", tweet.getLocationName()); + status.put("geo", tweet.getLocationCoordinates()); + status.put("replyUserID", tweet.getReplyUserId()); if (tweet.getReplyUserId() > 0) status.put("replyname", tweet.getReplyName()); storeUser(user, db, CONFLICT_IGNORE); 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 ef36f4f6..9ff01f29 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/DatabaseAdapter.java @@ -14,17 +14,18 @@ import static android.content.Context.MODE_PRIVATE; */ public class DatabaseAdapter { + private static final int DB_VERSION = 2; private static final String DB_NAME = "database.db"; private static final String TABLE_USER = "CREATE TABLE IF NOT EXISTS user (" + - "userID INTEGER PRIMARY KEY,username VARCHAR(50),scrname VARCHAR(15)," + + "userID INTEGER PRIMARY KEY,username TEXT,scrname TEXT," + "pbLink TEXT,banner TEXT,bio TEXT,location TEXT,link TEXT,userregister INTEGER," + "createdAt INTEGER,following INTEGER,follower INTEGER,tweetCount INTEGER,favorCount INTEGER);"; private static final String TABLE_TWEET = "CREATE TABLE IF NOT EXISTS tweet (" + "tweetID INTEGER PRIMARY KEY,userID INTEGER,retweetID INTEGER,replyID INTEGER,retweeterID INTEGER," + "replyname TEXT,replyUserID INTEGER,time INTEGER,tweet TEXT,media TEXT,retweet INTEGER,favorite INTEGER," + - "statusregister INTEGER,source VARCHAR(32),FOREIGN KEY (userID) REFERENCES user(userID));"; + "statusregister INTEGER,source TEXT,place TEXT,geo TEXT,FOREIGN KEY (userID) REFERENCES user(userID));"; private static final String TABLE_FAVORS = "CREATE TABLE IF NOT EXISTS favorit (" + "ownerID INTEGER,tweetID INTEGER," + @@ -42,6 +43,9 @@ public class DatabaseAdapter { private static final String INDX_FAVOR = "CREATE INDEX IF NOT EXISTS idx_favor ON favorit(ownerID,tweetID);"; private static final String INDX_TREND = "CREATE INDEX IF NOT EXISTS idx_trend ON trend(woeID);"; + private static final String TABLE_TWEET_ADD_PLACE = "ALTER TABLE tweet ADD COLUMN place TEXT"; + private static final String TABLE_TWEET_ADD_GEO = "ALTER TABLE tweet ADD COLUMN geo TEXT"; + private static DatabaseAdapter instance; private final File databasePath; @@ -53,6 +57,7 @@ public class DatabaseAdapter { databasePath = context.getDatabasePath(DB_NAME); db = context.openOrCreateDatabase(databasePath.toString(), MODE_PRIVATE, null); initTables(); + updateTable(); } @@ -78,6 +83,15 @@ public class DatabaseAdapter { } + private void updateTable() { + if (db.getVersion() < DB_VERSION) { + db.execSQL(TABLE_TWEET_ADD_PLACE); + db.execSQL(TABLE_TWEET_ADD_GEO); + db.setVersion(DB_VERSION); + } + } + + private void initTables() { db.execSQL(TABLE_USER); db.execSQL(TABLE_TWEET); diff --git a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/MessageLoader.java b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/MessageLoader.java index eeace8ee..82ccfc07 100644 --- a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/MessageLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/MessageLoader.java @@ -25,10 +25,11 @@ public class MessageLoader extends AsyncTask> { DEL } + @Nullable + private TwitterException twException; private Mode mode; private WeakReference ui; private TwitterEngine mTwitter; - private TwitterException twException; private AppDatabase db; private MessageAdapter adapter; diff --git a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TrendLoader.java b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TrendLoader.java index 8c1fb998..27979cb8 100644 --- a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TrendLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TrendLoader.java @@ -20,8 +20,9 @@ import twitter4j.TwitterException; public class TrendLoader extends AsyncTask> { - private WeakReference ui; + @Nullable private TwitterException twException; + private WeakReference ui; private TwitterEngine mTwitter; private AppDatabase db; private TrendAdapter adapter; diff --git a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TweetLoader.java b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TweetLoader.java index 1cae1af5..11dbad30 100644 --- a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TweetLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/TweetLoader.java @@ -17,6 +17,9 @@ import java.util.List; import twitter4j.TwitterException; +/** + * Timeline loader Task + */ public class TweetLoader extends AsyncTask> { public enum Mode { @@ -29,11 +32,12 @@ public class TweetLoader extends AsyncTask> { TWEET_SEARCH } + @Nullable + private TwitterException twException; private Mode mode; private WeakReference ui; private TweetAdapter adapter; private TwitterEngine mTwitter; - private TwitterException twException; private AppDatabase db; diff --git a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/UserLoader.java b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/UserLoader.java index 1ed07972..93359916 100644 --- a/app/src/main/java/org/nuclearfog/twidda/fragment/backend/UserLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/fragment/backend/UserLoader.java @@ -18,6 +18,9 @@ import java.util.List; import twitter4j.TwitterException; +/** + * User list loader task + */ public class UserLoader extends AsyncTask> { public enum Mode { @@ -28,10 +31,11 @@ public class UserLoader extends AsyncTask> { SEARCH } + @Nullable + private TwitterException twException; private Mode mode; private WeakReference ui; private TwitterEngine mTwitter; - private TwitterException twException; private UserAdapter adapter; diff --git a/app/src/main/java/org/nuclearfog/twidda/window/TweetDetail.java b/app/src/main/java/org/nuclearfog/twidda/window/TweetDetail.java index 2fecbdba..d582ec1c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/window/TweetDetail.java +++ b/app/src/main/java/org/nuclearfog/twidda/window/TweetDetail.java @@ -9,7 +9,10 @@ import android.net.ConnectivityManager; import android.net.Uri; import android.os.Bundle; import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextPaint; import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -51,6 +54,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static android.os.AsyncTask.Status.RUNNING; +import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static android.view.View.VISIBLE; @@ -76,7 +80,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O private static final Pattern linkPattern = Pattern.compile("/@?[\\w_]+/status/\\d{1,20}/?.*"); private View header, footer, videoButton, imageButton; - private TextView tweet_api, tweetDate, tweetText, scrName, usrName; + private TextView tweet_api, tweetDate, tweetText, scrName, usrName, tweetLoc; private Button rtwButton, favButton, replyName; private ImageView profile_img; @@ -123,6 +127,7 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O tweetText = findViewById(R.id.tweet_detailed); tweetDate = findViewById(R.id.timedetail); tweet_api = findViewById(R.id.used_api); + tweetLoc = findViewById(R.id.tweet_location); imageButton = findViewById(R.id.image_attach); videoButton = findViewById(R.id.video_attach); @@ -403,6 +408,30 @@ public class TweetDetail extends AppCompatActivity implements OnClickListener, O } if (settings.getImageLoad()) Picasso.get().load(tweet.getUser().getImageLink() + "_bigger").into(profile_img); + + SpannableStringBuilder locationText = new SpannableStringBuilder(""); + if (!tweet.getLocationName().isEmpty()) + locationText.append(tweet.getLocationName()); + if (!tweet.getLocationCoordinates().isEmpty()) { + final int start = locationText.length(); + locationText.append(tweet.getLocationCoordinates()); + final int end = locationText.length() - 1; + locationText.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Intent locationIntent = new Intent(Intent.ACTION_VIEW); + locationIntent.setData(Uri.parse("geo:" + tweet.getLocationCoordinates())); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setColor(settings.getHighlightColor()); + ds.setUnderlineText(false); + } + }, start, end, SPAN_EXCLUSIVE_EXCLUSIVE); + tweetLoc.setText(locationText); + tweetLoc.setVisibility(VISIBLE); + } } diff --git a/app/src/main/java/org/nuclearfog/twidda/window/TweetPopup.java b/app/src/main/java/org/nuclearfog/twidda/window/TweetPopup.java index fe905971..25c5fd46 100644 --- a/app/src/main/java/org/nuclearfog/twidda/window/TweetPopup.java +++ b/app/src/main/java/org/nuclearfog/twidda/window/TweetPopup.java @@ -275,6 +275,8 @@ public class TweetPopup extends AppCompatActivity implements OnClickListener, Lo @Override public void onProviderDisabled(String provider) { + if (gpsLocation == null) + Toast.makeText(this, R.string.error_gps, LENGTH_SHORT).show(); } diff --git a/app/src/main/res/layout/page_tweet.xml b/app/src/main/res/layout/page_tweet.xml index c3f12014..b1cc7e97 100644 --- a/app/src/main/res/layout/page_tweet.xml +++ b/app/src/main/res/layout/page_tweet.xml @@ -95,7 +95,7 @@ android:paddingRight="@dimen/button_padding" android:background="@drawable/button" android:singleLine="true" - android:textSize="@dimen/textsize_refer" + android:textSize="@dimen/tweet_textsize_api" android:visibility="gone" style="@style/Widget.AppCompat.Button.Borderless" /> @@ -103,13 +103,23 @@ android:id="@+id/tweet_detailed" android:layout_width="match_parent" android:layout_height="wrap_content" - android:visibility="gone" android:autoLink="web" android:fadeScrollbars="false" android:linksClickable="true" android:maxLines="@integer/text_tweet_lines" android:scrollbars="vertical" - android:textSize="@dimen/textsize_tweet" /> + android:textSize="@dimen/textsize_tweet" + android:visibility="gone" /> + + + android:textSize="@dimen/tweet_textsize_api" /> diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 1fcf0b3a..c4aa6ced 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -124,4 +124,5 @@ Link konnte nicht kopiert werden! GPS Position hinzugefĆ¼gt starte GPS lokalisierung... + GPS lokalisierung fehlgeschlagen! \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index c0cfb63e..235d9bbf 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -28,12 +28,13 @@ 18sp 18sp 18sp + 12sp 18sp 12sp 12sp 12sp 12sp - 12sp + 12sp 10sp 9 5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8bc2b2f..d8ba49f9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -125,4 +125,5 @@ can\'t copy link to clipboard! GPS position added starting location... + could not fetch GPS data! \ No newline at end of file