Added identicons based on public key when adding new contact.
Also fixed a derp with cases.
This commit is contained in:
parent
f31db97230
commit
e430608311
6 changed files with 176 additions and 22 deletions
60
app/src/main/res/layout/dialog_add_contact.xml
Normal file
60
app/src/main/res/layout/dialog_add_contact.xml
Normal file
|
@ -0,0 +1,60 @@
|
|||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="10dip">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_above="@+id/hint">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="10dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_identicon_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="@string/local_fingerprint_title"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/local_identicon"
|
||||
android:layout_width="150dip"
|
||||
android:layout_height="150dip"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_below="@id/local_identicon_title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remote_identicon_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_below="@id/local_identicon"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/remote_identicon"
|
||||
android:layout_width="150dip"
|
||||
android:layout_height="150dip"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_below="@id/remote_identicon_title"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</ScrollView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:gravity="center"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:text="@string/add_contact_dialog_hint"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -35,6 +35,15 @@
|
|||
<!-- Title of dialog shown when clicking a device -->
|
||||
<string name="add_contact_dialog">Do you want to add %1$s as a new contact?</string>
|
||||
|
||||
<!-- Text to describe the local device's identicon image -->
|
||||
<string name="local_fingerprint_title">Your key fingerprint:</string>
|
||||
|
||||
<!-- Text to describe the remote device's identicon image. Argument is the contact name -->
|
||||
<string name="remote_fingerprint_title">%1$s\'s key fingerprint:</string>
|
||||
|
||||
<!-- Information text shown in the "add contact" dialog -->
|
||||
<string name="add_contact_dialog_hint">Before accepting, make sure the images match on both devices</string>
|
||||
|
||||
<!-- Empty text for devices list -->
|
||||
<string name="no_devices_nearby">No nearby devices found</string>
|
||||
|
||||
|
|
|
@ -3,17 +3,17 @@ package com.nutomic.ensichat.activities
|
|||
import java.util.Date
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.DialogInterface.OnClickListener
|
||||
import android.content.{Context, DialogInterface}
|
||||
import android.os.Bundle
|
||||
import android.view._
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.{AdapterView, ListView, Toast}
|
||||
import android.widget._
|
||||
import com.nutomic.ensichat.R
|
||||
import com.nutomic.ensichat.bluetooth.ChatService.OnMessageReceivedListener
|
||||
import com.nutomic.ensichat.bluetooth.{ChatService, Device}
|
||||
import com.nutomic.ensichat.messages.{Message, RequestAddContactMessage, ResultAddContactMessage}
|
||||
import com.nutomic.ensichat.util.DevicesAdapter
|
||||
import com.nutomic.ensichat.messages.{Crypto, Message, RequestAddContactMessage, ResultAddContactMessage}
|
||||
import com.nutomic.ensichat.util.{DevicesAdapter, IdenticonGenerator}
|
||||
|
||||
import scala.collection.SortedSet
|
||||
|
||||
|
@ -29,6 +29,8 @@ class AddContactsActivity extends EnsiChatActivity with ChatService.OnConnection
|
|||
|
||||
private lazy val Database = service.database
|
||||
|
||||
private lazy val Crypto = new Crypto(this.getFilesDir)
|
||||
|
||||
/**
|
||||
* Map of devices that should be added.
|
||||
*/
|
||||
|
@ -109,8 +111,21 @@ class AddContactsActivity extends EnsiChatActivity with ChatService.OnConnection
|
|||
}
|
||||
}
|
||||
|
||||
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE).asInstanceOf[LayoutInflater]
|
||||
val view = inflater.inflate(R.layout.dialog_add_contact, null)
|
||||
|
||||
val local = view.findViewById(R.id.local_identicon).asInstanceOf[ImageView]
|
||||
local.setImageBitmap(
|
||||
IdenticonGenerator.generate(Crypto.getLocalPublicKey, (150, 150), this))
|
||||
val remoteTitle = view.findViewById(R.id.remote_identicon_title).asInstanceOf[TextView]
|
||||
remoteTitle.setText(getString(R.string.remote_fingerprint_title, device.Name))
|
||||
val remote = view.findViewById(R.id.remote_identicon).asInstanceOf[ImageView]
|
||||
remote.setImageBitmap(
|
||||
IdenticonGenerator.generate(Crypto.getPublicKey(device.Id), (150, 150), this))
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.add_contact_dialog, device.Name))
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.yes, onClick)
|
||||
.setNegativeButton(android.R.string.no, onClick)
|
||||
.show()
|
||||
|
@ -126,7 +141,7 @@ class AddContactsActivity extends EnsiChatActivity with ChatService.OnConnection
|
|||
messages.foreach(msg => {
|
||||
if (msg.receiver == service.localDeviceId) {
|
||||
msg match {
|
||||
case _: ResultAddContactMessage =>
|
||||
case _: RequestAddContactMessage =>
|
||||
// Remote device wants to add us as a contact, show dialog.
|
||||
val sender = getDevice(msg.sender)
|
||||
addDeviceDialog(sender)
|
||||
|
|
|
@ -4,9 +4,9 @@ import java.io._
|
|||
|
||||
import android.bluetooth.BluetoothSocket
|
||||
import android.util.Log
|
||||
import com.nutomic.ensichat.messages.Message._
|
||||
import com.nutomic.ensichat.messages.{Crypto, DeviceInfoMessage, Message}
|
||||
import org.msgpack.ScalaMessagePack
|
||||
import com.nutomic.ensichat.messages.Message._
|
||||
|
||||
/**
|
||||
* Transfers data between connnected devices.
|
||||
|
|
|
@ -72,6 +72,15 @@ class Crypto(filesDir: File) {
|
|||
*/
|
||||
def havePublicKey(device: Device.ID): Boolean = new File(keyFolder, device.toString).exists()
|
||||
|
||||
/**
|
||||
* Returns the public key for the given device.
|
||||
*
|
||||
* @throws RuntimeException If the key does not exist.
|
||||
*/
|
||||
def getPublicKey(device: Device.ID): PublicKey = {
|
||||
loadKey(device.toString, classOf[PublicKey])
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new public key for a remote device.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package com.nutomic.ensichat.util
|
||||
|
||||
import java.security.{MessageDigest, PublicKey}
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap.Config
|
||||
import android.graphics.{Bitmap, Canvas, Color}
|
||||
|
||||
/**
|
||||
* Calculates a unique identicon for the given hash.
|
||||
*
|
||||
* Based on "Contact Identicons" by David Hamp-Gonsalves (converted from Java to Scala).
|
||||
* https://github.com/davidhampgonsalves/Contact-Identicons
|
||||
*/
|
||||
object IdenticonGenerator {
|
||||
|
||||
val Height: Int = 5
|
||||
|
||||
val Width: Int = 5
|
||||
|
||||
/**
|
||||
* Generates an identicon for the key.
|
||||
*
|
||||
* The identicon size is fixed to [[Height]]x[[Width]].
|
||||
*
|
||||
* @param size The size of the bitmap returned.
|
||||
*/
|
||||
def generate(key: PublicKey, size: (Int, Int), context: Context): Bitmap = {
|
||||
// Hash the key.
|
||||
val digest = MessageDigest.getInstance("SHA-1")
|
||||
val hash = digest.digest(key.getEncoded)
|
||||
|
||||
// Create base image and colors.
|
||||
var identicon = Bitmap.createBitmap(Width, Height, Config.ARGB_8888)
|
||||
val background = Color.parseColor("#f0f0f0")
|
||||
val r = hash(0) & 255
|
||||
val g = hash(1) & 255
|
||||
val b = hash(2) & 255
|
||||
val foreground = Color.argb(255, r, g, b)
|
||||
|
||||
// Color pixels.
|
||||
for (x <- 0 until Width) {
|
||||
val i = if (x < 3) x else 4 - x
|
||||
var pixelColor: Int = 0
|
||||
for (y <- 0 until Height) {
|
||||
pixelColor = if ((hash(i) >> y & 1) == 1) foreground else background
|
||||
identicon.setPixel(x, y, pixelColor)
|
||||
}
|
||||
}
|
||||
|
||||
// Add border.
|
||||
val bmpWithBorder = Bitmap.createBitmap(12, 12, identicon.getConfig)
|
||||
val canvas = new Canvas(bmpWithBorder)
|
||||
canvas.drawColor(background)
|
||||
identicon = Bitmap.createScaledBitmap(identicon, 10, 10, false)
|
||||
canvas.drawBitmap(identicon, 1, 1, null)
|
||||
|
||||
Bitmap.createScaledBitmap(identicon, size._1, size._2, false)
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue