From 52ecd9347c9f9cbc3aa082b20bca60cfdadd5c3c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 23 Oct 2018 20:57:31 +0200 Subject: [PATCH] added dialogs for rate limiting and out of date version --- build.gradle | 2 +- .../generator/AbstractGenerator.java | 6 +- .../http/HttpDownloadConnection.java | 4 +- .../http/HttpUploadConnection.java | 2 +- src/main/res/values/strings.xml | 7 +- .../services/QuickConversationsService.java | 48 ++++++----- .../ui/EnterPhoneNumberActivity.java | 7 +- .../conversations/ui/VerifyActivity.java | 16 ++-- .../ui/util/ApiDialogHelper.java | 83 +++++++++++++++++++ .../ui/util/ApiErrorDialogHelper.java | 47 ----------- 10 files changed, 139 insertions(+), 83 deletions(-) create mode 100644 src/quick/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java delete mode 100644 src/quick/java/eu/siacs/conversations/ui/util/ApiErrorDialogHelper.java diff --git a/build.gradle b/build.gradle index aca9b99e0..c344a3ccd 100644 --- a/build.gradle +++ b/build.gradle @@ -104,7 +104,7 @@ android { quick { dimension "mode" applicationId = "im.conversations.quick" - resValue "string", "app_name", "Quick Conversations" + resValue "string", "app_name", "Quicksy" resValue "string", "applicationId", applicationId } diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 837b6f75e..74e5af850 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -69,7 +69,11 @@ public abstract class AbstractGenerator { } public String getIdentityName() { - return mXmppConnectionService.getString(R.string.app_name) + " " + getIdentityVersion(); + return mXmppConnectionService.getString(R.string.app_name) + ' ' + getIdentityVersion(); + } + + public String getUserAgent() { + return mXmppConnectionService.getString(R.string.app_name) + '/' + getIdentityVersion(); } String getIdentityType() { diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 0a85e2a06..a00bf9ceb 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -288,7 +288,7 @@ public class HttpDownloadConnection implements Transferable { } connection.setUseCaches(false); Log.d(Config.LOGTAG, "url: " + connection.getURL().toString()); - connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName()); + connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getUserAgent()); if (connection instanceof HttpsURLConnection) { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); } @@ -367,7 +367,7 @@ public class HttpDownloadConnection implements Transferable { mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); } connection.setUseCaches(false); - connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getIdentityName()); + connection.setRequestProperty("User-Agent", mXmppConnectionService.getIqGenerator().getUserAgent()); final boolean tryResume = file.exists() && file.getKey() == null && file.getSize() > 0; long resumeSize = 0; long expected = file.getExpectedSize(); diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index b2a7c7a1b..a38bfd906 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -166,7 +166,7 @@ public class HttpUploadConnection implements Transferable { connection.setUseCaches(false); connection.setRequestMethod("PUT"); connection.setFixedLengthStreamingMode(expectedFileSize); - connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getIdentityName()); + connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getUserAgent()); if(slot.getHeaders() != null) { for(HashMap.Entry entry : slot.getHeaders().entrySet()) { connection.setRequestProperty(entry.getKey(),entry.getValue()); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 44bbb6a09..bd43daf83 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -754,7 +754,7 @@ Choose a country phone number Verify your phone number - Quick Conversations will send an SMS message (carrier charges may apply) to verify your phone number. Enter your country code and phone number: + Quicksy will send an SMS message (carrier charges may apply) to verify your phone number. Enter your country code and phone number:
%s

Is this OK, or would you like to edit the number?]]>
%s is not a valid phone number. Please enter your phone number. @@ -783,4 +783,9 @@ Invalid user input Temporarily unavailable. Try again later. No network connection. + Please try again in %s + You are rate limited + Too many attempts + You are using an out of date version of this app. + Update diff --git a/src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java b/src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java index e625a8e60..8ffee4ff4 100644 --- a/src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java +++ b/src/quick/java/eu/siacs/conversations/services/QuickConversationsService.java @@ -1,7 +1,9 @@ package eu.siacs.conversations.services; +import android.content.SharedPreferences; import android.os.SystemClock; +import android.preference.PreferenceManager; import android.util.Log; import java.io.BufferedWriter; @@ -10,11 +12,12 @@ import java.io.OutputStreamWriter; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLConnection; import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.Collections; +import java.util.Locale; import java.util.Set; +import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -40,6 +43,8 @@ public class QuickConversationsService { private static final String BASE_URL = "http://venus.fritz.box:4567"; + private static final String INSTALLATION_ID = "eu.siacs.conversations.installation-id"; + private final XmppConnectionService service; private final Set mOnVerificationRequested = Collections.newSetFromMap(new WeakHashMap<>()); @@ -78,14 +83,6 @@ public class QuickConversationsService { public void requestVerification(Phonenumber.PhoneNumber phoneNumber) { final String e164 = PhoneNumberUtilWrapper.normalize(service, phoneNumber); - - /** - * GET /authentication/+phoneNumber - * - * - returns too many requests, (sms ist unterwegs), retry after seconden -- auf jeden fall in nächste activity (verify activity) weiter leiten weil es sein kann das sms noch ankommt - * - returns OK; success (auch in activity weiter lassen. aber ohne error paramater; dh send again button is activ; und vielleicht kurzer toast bzw snackbar - * - returns invalid request user error wenn die phone number falsch ist - */ if (mVerificationRequestInProgress.compareAndSet(false, true)) { new Thread(() -> { try { @@ -96,6 +93,7 @@ public class QuickConversationsService { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000); + setHeader(connection); final int code = connection.getResponseCode(); if (code == 200) { createAccountAndWait(phoneNumber, 0L); @@ -150,16 +148,6 @@ public class QuickConversationsService { } public void verify(final Account account, String pin) { - /** - * POST /password - * authentication gesetzt mit telephone nummber und verification code - * body = password - * - * retry after, too many requests - * code wrong - * OK (weiterleiten auf publish avatar activity - * - */ if (mVerificationInProgress.compareAndSet(false, true)) { new Thread(() -> { try { @@ -172,6 +160,7 @@ public class QuickConversationsService { connection.setReadTimeout(Config.SOCKET_TIMEOUT * 1000); connection.setRequestMethod("POST"); connection.setRequestProperty("Authorization", Plain.getMessage(account.getUsername(), pin)); + setHeader(connection); final OutputStream os = connection.getOutputStream(); final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); writer.write(account.getPassword()); @@ -217,6 +206,25 @@ public class QuickConversationsService { } } + private void setHeader(HttpURLConnection connection) { + connection.setRequestProperty("User-Agent", service.getIqGenerator().getUserAgent()); + connection.setRequestProperty("Installation-Id", getInstallationId()); + connection.setRequestProperty("Accept-Language", Locale.getDefault().getLanguage()); + } + + private String getInstallationId() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(service); + String id = preferences.getString(INSTALLATION_ID, null); + if (id != null) { + return id; + } else { + id = UUID.randomUUID().toString(); + preferences.edit().putString(INSTALLATION_ID, id).apply(); + return id; + } + + } + private int getApiErrorCode(Exception e) { if (!service.hasInternetConnection()) { return API_ERROR_AIRPLANE_MODE; @@ -227,7 +235,7 @@ public class QuickConversationsService { } else if (e instanceof SSLHandshakeException) { return API_ERROR_SSL_HANDSHAKE; } else { - Log.d(Config.LOGTAG,e.getClass().getName()); + Log.d(Config.LOGTAG, e.getClass().getName()); return API_ERROR_OTHER; } } diff --git a/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java b/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java index a98059372..733da33f4 100644 --- a/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java +++ b/src/quick/java/eu/siacs/conversations/ui/EnterPhoneNumberActivity.java @@ -4,23 +4,20 @@ import android.app.AlertDialog; import android.content.Intent; import android.databinding.DataBindingUtil; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.Html; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; -import android.view.KeyEvent; import android.view.View; -import android.widget.TextView; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.ActivityEnterNumberBinding; import eu.siacs.conversations.services.QuickConversationsService; import eu.siacs.conversations.ui.drawable.TextDrawable; -import eu.siacs.conversations.ui.util.ApiErrorDialogHelper; +import eu.siacs.conversations.ui.util.ApiDialogHelper; import eu.siacs.conversations.utils.PhoneNumberUtilWrapper; import io.michaelrocks.libphonenumber.android.NumberParseException; import io.michaelrocks.libphonenumber.android.PhoneNumberUtil; @@ -180,7 +177,7 @@ public class EnterPhoneNumberActivity extends XmppActivity implements QuickConve public void onVerificationRequestFailed(int code) { runOnUiThread(() -> { setRequestingVerificationState(false); - ApiErrorDialogHelper.create(this, code).show(); + ApiDialogHelper.createError(this, code).show(); }); } diff --git a/src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java b/src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java index e6718d809..b34574492 100644 --- a/src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java +++ b/src/quick/java/eu/siacs/conversations/ui/VerifyActivity.java @@ -19,7 +19,7 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.ActivityVerifyBinding; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.services.QuickConversationsService; -import eu.siacs.conversations.ui.util.ApiErrorDialogHelper; +import eu.siacs.conversations.ui.util.ApiDialogHelper; import eu.siacs.conversations.ui.util.PinEntryWrapper; import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.PhoneNumberUtilWrapper; @@ -286,7 +286,7 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP builder.setPositiveButton(R.string.ok, null); builder.create().show(); } else { - ApiErrorDialogHelper.create(this, code).show(); + ApiDialogHelper.createError(this, code).show(); } }); } @@ -299,7 +299,10 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP @Override public void onVerificationRetryAt(long timestamp) { this.retryVerificationAfter = timestamp; - runOnUiThread(() -> setVerifyingState(false)); + runOnUiThread(() -> { + ApiDialogHelper.createTooManyAttempts(this).show(); + setVerifyingState(false); + }); mHandler.removeCallbacks(VERIFICATION_TIMEOUT_UPDATER); runOnUiThread(VERIFICATION_TIMEOUT_UPDATER); } @@ -309,7 +312,7 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP public void onVerificationRequestFailed(int code) { runOnUiThread(() -> { setRequestingVerificationState(false); - ApiErrorDialogHelper.create(this, code).show(); + ApiDialogHelper.createError(this, code).show(); }); } @@ -328,7 +331,10 @@ public class VerifyActivity extends XmppActivity implements ClipboardManager.OnP @Override public void onVerificationRequestedRetryAt(long timestamp) { this.retrySmsAfter = timestamp; - runOnUiThread(() -> setRequestingVerificationState(false)); + runOnUiThread(() -> { + ApiDialogHelper.createRateLimited(this, timestamp).show(); + setRequestingVerificationState(false); + }); mHandler.removeCallbacks(SMS_TIMEOUT_UPDATER); runOnUiThread(SMS_TIMEOUT_UPDATER); } diff --git a/src/quick/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java b/src/quick/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java new file mode 100644 index 000000000..74dc49c20 --- /dev/null +++ b/src/quick/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java @@ -0,0 +1,83 @@ +package eu.siacs.conversations.ui.util; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.SystemClock; +import android.support.annotation.StringRes; + +import eu.siacs.conversations.R; +import eu.siacs.conversations.services.QuickConversationsService; +import eu.siacs.conversations.utils.TimeframeUtils; + +public class ApiDialogHelper { + + public static Dialog createError(final Context context, final int code) { + @StringRes final int res; + switch (code) { + case QuickConversationsService.API_ERROR_AIRPLANE_MODE: + res = R.string.no_network_connection; + break; + case QuickConversationsService.API_ERROR_OTHER: + res = R.string.unknown_api_error_network; + break; + case QuickConversationsService.API_ERROR_CONNECT: + res = R.string.unable_to_connect_to_server; + break; + case QuickConversationsService.API_ERROR_SSL_HANDSHAKE: + res = R.string.unable_to_establish_secure_connection; + break; + case QuickConversationsService.API_ERROR_UNKNOWN_HOST: + res = R.string.unable_to_find_server; + break; + case 400: + res = R.string.invalid_user_input; + break; + case 403: + res = R.string.the_app_is_out_of_date; + break; + case 502: + case 503: + case 504: + res = R.string.temporarily_unavailable; + break; + default: + res = R.string.unknown_api_error_response; + } + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(res); + if (code == 403 && resolvable(context, getMarketViewIntent(context))) { + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.update, (dialog, which) -> context.startActivity(getMarketViewIntent(context))); + } else { + builder.setPositiveButton(R.string.ok, null); + } + return builder.create(); + } + + public static Dialog createRateLimited(final Context context, final long timestamp) { + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.rate_limited); + builder.setMessage(context.getString(R.string.try_again_in_x, TimeframeUtils.resolve(context, timestamp - SystemClock.elapsedRealtime()))); + builder.setPositiveButton(R.string.ok, null); + return builder.create(); + } + + public static Dialog createTooManyAttempts(final Context context) { + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.too_many_attempts); + builder.setPositiveButton(R.string.ok, null); + return builder.create(); + } + + private static Intent getMarketViewIntent(Context context) { + return new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + context.getPackageName())); + } + + private static boolean resolvable(Context context, Intent intent) { + return context.getPackageManager().queryIntentActivities(intent, 0).size() > 0; + } +} diff --git a/src/quick/java/eu/siacs/conversations/ui/util/ApiErrorDialogHelper.java b/src/quick/java/eu/siacs/conversations/ui/util/ApiErrorDialogHelper.java deleted file mode 100644 index e22551570..000000000 --- a/src/quick/java/eu/siacs/conversations/ui/util/ApiErrorDialogHelper.java +++ /dev/null @@ -1,47 +0,0 @@ -package eu.siacs.conversations.ui.util; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.support.annotation.StringRes; - -import eu.siacs.conversations.R; -import eu.siacs.conversations.services.QuickConversationsService; - -public class ApiErrorDialogHelper { - - public static Dialog create(Context context, int code) { - @StringRes final int res; - switch (code) { - case QuickConversationsService.API_ERROR_AIRPLANE_MODE: - res = R.string.no_network_connection; - break; - case QuickConversationsService.API_ERROR_OTHER: - res = R.string.unknown_api_error_network; - break; - case QuickConversationsService.API_ERROR_CONNECT: - res = R.string.unable_to_connect_to_server; - break; - case QuickConversationsService.API_ERROR_SSL_HANDSHAKE: - res = R.string.unable_to_establish_secure_connection; - break; - case QuickConversationsService.API_ERROR_UNKNOWN_HOST: - res = R.string.unable_to_find_server; - break; - case 400: - res = R.string.invalid_user_input; - break; - case 502: - case 503: - case 504: - res = R.string.temporarily_unavailable; - break; - default: - res = R.string.unknown_api_error_response; - } - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(res); - builder.setPositiveButton(R.string.ok, null); - return builder.create(); - } -} -- 2.45.2