added directmessage media button, added proxy settings for picasso and okhttp

This commit is contained in:
nuclearfog 2022-01-12 19:29:23 +01:00
parent c3ec249efb
commit 7f8fd9e7bc
No known key found for this signature in database
GPG Key ID: AA0271FBE406DB98
17 changed files with 293 additions and 183 deletions

View File

@ -715,9 +715,11 @@ public class UserProfile extends AppCompatActivity implements OnClickListener, O
* @param err Engine Exception * @param err Engine Exception
*/ */
public void onError(@Nullable ErrorHandler.TwitterError err) { public void onError(@Nullable ErrorHandler.TwitterError err) {
ErrorHandler.handleFailure(this, err); // fixme ErrorHandler.handleFailure(this, err);
//if (user == null || (err != null && err.resourceNotFound())) { if (user == null || (err != null
// finish(); && (err.getErrorType() == ErrorHandler.TwitterError.RESOURCE_NOT_FOUND
//} || err.getErrorType() == ErrorHandler.TwitterError.USER_NOT_FOUND))) {
finish();
}
} }
} }

View File

@ -171,6 +171,15 @@ public class MessageAdapter extends Adapter<ViewHolder> {
} }
} }
}); });
vh.mediaButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = vh.getLayoutPosition();
if (position != NO_POSITION) {
itemClickListener.onClick(data.get(position), OnItemSelected.Action.MEDIA);
}
}
});
return vh; return vh;
} else { } else {
final Footer footer = new Footer(parent, settings, false); final Footer footer = new Footer(parent, settings, false);
@ -216,6 +225,11 @@ public class MessageAdapter extends Adapter<ViewHolder> {
} else { } else {
holder.lockedIcon.setVisibility(GONE); holder.lockedIcon.setVisibility(GONE);
} }
if (message.getMedia() != null && !message.getMedia().isEmpty()) {
holder.mediaButton.setVisibility(VISIBLE);
} else {
holder.mediaButton.setVisibility(GONE);
}
if (settings.imagesEnabled() && !sender.getImageUrl().isEmpty()) { if (settings.imagesEnabled() && !sender.getImageUrl().isEmpty()) {
String pbLink = sender.getImageUrl(); String pbLink = sender.getImageUrl();
if (!sender.hasDefaultProfileImage()) if (!sender.hasDefaultProfileImage())
@ -255,6 +269,7 @@ public class MessageAdapter extends Adapter<ViewHolder> {
ANSWER, ANSWER,
DELETE, DELETE,
PROFILE, PROFILE,
MEDIA
} }
/** /**

View File

@ -4,6 +4,7 @@ import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@ -27,6 +28,7 @@ public class MessageHolder extends ViewHolder {
public final TextView[] textViews = new TextView[5]; public final TextView[] textViews = new TextView[5];
public final Button[] buttons = new Button[2]; public final Button[] buttons = new Button[2];
public final ImageView profile_img, verifiedIcon, lockedIcon; public final ImageView profile_img, verifiedIcon, lockedIcon;
public final ImageButton mediaButton;
/** /**
* @param parent Parent view from adapter * @param parent Parent view from adapter
@ -39,6 +41,7 @@ public class MessageHolder extends ViewHolder {
profile_img = itemView.findViewById(R.id.dm_profile_img); profile_img = itemView.findViewById(R.id.dm_profile_img);
verifiedIcon = itemView.findViewById(R.id.dm_user_verified); verifiedIcon = itemView.findViewById(R.id.dm_user_verified);
lockedIcon = itemView.findViewById(R.id.dm_user_locked); lockedIcon = itemView.findViewById(R.id.dm_user_locked);
mediaButton = itemView.findViewById(R.id.dm_media);
textViews[0] = itemView.findViewById(R.id.dm_username); textViews[0] = itemView.findViewById(R.id.dm_username);
textViews[1] = itemView.findViewById(R.id.dm_screenname); textViews[1] = itemView.findViewById(R.id.dm_screenname);
textViews[2] = itemView.findViewById(R.id.dm_receiver); textViews[2] = itemView.findViewById(R.id.dm_receiver);
@ -50,6 +53,7 @@ public class MessageHolder extends ViewHolder {
receiver_icon.setImageResource(R.drawable.right); receiver_icon.setImageResource(R.drawable.right);
verifiedIcon.setImageResource(R.drawable.verify); verifiedIcon.setImageResource(R.drawable.verify);
lockedIcon.setImageResource(R.drawable.lock); lockedIcon.setImageResource(R.drawable.lock);
mediaButton.setImageResource(R.drawable.image);
// theme views // theme views
for (TextView tv : textViews) { for (TextView tv : textViews) {
tv.setTextColor(settings.getFontColor()); tv.setTextColor(settings.getFontColor());
@ -62,6 +66,7 @@ public class MessageHolder extends ViewHolder {
} }
verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN); verifiedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN); lockedIcon.setColorFilter(settings.getIconColor(), SRC_IN);
mediaButton.setColorFilter(settings.getIconColor(), SRC_IN);
receiver_icon.setColorFilter(settings.getIconColor(), SRC_IN); receiver_icon.setColorFilter(settings.getIconColor(), SRC_IN);
background.setCardBackgroundColor(settings.getCardColor()); background.setCardBackgroundColor(settings.getCardColor());
// make links clickable // make links clickable

View File

@ -22,6 +22,13 @@ class TweetV1 implements Tweet {
*/ */
static final String EXT_MODE = "tweet_mode=extended"; static final String EXT_MODE = "tweet_mode=extended";
/**
* include ID of the reteet if available
*/
static final String INCL_RT_ID = "include_my_retweet=true";
static final String INCL_ENTITIES = "include_entities=true";
/** /**
* twitter video/gif MIME * twitter video/gif MIME
*/ */
@ -65,7 +72,7 @@ class TweetV1 implements Tweet {
String replyName = json.optString("in_reply_to_screen_name"); String replyName = json.optString("in_reply_to_screen_name");
JSONObject user = json.getJSONObject("user"); JSONObject user = json.getJSONObject("user");
JSONObject quoted_tweet = json.optJSONObject("quoted_status"); JSONObject quoted_tweet = json.optJSONObject("retweeted_status");
JSONObject user_retweet = json.optJSONObject("current_user_retweet"); JSONObject user_retweet = json.optJSONObject("current_user_retweet");
JSONObject entities = json.optJSONObject("entities"); JSONObject entities = json.optJSONObject("entities");
JSONObject extEntities = json.optJSONObject("extended_entities"); JSONObject extEntities = json.optJSONObject("extended_entities");

View File

@ -12,6 +12,9 @@ import org.json.JSONObject;
import org.nuclearfog.twidda.backend.lists.Directmessages; import org.nuclearfog.twidda.backend.lists.Directmessages;
import org.nuclearfog.twidda.backend.lists.UserLists; import org.nuclearfog.twidda.backend.lists.UserLists;
import org.nuclearfog.twidda.backend.lists.Users; import org.nuclearfog.twidda.backend.lists.Users;
import org.nuclearfog.twidda.backend.proxy.ProxyAuthenticator;
import org.nuclearfog.twidda.backend.proxy.ProxySetup;
import org.nuclearfog.twidda.backend.proxy.UserProxy;
import org.nuclearfog.twidda.backend.utils.StringTools; import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.backend.utils.TLSSocketFactory; import org.nuclearfog.twidda.backend.utils.TLSSocketFactory;
import org.nuclearfog.twidda.backend.utils.Tokens; import org.nuclearfog.twidda.backend.utils.Tokens;
@ -27,6 +30,7 @@ import org.nuclearfog.twidda.model.UserList;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.ArrayList; import java.util.ArrayList;
@ -122,8 +126,17 @@ public class Twitter {
private Twitter(Context context) { private Twitter(Context context) {
settings = GlobalSettings.getInstance(context);
tokens = Tokens.getInstance(context);
filterList = new ExcludeDatabase(context);
OkHttpClient.Builder builder = new OkHttpClient.Builder(); OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.writeTimeout(60, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS); builder.writeTimeout(60, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS);
// setup proxy settings
builder.proxy(UserProxy.get(settings));
builder.proxyAuthenticator(new ProxyAuthenticator(settings));
// apply global proxy settings
ProxySetup.setConnection(settings);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// set TLS 1.2 support for default connections // set TLS 1.2 support for default connections
TLSSocketFactory.setSupportTLS(); TLSSocketFactory.setSupportTLS();
@ -134,14 +147,10 @@ public class Twitter {
X509TrustManager manager = (X509TrustManager) factory.getTrustManagers()[0]; X509TrustManager manager = (X509TrustManager) factory.getTrustManagers()[0];
builder.sslSocketFactory(new TLSSocketFactory(), manager); builder.sslSocketFactory(new TLSSocketFactory(), manager);
} catch (Exception e) { } catch (Exception e) {
// ignore, user default setting // ignore, use default setting
} }
} }
// todo add proxy settings
client = builder.build(); client = builder.build();
tokens = Tokens.getInstance(context);
settings = GlobalSettings.getInstance(context);
filterList = new ExcludeDatabase(context);
} }
/** /**
@ -604,7 +613,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getHomeTimeline(long minId, long maxId) throws TwitterException { public List<Tweet> getHomeTimeline(long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(5); List<String> params = new ArrayList<>(7);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 0) if (maxId > 0)
@ -620,7 +629,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getMentionTimeline(long minId, long maxId) throws TwitterException { public List<Tweet> getMentionTimeline(long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(5); List<String> params = new ArrayList<>(7);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -637,7 +646,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getUserTimeline(long userId, long minId, long maxId) throws TwitterException { public List<Tweet> getUserTimeline(long userId, long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(6); List<String> params = new ArrayList<>(8);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -655,7 +664,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getUserTimeline(String screen_name, long minId, long maxId) throws TwitterException { public List<Tweet> getUserTimeline(String screen_name, long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(6); List<String> params = new ArrayList<>(8);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -673,7 +682,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getUserFavorits(long userId, long minId, long maxId) throws TwitterException { public List<Tweet> getUserFavorits(long userId, long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(6); List<String> params = new ArrayList<>(8);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -691,7 +700,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getUserFavorits(String screen_name, long minId, long maxId) throws TwitterException { public List<Tweet> getUserFavorits(String screen_name, long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(6); List<String> params = new ArrayList<>(8);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -709,7 +718,7 @@ public class Twitter {
* @return list of tweets * @return list of tweets
*/ */
public List<Tweet> getUserlistTweets(long listId, long minId, long maxId) throws TwitterException { public List<Tweet> getUserlistTweets(long listId, long minId, long maxId) throws TwitterException {
List<String> params = new ArrayList<>(6); List<String> params = new ArrayList<>(8);
if (minId > 0) if (minId > 0)
params.add("since_id=" + minId); params.add("since_id=" + minId);
if (maxId > 1) if (maxId > 1)
@ -1174,8 +1183,8 @@ public class Twitter {
if (link.startsWith("https://ton.twitter.com/")) { if (link.startsWith("https://ton.twitter.com/")) {
Response response = get(link, new ArrayList<>(0)); Response response = get(link, new ArrayList<>(0));
if (response.code() == 200) { if (response.code() == 200) {
byte[] data = response.body().bytes(); InputStream is = response.body().byteStream();
return BitmapFactory.decodeByteArray(data, 0, 0); return BitmapFactory.decodeStream(is);
} else { } else {
throw new TwitterException(response); throw new TwitterException(response);
} }
@ -1237,6 +1246,8 @@ public class Twitter {
private List<Tweet> getTweets1(String endpoint, List<String> params) throws TwitterException { private List<Tweet> getTweets1(String endpoint, List<String> params) throws TwitterException {
try { try {
params.add(TweetV1.EXT_MODE); params.add(TweetV1.EXT_MODE);
params.add(TweetV1.INCL_RT_ID);
params.add(TweetV1.INCL_ENTITIES);
params.add("count=" + settings.getListSize()); params.add("count=" + settings.getListSize());
Response response = get(endpoint, params); Response response = get(endpoint, params);
if (response.body() != null) { if (response.body() != null) {
@ -1275,6 +1286,8 @@ public class Twitter {
private Tweet getTweet(String endpoint, List<String> params) throws TwitterException { private Tweet getTweet(String endpoint, List<String> params) throws TwitterException {
try { try {
params.add(TweetV1.EXT_MODE); params.add(TweetV1.EXT_MODE);
params.add(TweetV1.INCL_RT_ID);
params.add(TweetV1.INCL_ENTITIES);
Response response; Response response;
if (endpoint.equals(SHOW_TWEET)) { if (endpoint.equals(SHOW_TWEET)) {
response = get(endpoint, params); response = get(endpoint, params);

View File

@ -0,0 +1,38 @@
package org.nuclearfog.twidda.backend.proxy;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* This class hosts {@link UserProxy} and provides the proxy settings on demand
*
* @author nuclearfog
*/
public class AppProxySelector extends ProxySelector {
private List<Proxy> proxyList;
public AppProxySelector(GlobalSettings settings) {
Proxy httpsProxy = UserProxy.get(settings);
proxyList = new ArrayList<>(2);
proxyList.add(httpsProxy);
}
@Override
public List<Proxy> select(URI uri) {
return proxyList;
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// ignore
}
}

View File

@ -0,0 +1,47 @@
package org.nuclearfog.twidda.backend.proxy;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import okhttp3.Credentials;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;
/**
* this class provides proxy authentication
* when proxy settings changes, the new setup will be applied immediately
*
* @author nuclearfog
*/
public class ProxyAuthenticator extends Authenticator implements okhttp3.Authenticator {
private GlobalSettings settings;
public ProxyAuthenticator(GlobalSettings settings) {
this.settings = settings;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (settings.isProxyAuthSet()) {
String username = settings.getProxyUser();
char[] password = settings.getProxyPass().toCharArray();
return new PasswordAuthentication(username, password);
}
return new PasswordAuthentication("", new char[0]);
}
@Override
public Request authenticate(Route route, Response response) {
if (settings.isProxyAuthSet()) {
String credential = Credentials.basic(settings.getProxyUser(), settings.getProxyPass());
return response.request().newBuilder().header("Proxy-Authorization", credential).build();
}
return null;
}
}

View File

@ -0,0 +1,39 @@
package org.nuclearfog.twidda.backend.proxy;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.net.Authenticator;
import java.net.ProxySelector;
/**
* This class setups a proxy for libraries which don't support proxy setup
* like VideoView
*
* @author nuclearfog
*/
public class ProxySetup {
private ProxySetup() {
}
/**
* initializes the proxy connection with login
*
* @param settings App settings
*/
public static void setConnection(GlobalSettings settings) {
init(settings);
}
private static void init(GlobalSettings settings) {
AppProxySelector proxyConnection = new AppProxySelector(settings);
ProxyAuthenticator proxyLogin = new ProxyAuthenticator(settings);
try {
ProxySelector.setDefault(proxyConnection);
Authenticator.setDefault(proxyLogin);
} catch (SecurityException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,34 @@
package org.nuclearfog.twidda.backend.proxy;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
/**
* custom proxy implementation
*
* @author nuclearfog
*/
public class UserProxy extends Proxy {
private UserProxy(Type type, SocketAddress sa) {
super(type, sa);
}
/**
* return proxy instance with custom settings
* @param settings app settings
* @return proxy instance
*/
public static Proxy get(GlobalSettings settings) {
if (settings.isProxyEnabled()) {
String proxyHost = settings.getProxyHost();
int proxyPort = settings.getProxyPortNumber();
InetSocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
return new UserProxy(Type.HTTP, addr);
}
return Proxy.NO_PROXY;
}
}

View File

@ -147,9 +147,6 @@ public final class ErrorHandler {
int REQUEST_FORBIDDEN = 18; int REQUEST_FORBIDDEN = 18;
int APP_SUSPENDED = 19; int APP_SUSPENDED = 19;
int ERROR_API_ACCESS_DENIED = 20; int ERROR_API_ACCESS_DENIED = 20;
int FILENOTFOUND = 23;
int TOKENNOTSET = 22;
int BITMAP_FAILURE = 21;
int getErrorType(); int getErrorType();

View File

@ -6,6 +6,10 @@ import android.os.Build;
import com.squareup.picasso.OkHttp3Downloader; import com.squareup.picasso.OkHttp3Downloader;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.nuclearfog.twidda.backend.proxy.ProxyAuthenticator;
import org.nuclearfog.twidda.backend.proxy.UserProxy;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.security.KeyStore; import java.security.KeyStore;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
@ -29,24 +33,37 @@ public class PicassoBuilder {
*/ */
public static Picasso get(Context context) { public static Picasso get(Context context) {
if (downloader == null) { if (downloader == null) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { GlobalSettings settings = GlobalSettings.getInstance(context);
try { init(settings);
// try to enable TLS 1.2 support for picasso
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);
X509TrustManager manager = (X509TrustManager) factory.getTrustManagers()[0];
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(new TLSSocketFactory(), manager);
downloader = new OkHttp3Downloader(builder.build());
} catch (Exception e) {
// fallback to default downloader
downloader = new OkHttp3Downloader(context);
}
} else {
// use default downloader
downloader = new OkHttp3Downloader(context);
}
} }
return new Picasso.Builder(context).downloader(downloader).build(); return new Picasso.Builder(context).downloader(downloader).build();
} }
private static void init(GlobalSettings settings) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// setup proxy
if (settings.isProxyEnabled()) {
builder.proxy(UserProxy.get(settings));
if (settings.isProxyAuthSet()) {
builder.proxyAuthenticator(new ProxyAuthenticator(settings));
}
}
// setup TLS 1.2 support if needed
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
try {
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init((KeyStore) null);
X509TrustManager manager = (X509TrustManager) factory.getTrustManagers()[0];
builder.sslSocketFactory(new TLSSocketFactory(), manager);
downloader = new OkHttp3Downloader(builder.build());
return;
} catch (Exception e) {
// ignore, try without TLS 1.2 support
}
}
downloader = new OkHttp3Downloader(builder.build());
}
} }

View File

@ -1,128 +0,0 @@
package org.nuclearfog.twidda.backend.utils;
import org.nuclearfog.twidda.database.GlobalSettings;
import java.io.IOException;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* Creates a https proxy connection for all connections except Twitter4J
*
* @author nuclearfog
*/
public class ProxySetup {
private ProxySetup() {
}
/**
* initializes the proxy connection with login
*
* @param settings App settings
*/
public static void setConnection(GlobalSettings settings) {
ProxyConnection proxyConnection;
ProxyAuthenticator proxyLogin;
if (settings.isProxyEnabled()) {
proxyConnection = new ProxyConnection(settings);
} else {
proxyConnection = new ProxyConnection();
}
if (settings.isProxyAuthSet()) {
proxyLogin = new ProxyAuthenticator(settings);
} else {
proxyLogin = new ProxyAuthenticator();
}
try {
ProxySelector.setDefault(proxyConnection);
Authenticator.setDefault(proxyLogin);
} catch (SecurityException sErr) {
sErr.printStackTrace();
}
}
/**
* Connect to a proxy server
*/
private static class ProxyConnection extends ProxySelector {
private List<Proxy> proxyList;
/**
* Creates a direct connection without proxy
*/
ProxyConnection() {
proxyList = new ArrayList<>(1);
proxyList.add(Proxy.NO_PROXY);
}
/**
* set system proxy for all http requests
*
* @param settings App settings
*/
ProxyConnection(GlobalSettings settings) {
String proxyHost = settings.getProxyHost();
int proxyPort = settings.getProxyPortNumber();
InetSocketAddress socket = new InetSocketAddress(proxyHost, proxyPort);
Proxy httpsProxy = new Proxy(Proxy.Type.HTTP, socket);
proxyList = new ArrayList<>(1);
proxyList.add(httpsProxy);
}
@Override
public List<Proxy> select(URI uri) {
return proxyList;
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
// ignore to force using proxy and avoid data leak
}
}
/**
* Creates an authenticator for proxy login
*/
private static class ProxyAuthenticator extends Authenticator {
private static final String NO_NM = "";
private static final char[] NO_PW = {};
private PasswordAuthentication proxyPass;
/**
* unset all login information for proxy
*/
ProxyAuthenticator() {
proxyPass = new PasswordAuthentication(NO_NM, NO_PW);
}
/**
* set proxy login
*
* @param settings App settings
*/
ProxyAuthenticator(GlobalSettings settings) {
String username = settings.getProxyUser();
char[] password = settings.getProxyPass().toCharArray();
proxyPass = new PasswordAuthentication(username, password);
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return proxyPass;
}
}
}

View File

@ -11,6 +11,7 @@ class DirectMessageDB implements DirectMessage {
private long id; private long id;
private long time; private long time;
private String text; private String text;
private String media = "";
private User sender; private User sender;
private User receiver; private User receiver;
@ -50,6 +51,6 @@ class DirectMessageDB implements DirectMessage {
@Override @Override
public String getMedia() { public String getMedia() {
return ""; return media;
} }
} }

View File

@ -12,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.activities.MediaViewer;
import org.nuclearfog.twidda.activities.MessageEditor; import org.nuclearfog.twidda.activities.MessageEditor;
import org.nuclearfog.twidda.activities.SearchPage; import org.nuclearfog.twidda.activities.SearchPage;
import org.nuclearfog.twidda.activities.TweetActivity; import org.nuclearfog.twidda.activities.TweetActivity;
@ -28,6 +29,9 @@ import org.nuclearfog.twidda.model.DirectMessage;
import static android.os.AsyncTask.Status.RUNNING; import static android.os.AsyncTask.Status.RUNNING;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_LINK;
import static org.nuclearfog.twidda.activities.MediaViewer.KEY_MEDIA_TYPE;
import static org.nuclearfog.twidda.activities.MediaViewer.MEDIAVIEWER_IMAGE;
import static org.nuclearfog.twidda.activities.MessageEditor.KEY_DM_PREFIX; import static org.nuclearfog.twidda.activities.MessageEditor.KEY_DM_PREFIX;
import static org.nuclearfog.twidda.activities.SearchPage.KEY_SEARCH_QUERY; import static org.nuclearfog.twidda.activities.SearchPage.KEY_SEARCH_QUERY;
import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_ID; import static org.nuclearfog.twidda.activities.TweetActivity.KEY_TWEET_ID;
@ -149,6 +153,13 @@ public class MessageFragment extends ListFragment implements OnItemSelected, OnC
profile.putExtra(KEY_PROFILE_DATA, message.getSender()); profile.putExtra(KEY_PROFILE_DATA, message.getSender());
startActivity(profile); startActivity(profile);
break; break;
case MEDIA:
Intent mediaIntent = new Intent(requireContext(), MediaViewer.class);
mediaIntent.putExtra(KEY_MEDIA_LINK, new String[]{message.getMedia()});
mediaIntent.putExtra(KEY_MEDIA_TYPE, MEDIAVIEWER_IMAGE);
startActivity(mediaIntent);
break;
} }
} }
} }

View File

@ -28,7 +28,6 @@
app:layout_constraintStart_toEndOf="@id/dm_profile_img" app:layout_constraintStart_toEndOf="@id/dm_profile_img"
app:layout_constraintTop_toTopOf="@id/dm_username" app:layout_constraintTop_toTopOf="@id/dm_username"
app:layout_constraintBottom_toBottomOf="@id/dm_username" app:layout_constraintBottom_toBottomOf="@id/dm_username"
app:layout_constraintEnd_toStartOf="@id/dm_username"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
<TextView <TextView
@ -52,7 +51,6 @@
android:singleLine="true" android:singleLine="true"
android:textAlignment="gravity" android:textAlignment="gravity"
android:textSize="@dimen/dmitem_textsize_date" android:textSize="@dimen/dmitem_textsize_date"
app:layout_constraintStart_toEndOf="@id/dm_username"
app:layout_constraintTop_toTopOf="@id/dm_username" app:layout_constraintTop_toTopOf="@id/dm_username"
app:layout_constraintBottom_toBottomOf="@id/dm_username" app:layout_constraintBottom_toBottomOf="@id/dm_username"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
@ -66,9 +64,6 @@
app:layout_constraintStart_toEndOf="@id/dm_profile_img" app:layout_constraintStart_toEndOf="@id/dm_profile_img"
app:layout_constraintTop_toTopOf="@id/dm_screenname" app:layout_constraintTop_toTopOf="@id/dm_screenname"
app:layout_constraintBottom_toBottomOf="@id/dm_screenname" app:layout_constraintBottom_toBottomOf="@id/dm_screenname"
app:layout_constraintEnd_toStartOf="@id/dm_screenname"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
@ -85,7 +80,6 @@
app:layout_constraintStart_toEndOf="@id/dm_user_locked" app:layout_constraintStart_toEndOf="@id/dm_user_locked"
app:layout_constraintTop_toBottomOf="@id/dm_username" app:layout_constraintTop_toBottomOf="@id/dm_username"
app:layout_constraintBottom_toBottomOf="@id/dm_profile_img" app:layout_constraintBottom_toBottomOf="@id/dm_profile_img"
app:layout_constraintEnd_toStartOf="@id/dm_receiver_icon"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed" /> app:layout_constraintHorizontal_chainStyle="packed" />
@ -95,12 +89,9 @@
android:layout_height="@dimen/dmitem_icon_size" android:layout_height="@dimen/dmitem_icon_size"
android:layout_marginLeft="@dimen/dmitem_padding_drawable" android:layout_marginLeft="@dimen/dmitem_padding_drawable"
android:layout_marginStart="@dimen/dmitem_padding_drawable" android:layout_marginStart="@dimen/dmitem_padding_drawable"
android:layout_marginRight="@dimen/dmitem_padding_drawable"
android:layout_marginEnd="@dimen/dmitem_padding_drawable"
app:layout_constraintStart_toEndOf="@id/dm_screenname" app:layout_constraintStart_toEndOf="@id/dm_screenname"
app:layout_constraintTop_toTopOf="@id/dm_screenname" app:layout_constraintTop_toTopOf="@id/dm_screenname"
app:layout_constraintBottom_toBottomOf="@id/dm_screenname" app:layout_constraintBottom_toBottomOf="@id/dm_screenname"
app:layout_constraintEnd_toStartOf="@id/dm_receiver"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
@ -112,6 +103,8 @@
android:drawablePadding="@dimen/dmitem_padding_drawable" android:drawablePadding="@dimen/dmitem_padding_drawable"
android:singleLine="true" android:singleLine="true"
android:textSize="@dimen/dmitem_textsize_name" android:textSize="@dimen/dmitem_textsize_name"
android:layout_marginLeft="@dimen/dmitem_padding_drawable"
android:layout_marginStart="@dimen/dmitem_padding_drawable"
app:layout_constraintStart_toEndOf="@id/dm_receiver_icon" app:layout_constraintStart_toEndOf="@id/dm_receiver_icon"
app:layout_constraintTop_toTopOf="@id/dm_screenname" app:layout_constraintTop_toTopOf="@id/dm_screenname"
app:layout_constraintBottom_toBottomOf="@id/dm_screenname" app:layout_constraintBottom_toBottomOf="@id/dm_screenname"
@ -133,8 +126,27 @@
android:linksClickable="true" android:linksClickable="true"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dm_profile_barrier" app:layout_constraintTop_toBottomOf="@id/dm_profile_barrier"
app:layout_constraintBottom_toTopOf="@id/dm_media"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="@+id/dm_media"
android:layout_width="@dimen/dmitem_button_media_width"
android:layout_height="@dimen/dmitem_button_media_height"
android:visibility="gone"
android:contentDescription="@string/directmessage_media_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/dm_button_barrier"
app:layout_constraintEnd_toEndOf="parent"
style="@style/RoundButton" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/dm_button_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="dm_answer,dm_delete"/>
<Button <Button
android:id="@+id/dm_answer" android:id="@+id/dm_answer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -148,10 +160,9 @@
android:text="@string/dm_answer" android:text="@string/dm_answer"
android:textSize="@dimen/dmitem_textsize_button" android:textSize="@dimen/dmitem_textsize_button"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dm_message" app:layout_constraintTop_toBottomOf="@id/dm_button_barrier"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/dm_delete" app:layout_constraintEnd_toStartOf="@id/dm_delete"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
style="@style/FeedbackButton" /> style="@style/FeedbackButton" />
<Button <Button
@ -165,10 +176,8 @@
android:text="@string/delete_dm" android:text="@string/delete_dm"
android:textSize="@dimen/dmitem_textsize_button" android:textSize="@dimen/dmitem_textsize_button"
app:layout_constraintStart_toEndOf="@id/dm_answer" app:layout_constraintStart_toEndOf="@id/dm_answer"
app:layout_constraintTop_toBottomOf="@id/dm_message" app:layout_constraintTop_toBottomOf="@id/dm_button_barrier"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
style="@style/FeedbackButton" /> style="@style/FeedbackButton" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -119,6 +119,8 @@
<dimen name="dmitem_button_margin">5dp</dimen> <dimen name="dmitem_button_margin">5dp</dimen>
<dimen name="dmitem_margin_layout">5dp</dimen> <dimen name="dmitem_margin_layout">5dp</dimen>
<dimen name="dmitem_icon_size">16sp</dimen> <dimen name="dmitem_icon_size">16sp</dimen>
<dimen name="dmitem_button_media_width">48dp</dimen>
<dimen name="dmitem_button_media_height">30dp</dimen>
<!--dimens of page_login.xml--> <!--dimens of page_login.xml-->
<dimen name="loginpage_toolbar_height">@dimen/toolbar_height</dimen> <dimen name="loginpage_toolbar_height">@dimen/toolbar_height</dimen>

View File

@ -38,6 +38,7 @@
<string name="confirm_delete_database">clear app data?</string> <string name="confirm_delete_database">clear app data?</string>
<string name="tweet_sent_from">"sent from: "</string> <string name="tweet_sent_from">"sent from: "</string>
<string name="directmessage">Directmessage</string> <string name="directmessage">Directmessage</string>
<string name="directmessage_media_button">media attachment</string>
<string name="username">Username</string> <string name="username">Username</string>
<string name="dm_message">Message</string> <string name="dm_message">Message</string>
<string name="confirm_cancel_message">discard message?</string> <string name="confirm_cancel_message">discard message?</string>