diff --git a/app/build.gradle b/app/build.gradle index 00107ffe..0d76bda3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'org.nuclearfog.twidda' minSdkVersion 16 targetSdkVersion 30 - versionCode 30 - versionName '1.8.6' + versionCode 31 + versionName '1.8.7' // limiting language support for smaller APK size resConfigs 'en', 'de-rDE' vectorDrawables.useSupportLibrary true diff --git a/app/src/main/java/org/nuclearfog/twidda/activity/AppSettings.java b/app/src/main/java/org/nuclearfog/twidda/activity/AppSettings.java index 4de11556..3492d922 100644 --- a/app/src/main/java/org/nuclearfog/twidda/activity/AppSettings.java +++ b/app/src/main/java/org/nuclearfog/twidda/activity/AppSettings.java @@ -75,13 +75,14 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O private LocationLoader locationAsync; private LocationAdapter locationAdapter; - private Dialog proxyDialog, databaseDialog, logoutDialog, color_dialog_selector; - private EditText proxyAddr, proxyPort, proxyUser, proxyPass; - private CompoundButton enableProxy, enableAuth, hqImage; + private Dialog connectDialog, databaseDialog, logoutDialog, color_dialog_selector; + private EditText proxyAddr, proxyPort, proxyUser, proxyPass, api_key1, api_key2; + private CompoundButton enableProxy, enableAuth, hqImage, enableAPI; private SeekBar listSizeSelector; private Spinner locationSpinner; private TextView list_size; private Button[] colorButtons; + private View layout_key, layout_proxy, layout_auth_en, layout_auth; private View root; private ColorMode mode = ColorMode.NONE; @@ -103,12 +104,21 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O enableProxy = findViewById(R.id.settings_enable_proxy); enableAuth = findViewById(R.id.settings_enable_auth); hqImage = findViewById(R.id.settings_image_hq); + enableAPI = findViewById(R.id.settings_set_custom_keys); locationSpinner = findViewById(R.id.spinner_woeid); - proxyAddr = findViewById(R.id.edit_proxyadress); - proxyPort = findViewById(R.id.edit_proxyport); + proxyAddr = findViewById(R.id.edit_proxy_address); + proxyPort = findViewById(R.id.edit_proxy_port); proxyUser = findViewById(R.id.edit_proxyuser); proxyPass = findViewById(R.id.edit_proxypass); + api_key1 = findViewById(R.id.settings_custom_key1); + api_key2 = findViewById(R.id.settings_custom_key2); list_size = findViewById(R.id.settings_list_size); + + layout_proxy = findViewById(R.id.settings_layout_proxy); + layout_auth_en = findViewById(R.id.settings_layout_auth_enable); + layout_auth = findViewById(R.id.settings_layout_proxy_auth); + layout_key = findViewById(R.id.settings_layout_key); + root = findViewById(R.id.settings_layout); TypedArray buttons = getResources().obtainTypedArray(R.array.color_button); @@ -125,6 +135,14 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O trend_card.setVisibility(GONE); user_card.setVisibility(GONE); } + if (!settings.isProxyEnabled()) { + layout_proxy.setVisibility(GONE); + layout_auth_en.setVisibility(GONE); + layout_auth.setVisibility(GONE); + } + if (!settings.isCustomApiSet()) { + layout_key.setVisibility(GONE); + } locationAdapter = new LocationAdapter(settings); locationAdapter.addTop(settings.getTrendLocation()); locationSpinner.setAdapter(locationAdapter); @@ -138,19 +156,21 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O setButtonColors(); toggleImg.setChecked(settings.getImageLoad()); toggleAns.setChecked(settings.getAnswerLoad()); + enableAPI.setChecked(settings.isCustomApiSet()); proxyAddr.setText(settings.getProxyHost()); proxyPort.setText(settings.getProxyPort()); proxyUser.setText(settings.getProxyUser()); proxyPass.setText(settings.getProxyPass()); + api_key1.setText(settings.getConsumerKey()); + api_key2.setText(settings.getConsumerSecret()); list_size.setText(Integer.toString(settings.getListSize())); listSizeSelector.setProgress(settings.getListSize() / 10 - 1); enableProxy.setChecked(settings.isProxyEnabled()); enableAuth.setChecked(settings.isProxyAuthSet()); hqImage.setEnabled(settings.getImageLoad()); hqImage.setChecked(settings.getImageQuality()); - setProxySetupVisibility(settings.isProxyEnabled(), settings.isProxyAuthSet()); - proxyDialog = DialogBuilder.create(this, WRONG_PROXY, this); + connectDialog = DialogBuilder.create(this, WRONG_PROXY, this); databaseDialog = DialogBuilder.create(this, DEL_DATABASE, this); logoutDialog = DialogBuilder.create(this, LOGOUT_APP, this); @@ -160,6 +180,7 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O delButton.setOnClickListener(this); toggleImg.setOnCheckedChangeListener(this); toggleAns.setOnCheckedChangeListener(this); + enableAPI.setOnCheckedChangeListener(this); enableProxy.setOnCheckedChangeListener(this); enableAuth.setOnCheckedChangeListener(this); hqImage.setOnCheckedChangeListener(this); @@ -181,12 +202,12 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O @Override public void onBackPressed() { - if (saveProxySettings()) { + if (saveConnectionSettings()) { TwitterEngine.resetTwitter(); super.onBackPressed(); } else { - if (!proxyDialog.isShowing()) { - proxyDialog.show(); + if (!connectDialog.isShowing()) { + connectDialog.show(); } } } @@ -356,16 +377,35 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O } // enable proxy settings else if (viewId == R.id.settings_enable_proxy) { - setProxySetupVisibility(checked, checked & enableAuth.isChecked()); + if (checked) { + layout_proxy.setVisibility(VISIBLE); + layout_auth_en.setVisibility(VISIBLE); + } else { + layout_proxy.setVisibility(GONE); + layout_auth_en.setVisibility(GONE); + enableAuth.setChecked(false); + } } //enable proxy authentication else if (viewId == R.id.settings_enable_auth) { - setProxySetupVisibility(true, checked); + if (checked) { + layout_auth.setVisibility(VISIBLE); + } else { + layout_auth.setVisibility(GONE); + } } // enable high quality images else if (viewId == R.id.settings_image_hq) { settings.setHighQualityImage(checked); } + // enable custom API setup + else if (viewId == R.id.settings_set_custom_keys) { + if (checked) { + layout_key.setVisibility(VISIBLE); + } else { + layout_key.setVisibility(GONE); + } + } } @@ -463,28 +503,12 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O } } - /** - * set visibility of proxy layouts - * - * @param proxySetup visibility of proxy setup - * @param proxyLogin visibility of proxy login - */ - private void setProxySetupVisibility(boolean proxySetup, boolean proxyLogin) { - int setupVisibility = proxySetup ? VISIBLE : GONE; - int authVisibility = proxyLogin ? VISIBLE : GONE; - proxyAddr.setVisibility(setupVisibility); - proxyPort.setVisibility(setupVisibility); - enableAuth.setVisibility(setupVisibility); - proxyUser.setVisibility(authVisibility); - proxyPass.setVisibility(authVisibility); - } - /** * check proxy settings and save them if they are correct * * @return true if settings are saved successfully */ - private boolean saveProxySettings() { + private boolean saveConnectionSettings() { boolean checkPassed = true; if (enableProxy.isChecked()) { checkPassed = proxyAddr.length() > 0 && proxyPort.length() > 0; @@ -515,6 +539,17 @@ public class AppSettings extends AppCompatActivity implements OnClickListener, O } else { settings.clearProxyServer(); } + if (enableAPI.isChecked()) { + if (api_key1.length() > 0 && api_key2.length() > 0) { + String key1 = api_key1.getText().toString(); + String key2 = api_key2.getText().toString(); + settings.setCustomAPI(key1, key2); + } else { + checkPassed = false; + } + } else { + settings.removeCustomAPI(); + } return checkPassed; } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/engine/EngineException.java b/app/src/main/java/org/nuclearfog/twidda/backend/engine/EngineException.java index 4888d536..ed4b2dda 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/engine/EngineException.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/engine/EngineException.java @@ -29,6 +29,7 @@ public class EngineException extends Exception { NO_CONNECTION, IMAGE_NOT_LOADED, ACCOUNT_UPDATE_FAILED, + ERROR_API_ACCESS_DENIED, ERROR_NOT_DEFINED } @@ -104,6 +105,10 @@ public class EngineException extends Exception { errorType = ErrorType.NO_DM_TO_USER; break; + case 261: + errorType = ErrorType.ERROR_API_ACCESS_DENIED; + break; + case 354: errorType = ErrorType.DM_TOO_LONG; break; diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java b/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java index 6d2c017b..ec76dddf 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/engine/TwitterEngine.java @@ -73,8 +73,13 @@ public class TwitterEngine { */ private void initTwitter() { ConfigurationBuilder builder = new ConfigurationBuilder(); - builder.setOAuthConsumerKey(Constants.TWITTER_CONSUMER_KEY); - builder.setOAuthConsumerSecret(Constants.TWITTER_CONSUMER_SECRET); + if (settings.isCustomApiSet()) { + builder.setOAuthConsumerKey(settings.getConsumerKey()); + builder.setOAuthConsumerSecret(settings.getConsumerSecret()); + } else { + builder.setOAuthConsumerKey(Constants.TWITTER_CONSUMER_KEY); + builder.setOAuthConsumerSecret(Constants.TWITTER_CONSUMER_SECRET); + } // Twitter4J has its own proxy settings if (settings.isProxyEnabled()) { builder.setHttpProxyHost(settings.getProxyHost()); diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/utils/DialogBuilder.java b/app/src/main/java/org/nuclearfog/twidda/backend/utils/DialogBuilder.java index 32e0a618..11a1eeb6 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/utils/DialogBuilder.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/utils/DialogBuilder.java @@ -60,9 +60,9 @@ public final class DialogBuilder { case WRONG_PROXY: title = R.string.info_error; - message = R.string.error_wrong_proxy_settings; - posButton = R.string.confirm_discard_button; - negButton = R.string.dialog_button_cancel; + message = R.string.error_wrong_connection_settings; + posButton = R.string.dialog_button_cancel; + negButton = R.string.confirm_back; break; case DEL_DATABASE: diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/utils/ErrorHandler.java b/app/src/main/java/org/nuclearfog/twidda/backend/utils/ErrorHandler.java index 89867c90..7ee3ecc3 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/utils/ErrorHandler.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/utils/ErrorHandler.java @@ -97,7 +97,7 @@ public final class ErrorHandler { break; case ACCESS_TOKEN_DEAD: - Toast.makeText(context, R.string.error_cant_login, Toast.LENGTH_SHORT).show(); + Toast.makeText(context, R.string.error_corrupt_api_key, Toast.LENGTH_SHORT).show(); break; case TWEET_CANT_REPLY: @@ -112,6 +112,10 @@ public final class ErrorHandler { case ACCOUNT_UPDATE_FAILED: Toast.makeText(context, R.string.error_acc_update, Toast.LENGTH_LONG).show(); break; + + case ERROR_API_ACCESS_DENIED: + Toast.makeText(context, R.string.error_api_access_denied, Toast.LENGTH_LONG).show(); + break; } } else { Toast.makeText(context, R.string.error_not_defined, Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java b/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java index f6a11424..6e161b00 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/GlobalSettings.java @@ -81,6 +81,9 @@ public class GlobalSettings { private static final String PROXY_PASS = "proxy_pass"; private static final String TREND_LOC = "location"; private static final String TREND_ID = "world_id"; + private static final String CUSTOM_CONSUMER_KEY_SET = "custom_api_keys"; + private static final String CUSTOM_CONSUMER_KEY_1 = "api_key1"; + private static final String CUSTOM_CONSUMER_KEY_2 = "api_key2"; // file name of the preferences private static final String APP_SETTINGS = "settings"; @@ -104,13 +107,15 @@ public class GlobalSettings { private SharedPreferences settings; private TrendLocation location; - private String key1, key2; + private String api_key1, api_key2; + private String auth_key1, auth_key2; private boolean loadImage; private boolean hqImages; private boolean loadAnswer; private boolean loggedIn; private boolean isProxyEnabled; private boolean isProxyAuthSet; + private boolean isCustomAPIkeySet; private int indexFont; private int background_color; private int font_color; @@ -604,11 +609,29 @@ public class GlobalSettings { */ public String[] getCurrentUserAccessToken() { String[] out = new String[2]; - out[0] = key1; - out[1] = key2; + out[0] = auth_key1; + out[1] = auth_key2; return out; } + /** + * get Consumer keys + * + * @return key string + */ + public String getConsumerKey() { + return api_key1; + } + + /** + * get consumer key secret + * + * @return key string + */ + public String getConsumerSecret() { + return api_key2; + } + /** * get current users ID * @@ -627,8 +650,8 @@ public class GlobalSettings { */ public void setConnection(String key1, String key2, long userId) { loggedIn = true; - this.key1 = key1; - this.key2 = key2; + this.auth_key1 = key1; + this.auth_key2 = key2; this.userId = userId; Editor e = settings.edit(); @@ -639,6 +662,48 @@ public class GlobalSettings { e.apply(); } + /** + * sets custom API consumer keys + * + * @param key1 consumer key + * @param key2 consumer key secret + */ + public void setCustomAPI(String key1, String key2) { + isCustomAPIkeySet = true; + this.api_key1 = key1; + this.api_key2 = key2; + + Editor e = settings.edit(); + e.putBoolean(CUSTOM_CONSUMER_KEY_SET, true); + e.putString(CUSTOM_CONSUMER_KEY_1, key1); + e.putString(CUSTOM_CONSUMER_KEY_2, key2); + e.apply(); + } + + /** + * remove all API keys + */ + public void removeCustomAPI() { + isCustomAPIkeySet = false; + this.api_key1 = ""; + this.api_key2 = ""; + + Editor e = settings.edit(); + e.remove(CUSTOM_CONSUMER_KEY_SET); + e.remove(CUSTOM_CONSUMER_KEY_1); + e.remove(CUSTOM_CONSUMER_KEY_2); + e.apply(); + } + + /** + * check if custom API consumer keys are set + * + * @return true if custom API keys are set + */ + public boolean isCustomApiSet() { + return isCustomAPIkeySet; + } + /** * Remove all user content from Shared Preferences */ @@ -663,8 +728,11 @@ public class GlobalSettings { loadAnswer = settings.getBoolean(ANSWER_LOAD, DEFAULT_DATA_USAGE); hqImages = settings.getBoolean(IMAGE_QUALITY, DEFAULT_DATA_USAGE); loggedIn = settings.getBoolean(LOGGED_IN, false); - key1 = settings.getString(AUTH_KEY1, ""); - key2 = settings.getString(AUTH_KEY2, ""); + isCustomAPIkeySet = settings.getBoolean(CUSTOM_CONSUMER_KEY_SET, false); + api_key1 = settings.getString(CUSTOM_CONSUMER_KEY_1, ""); + api_key2 = settings.getString(CUSTOM_CONSUMER_KEY_2, ""); + auth_key1 = settings.getString(AUTH_KEY1, ""); + auth_key2 = settings.getString(AUTH_KEY2, ""); userId = settings.getLong(USER_ID, 0); isProxyEnabled = settings.getBoolean(PROXY_SET, false); isProxyAuthSet = settings.getBoolean(AUTH_SET, false); diff --git a/app/src/main/res/layout/page_settings.xml b/app/src/main/res/layout/page_settings.xml index 24813787..a2504de5 100644 --- a/app/src/main/res/layout/page_settings.xml +++ b/app/src/main/res/layout/page_settings.xml @@ -399,14 +399,14 @@ + android:gravity="center_vertical" + android:orientation="horizontal"> + tools:ignore="Autofill" /> + tools:ignore="Autofill" /> + android:gravity="center_vertical" + android:orientation="horizontal"> + tools:ignore="Autofill" /> + tools:ignore="Autofill" /> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index a71848a7..c062b181 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -87,7 +87,7 @@ Tweet favorisiert! tweet aus den favoriten entfernt! Port - Proxy Einstellung + Verbindungseinsellungen Login Popup PIN eingeben @@ -132,8 +132,7 @@ Video kann nicht abgespielt werden! Proxy aktivieren Proxy Authentifizierung aktivieren - Falsche Proxy Einstellungen - verwerfen + Falsche Verbindung angegeben! Link konnte nicht geöffnet werden! Sensible Inhalte TLS 1.2 wird nicht unterstützt. Die App wird möglicherweise nicht funktionieren! @@ -182,8 +181,13 @@ Abbrechen OK Tweet ist nicht für Antworten freigegeben! - App ist nicht eingeloggt, Bitte neu einloggen! + Fehler, API Schlüssel ist fehlerhaft! Account Informationen konnten nicht aktualisiert werden! Bitte Eingaben überprüfen. Kartenfarbe Symbolfarbe + Consumer Key + Consumer Secret + Eigene API Schlüssel verwenden + Fehler, API Zugang wurde abgelehnt! + Zurück \ 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 5fe95bc1..dba1fdb6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,11 +48,13 @@ User data follows you load Tweet replies automatically - Proxy settings + Connection settings enter IP address Port Username Password + Consumer key + Consumer secret https://github.com/nuclearfog/Shitter mute user? Profile of the list owner @@ -77,7 +79,8 @@ save image enable proxy enable proxy authentication - discard + setup custom API keys + back Potentially sensitive content 3 steps to login 1. @@ -107,7 +110,7 @@ login to Twitter Please click the link below to login Phone does not support TLS 1.2. App will probably not work! - Wrong proxy settings + Wrong connection settings! can\'t add video get Twitter PIN from browser first. Please press the first button! GPS position added @@ -166,7 +169,7 @@ Empty list title! Wrong username format! You can\'t reply to this Tweet! - can\'t login to Twitter, please Re-Login! + Error, corrupt API key! Account update failed! Please check your input! Not defined Error! @@ -202,4 +205,5 @@ OK Card color Icon color + Error, API access denied! \ No newline at end of file