diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java b/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java index 01f61bd1..397d5e5e 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/MessageEditor.java @@ -82,7 +82,7 @@ public class MessageEditor extends MediaActivity implements OnClickListener, OnC receiver.append(prefix); } send.setImageResource(R.drawable.right); - media.setImageResource(R.drawable.image_add); + media.setImageResource(R.drawable.attachment); preview.setImageResource(R.drawable.image); preview.setVisibility(GONE); AppStyles.setEditorTheme(root, background); diff --git a/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java b/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java index 3b90bd2d..0b639373 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/activities/TweetEditor.java @@ -125,7 +125,7 @@ public class TweetEditor extends MediaActivity implements OnClickListener, OnPro tweetText.append(prefix); } previewBtn.setImageResource(R.drawable.image); - mediaBtn.setImageResource(R.drawable.image_add); + mediaBtn.setImageResource(R.drawable.attachment); locationBtn.setImageResource(R.drawable.location); tweetButton.setImageResource(R.drawable.tweet); closeButton.setImageResource(R.drawable.cross); diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java index 94bd129d..8d95c472 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/ImageLoader.java @@ -9,6 +9,7 @@ import androidx.annotation.Nullable; import org.nuclearfog.twidda.activities.MediaViewer; import org.nuclearfog.twidda.backend.api.Twitter; +import org.nuclearfog.twidda.backend.api.TwitterException; import org.nuclearfog.twidda.backend.holder.ImageHolder; import org.nuclearfog.twidda.backend.utils.ErrorHandler; @@ -16,6 +17,7 @@ import java.lang.ref.WeakReference; /** * Background task to load images from twitter and storage + * todo: cache files to prevent out of memory error * * @author nuclearfog * @see MediaViewer @@ -44,9 +46,9 @@ public class ImageLoader extends AsyncTask { protected Boolean doInBackground(String[] links) { try { for (String link : links) { - Bitmap image = null; - if (link.startsWith("https://")) { // fixme - //image = mTwitter.getImage(link); + Bitmap image; + if (link.startsWith("https://")) { + image = twitter.downloadImage(link); } else { image = BitmapFactory.decodeFile(link); } @@ -56,9 +58,9 @@ public class ImageLoader extends AsyncTask { } } return true; - } /*catch (EngineException err) { + } catch (TwitterException err) { this.err = err; - }*/ catch (Exception exception) { + } catch (Exception exception) { exception.printStackTrace(); } return false; diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/UserUpdater.java b/app/src/main/java/org/nuclearfog/twidda/backend/UserUpdater.java index f4c8d309..7567db8c 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/UserUpdater.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/UserUpdater.java @@ -44,14 +44,14 @@ public class UserUpdater extends AsyncTask { String location = param[2]; String bio = param[3]; String profileImg = param[4]; - String bannerImg = param[5];// fixme - //User user = mTwitter.updateProfile(name, link, location, bio, profileImg, bannerImg); - //db.storeUser(user); - //return user; - } /*catch (TwitterException twException) { + String bannerImg = param[5]; + twitter.updateProfileImage(profileImg); + twitter.updateProfileBanner(bannerImg); + User user = twitter.updateProfile(name, link, location, bio); + db.storeUser(user); + return user; + } catch (TwitterException twException) { this.twException = twException; - }*/ catch (Exception err) { - err.printStackTrace(); } return null; } 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 a222ea13..749c9616 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 @@ -1,6 +1,8 @@ package org.nuclearfog.twidda.backend.api; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; @@ -24,6 +26,8 @@ import org.nuclearfog.twidda.model.UserList; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.security.KeyStore; import java.util.ArrayList; import java.util.LinkedList; @@ -104,6 +108,9 @@ public class Twitter { private static final String DIRECTMESSAGE_CREATE = API + "1.1/direct_messages/events/new.json"; private static final String DIRECTMESSAGE_DELETE = API + "1.1/direct_messages/events/destroy.json"; private static final String MEDIA_UPLOAD = UPLOAD + "1.1/media/upload.json"; + private static final String PROFILE_UPDATE = API + "1.1/account/update_profile.json"; + private static final String PROFILE_UPDATE_IMAGE = API + "1.1/account/update_profile_image.json"; + private static final String PROFILE_UPDATE_BANNER = API + "1.1/account/update_profile_banner.json"; public static final String REQUEST_URL = AUTHENTICATE + "?oauth_token="; private static Twitter instance; @@ -118,6 +125,8 @@ public class Twitter { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.writeTimeout(60, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // set TLS 1.2 support for default connections + TLSSocketFactory.setSupportTLS(); try { // use experimental TLS 1.2 support for pre-Lollipop devices TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); @@ -229,7 +238,7 @@ public class Twitter { public User showUser(long id) throws TwitterException { List params = new ArrayList<>(3); params.add("user_id=" + id); - return showUser(params); + return getUser1(USER_LOOKUP, params); } /** @@ -241,7 +250,7 @@ public class Twitter { public User showUser(String name) throws TwitterException { List params = new ArrayList<>(3); params.add("screen_name=" + name); - return showUser(params); + return getUser1(USER_LOOKUP, params); } /** @@ -1059,7 +1068,7 @@ public class Twitter { public Directmessages getDirectmessages(String cursor) throws TwitterException { List params = new ArrayList<>(3); params.add("count=" + settings.getListSize()); - if (!cursor.isEmpty()) + if (cursor != null && !cursor.isEmpty()) params.add("cursor=" + cursor); try { Response response = get(DIRECTMESSAGE, params); @@ -1154,32 +1163,70 @@ public class Twitter { } /** - * lookup single user + * download image from twitter * - * @param params additional parameter added to request - * @return user information + * @param link link to the image + * @return image bitmap */ - private User showUser(List params) throws TwitterException { + public Bitmap downloadImage(String link) throws TwitterException { try { - params.add(UserV1.INCLUDE_ENTITIES); - Response response = get(USER_LOOKUP, params); - if (response.body() != null) { - JSONObject json = new JSONObject(response.body().string()); + // this type of link requires authentication + if (link.startsWith("https://ton.twitter.com/")) { + Response response = get(link, new ArrayList<>(0)); if (response.code() == 200) { - return new UserV1(json, settings.getCurrentUserId()); + byte[] data = response.body().bytes(); + return BitmapFactory.decodeByteArray(data, 0, 0); } else { - throw new TwitterException(json); + throw new TwitterException(response); } - } else { - throw new TwitterException(response); } - } catch (IOException err) { - throw new TwitterException(err); - } catch (JSONException err) { - throw new TwitterException(err); + // public link, no authentication required + else { + URL imageUrl = new URL(link); + InputStream is = imageUrl.openConnection().getInputStream(); + return BitmapFactory.decodeStream(is); + } + } catch (IOException e) { + throw new TwitterException(e); } } + /** + * updates current user's profile + * + * @param username new username + * @param url new profile URL + * @param location new location name + * @param description new description + * @return updated user information + */ + public User updateProfile(String username, String url, String location, String description) throws TwitterException { + List params = new ArrayList<>(7); + params.add("name=" + StringTools.encode(username)); + params.add("url=" + StringTools.encode(url)); + params.add("location=" + StringTools.encode(location)); + params.add("description=" + StringTools.encode(description)); + return getUser1(PROFILE_UPDATE, params); + } + + /** + * update current user's profile image + * + * @param path local path to the image + */ + public void updateProfileImage(String path) throws TwitterException { + updateImage(PROFILE_UPDATE_IMAGE, path, "image"); + } + + /** + * update current user's profile banner image + * + * @param path local path to the image + */ + public void updateProfileBanner(String path) throws TwitterException { + updateImage(PROFILE_UPDATE_BANNER, path, "banner"); + } + /** * get tweets using an endpoint * @@ -1262,7 +1309,7 @@ public class Twitter { private Users getUsers1(String endpoint, List params) throws TwitterException { try { params.add("count=" + settings.getListSize()); - params.add(UserV1.SKIP_STAT); + params.add("skip_status=true"); Response response = get(endpoint, params); if (response.body() != null) { String jsonResult = response.body().string(); @@ -1346,7 +1393,13 @@ public class Twitter { */ private User getUser1(String endpoint, List params) throws TwitterException { try { - Response response = post(endpoint, params); + params.add("skip_status=true"); + params.add("include_entities=true"); + Response response; + if (endpoint.equals(USER_LOOKUP)) + response = get(endpoint, params); + else + response = post(endpoint, params); if (response.body() != null) { JSONObject json = new JSONObject(response.body().string()); if (response.code() == 200) { @@ -1404,7 +1457,7 @@ public class Twitter { * @return list of userlists */ private UserLists getUserlists(String endpoint, List params) throws TwitterException { - params.add(UserV1.INCLUDE_ENTITIES); + params.add("include_entities=true"); try { Response response = get(endpoint, params); if (response.body() != null) { @@ -1442,6 +1495,34 @@ public class Twitter { } } + /** + * update profile images + * + * @param endpoint endpoint to use + * @param imagePath path to the local image + * @param key key name used to identify the type of image + */ + private void updateImage(String endpoint, String imagePath, String key) throws TwitterException { + try { + List params = new ArrayList<>(3); + params.add("skip_status=true"); + params.add("include_entities=false"); + Response response = post(endpoint, params, new File(imagePath), key); + if (response.body() != null) { + JSONObject json = new JSONObject(response.body().string()); + if (response.code() != 200) { + throw new TwitterException(json); + } + } else { + throw new TwitterException(response); + } + } catch (IOException err) { + throw new TwitterException(err); + } catch (JSONException err) { + throw new TwitterException(err); + } + } + /** * send post without return value * diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/UserV1.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/UserV1.java index 75f92543..0f669aff 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/UserV1.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/UserV1.java @@ -15,9 +15,6 @@ import org.nuclearfog.twidda.model.User; */ class UserV1 implements User { - static final String SKIP_STAT = "skip_status=true"; - static final String INCLUDE_ENTITIES = "include_entities=true"; - private long userID; private long created; private String username; @@ -45,7 +42,7 @@ class UserV1 implements User { UserV1(JSONObject json) { - String bannerLink = json.optString("profile_banner_url"); + profileBannerUrl = json.optString("profile_banner_url"); description = json.optString("description"); username = json.optString("name"); screenName = '@' + json.optString("screen_name"); @@ -63,11 +60,6 @@ class UserV1 implements User { profileUrl = json.optString("profile_image_url_https"); created = StringTools.getTime(json.optString("created_at")); - // remove link suffix from banner URL - if (bannerLink.length() > 4) { - profileBannerUrl = bannerLink.substring(0, bannerLink.length() - 4); - } - // expand URLs JSONObject entities = json.optJSONObject("entities"); if (entities != null) { diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/utils/TLSSocketFactory.java b/app/src/main/java/org/nuclearfog/twidda/backend/utils/TLSSocketFactory.java index 1cdc35e8..e8c9c6e3 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/utils/TLSSocketFactory.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/utils/TLSSocketFactory.java @@ -1,7 +1,5 @@ package org.nuclearfog.twidda.backend.utils; -import android.os.Build; - import java.io.IOException; import java.net.InetAddress; import java.net.Socket; @@ -36,25 +34,22 @@ public class TLSSocketFactory extends SSLSocketFactory { /** * check if TLS 1.2 is enabled and enable experimental TLS 1.2 support on Pre-Lollipop devices */ - public static void getSupportTLSifNeeded() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - // check for TLS 1.2 support and activate it - try { - boolean tlsEnabled = false; - SSLParameters param = SSLContext.getDefault().getDefaultSSLParameters(); - String[] protocols = param.getProtocols(); - for (String protocol : protocols) { - if (protocol.equals(TLS_1_2) || protocol.equals(TLS_1_3)) { - tlsEnabled = true; - break; - } + public static void setSupportTLS() { + try { + boolean tlsEnabled = false; + SSLParameters param = SSLContext.getDefault().getDefaultSSLParameters(); + String[] protocols = param.getProtocols(); + for (String protocol : protocols) { + if (protocol.equals(TLS_1_2) || protocol.equals(TLS_1_3)) { + tlsEnabled = true; + break; } - if (!tlsEnabled) { - HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory()); - } - } catch (Exception err) { - err.printStackTrace(); } + if (!tlsEnabled) { + HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory()); + } + } catch (Exception err) { + err.printStackTrace(); } } diff --git a/app/src/main/res/drawable/attachment.xml b/app/src/main/res/drawable/attachment.xml new file mode 100644 index 00000000..34667512 --- /dev/null +++ b/app/src/main/res/drawable/attachment.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/twitter4j_badge.png b/app/src/main/res/drawable/twitter4j_badge.png deleted file mode 100644 index 4267fc26..00000000 Binary files a/app/src/main/res/drawable/twitter4j_badge.png and /dev/null differ diff --git a/app/src/main/res/layout/dialog_app_info.xml b/app/src/main/res/layout/dialog_app_info.xml index 7741dd7b..dee10b2a 100644 --- a/app/src/main/res/layout/dialog_app_info.xml +++ b/app/src/main/res/layout/dialog_app_info.xml @@ -15,12 +15,6 @@ android:text="@string/app_name" android:textSize="@dimen/infopopup_appname_font" /> - -