[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [orbot/master] Improved UI: show progress spinner, start request on keyboard enter.
commit a34ee2213a1ba7c2be06798e50c2c17bc96789f2
Author: Benjamin Erhart <berhart@xxxxxxxxxxxxxxxxxxx>
Date: Wed Apr 22 11:27:13 2020 +0200
Improved UI: show progress spinner, start request on keyboard enter.
---
.../android/ui/onboarding/MoatActivity.java | 118 ++++++++++++++-------
app/src/main/res/layout/activity_moat.xml | 23 +++-
2 files changed, 98 insertions(+), 43 deletions(-)
diff --git a/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java b/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
index e2f642fd..0b99477f 100644
--- a/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
+++ b/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
@@ -2,10 +2,9 @@
/* See LICENSE for licensing information */
package org.torproject.android.ui.onboarding;
-import android.app.Dialog;
+import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
@@ -13,11 +12,17 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
@@ -50,12 +55,14 @@ import org.torproject.android.service.util.Prefs;
API description:
https://github.com/NullHypothesis/bridgedb#accessing-the-moat-interface
*/
-public class MoatActivity extends AppCompatActivity implements View.OnClickListener {
+public class MoatActivity extends AppCompatActivity implements View.OnClickListener, TextView.OnEditorActionListener {
private static String moatBaseUrl = "https://bridges.torproject.org/moat";
private ImageView mCaptchaIv;
+ private ProgressBar mProgressBar;
private EditText mSolutionEt;
+ private Button mRequestBt;
private String mChallenge;
@@ -103,9 +110,14 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
setTitle(getString(R.string.request_bridges));
mCaptchaIv = findViewById(R.id.captchaIv);
+ mProgressBar = findViewById(R.id.progressBar);
mSolutionEt = findViewById(R.id.solutionEt);
+ mRequestBt = findViewById(R.id.requestBt);
- findViewById(R.id.requestBt).setOnClickListener(this);
+ mCaptchaIv.setVisibility(View.GONE);
+ mSolutionEt.setOnEditorActionListener(this);
+ mRequestBt.setEnabled(false);
+ mRequestBt.setOnClickListener(this);
LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver,
new IntentFilter(TorServiceConstants.ACTION_STATUS));
@@ -159,6 +171,27 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
return super.onOptionsItemSelected(item);
}
+
+ @Override
+ public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
+ Log.d(MoatActivity.class.getSimpleName(), "Editor Action: actionId=" + actionId + ", IME_ACTION_GO=" + EditorInfo.IME_ACTION_GO);
+
+ if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
+ if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.hideSoftInputFromWindow(textView.getWindowToken(), 0);
+ }
+
+ onClick(mSolutionEt);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
@@ -167,6 +200,10 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
}
private void fetchCaptcha() {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mCaptchaIv.setVisibility(View.GONE);
+ mRequestBt.setEnabled(false);
+
JsonObjectRequest request = buildRequest("fetch",
"\"type\": \"client-transports\", \"supported\": [\"obfs4\"]",
new Response.Listener<JSONObject>() {
@@ -179,19 +216,14 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
byte[] image = Base64.decode(data.getString("image"), Base64.DEFAULT);
mCaptchaIv.setImageBitmap(BitmapFactory.decodeByteArray(image, 0, image.length));
+ mProgressBar.setVisibility(View.GONE);
+ mCaptchaIv.setVisibility(View.VISIBLE);
+ mRequestBt.setEnabled(true);
+
} catch (JSONException e) {
- Log.d(MoatActivity.class.getSimpleName(), "Error decoding answer.");
-
- new AlertDialog.Builder(MoatActivity.this)
- .setTitle(R.string.error)
- .setMessage(e.getLocalizedMessage())
- .setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //Do nothing.
- }
- })
- .show();
+ Log.d(MoatActivity.class.getSimpleName(), "Error decoding answer: " + response.toString());
+
+ displayError(e, response);
}
}
});
@@ -202,6 +234,9 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
}
private void requestBridges(String solution) {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mRequestBt.setEnabled(false);
+
JsonObjectRequest request = buildRequest("check",
"\"id\": \"2\", \"type\": \"moat-solution\", \"transport\": \"obfs4\", \"challenge\": \""
+ mChallenge + "\", \"solution\": \"" + solution + "\", \"qrcode\": \"false\"",
@@ -222,21 +257,14 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
Prefs.setBridgesList(sb.toString());
Prefs.putBridgesEnabled(true);
- MoatActivity.this.finish();
+ mProgressBar.setVisibility(View.GONE);
- } catch (JSONException e) {
+ MoatActivity.this.finish();
+ }
+ catch (JSONException e) {
Log.d(MoatActivity.class.getSimpleName(), "Error decoding answer: " + response.toString());
- new AlertDialog.Builder(MoatActivity.this)
- .setTitle(R.string.error)
- .setMessage(e.getLocalizedMessage())
- .setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //Do nothing.
- }
- })
- .show();
+ displayError(e, response);
}
}
});
@@ -267,16 +295,7 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
public void onErrorResponse(VolleyError error) {
Log.d(MoatActivity.class.getSimpleName(), "Error response.");
- new AlertDialog.Builder(MoatActivity.this)
- .setTitle(R.string.error)
- .setMessage(error.getLocalizedMessage())
- .setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //Do nothing.
- }
- })
- .show();
+ displayError(error, null);
}
}
) {
@@ -322,4 +341,27 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
sendIntentToService(TorServiceConstants.ACTION_STATUS);
}
}
+
+ private void displayError(Exception exception, JSONObject response) {
+
+ String detail = null;
+
+ // Try to decode potential error response.
+ if (response != null) {
+ try {
+ detail = response.getJSONArray("errors").getJSONObject(0).getString("detail");
+ } catch (JSONException e2) {
+ // Ignore. Show first exception instead.
+ }
+ }
+
+ mProgressBar.setVisibility(View.GONE);
+ mRequestBt.setEnabled(mCaptchaIv.getVisibility() == View.VISIBLE);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.error)
+ .setMessage(TextUtils.isEmpty(detail) ? exception.getLocalizedMessage() : detail)
+ .setNegativeButton(R.string.btn_cancel, null)
+ .show();
+ }
}
diff --git a/app/src/main/res/layout/activity_moat.xml b/app/src/main/res/layout/activity_moat.xml
index fe01873c..b0a2b42f 100644
--- a/app/src/main/res/layout/activity_moat.xml
+++ b/app/src/main/res/layout/activity_moat.xml
@@ -28,20 +28,33 @@
android:layout_margin="12dp"
android:text="@string/solve_captcha_instruction" />
- <ImageView
- android:id="@+id/captchaIv"
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="240dp"
- android:contentDescription="@string/captcha"
- tools:srcCompat="@tools:sample/backgrounds/scenic" />
+ android:layout_margin="12dp">
+
+ <ImageView
+ android:id="@+id/captchaIv"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/captcha"
+ tools:srcCompat="@tools:sample/backgrounds/scenic" />
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </RelativeLayout>
<EditText
android:id="@+id/solutionEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints=""
- android:hint="@string/enter_characters_from_image"
android:ems="10"
+ android:hint="@string/enter_characters_from_image"
+ android:imeOptions="actionSend"
android:inputType="textShortMessage|text" />
<Button
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits