aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/AndroidManifest.xml12
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationActivity.java20
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationsOverviewFragment.java5
-rw-r--r--src/main/java/de/pixart/messenger/ui/OmemoActivity.java9
-rw-r--r--src/main/java/de/pixart/messenger/ui/ScanActivity.java292
-rw-r--r--src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java82
-rw-r--r--src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java12
-rw-r--r--src/main/java/de/pixart/messenger/ui/service/CameraManager.java305
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/ScannerView.java158
-rw-r--r--src/main/java/de/pixart/messenger/utils/zxing/IntentIntegrator.java533
-rw-r--r--src/main/java/de/pixart/messenger/utils/zxing/IntentResult.java93
-rw-r--r--src/main/res/layout/activity_scan.xml17
-rw-r--r--src/main/res/values/colors.xml7
-rw-r--r--src/main/res/values/dimens.xml4
-rw-r--r--src/main/res/values/strings.xml1
-rw-r--r--src/main/res/values/themes.xml8
17 files changed, 895 insertions, 667 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 961c4df2a..da6744a24 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" />
@@ -82,11 +83,16 @@
android:launchMode="singleTask"
android:minWidth="300dp"
android:minHeight="300dp"
- android:windowSoftInputMode="stateHidden"></activity>
+ android:windowSoftInputMode="stateHidden">
+ </activity>
+ <activity
+ android:name=".ui.ScanActivity"
+ android:screenOrientation="portrait"
+ android:theme="@style/ConversationsTheme.FullScreen"
+ android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".ui.UriHandlerActivity"
- android:label="@string/title_activity_start_conversation"
- android:launchMode="singleTop">
+ android:label="@string/title_activity_start_conversation">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
index b80469649..2783a4b1f 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java
@@ -312,6 +312,7 @@ public class ConversationActivity extends XmppActivity implements OnConversation
}
private boolean processViewIntent(Intent intent) {
+ Log.d(Config.LOGTAG,"process view intent");
String uuid = intent.getStringExtra(EXTRA_CONVERSATION);
Conversation conversation = uuid != null ? xmppConnectionService.findConversationByUuid(uuid) : null;
if (conversation == null) {
@@ -323,8 +324,12 @@ public class ConversationActivity extends XmppActivity implements OnConversation
}
@Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+ UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
+ }
+
+ @Override
public void onActivityResult(int requestCode, int resultCode, final Intent data) {
- Log.d(Config.LOGTAG, "on activity result");
if (resultCode == RESULT_OK) {
handlePositiveActivityResult(requestCode, data);
} else {
@@ -368,7 +373,12 @@ public class ConversationActivity extends XmppActivity implements OnConversation
this.getFragmentManager().addOnBackStackChangedListener(this::showDialogsIfMainIsOverview);
this.initializeFragments();
this.invalidateActionBarTitle();
- final Intent intent = getIntent();
+ final Intent intent;
+ if (savedInstanceState == null) {
+ intent = getIntent();
+ } else {
+ intent = savedInstanceState.getParcelable("intent");
+ }
if (isViewIntent(intent)) {
pendingViewIntent.push(intent);
setIntent(createLauncherIntent(this));
@@ -451,6 +461,12 @@ public class ConversationActivity extends XmppActivity implements OnConversation
}
@Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ savedInstanceState.putParcelable("intent", getIntent());
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
protected void onStart() {
final int theme = findTheme();
if (this.mTheme != theme) {
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsOverviewFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationsOverviewFragment.java
index 35e1e02b7..476cc267d 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationsOverviewFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationsOverviewFragment.java
@@ -144,7 +144,10 @@ public class ConversationsOverviewFragment extends XmppFragment {
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
- bundle.putParcelable(STATE_SCROLL_POSITION, getScrollState());
+ ScrollState scrollState = getScrollState();
+ if (scrollState != null) {
+ bundle.putParcelable(STATE_SCROLL_POSITION, scrollState);
+ }
}
private ScrollState getScrollState() {
diff --git a/src/main/java/de/pixart/messenger/ui/OmemoActivity.java b/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
index a549c76e3..3406d0c46 100644
--- a/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/OmemoActivity.java
@@ -15,7 +15,6 @@ import android.widget.TextView;
import android.widget.Toast;
import java.security.cert.X509Certificate;
-import java.util.Arrays;
import de.pixart.messenger.Config;
import de.pixart.messenger.R;
@@ -25,8 +24,6 @@ import de.pixart.messenger.databinding.ContactKeyBinding;
import de.pixart.messenger.entities.Account;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.XmppUri;
-import de.pixart.messenger.utils.zxing.IntentIntegrator;
-import de.pixart.messenger.utils.zxing.IntentResult;
public abstract class OmemoActivity extends XmppActivity {
@@ -74,7 +71,7 @@ public abstract class OmemoActivity extends XmppActivity {
copyOmemoFingerprint(mSelectedFingerprint);
break;
case R.id.verify_scan:
- new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
+ //new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
break;
}
return true;
@@ -82,7 +79,7 @@ public abstract class OmemoActivity extends XmppActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
+ /*IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null && scanResult.getFormatName() != null) {
String data = scanResult.getContents();
XmppUri uri = new XmppUri(data);
@@ -91,7 +88,7 @@ public abstract class OmemoActivity extends XmppActivity {
} else {
this.mPendingFingerprintVerificationUri =uri;
}
- }
+ }*/
}
protected abstract void processFingerprintVerification(XmppUri uri);
diff --git a/src/main/java/de/pixart/messenger/ui/ScanActivity.java b/src/main/java/de/pixart/messenger/ui/ScanActivity.java
new file mode 100644
index 000000000..d44f50512
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/ScanActivity.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.pixart.messenger.ui;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.Vibrator;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeReader;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import de.pixart.messenger.Config;
+import de.pixart.messenger.R;
+import de.pixart.messenger.ui.service.CameraManager;
+import de.pixart.messenger.ui.widget.ScannerView;
+
+/**
+ * @author Andreas Schildbach
+ */
+@SuppressWarnings("deprecation")
+public final class ScanActivity extends Activity implements SurfaceTextureListener, ActivityCompat.OnRequestPermissionsResultCallback {
+ public static final String INTENT_EXTRA_RESULT = "result";
+
+ private static final long VIBRATE_DURATION = 50L;
+ private static final long AUTO_FOCUS_INTERVAL_MS = 2500L;
+ private static boolean DISABLE_CONTINUOUS_AUTOFOCUS = Build.MODEL.equals("GT-I9100") // Galaxy S2
+ || Build.MODEL.equals("SGH-T989") // Galaxy S2
+ || Build.MODEL.equals("SGH-T989D") // Galaxy S2 X
+ || Build.MODEL.equals("SAMSUNG-SGH-I727") // Galaxy S2 Skyrocket
+ || Build.MODEL.equals("GT-I9300") // Galaxy S3
+ || Build.MODEL.equals("GT-N7000"); // Galaxy Note
+ private final CameraManager cameraManager = new CameraManager();
+ private ScannerView scannerView;
+ private TextureView previewView;
+ private volatile boolean surfaceCreated = false;
+ private Vibrator vibrator;
+ private HandlerThread cameraThread;
+ private volatile Handler cameraHandler;
+ private final Runnable closeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cameraHandler.removeCallbacksAndMessages(null);
+ cameraManager.close();
+ }
+ };
+ private final Runnable fetchAndDecodeRunnable = new Runnable() {
+ private final QRCodeReader reader = new QRCodeReader();
+ private final Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
+
+ @Override
+ public void run() {
+ cameraManager.requestPreviewFrame((data, camera) -> decode(data));
+ }
+
+ private void decode(final byte[] data) {
+ final PlanarYUVLuminanceSource source = cameraManager.buildLuminanceSource(data);
+ final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+
+ try {
+ hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, (ResultPointCallback) dot -> runOnUiThread(() -> scannerView.addDot(dot)));
+ final Result scanResult = reader.decode(bitmap, hints);
+
+ runOnUiThread(() -> handleResult(scanResult));
+ } catch (final ReaderException x) {
+ // retry
+ cameraHandler.post(fetchAndDecodeRunnable);
+ } finally {
+ reader.reset();
+ }
+ }
+ };
+ private final Runnable openRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final Camera camera = cameraManager.open(previewView, displayRotation(), !DISABLE_CONTINUOUS_AUTOFOCUS);
+
+ final Rect framingRect = cameraManager.getFrame();
+ final RectF framingRectInPreview = new RectF(cameraManager.getFramePreview());
+ framingRectInPreview.offsetTo(0, 0);
+ final boolean cameraFlip = cameraManager.getFacing() == CameraInfo.CAMERA_FACING_FRONT;
+ final int cameraRotation = cameraManager.getOrientation();
+
+ runOnUiThread(() -> scannerView.setFraming(framingRect, framingRectInPreview, displayRotation(), cameraRotation, cameraFlip));
+
+ final String focusMode = camera.getParameters().getFocusMode();
+ final boolean nonContinuousAutoFocus = Camera.Parameters.FOCUS_MODE_AUTO.equals(focusMode)
+ || Camera.Parameters.FOCUS_MODE_MACRO.equals(focusMode);
+
+ if (nonContinuousAutoFocus)
+ cameraHandler.post(new AutoFocusRunnable(camera));
+
+ cameraHandler.post(fetchAndDecodeRunnable);
+ } catch (final Exception x) {
+ Log.d(Config.LOGTAG, "problem opening camera", x);
+ }
+ }
+
+ private int displayRotation() {
+ final int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ if (rotation == Surface.ROTATION_0)
+ return 0;
+ else if (rotation == Surface.ROTATION_90)
+ return 90;
+ else if (rotation == Surface.ROTATION_180)
+ return 180;
+ else if (rotation == Surface.ROTATION_270)
+ return 270;
+ else
+ throw new IllegalStateException("rotation: " + rotation);
+ }
+ };
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+
+ setContentView(R.layout.activity_scan);
+ scannerView = findViewById(R.id.scan_activity_mask);
+ previewView = findViewById(R.id.scan_activity_preview);
+ previewView.setSurfaceTextureListener(this);
+
+ cameraThread = new HandlerThread("cameraThread", Process.THREAD_PRIORITY_BACKGROUND);
+ cameraThread.start();
+ cameraHandler = new Handler(cameraThread.getLooper());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ maybeOpenCamera();
+ }
+
+ @Override
+ protected void onPause() {
+ cameraHandler.post(closeRunnable);
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ // cancel background thread
+ cameraHandler.removeCallbacksAndMessages(null);
+ cameraThread.quit();
+
+ previewView.setSurfaceTextureListener(null);
+
+ super.onDestroy();
+ }
+
+ private void maybeOpenCamera() {
+ if (surfaceCreated && ContextCompat.checkSelfPermission(this,
+ Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
+ cameraHandler.post(openRunnable);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
+ surfaceCreated = true;
+ maybeOpenCamera();
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
+ surfaceCreated = false;
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) {
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+
+ @Override
+ public void onBackPressed() {
+ scannerView.setVisibility(View.GONE);
+ setResult(RESULT_CANCELED);
+ postFinish();
+ }
+
+ @Override
+ public boolean onKeyDown(final int keyCode, final KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_FOCUS:
+ case KeyEvent.KEYCODE_CAMERA:
+ // don't launch camera app
+ return true;
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ cameraHandler.post(() -> cameraManager.setTorch(keyCode == KeyEvent.KEYCODE_VOLUME_UP));
+ return true;
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public void handleResult(final Result scanResult) {
+ vibrator.vibrate(VIBRATE_DURATION);
+
+ scannerView.setIsResult(true);
+
+ final Intent result = new Intent();
+ result.putExtra(INTENT_EXTRA_RESULT, scanResult.getText());
+ setResult(RESULT_OK, result);
+ postFinish();
+ }
+
+ private void postFinish() {
+ new Handler().postDelayed(() -> finish(), 50);
+ }
+
+ private final class AutoFocusRunnable implements Runnable {
+ private final Camera camera;
+ private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
+ @Override
+ public void onAutoFocus(final boolean success, final Camera camera) {
+ // schedule again
+ cameraHandler.postDelayed(AutoFocusRunnable.this, AUTO_FOCUS_INTERVAL_MS);
+ }
+ };
+
+ public AutoFocusRunnable(final Camera camera) {
+ this.camera = camera;
+ }
+
+ @Override
+ public void run() {
+ try {
+ camera.autoFocus(autoFocusCallback);
+ } catch (final Exception x) {
+ Log.d(Config.LOGTAG, "problem with auto-focus, will not schedule again", x);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
index 385d0a68f..3499b8f00 100644
--- a/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/TrustKeysActivity.java
@@ -16,7 +16,6 @@ import android.widget.Toast;
import org.whispersystems.libsignal.IdentityKey;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -33,7 +32,6 @@ import de.pixart.messenger.entities.Account;
import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.XmppUri;
-import de.pixart.messenger.utils.zxing.IntentIntegrator;
import de.pixart.messenger.xmpp.OnKeyStatusUpdated;
import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
@@ -130,7 +128,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
if (hasPendingKeyFetches()) {
Toast.makeText(this, R.string.please_wait_for_keys_to_be_fetched, Toast.LENGTH_SHORT).show();
} else {
- new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
+ //new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
return true;
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java b/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java
index c4374df33..d71feb3f7 100644
--- a/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/UriHandlerActivity.java
@@ -1,21 +1,60 @@
package de.pixart.messenger.ui;
+import android.Manifest;
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
+import android.widget.Toast;
-import java.util.Arrays;
import java.util.List;
+import de.pixart.messenger.R;
import de.pixart.messenger.persistance.DatabaseBackend;
import de.pixart.messenger.utils.XmppUri;
-import de.pixart.messenger.utils.zxing.IntentIntegrator;
-import de.pixart.messenger.utils.zxing.IntentResult;
import de.pixart.messenger.xmpp.jid.Jid;
public class UriHandlerActivity extends AppCompatActivity {
+
public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
+ private static final int REQUEST_SCAN_QR_CODE = 0x1234;
+ private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
+
+ private boolean handled = false;
+
+ public static void scan(Activity activity) {
+ if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
+ Intent intent = new Intent(activity, UriHandlerActivity.class);
+ intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ activity.startActivity(intent);
+ } else {
+ ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSIONS_TO_SCAN);
+ }
+ }
+
+ public static void onRequestPermissionResult(Activity activity, int requestCode, int[] grantResults) {
+ if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN) {
+ return;
+ }
+ if (grantResults.length > 0) {
+ if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ scan(activity);
+ } else {
+ Toast.makeText(activity, R.string.qr_code_scanner_needs_access_to_camera, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.handled = savedInstanceState != null && savedInstanceState.getBoolean("handled", false);
+ }
@Override
public void onStart() {
@@ -24,6 +63,12 @@ public class UriHandlerActivity extends AppCompatActivity {
}
@Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ savedInstanceState.putBoolean("handled", this.handled);
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
public void onNewIntent(Intent intent) {
handleIntent(intent);
}
@@ -31,7 +76,7 @@ public class UriHandlerActivity extends AppCompatActivity {
private void handleUri(Uri uri) {
final Intent intent;
final XmppUri xmppUri = new XmppUri(uri);
- final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids();
+ final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(); //TODO only look at enabled accounts
if (accounts.size() == 0) {
intent = new Intent(getApplicationContext(), WelcomeActivity.class);
@@ -70,18 +115,24 @@ public class UriHandlerActivity extends AppCompatActivity {
}
private void handleIntent(Intent data) {
+ if (handled) {
+ return;
+ }
if (data == null || data.getAction() == null) {
finish();
return;
}
+ handled = true;
+
switch (data.getAction()) {
case Intent.ACTION_VIEW:
case Intent.ACTION_SENDTO:
handleUri(data.getData());
break;
case ACTION_SCAN_QR_CODE:
- new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC", "QR_CODE"));
+ Intent intent = new Intent(this, ScanActivity.class);
+ startActivityForResult(intent, REQUEST_SCAN_QR_CODE);
return;
}
@@ -90,23 +141,14 @@ public class UriHandlerActivity extends AppCompatActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
- IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
-
- if (scanResult != null && scanResult.getFormatName() != null) {
- String data = scanResult.getContents();
- handleUri(Uri.parse(data));
+ super.onActivityResult(requestCode, requestCode, intent);
+ if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
+ String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
+ if (result != null) {
+ Uri uri = Uri.parse(result);
+ handleUri(uri);
}
}
-
finish();
- super.onActivityResult(requestCode, requestCode, intent);
- }
-
- public static void scan(Activity activity) {
- Intent intent = new Intent(activity, UriHandlerActivity.class);
- intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
- activity.startActivity(intent);
}
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
index ea9bcc5a8..ded1f90c7 100644
--- a/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/VerifyOTRActivity.java
@@ -1,10 +1,10 @@
package de.pixart.messenger.ui;
-import android.support.v7.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
@@ -23,8 +23,6 @@ import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.XmppUri;
-import de.pixart.messenger.utils.zxing.IntentIntegrator;
-import de.pixart.messenger.utils.zxing.IntentResult;
import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
@@ -208,7 +206,8 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
}
this.mode = intent.getIntExtra("mode", MODE_MANUAL_VERIFICATION);
if (this.mode == MODE_SCAN_FINGERPRINT) {
- new IntentIntegrator(this).initiateScan();
+ // todo
+ // new IntentIntegrator(this).initiateScan();
return false;
}
return true;
@@ -219,7 +218,8 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
+ // todo
+ /*if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
if (scanResult != null && scanResult.getFormatName() != null) {
String data = scanResult.getContents();
@@ -233,7 +233,7 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
} else {
finish();
}
- }
+ }*/
super.onActivityResult(requestCode, requestCode, intent);
}
diff --git a/src/main/java/de/pixart/messenger/ui/service/CameraManager.java b/src/main/java/de/pixart/messenger/ui/service/CameraManager.java
new file mode 100644
index 000000000..1c232928f
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/service/CameraManager.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.pixart.messenger.ui.service;
+
+import android.annotation.SuppressLint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.util.Log;
+import android.view.TextureView;
+
+import com.google.zxing.PlanarYUVLuminanceSource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import de.pixart.messenger.Config;
+
+/**
+ * @author Andreas Schildbach
+ */
+@SuppressWarnings("deprecation")
+public final class CameraManager {
+ private static final int MIN_FRAME_SIZE = 240;
+ private static final int MAX_FRAME_SIZE = 600;
+ private static final int MIN_PREVIEW_PIXELS = 470 * 320; // normal screen
+ private static final int MAX_PREVIEW_PIXELS = 1280 * 720;
+
+ private Camera camera;
+ private CameraInfo cameraInfo = new CameraInfo();
+ private Camera.Size cameraResolution;
+ private Rect frame;
+ private RectF framePreview;
+
+ public Rect getFrame() {
+ return frame;
+ }
+
+ public RectF getFramePreview() {
+ return framePreview;
+ }
+
+ public int getFacing() {
+ return cameraInfo.facing;
+ }
+
+ public int getOrientation() {
+ return cameraInfo.orientation;
+ }
+
+ public Camera open(final TextureView textureView, final int displayOrientation, final boolean continuousAutoFocus)
+ throws IOException {
+ final int cameraId = determineCameraId();
+ Camera.getCameraInfo(cameraId, cameraInfo);
+
+ camera = Camera.open(cameraId);
+
+ if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
+ camera.setDisplayOrientation((720 - displayOrientation - cameraInfo.orientation) % 360);
+ else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
+ camera.setDisplayOrientation((720 - displayOrientation + cameraInfo.orientation) % 360);
+ else
+ throw new IllegalStateException("facing: " + cameraInfo.facing);
+
+ camera.setPreviewTexture(textureView.getSurfaceTexture());
+
+ final Camera.Parameters parameters = camera.getParameters();
+
+ cameraResolution = findBestPreviewSizeValue(parameters, textureView.getWidth(), textureView.getHeight());
+
+ final int width = textureView.getWidth();
+ final int height = textureView.getHeight();
+
+ final int rawSize = Math.min(width * 2 / 3, height * 2 / 3);
+ final int frameSize = Math.max(MIN_FRAME_SIZE, Math.min(MAX_FRAME_SIZE, rawSize));
+
+ final int leftOffset = (width - frameSize) / 2;
+ final int topOffset = (height - frameSize) / 2;
+ frame = new Rect(leftOffset, topOffset, leftOffset + frameSize, topOffset + frameSize);
+ framePreview = new RectF(frame.left * cameraResolution.width / width,
+ frame.top * cameraResolution.height / height, frame.right * cameraResolution.width / width,
+ frame.bottom * cameraResolution.height / height);
+
+ final String savedParameters = parameters == null ? null : parameters.flatten();
+
+ try {
+ setDesiredCameraParameters(camera, cameraResolution, continuousAutoFocus);
+ } catch (final RuntimeException x) {
+ if (savedParameters != null) {
+ final Camera.Parameters parameters2 = camera.getParameters();
+ parameters2.unflatten(savedParameters);
+ try {
+ camera.setParameters(parameters2);
+ setDesiredCameraParameters(camera, cameraResolution, continuousAutoFocus);
+ } catch (final RuntimeException x2) {
+ Log.d(Config.LOGTAG,"problem setting camera parameters", x2);
+ }
+ }
+ }
+
+ try {
+ camera.startPreview();
+ return camera;
+ } catch (final RuntimeException x) {
+ Log.w(Config.LOGTAG,"something went wrong while starting camera preview", x);
+ camera.release();
+ throw x;
+ }
+ }
+
+ private int determineCameraId() {
+ final int cameraCount = Camera.getNumberOfCameras();
+ final CameraInfo cameraInfo = new CameraInfo();
+
+ // prefer back-facing camera
+ for (int i = 0; i < cameraCount; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
+ return i;
+ }
+
+ // fall back to front-facing camera
+ for (int i = 0; i < cameraCount; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
+ return i;
+ }
+
+ return -1;
+ }
+
+ public void close() {
+ if (camera != null) {
+ try {
+ camera.stopPreview();
+ } catch (final RuntimeException x) {
+ Log.w(Config.LOGTAG,"something went wrong while stopping camera preview", x);
+ }
+
+ camera.release();
+ }
+ }
+
+ private static final Comparator<Camera.Size> numPixelComparator = new Comparator<Camera.Size>() {
+ @Override
+ public int compare(final Camera.Size size1, final Camera.Size size2) {
+ final int pixels1 = size1.height * size1.width;
+ final int pixels2 = size2.height * size2.width;
+
+ if (pixels1 < pixels2)
+ return 1;
+ else if (pixels1 > pixels2)
+ return -1;
+ else
+ return 0;
+ }
+ };
+
+ private static Camera.Size findBestPreviewSizeValue(final Camera.Parameters parameters, int width, int height) {
+ if (height > width) {
+ final int temp = width;
+ width = height;
+ height = temp;
+ }
+
+ final float screenAspectRatio = (float) width / (float) height;
+
+ final List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
+ if (rawSupportedSizes == null)
+ return parameters.getPreviewSize();
+
+ // sort by size, descending
+ final List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
+ Collections.sort(supportedPreviewSizes, numPixelComparator);
+
+ Camera.Size bestSize = null;
+ float diff = Float.POSITIVE_INFINITY;
+
+ for (final Camera.Size supportedPreviewSize : supportedPreviewSizes) {
+ final int realWidth = supportedPreviewSize.width;
+ final int realHeight = supportedPreviewSize.height;
+ final int realPixels = realWidth * realHeight;
+ if (realPixels < MIN_PREVIEW_PIXELS || realPixels > MAX_PREVIEW_PIXELS)
+ continue;
+
+ final boolean isCandidatePortrait = realWidth < realHeight;
+ final int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
+ final int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
+ if (maybeFlippedWidth == width && maybeFlippedHeight == height)
+ return supportedPreviewSize;
+
+ final float aspectRatio = (float) maybeFlippedWidth / (float) maybeFlippedHeight;
+ final float newDiff = Math.abs(aspectRatio - screenAspectRatio);
+ if (newDiff < diff) {
+ bestSize = supportedPreviewSize;
+ diff = newDiff;
+ }
+ }
+
+ if (bestSize != null)
+ return bestSize;
+ else
+ return parameters.getPreviewSize();
+ }
+
+ @SuppressLint("InlinedApi")
+ private static void setDesiredCameraParameters(final Camera camera, final Camera.Size cameraResolution,
+ final boolean continuousAutoFocus) {
+ final Camera.Parameters parameters = camera.getParameters();
+ if (parameters == null)
+ return;
+
+ final List<String> supportedFocusModes = parameters.getSupportedFocusModes();
+ final String focusMode = continuousAutoFocus
+ ? findValue(supportedFocusModes, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
+ Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, Camera.Parameters.FOCUS_MODE_AUTO,
+ Camera.Parameters.FOCUS_MODE_MACRO)
+ : findValue(supportedFocusModes, Camera.Parameters.FOCUS_MODE_AUTO, Camera.Parameters.FOCUS_MODE_MACRO);
+ if (focusMode != null)
+ parameters.setFocusMode(focusMode);
+
+ parameters.setPreviewSize(cameraResolution.width, cameraResolution.height);
+
+ camera.setParameters(parameters);
+ }
+
+ public void requestPreviewFrame(final android.hardware.Camera.PreviewCallback callback) {
+ try {
+ camera.setOneShotPreviewCallback(callback);
+ } catch (final RuntimeException x) {
+ Log.d(Config.LOGTAG,"problem requesting preview frame, callback won't be called", x);
+ }
+ }
+
+ public PlanarYUVLuminanceSource buildLuminanceSource(final byte[] data) {
+ return new PlanarYUVLuminanceSource(data, cameraResolution.width, cameraResolution.height,
+ (int) framePreview.left, (int) framePreview.top, (int) framePreview.width(),
+ (int) framePreview.height(), false);
+ }
+
+ private static boolean getTorchEnabled(final Camera camera) {
+ final Camera.Parameters parameters = camera.getParameters();
+ if (parameters != null) {
+ final String flashMode = camera.getParameters().getFlashMode();
+ return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)
+ || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
+ }
+
+ return false;
+ }
+
+ private static void setTorchEnabled(final Camera camera, final boolean enabled) {
+ final Camera.Parameters parameters = camera.getParameters();
+
+ final List<String> supportedFlashModes = parameters.getSupportedFlashModes();
+ if (supportedFlashModes != null) {
+ final String flashMode;
+ if (enabled)
+ flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_TORCH,
+ Camera.Parameters.FLASH_MODE_ON);
+ else
+ flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
+
+ if (flashMode != null) {
+ camera.cancelAutoFocus(); // autofocus can cause conflict
+
+ parameters.setFlashMode(flashMode);
+ camera.setParameters(parameters);
+ }
+ }
+ }
+
+ private static String findValue(final Collection<String> values, final String... valuesToFind) {
+ for (final String valueToFind : valuesToFind)
+ if (values.contains(valueToFind))
+ return valueToFind;
+
+ return null;
+ }
+
+ public void setTorch(final boolean enabled) {
+ if (enabled != getTorchEnabled(camera))
+ setTorchEnabled(camera, enabled);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/widget/ScannerView.java b/src/main/java/de/pixart/messenger/ui/widget/ScannerView.java
new file mode 100644
index 000000000..0a2a80039
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/widget/ScannerView.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.pixart.messenger.ui.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.google.zxing.ResultPoint;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import de.pixart.messenger.R;
+
+/**
+ * @author Andreas Schildbach
+ */
+
+public class ScannerView extends View {
+ private static final long LASER_ANIMATION_DELAY_MS = 100l;
+ private static final int DOT_OPACITY = 0xa0;
+ private static final int DOT_TTL_MS = 500;
+
+ private final Paint maskPaint;
+ private final Paint laserPaint;
+ private final Paint dotPaint;
+ private final int maskColor, maskResultColor;
+ private final int laserColor;
+ private final int dotColor, dotResultColor;
+ private final Map<float[], Long> dots = new HashMap<float[], Long>(16);
+ private final Matrix matrix = new Matrix();
+ private boolean isResult;
+ private Rect frame;
+
+ public ScannerView(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+
+ final Resources res = getResources();
+ maskColor = res.getColor(R.color.scan_mask);
+ maskResultColor = res.getColor(R.color.scan_result_view);
+ laserColor = res.getColor(R.color.scan_laser);
+ dotColor = res.getColor(R.color.scan_dot);
+ dotResultColor = res.getColor(R.color.scan_result_dots);
+
+ maskPaint = new Paint();
+ maskPaint.setStyle(Style.FILL);
+
+ laserPaint = new Paint();
+ laserPaint.setStrokeWidth(res.getDimensionPixelSize(R.dimen.scan_laser_width));
+ laserPaint.setStyle(Style.STROKE);
+
+ dotPaint = new Paint();
+ dotPaint.setAlpha(DOT_OPACITY);
+ dotPaint.setStyle(Style.STROKE);
+ dotPaint.setStrokeWidth(res.getDimension(R.dimen.scan_dot_size));
+ dotPaint.setAntiAlias(true);
+ }
+
+ public void setFraming(final Rect frame, final RectF framePreview, final int displayRotation,
+ final int cameraRotation, final boolean cameraFlip) {
+ this.frame = frame;
+ matrix.setRectToRect(framePreview, new RectF(frame), ScaleToFit.FILL);
+ matrix.postRotate(-displayRotation, frame.exactCenterX(), frame.exactCenterY());
+ matrix.postScale(cameraFlip ? -1 : 1, 1, frame.exactCenterX(), frame.exactCenterY());
+ matrix.postRotate(cameraRotation, frame.exactCenterX(), frame.exactCenterY());
+
+ invalidate();
+ }
+
+ public void setIsResult(final boolean isResult) {
+ this.isResult = isResult;
+
+ invalidate();
+ }
+
+ public void addDot(final ResultPoint dot) {
+ dots.put(new float[]{dot.getX(), dot.getY()}, System.currentTimeMillis());
+
+ invalidate();
+ }
+
+ @Override
+ public void onDraw(final Canvas canvas) {
+ if (frame == null)
+ return;
+
+ final long now = System.currentTimeMillis();
+
+ final int width = canvas.getWidth();
+ final int height = canvas.getHeight();
+
+ final float[] point = new float[2];
+
+ // draw mask darkened
+ maskPaint.setColor(isResult ? maskResultColor : maskColor);
+ canvas.drawRect(0, 0, width, frame.top, maskPaint);
+ canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, maskPaint);
+ canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, maskPaint);
+ canvas.drawRect(0, frame.bottom + 1, width, height, maskPaint);
+
+ if (isResult) {
+ laserPaint.setColor(dotResultColor);
+ laserPaint.setAlpha(160);
+
+ dotPaint.setColor(dotResultColor);
+ } else {
+ laserPaint.setColor(laserColor);
+ final boolean laserPhase = (now / 600) % 2 == 0;
+ laserPaint.setAlpha(laserPhase ? 160 : 255);
+
+ dotPaint.setColor(dotColor);
+
+ // schedule redraw
+ postInvalidateDelayed(LASER_ANIMATION_DELAY_MS);
+ }
+
+ canvas.drawRect(frame, laserPaint);
+
+ // draw points
+ for (final Iterator<Map.Entry<float[], Long>> i = dots.entrySet().iterator(); i.hasNext(); ) {
+ final Map.Entry<float[], Long> entry = i.next();
+ final long age = now - entry.getValue();
+ if (age < DOT_TTL_MS) {
+ dotPaint.setAlpha((int) ((DOT_TTL_MS - age) * 256 / DOT_TTL_MS));
+
+ matrix.mapPoints(point, entry.getKey());
+ canvas.drawPoint(point[0], point[1], dotPaint);
+ } else {
+ i.remove();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/zxing/IntentIntegrator.java b/src/main/java/de/pixart/messenger/utils/zxing/IntentIntegrator.java
deleted file mode 100644
index ebb92ae4c..000000000
--- a/src/main/java/de/pixart/messenger/utils/zxing/IntentIntegrator.java
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * Copyright 2009 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package de.pixart.messenger.utils.zxing;
-
-import android.app.Activity;
-import android.support.v7.app.AlertDialog;
-import android.app.Fragment;
-import android.content.ActivityNotFoundException;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import de.pixart.messenger.ui.UriHandlerActivity;
-
-/**
- * <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple
- * way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the
- * project's source code.</p>
- *
- * <h2>Initiating a barcode scan</h2>
- *
- * <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait
- * for the result in your app.</p>
- *
- * <p>It does require that the Barcode Scanner (or work-alike) application is installed. The
- * {@link #initiateScan()} method will prompt the user to download the application, if needed.</p>
- *
- * <p>There are a few steps to using this integration. First, your {@link Activity} must implement
- * the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p>
- *
- * <pre>{@code
- * public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- * IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
- * if (scanResult != null) {
- * // handle scan result
- * }
- * // else continue with any other code you need in the method
- * ...
- * }
- * }</pre>
- *
- * <p>This is where you will handle a scan result.</p>
- *
- * <p>Second, just call this in response to a user action somewhere to begin the scan process:</p>
- *
- * <pre>{@code
- * IntentIntegrator integrator = new IntentIntegrator(yourActivity);
- * integrator.initiateScan();
- * }</pre>
- *
- * <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the
- * user was prompted to download the application. This lets the calling app potentially manage the dialog.
- * In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()}
- * method.</p>
- *
- * <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use
- * {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and
- * yes/no button labels can be changed.</p>
- *
- * <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used
- * to invoke the scanner. This can be used to set additional options not directly exposed by this
- * simplified API.</p>
- *
- * <p>By default, this will only allow applications that are known to respond to this intent correctly
- * do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}.
- * For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p>
- *
- * <h2>Sharing text via barcode</h2>
- *
- * <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p>
- *
- * <p>Some code, particularly download integration, was contributed from the Anobiit application.</p>
- *
- * <h2>Enabling experimental barcode formats</h2>
- *
- * <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as
- * PDF417. Use {@link #initiateScan(Collection)} with
- * a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such
- * formats.</p>
- *
- * @author Sean Owen
- * @author Fred Lin
- * @author Isaac Potoczny-Jones
- * @author Brad Drehmer
- * @author gcstang
- */
-public class IntentIntegrator {
-
- public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
- private static final String TAG = IntentIntegrator.class.getSimpleName();
-
- public static final String DEFAULT_TITLE = "Install Barcode Scanner?";
- public static final String DEFAULT_MESSAGE =
- "This application requires Barcode Scanner. Would you like to install it?";
- public static final String DEFAULT_YES = "Yes";
- public static final String DEFAULT_NO = "No";
-
- private static final String BS_PACKAGE = "com.google.zxing.client.android";
- private static final String BSPLUS_PACKAGE = "com.srowen.bs.android";
-
- // supported barcode formats
- public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14");
- public static final Collection<String> ONE_D_CODE_TYPES =
- list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128",
- "ITF", "RSS_14", "RSS_EXPANDED");
- public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE");
- public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX");
-
- public static final Collection<String> ALL_CODE_TYPES = null;
-
- public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE);
- public static final List<String> TARGET_ALL_KNOWN = list(
- BSPLUS_PACKAGE, // Barcode Scanner+
- BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
- BS_PACKAGE // Barcode Scanner
- // What else supports this intent?
- );
-
- // Should be FLAG_ACTIVITY_NEW_DOCUMENT in API 21+.
- // Defined once here because the current value is deprecated, so generates just one warning
- private static final int FLAG_NEW_DOC = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
-
- private final Activity activity;
- private final Fragment fragment;
-
- private String title;
- private String message;
- private String buttonYes;
- private String buttonNo;
- private List<String> targetApplications;
- private final Map<String,Object> moreExtras = new HashMap<String,Object>(3);
-
- /**
- * @param activity {@link Activity} invoking the integration
- */
- public IntentIntegrator(Activity activity) {
- this.activity = activity;
- this.fragment = null;
- initializeConfiguration();
- }
-
- /**
- * @param fragment {@link Fragment} invoking the integration.
- * {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead
- * of an {@link Activity}
- */
- public IntentIntegrator(Fragment fragment) {
- this.activity = fragment.getActivity();
- this.fragment = fragment;
- initializeConfiguration();
- }
-
- private void initializeConfiguration() {
- title = DEFAULT_TITLE;
- message = DEFAULT_MESSAGE;
- buttonYes = DEFAULT_YES;
- buttonNo = DEFAULT_NO;
- targetApplications = TARGET_ALL_KNOWN;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public void setTitleByID(int titleID) {
- title = activity.getString(titleID);
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
-
- public void setMessageByID(int messageID) {
- message = activity.getString(messageID);
- }
-
- public String getButtonYes() {
- return buttonYes;
- }
-
- public void setButtonYes(String buttonYes) {
- this.buttonYes = buttonYes;
- }
-
- public void setButtonYesByID(int buttonYesID) {
- buttonYes = activity.getString(buttonYesID);
- }
-
- public String getButtonNo() {
- return buttonNo;
- }
-
- public void setButtonNo(String buttonNo) {
- this.buttonNo = buttonNo;
- }
-
- public void setButtonNoByID(int buttonNoID) {
- buttonNo = activity.getString(buttonNoID);
- }
-
- public Collection<String> getTargetApplications() {
- return targetApplications;
- }
-
- public final void setTargetApplications(List<String> targetApplications) {
- if (targetApplications.isEmpty()) {
- throw new IllegalArgumentException("No target applications");
- }
- this.targetApplications = targetApplications;
- }
-
- public void setSingleTargetApplication(String targetApplication) {
- this.targetApplications = Collections.singletonList(targetApplication);
- }
-
- public Map<String,?> getMoreExtras() {
- return moreExtras;
- }
-
- public final void addExtra(String key, Object value) {
- moreExtras.put(key, value);
- }
-
- /**
- * Initiates a scan for all known barcode types with the default camera.
- *
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise.
- */
- public final AlertDialog initiateScan() {
- return initiateScan(ALL_CODE_TYPES, -1);
- }
-
- /**
- * Initiates a scan for all known barcode types with the specified camera.
- *
- * @param cameraId camera ID of the camera to use. A negative value means "no preference".
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise.
- */
- public final AlertDialog initiateScan(int cameraId) {
- return initiateScan(ALL_CODE_TYPES, cameraId);
- }
-
- /**
- * Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding
- * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
- * like {@link #PRODUCT_CODE_TYPES} for example.
- *
- * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise.
- */
- public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) {
- return initiateScan(desiredBarcodeFormats, -1);
- }
-
- /**
- * Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding
- * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants
- * like {@link #PRODUCT_CODE_TYPES} for example.
- *
- * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
- * @param cameraId camera ID of the camera to use. A negative value means "no preference".
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise
- */
- public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) {
- Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
- intentScan.addCategory(Intent.CATEGORY_DEFAULT);
-
- // check which types of codes to scan for
- if (desiredBarcodeFormats != null) {
- // set the desired barcode types
- StringBuilder joinedByComma = new StringBuilder();
- for (String format : desiredBarcodeFormats) {
- if (joinedByComma.length() > 0) {
- joinedByComma.append(',');
- }
- joinedByComma.append(format);
- }
- intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
- }
-
- // check requested camera ID
- if (cameraId >= 0) {
- intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
- }
-
- String targetAppPackage = findTargetAppPackage(intentScan);
- if (targetAppPackage == null) {
- return showDownloadDialog();
- }
- intentScan.setPackage(targetAppPackage);
- intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intentScan.addFlags(FLAG_NEW_DOC);
- attachMoreExtras(intentScan);
- startActivityForResult(intentScan, REQUEST_CODE);
- return null;
- }
-
- /**
- * Start an activity. This method is defined to allow different methods of activity starting for
- * newer versions of Android and for compatibility library.
- *
- * @param intent Intent to start.
- * @param code Request code for the activity
- * @see Activity#startActivityForResult(Intent, int)
- * @see Fragment#startActivityForResult(Intent, int)
- */
- protected void startActivityForResult(Intent intent, int code) {
- if (fragment == null) {
- activity.startActivityForResult(intent, code);
- } else {
- fragment.startActivityForResult(intent, code);
- }
- }
-
- private String findTargetAppPackage(Intent intent) {
- PackageManager pm = activity.getPackageManager();
- List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if (availableApps != null) {
- for (String targetApp : targetApplications) {
- if (contains(availableApps, targetApp)) {
- return targetApp;
- }
- }
- }
- return null;
- }
-
- private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) {
- for (ResolveInfo availableApp : availableApps) {
- String packageName = availableApp.activityInfo.packageName;
- if (targetApp.equals(packageName)) {
- return true;
- }
- }
- return false;
- }
-
- private AlertDialog showDownloadDialog() {
- AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
- downloadDialog.setTitle(title);
- downloadDialog.setMessage(message);
- downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- String packageName;
- if (targetApplications.contains(BS_PACKAGE)) {
- // Prefer to suggest download of BS if it's anywhere in the list
- packageName = BS_PACKAGE;
- } else {
- // Otherwise, first option:
- packageName = targetApplications.get(0);
- }
- Uri uri = Uri.parse("market://details?id=" + packageName);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- try {
- if (fragment == null) {
- activity.startActivity(intent);
- finishIfNeeded();
- } else {
- fragment.startActivity(intent);
- }
- } catch (ActivityNotFoundException anfe) {
- // Hmm, market is not installed
- Log.w(TAG, "Google Play is not installed; cannot install " + packageName);
- }
- }
- });
- downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- finishIfNeeded();
- }
- });
- downloadDialog.setCancelable(true);
- downloadDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- finishIfNeeded();
- }
- });
- return downloadDialog.show();
- }
-
- private void finishIfNeeded() {
- if (fragment != null) {
- return;
- }
- if (activity != null && activity instanceof UriHandlerActivity) {
- activity.finish();
- }
- }
-
-
- /**
- * <p>Call this from your {@link Activity}'s
- * {@link Activity#onActivityResult(int, int, Intent)} method.</p>
- *
- * @param requestCode request code from {@code onActivityResult()}
- * @param resultCode result code from {@code onActivityResult()}
- * @param intent {@link Intent} from {@code onActivityResult()}
- * @return null if the event handled here was not related to this class, or
- * else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning,
- * the fields will be null.
- */
- public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) {
- if (requestCode == REQUEST_CODE) {
- if (resultCode == Activity.RESULT_OK) {
- String contents = intent.getStringExtra("SCAN_RESULT");
- String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT");
- byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES");
- int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE);
- Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation;
- String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL");
- return new IntentResult(contents,
- formatName,
- rawBytes,
- orientation,
- errorCorrectionLevel);
- }
- return new IntentResult();
- }
- return null;
- }
-
-
- /**
- * Defaults to type "TEXT_TYPE".
- *
- * @param text the text string to encode as a barcode
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise
- * @see #shareText(CharSequence, CharSequence)
- */
- public final AlertDialog shareText(CharSequence text) {
- return shareText(text, "TEXT_TYPE");
- }
-
- /**
- * Shares the given text by encoding it as a barcode, such that another user can
- * scan the text off the screen of the device.
- *
- * @param text the text string to encode as a barcode
- * @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants.
- * @return the {@link AlertDialog} that was shown to the user prompting them to download the app
- * if a prompt was needed, or null otherwise
- */
- public final AlertDialog shareText(CharSequence text, CharSequence type) {
- Intent intent = new Intent();
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setAction(BS_PACKAGE + ".ENCODE");
- intent.putExtra("ENCODE_TYPE", type);
- intent.putExtra("ENCODE_DATA", text);
- String targetAppPackage = findTargetAppPackage(intent);
- if (targetAppPackage == null) {
- return showDownloadDialog();
- }
- intent.setPackage(targetAppPackage);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.addFlags(FLAG_NEW_DOC);
- attachMoreExtras(intent);
- if (fragment == null) {
- activity.startActivity(intent);
- } else {
- fragment.startActivity(intent);
- }
- return null;
- }
-
- private static List<String> list(String... values) {
- return Collections.unmodifiableList(Arrays.asList(values));
- }
-
- private void attachMoreExtras(Intent intent) {
- for (Map.Entry<String,Object> entry : moreExtras.entrySet()) {
- String key = entry.getKey();
- Object value = entry.getValue();
- // Kind of hacky
- if (value instanceof Integer) {
- intent.putExtra(key, (Integer) value);
- } else if (value instanceof Long) {
- intent.putExtra(key, (Long) value);
- } else if (value instanceof Boolean) {
- intent.putExtra(key, (Boolean) value);
- } else if (value instanceof Double) {
- intent.putExtra(key, (Double) value);
- } else if (value instanceof Float) {
- intent.putExtra(key, (Float) value);
- } else if (value instanceof Bundle) {
- intent.putExtra(key, (Bundle) value);
- } else {
- intent.putExtra(key, value.toString());
- }
- }
- }
-
-}
diff --git a/src/main/java/de/pixart/messenger/utils/zxing/IntentResult.java b/src/main/java/de/pixart/messenger/utils/zxing/IntentResult.java
deleted file mode 100644
index e238ef397..000000000
--- a/src/main/java/de/pixart/messenger/utils/zxing/IntentResult.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2009 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package de.pixart.messenger.utils.zxing;
-
-/**
- * <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p>
- *
- * @author Sean Owen
- */
-public final class IntentResult {
-
- private final String contents;
- private final String formatName;
- private final byte[] rawBytes;
- private final Integer orientation;
- private final String errorCorrectionLevel;
-
- IntentResult() {
- this(null, null, null, null, null);
- }
-
- IntentResult(String contents,
- String formatName,
- byte[] rawBytes,
- Integer orientation,
- String errorCorrectionLevel) {
- this.contents = contents;
- this.formatName = formatName;
- this.rawBytes = rawBytes;
- this.orientation = orientation;
- this.errorCorrectionLevel = errorCorrectionLevel;
- }
-
- /**
- * @return raw content of barcode
- */
- public String getContents() {
- return contents;
- }
-
- /**
- * @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names.
- */
- public String getFormatName() {
- return formatName;
- }
-
- /**
- * @return raw bytes of the barcode content, if applicable, or null otherwise
- */
- public byte[] getRawBytes() {
- return rawBytes;
- }
-
- /**
- * @return rotation of the image, in degrees, which resulted in a successful scan. May be null.
- */
- public Integer getOrientation() {
- return orientation;
- }
-
- /**
- * @return name of the error correction level used in the barcode, if applicable
- */
- public String getErrorCorrectionLevel() {
- return errorCorrectionLevel;
- }
-
- @Override
- public String toString() {
- int rawBytesLength = rawBytes == null ? 0 : rawBytes.length;
- return "Format: " + formatName + '\n' +
- "Contents: " + contents + '\n' +
- "Raw bytes: (" + rawBytesLength + " bytes)\n" +
- "Orientation: " + orientation + '\n' +
- "EC level: " + errorCorrectionLevel + '\n';
- }
-
-}
diff --git a/src/main/res/layout/activity_scan.xml b/src/main/res/layout/activity_scan.xml
new file mode 100644
index 000000000..0f3765b15
--- /dev/null
+++ b/src/main/res/layout/activity_scan.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextureView
+ android:id="@+id/scan_activity_preview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:keepScreenOn="true" />
+
+ <eu.siacs.conversations.ui.widget.ScannerView
+ android:id="@+id/scan_activity_mask"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</merge> \ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index d3b0e6a88..2442282e3 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -26,4 +26,11 @@
<color name="away">#ffff9800</color>
<color name="notavailable">#fff44336</color>
<color name="warning_button">#fff5d9d9</color>
+
+ <!-- scanner -->
+ <color name="scan_mask">#60000000</color>
+ <color name="scan_laser">#cc0000</color>
+ <color name="scan_dot">#ff6600</color>
+ <color name="scan_result_view">#b0000000</color>
+ <color name="scan_result_dots">#c099cc00</color>
</resources>
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index 933c7daea..d6a9a5ede 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -10,4 +10,8 @@
<dimen name="fineprint_size">11sp</dimen>
<dimen name="audio_player_width">224dp</dimen>
<dimen name="avatar_item_distance">16dp</dimen>
+
+ <!-- scanner -->
+ <dimen name="scan_laser_width">4dp</dimen>
+ <dimen name="scan_dot_size">8dp</dimen>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 4f8554b0c..1fd306d37 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -770,4 +770,5 @@
<string name="mtm_notification">Certificate Verification</string>
<string name="once">Once</string>
<string name="theme">Theme</string>
+ <string name="qr_code_scanner_needs_access_to_camera">The QR code scanner needs access to the camera</string>
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index b70fe0bd7..4db3ae107 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -85,4 +85,12 @@
<item name="android:windowFullscreen">true</item>
</style>
+ <style name="ConversationsTheme.FullScreen" parent="@style/Theme.AppCompat.Light">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowBackground">@android:color/black</item>
+ </style>
+
</resources> \ No newline at end of file