After Width: | Height: | Size: 276 B |
After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 199 B |
After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 386 B |
After Width: | Height: | Size: 524 B |
After Width: | Height: | Size: 466 B |
After Width: | Height: | Size: 707 B |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/white"/>
|
||||
</shape>
|
@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:background="?verification_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SquareImageView
|
||||
android:id="@+id/qr_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:padding="20dp"
|
||||
android:background="@drawable/qr_code_background"
|
||||
tools:src="@drawable/splash_logo"/>
|
||||
|
||||
<TextView android:id="@+id/tap_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="35dp"
|
||||
android:textSize="11sp"
|
||||
android:text="@string/verify_display_fragment__tap_to_scan"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.SquareImageView
|
||||
android:id="@+id/qr_verified"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="20dp"
|
||||
android:padding="20dp"
|
||||
android:src="@drawable/ic_check_white_48dp"
|
||||
android:background="@drawable/qr_code_background"
|
||||
android:backgroundTint="@color/green_500"
|
||||
android:visibility="gone"/>
|
||||
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<TableLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp">
|
||||
|
||||
<TableRow android:gravity="center_horizontal">
|
||||
<TextView android:id="@+id/code_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="22934"/>
|
||||
|
||||
<TextView android:id="@+id/code_second"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="56944"/>
|
||||
|
||||
<TextView android:id="@+id/code_third"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="42738"/>
|
||||
|
||||
<TextView android:id="@+id/code_fourth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="20038"/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:gravity="center_horizontal">
|
||||
<TextView android:id="@+id/code_fifth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="34431"/>
|
||||
|
||||
<TextView android:id="@+id/code_sixth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="24922"/>
|
||||
|
||||
<TextView android:id="@+id/code_seventh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="58594"/>
|
||||
|
||||
<TextView android:id="@+id/code_eighth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="24109"/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:gravity="center_horizontal">
|
||||
<TextView android:id="@+id/code_ninth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="00257"/>
|
||||
|
||||
<TextView android:id="@+id/code_tenth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="34956"/>
|
||||
|
||||
<TextView android:id="@+id/code_eleventh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="32440"/>
|
||||
|
||||
<TextView android:id="@+id/code_twelth"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
style="@style/IdentityKey"
|
||||
tools:text="15774"/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<TextView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="25dp"
|
||||
android:textSize="17sp"
|
||||
android:lineSpacingExtra="3sp"
|
||||
android:text="@string/verify_display_fragment__scan_the_code_on_your_contact_s_phone_or_ask_them_to_scan_your_code_to_verify_that_your_messages_are_end_to_end_encrypted_you_can_alternately_compare_the_number_above"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/verify_identity_activity__their_identity_they_read"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:typeface="monospace"
|
||||
android:fontFamily="monospace"
|
||||
android:id="@+id/friend_reads"
|
||||
android:text=""
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/verify_identity_activity__your_identity_you_read"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/you_read"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:typeface="monospace"
|
||||
android:fontFamily="monospace"
|
||||
android:text="" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<org.thoughtcrime.securesms.components.camera.CameraView
|
||||
android:id="@+id/scanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:camera="0"/>
|
||||
|
||||
<LinearLayout android:id="@+id/overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.ShapeScrim
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:radius="0.3"
|
||||
app:shape="circle"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:layout_marginRight="16dip"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:typeface="monospace"
|
||||
android:id="@+id/identity_fingerprint"
|
||||
android:text=""
|
||||
android:padding="7dip" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -1,136 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* 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 org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
import com.google.zxing.integration.android.IntentResult;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
|
||||
/**
|
||||
* Activity for initiating/receiving key QR code scans.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public abstract class KeyScanningActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
dynamicLanguage.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
inflater.inflate(R.menu.key_scanning, menu);
|
||||
|
||||
menu.findItem(R.id.menu_scan).setTitle(getScanString());
|
||||
menu.findItem(R.id.menu_get_scanned).setTitle(getDisplayString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_scan: initiateScan(); return true;
|
||||
case R.id.menu_get_scanned: initiateDisplay(); return true;
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
if ((scanResult != null) && (scanResult.getContents() != null)) {
|
||||
String data = scanResult.getContents();
|
||||
|
||||
if (data.equals(Base64.encodeBytes(getIdentityKeyToCompare().serialize()))) {
|
||||
Dialogs.showInfoDialog(this, getVerifiedTitle(), getVerifiedMessage());
|
||||
} else {
|
||||
Dialogs.showAlertDialog(this, getNotVerifiedTitle(), getNotVerifiedMessage());
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, R.string.KeyScanningActivity_no_scanned_key_found_exclamation,
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private IntentIntegrator getIntentIntegrator() {
|
||||
IntentIntegrator intentIntegrator = new IntentIntegrator(this);
|
||||
intentIntegrator.setButtonYesByID(R.string.yes);
|
||||
intentIntegrator.setButtonNoByID(R.string.no);
|
||||
intentIntegrator.setTitleByID(R.string.KeyScanningActivity_install_barcode_Scanner);
|
||||
intentIntegrator.setMessageByID(R.string.KeyScanningActivity_this_application_requires_barcode_scanner_would_you_like_to_install_it);
|
||||
return intentIntegrator;
|
||||
}
|
||||
|
||||
protected void initiateScan() {
|
||||
IntentIntegrator intentIntegrator = getIntentIntegrator();
|
||||
intentIntegrator.initiateScan();
|
||||
}
|
||||
|
||||
protected void initiateDisplay() {
|
||||
IntentIntegrator intentIntegrator = getIntentIntegrator();
|
||||
intentIntegrator.shareText(Base64.encodeBytes(getIdentityKeyToDisplay().serialize()));
|
||||
}
|
||||
|
||||
protected abstract String getScanString();
|
||||
protected abstract String getDisplayString();
|
||||
|
||||
protected abstract String getNotVerifiedTitle();
|
||||
protected abstract String getNotVerifiedMessage();
|
||||
|
||||
protected abstract IdentityKey getIdentityKeyToCompare();
|
||||
protected abstract IdentityKey getIdentityKeyToDisplay();
|
||||
|
||||
protected abstract String getVerifiedTitle();
|
||||
protected abstract String getVerifiedMessage();
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* 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 org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
|
||||
/**
|
||||
* Activity for displaying an identity key.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class ViewIdentityActivity extends KeyScanningActivity {
|
||||
|
||||
public static final String IDENTITY_KEY = "identity_key";
|
||||
public static final String TITLE = "title";
|
||||
|
||||
private TextView identityFingerprint;
|
||||
private IdentityKey identityKey;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setContentView(R.layout.view_identity_activity);
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
initializeResources();
|
||||
initializeFingerprint();
|
||||
}
|
||||
|
||||
private void initializeFingerprint() {
|
||||
if (identityKey == null) {
|
||||
identityFingerprint.setText(R.string.ViewIdentityActivity_you_do_not_have_an_identity_key);
|
||||
} else {
|
||||
identityFingerprint.setText(Hex.toString(identityKey.serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
IdentityKeyParcelable identityKeyParcelable = getIntent().getParcelableExtra(IDENTITY_KEY);
|
||||
|
||||
if (identityKeyParcelable == null) {
|
||||
throw new AssertionError("No identity key!");
|
||||
}
|
||||
|
||||
this.identityKey = identityKeyParcelable.get();
|
||||
this.identityFingerprint = (TextView)findViewById(R.id.identity_fingerprint);
|
||||
String title = getIntent().getStringExtra(TITLE);
|
||||
|
||||
if (title != null) {
|
||||
getSupportActionBar().setTitle(getIntent().getStringExtra(TITLE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getScanString() {
|
||||
return getString(R.string.ViewIdentityActivity_scan_contacts_qr_code);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDisplayString() {
|
||||
return getString(R.string.ViewIdentityActivity_display_your_qr_code);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdentityKey getIdentityKeyToCompare() {
|
||||
return identityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IdentityKey getIdentityKeyToDisplay() {
|
||||
return identityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNotVerifiedMessage() {
|
||||
return getString(R.string.ViewIdentityActivity_warning_the_scanned_key_does_not_match_exclamation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNotVerifiedTitle() {
|
||||
return getString(R.string.ViewIdentityActivity_not_verified_exclamation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVerifiedMessage() {
|
||||
return getString(R.string.ViewIdentityActivity_the_scanned_key_matches_exclamation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVerifiedTitle() {
|
||||
return getString(R.string.ViewIdentityActivity_verified_exclamation);
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
* Copyright (C) 2013 Open Whisper Systems
|
||||
*
|
||||
* 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 org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
|
||||
/**
|
||||
* Activity that displays the local identity key and offers the option to regenerate it.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class ViewLocalIdentityActivity extends ViewIdentityActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
|
||||
getIntent().putExtra(ViewIdentityActivity.IDENTITY_KEY,
|
||||
new IdentityKeyParcelable(IdentityKeyUtil.getIdentityKey(this)));
|
||||
getIntent().putExtra(ViewIdentityActivity.TITLE,
|
||||
getString(R.string.ViewIdentityActivity_your_identity_fingerprint));
|
||||
super.onCreate(icicle, masterSecret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
inflater.inflate(R.menu.local_identity, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
public class SquareImageView extends ImageView {
|
||||
public SquareImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.qr;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
|
||||
public class QrCode {
|
||||
|
||||
public static final String TAG = QrCode.class.getSimpleName();
|
||||
|
||||
public static @NonNull Bitmap create(String data) {
|
||||
try {
|
||||
BitMatrix result = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, 512, 512);
|
||||
Bitmap bitmap = Bitmap.createBitmap(result.getWidth(), result.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
for (int y = 0; y < result.getHeight(); y++) {
|
||||
for (int x = 0; x < result.getWidth(); x++) {
|
||||
if (result.get(x, y)) {
|
||||
bitmap.setPixel(x, y, Color.BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
} catch (WriterException e) {
|
||||
Log.w(TAG, e);
|
||||
return Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.thoughtcrime.securesms.qr;
|
||||
|
||||
public interface ScanListener {
|
||||
public void onQrDataFound(String data);
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package org.thoughtcrime.securesms.qr;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.ChecksumException;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.PlanarYUVLuminanceSource;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
|
||||
import org.thoughtcrime.securesms.components.camera.CameraView;
|
||||
import org.thoughtcrime.securesms.components.camera.CameraView.PreviewFrame;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ScanningThread extends Thread implements CameraView.PreviewCallback {
|
||||
|
||||
private static final String TAG = ScanningThread.class.getSimpleName();
|
||||
|
||||
private final QRCodeReader reader = new QRCodeReader();
|
||||
private final AtomicReference<ScanListener> scanListener = new AtomicReference<>();
|
||||
private final Map<DecodeHintType, String> hints = new HashMap<>();
|
||||
|
||||
private boolean scanning = true;
|
||||
private PreviewFrame previewFrame;
|
||||
|
||||
public void setCharacterSet(String characterSet) {
|
||||
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
|
||||
}
|
||||
|
||||
public void setScanListener(ScanListener scanListener) {
|
||||
this.scanListener.set(scanListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(@NonNull PreviewFrame previewFrame) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
this.previewFrame = previewFrame;
|
||||
this.notify();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
PreviewFrame ourFrame;
|
||||
|
||||
synchronized (this) {
|
||||
while (scanning && previewFrame == null) {
|
||||
Util.wait(this, 0);
|
||||
}
|
||||
|
||||
if (!scanning) return;
|
||||
else ourFrame = previewFrame;
|
||||
|
||||
previewFrame = null;
|
||||
}
|
||||
|
||||
String data = getScannedData(ourFrame.getData(), ourFrame.getWidth(), ourFrame.getHeight(), ourFrame.getOrientation());
|
||||
ScanListener scanListener = this.scanListener.get();
|
||||
|
||||
if (data != null && scanListener != null) {
|
||||
scanListener.onQrDataFound(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopScanning() {
|
||||
synchronized (this) {
|
||||
scanning = false;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String getScannedData(byte[] data, int width, int height, int orientation) {
|
||||
try {
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
byte[] rotatedData = new byte[data.length];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
rotatedData[x * height + height - y - 1] = data[x + y * width];
|
||||
}
|
||||
}
|
||||
|
||||
int tmp = width;
|
||||
width = height;
|
||||
height = tmp;
|
||||
data = rotatedData;
|
||||
}
|
||||
|
||||
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, width, height,
|
||||
0, 0, width, height,
|
||||
false);
|
||||
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
Result result = reader.decode(bitmap, hints);
|
||||
|
||||
if (result != null) return result.getText();
|
||||
|
||||
} catch (NullPointerException | ChecksumException | FormatException e) {
|
||||
Log.w(TAG, e);
|
||||
} catch (NotFoundException e) {
|
||||
// Thanks ZXing...
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|