mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Merge branch '82-identicons' into 'master'
Identicons Closes #120. Part of #82. I pulled in the identicon code because there is no published library, and also because when I used it for Bote I ended up modifying the code somewhat for my purposes; I expect the same to happen here. I am not sure what information should be used to seed the identicon. I originally thought of using the public key, but that seemed to change the identicon every single page load (obviously the representation wasn't consistent). Then I tried using the `ContactId`, which was fine until I got to adding an identicon to `AuthorView`: activities that use it are only passed (via Intent) a name, not anything else. Hence `AuthorView` identicons are currently inconsistent with the contact list and conversations. See merge request !51
This commit is contained in:
@@ -17,6 +17,7 @@ dependencies {
|
|||||||
compile "org.roboguice:roboguice:2.0"
|
compile "org.roboguice:roboguice:2.0"
|
||||||
compile "info.guardianproject.panic:panic:0.5"
|
compile "info.guardianproject.panic:panic:0.5"
|
||||||
compile "info.guardianproject.trustedintents:trustedintents:0.2"
|
compile "info.guardianproject.trustedintents:trustedintents:0.2"
|
||||||
|
compile "de.hdodenhof:circleimageview:2.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
|
|||||||
42
briar-android/res/layout/author_view.xml
Normal file
42
briar-android/res/layout/author_view.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/avatarView"
|
||||||
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/statusView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:src="@drawable/identity_anonymous"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nameView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:layout_marginLeft="@dimen/listitem_text_left_margin"
|
||||||
|
android:layout_marginRight="@dimen/margin_small"
|
||||||
|
android:layout_marginStart="@dimen/listitem_text_left_margin"
|
||||||
|
android:layout_toLeftOf="@id/statusView"
|
||||||
|
android:layout_toStartOf="@id/statusView"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/anonymous"
|
||||||
|
android:textSize="@dimen/text_size_medium"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
25
briar-android/res/layout/dropdown_author.xml
Normal file
25
briar-android/res/layout/dropdown_author.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/avatarView"
|
||||||
|
android:layout_width="@dimen/dropdown_picture_size"
|
||||||
|
android:layout_height="@dimen/dropdown_picture_size"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nameView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textSize="@dimen/text_size_medium"
|
||||||
|
tools:text="This is a name of an author. It can be quite long."/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -2,49 +2,71 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/listitem_height_one_line_avatar"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground">
|
||||||
android:padding="12dp">
|
|
||||||
|
|
||||||
<ImageView
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
android:id="@+id/bulbView"
|
android:id="@+id/avatarView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
android:layout_marginRight="@dimen/margin_medium"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_marginEnd="@dimen/margin_medium"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_centerVertical="true"
|
||||||
tools:src="@drawable/contact_disconnected"/>
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"/>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/bulbHolder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_marginRight="@dimen/margin_small"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:textSize="@dimen/text_size_medium"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
android:gravity="center_vertical"
|
android:gravity="right"
|
||||||
tools:text="This is a name of a contact. It can be quite long."/>
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/dateView"
|
android:id="@+id/bulbView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginRight="@dimen/margin_small"
|
tools:src="@drawable/contact_disconnected"/>
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textColor="@color/no_private_messages"
|
|
||||||
tools:text="Dec 24"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/dateView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/no_private_messages"
|
||||||
|
tools:text="Dec 24"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nameView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:layout_marginLeft="@dimen/listitem_text_left_margin"
|
||||||
|
android:layout_marginRight="@dimen/margin_small"
|
||||||
|
android:layout_marginStart="@dimen/listitem_text_left_margin"
|
||||||
|
android:layout_toLeftOf="@id/bulbHolder"
|
||||||
|
android:layout_toStartOf="@id/bulbHolder"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="@dimen/text_size_medium"
|
||||||
|
tools:text="This is a name of a contact. It can be quite long."/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<View style="@style/Divider.Horizontal"/>
|
<View style="@style/Divider.Horizontal"/>
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,20 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:paddingRight="@dimen/margin_medium"
|
android:paddingRight="@dimen/margin_medium"
|
||||||
android:paddingEnd="@dimen/margin_medium"
|
android:paddingEnd="@dimen/margin_medium"
|
||||||
android:paddingTop="@dimen/margin_small"
|
android:paddingTop="@dimen/margin_small"
|
||||||
android:paddingBottom="@dimen/margin_small">
|
android:paddingBottom="@dimen/margin_small">
|
||||||
|
|
||||||
|
<de.hdodenhof.circleimageview.CircleImageView
|
||||||
|
android:id="@+id/msgAvatar"
|
||||||
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
|
/>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/msgLayout"
|
android:id="@+id/msgLayout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -20,4 +20,12 @@
|
|||||||
<dimen name="nav_drawer_width">300dp</dimen>
|
<dimen name="nav_drawer_width">300dp</dimen>
|
||||||
<dimen name="nav_seperator_height">1dp</dimen>
|
<dimen name="nav_seperator_height">1dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="listitem_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="listitem_picture_size">40dp</dimen>
|
||||||
|
<dimen name="listitem_text_left_margin">72dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="listitem_height_one_line_avatar">56dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="dropdown_picture_size">32dp</dimen>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package im.delight.android.identicons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2014 www.delight.im <info@delight.im>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import roboguice.RoboGuice;
|
||||||
|
|
||||||
|
public class AsymmetricIdenticon extends IdenticonView {
|
||||||
|
|
||||||
|
@Inject private CryptoComponent mCrypto;
|
||||||
|
private IdenticonBase mDelegate;
|
||||||
|
|
||||||
|
public AsymmetricIdenticon(Context context) {
|
||||||
|
super(context);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsymmetricIdenticon(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsymmetricIdenticon(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IdenticonBase getDelegate() {
|
||||||
|
return mDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDelegate() {
|
||||||
|
RoboGuice.injectMembers(getContext(), this);
|
||||||
|
mDelegate = new IdenticonBase() {
|
||||||
|
@Override
|
||||||
|
protected CryptoComponent getCrypto() {
|
||||||
|
return mCrypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getRowCount() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getColumnCount() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCellVisible(int row, int column) {
|
||||||
|
return getByte(3 + row * getColumnCount() + column) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getIconColor() {
|
||||||
|
return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package im.delight.android.identicons;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by saiimons on 05/10/14.
|
||||||
|
*/
|
||||||
|
public abstract class IdenticonBase {
|
||||||
|
private final CryptoComponent mCrypto;
|
||||||
|
private final int mRowCount;
|
||||||
|
private final int mColumnCount;
|
||||||
|
private final Paint mPaint;
|
||||||
|
private volatile int mCellWidth;
|
||||||
|
private volatile int mCellHeight;
|
||||||
|
private volatile byte[] mHash;
|
||||||
|
private volatile int[][] mColors;
|
||||||
|
private volatile boolean mReady;
|
||||||
|
|
||||||
|
public IdenticonBase() {
|
||||||
|
mCrypto = getCrypto();
|
||||||
|
mRowCount = getRowCount();
|
||||||
|
mColumnCount = getColumnCount();
|
||||||
|
mPaint = new Paint();
|
||||||
|
|
||||||
|
mPaint.setStyle(Paint.Style.FILL);
|
||||||
|
mPaint.setAntiAlias(true);
|
||||||
|
mPaint.setDither(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getHash(byte[] input) {
|
||||||
|
byte[] mHash;
|
||||||
|
// if the input was null
|
||||||
|
if (input == null) {
|
||||||
|
// we can't create a hash value and have nothing to show (draw to the view)
|
||||||
|
mHash = null;
|
||||||
|
} else {
|
||||||
|
// generate a hash from the input to get unique but deterministic byte values
|
||||||
|
try {
|
||||||
|
mHash = mCrypto.hash(input);
|
||||||
|
} catch (Exception e) {
|
||||||
|
mHash = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupColors() {
|
||||||
|
mColors = new int[mRowCount][mColumnCount];
|
||||||
|
int colorVisible = getIconColor();
|
||||||
|
int colorInvisible = getBackgroundColor();
|
||||||
|
|
||||||
|
for (int r = 0; r < mRowCount; r++) {
|
||||||
|
for (int c = 0; c < mColumnCount; c++) {
|
||||||
|
if (isCellVisible(r, c)) {
|
||||||
|
mColors[r][c] = colorVisible;
|
||||||
|
} else {
|
||||||
|
mColors[r][c] = colorInvisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(byte[] input) {
|
||||||
|
if(input != null) {
|
||||||
|
mHash = getHash(input);
|
||||||
|
} else {
|
||||||
|
mHash = null;
|
||||||
|
}
|
||||||
|
// set up the cell colors according to the input that was provided via show(...)
|
||||||
|
setupColors();
|
||||||
|
|
||||||
|
// this view may now be drawn (and thus must be re-drawn)
|
||||||
|
mReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getByte(int index) {
|
||||||
|
if (mHash == null) {
|
||||||
|
return -128;
|
||||||
|
} else {
|
||||||
|
return mHash[index % mHash.length];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected CryptoComponent getCrypto();
|
||||||
|
|
||||||
|
abstract protected int getRowCount();
|
||||||
|
|
||||||
|
abstract protected int getColumnCount();
|
||||||
|
|
||||||
|
abstract protected boolean isCellVisible(int row, int column);
|
||||||
|
|
||||||
|
abstract protected int getIconColor();
|
||||||
|
|
||||||
|
protected int getBackgroundColor() {
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
Color.colorToHSV(getIconColor(), hsv);
|
||||||
|
if (hsv[2] < 0.5)
|
||||||
|
return Color.parseColor("#ffeeeeee"); // @color/background_material_light
|
||||||
|
else
|
||||||
|
return Color.parseColor("#ff303030"); // @color/background_material_dark
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSize(int w, int h) {
|
||||||
|
mCellWidth = w / mColumnCount;
|
||||||
|
mCellHeight = h / mRowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void draw(Canvas canvas) {
|
||||||
|
if (mReady) {
|
||||||
|
int x, y;
|
||||||
|
for (int r = 0; r < mRowCount; r++) {
|
||||||
|
for (int c = 0; c < mColumnCount; c++) {
|
||||||
|
x = mCellWidth * c;
|
||||||
|
y = mCellHeight * r;
|
||||||
|
|
||||||
|
mPaint.setColor(mColors[r][c]);
|
||||||
|
|
||||||
|
canvas.drawRect(x, y + mCellHeight, x + mCellWidth, y, mPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package im.delight.android.identicons;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by saiimons on 05/10/14.
|
||||||
|
*/
|
||||||
|
public class IdenticonDrawable extends Drawable {
|
||||||
|
private IdenticonBase mDelegate;
|
||||||
|
|
||||||
|
private static final int CENTER_COLUMN_INDEX = 5;
|
||||||
|
|
||||||
|
public IdenticonDrawable(final CryptoComponent crypto, byte[] toShow) {
|
||||||
|
super();
|
||||||
|
mDelegate = new IdenticonBase() {
|
||||||
|
@Override
|
||||||
|
protected CryptoComponent getCrypto() {
|
||||||
|
return crypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getRowCount() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getColumnCount() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCellVisible(int row, int column) {
|
||||||
|
return getByte(3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getIconColor() {
|
||||||
|
return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mDelegate.show(toShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicHeight() {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicWidth() {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBounds(Rect bounds) {
|
||||||
|
super.setBounds(bounds);
|
||||||
|
Log.d("IDENTICON", "SIZE : " + (bounds.right - bounds.left) + " " + (bounds.bottom - bounds.top));
|
||||||
|
mDelegate.updateSize(bounds.right - bounds.left, bounds.bottom - bounds.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBounds(int left, int top, int right, int bottom) {
|
||||||
|
super.setBounds(left, top, right, bottom);
|
||||||
|
mDelegate.updateSize(right - left, bottom - top);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
Log.d("IDENTICON", "DRAW IN PROGRESS");
|
||||||
|
mDelegate.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter cf) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getSymmetricColumnIndex(int row) {
|
||||||
|
if (row < CENTER_COLUMN_INDEX) {
|
||||||
|
return row;
|
||||||
|
} else {
|
||||||
|
return mDelegate.getColumnCount() - row - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package im.delight.android.identicons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2014 www.delight.im <info@delight.im>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
abstract public class IdenticonView extends View {
|
||||||
|
|
||||||
|
|
||||||
|
public IdenticonView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdenticonView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdenticonView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
protected void init() {
|
||||||
|
setWillNotDraw(false);
|
||||||
|
if (Build.VERSION.SDK_INT >= 11) {
|
||||||
|
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(byte[] input) {
|
||||||
|
getDelegate().show(input);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(String input) {
|
||||||
|
show(input.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(int input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(long input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(float input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(double input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(byte input) {
|
||||||
|
show(new byte[] { input });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(char input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(boolean input) {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(Object input) {
|
||||||
|
if (input == null) {
|
||||||
|
getDelegate().show(null);
|
||||||
|
} else {
|
||||||
|
show(String.valueOf(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte getByte(int index) {
|
||||||
|
return getDelegate().getByte(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected IdenticonBase getDelegate();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
getDelegate().updateSize(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
setMeasuredDimension(size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
getDelegate().draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package im.delight.android.identicons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2014 www.delight.im <info@delight.im>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import roboguice.RoboGuice;
|
||||||
|
|
||||||
|
public class SymmetricIdenticon extends IdenticonView {
|
||||||
|
|
||||||
|
private static final int CENTER_COLUMN_INDEX = 5;
|
||||||
|
|
||||||
|
@Inject private CryptoComponent mCrypto;
|
||||||
|
private IdenticonBase mDelegate;
|
||||||
|
|
||||||
|
public SymmetricIdenticon(Context context) {
|
||||||
|
super(context);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymmetricIdenticon(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymmetricIdenticon(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
initDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDelegate() {
|
||||||
|
RoboGuice.injectMembers(getContext(), this);
|
||||||
|
mDelegate = new IdenticonBase() {
|
||||||
|
@Override
|
||||||
|
protected CryptoComponent getCrypto() {
|
||||||
|
return mCrypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getRowCount() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getColumnCount() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCellVisible(int row, int column) {
|
||||||
|
return getByte(3 + row * CENTER_COLUMN_INDEX + getSymmetricColumnIndex(column)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getIconColor() {
|
||||||
|
return Color.rgb(getByte(0) + 128, getByte(1) + 128, getByte(2) + 128);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IdenticonBase getDelegate() {
|
||||||
|
return mDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getSymmetricColumnIndex(int row) {
|
||||||
|
if (row < CENTER_COLUMN_INDEX) {
|
||||||
|
return row;
|
||||||
|
} else {
|
||||||
|
return getDelegate().getColumnCount() - row - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,10 +14,14 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.api.contact.ContactId;
|
import org.briarproject.api.contact.ContactId;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||||
|
|
||||||
public class ContactListAdapter
|
public class ContactListAdapter
|
||||||
@@ -83,9 +87,11 @@ public class ContactListAdapter
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
private Context ctx;
|
private Context ctx;
|
||||||
|
private CryptoComponent crypto;
|
||||||
|
|
||||||
public ContactListAdapter(Context context) {
|
public ContactListAdapter(Context context, CryptoComponent cryptoComponent) {
|
||||||
ctx = context;
|
ctx = context;
|
||||||
|
crypto = cryptoComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,7 +119,10 @@ public class ContactListAdapter
|
|||||||
ui.bulb.setImageResource(R.drawable.contact_disconnected);
|
ui.bulb.setImageResource(R.drawable.contact_disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
String contactName = item.getContact().getAuthor().getName();
|
Author author = item.getContact().getAuthor();
|
||||||
|
ui.avatar.setImageDrawable(
|
||||||
|
new IdenticonDrawable(crypto, author.getId().getBytes()));
|
||||||
|
String contactName = author.getName();
|
||||||
if (unread > 0) {
|
if (unread > 0) {
|
||||||
ui.name.setText(contactName + " (" + unread + ")");
|
ui.name.setText(contactName + " (" + unread + ")");
|
||||||
} else {
|
} else {
|
||||||
@@ -193,6 +202,7 @@ public class ContactListAdapter
|
|||||||
public static class ContactHolder extends RecyclerView.ViewHolder {
|
public static class ContactHolder extends RecyclerView.ViewHolder {
|
||||||
public ViewGroup layout;
|
public ViewGroup layout;
|
||||||
public ImageView bulb;
|
public ImageView bulb;
|
||||||
|
public ImageView avatar;
|
||||||
public TextView name;
|
public TextView name;
|
||||||
public TextView date;
|
public TextView date;
|
||||||
|
|
||||||
@@ -201,6 +211,7 @@ public class ContactListAdapter
|
|||||||
|
|
||||||
layout = (ViewGroup) v;
|
layout = (ViewGroup) v;
|
||||||
bulb = (ImageView) v.findViewById(R.id.bulbView);
|
bulb = (ImageView) v.findViewById(R.id.bulbView);
|
||||||
|
avatar = (ImageView) v.findViewById(R.id.avatarView);
|
||||||
name = (TextView) v.findViewById(R.id.nameView);
|
name = (TextView) v.findViewById(R.id.nameView);
|
||||||
date = (TextView) v.findViewById(R.id.dateView);
|
date = (TextView) v.findViewById(R.id.dateView);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.briarproject.android.util.BriarRecyclerView;
|
|||||||
import org.briarproject.api.contact.Contact;
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.contact.ContactId;
|
import org.briarproject.api.contact.ContactId;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.NoSuchContactException;
|
import org.briarproject.api.db.NoSuchContactException;
|
||||||
import org.briarproject.api.event.ContactAddedEvent;
|
import org.briarproject.api.event.ContactAddedEvent;
|
||||||
@@ -62,6 +63,8 @@ public class ContactListFragment extends BaseEventFragment {
|
|||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private CryptoComponent crypto;
|
||||||
@Inject
|
@Inject
|
||||||
private ConnectionRegistry connectionRegistry;
|
private ConnectionRegistry connectionRegistry;
|
||||||
private ContactListAdapter adapter = null;
|
private ContactListAdapter adapter = null;
|
||||||
@@ -83,7 +86,7 @@ public class ContactListFragment extends BaseEventFragment {
|
|||||||
inflater.inflate(R.layout.activity_contact_list, container,
|
inflater.inflate(R.layout.activity_contact_list, container,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
adapter = new ContactListAdapter(getContext());
|
adapter = new ContactListAdapter(getContext(), crypto);
|
||||||
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
|
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getContext()));
|
list.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.briarproject.api.android.AndroidNotificationManager;
|
|||||||
import org.briarproject.api.contact.Contact;
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.contact.ContactId;
|
import org.briarproject.api.contact.ContactId;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.CryptoExecutor;
|
import org.briarproject.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.NoSuchContactException;
|
import org.briarproject.api.db.NoSuchContactException;
|
||||||
@@ -71,6 +72,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ConversationActivity.class.getName());
|
Logger.getLogger(ConversationActivity.class.getName());
|
||||||
|
|
||||||
|
@Inject private CryptoComponent crypto;
|
||||||
@Inject private AndroidNotificationManager notificationManager;
|
@Inject private AndroidNotificationManager notificationManager;
|
||||||
@Inject private ConnectionRegistry connectionRegistry;
|
@Inject private ConnectionRegistry connectionRegistry;
|
||||||
@Inject @CryptoExecutor private Executor cryptoExecutor;
|
@Inject @CryptoExecutor private Executor cryptoExecutor;
|
||||||
@@ -88,6 +90,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private volatile GroupId groupId = null;
|
private volatile GroupId groupId = null;
|
||||||
private volatile ContactId contactId = null;
|
private volatile ContactId contactId = null;
|
||||||
private volatile String contactName = null;
|
private volatile String contactName = null;
|
||||||
|
private volatile byte[] contactIdenticonKey = null;
|
||||||
private volatile boolean connected = false;
|
private volatile boolean connected = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,7 +104,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
setContentView(R.layout.activity_conversation);
|
setContentView(R.layout.activity_conversation);
|
||||||
|
|
||||||
adapter = new ConversationAdapter(this);
|
adapter = new ConversationAdapter(this, crypto);
|
||||||
list = (BriarRecyclerView) findViewById(R.id.conversationView);
|
list = (BriarRecyclerView) findViewById(R.id.conversationView);
|
||||||
list.setLayoutManager(new LinearLayoutManager(this));
|
list.setLayoutManager(new LinearLayoutManager(this));
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
@@ -165,6 +168,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
contactId = messagingManager.getContactId(groupId);
|
contactId = messagingManager.getContactId(groupId);
|
||||||
Contact contact = contactManager.getContact(contactId);
|
Contact contact = contactManager.getContact(contactId);
|
||||||
contactName = contact.getAuthor().getName();
|
contactName = contact.getAuthor().getName();
|
||||||
|
contactIdenticonKey = contact.getAuthor().getId().getBytes();
|
||||||
connected = connectionRegistry.isConnected(contactId);
|
connected = connectionRegistry.isConnected(contactId);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
@@ -192,6 +196,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
actionBar.setSubtitle(getString(R.string.offline));
|
actionBar.setSubtitle(getString(R.string.offline));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
adapter.setIdenticonKey(contactIdenticonKey);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,12 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||||
|
|
||||||
class ConversationAdapter extends
|
class ConversationAdapter extends
|
||||||
@@ -70,9 +73,17 @@ class ConversationAdapter extends
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
private Context ctx;
|
private Context ctx;
|
||||||
|
private CryptoComponent crypto;
|
||||||
|
private byte[] identiconKey;
|
||||||
|
|
||||||
public ConversationAdapter(Context context) {
|
public ConversationAdapter(Context context, CryptoComponent cryptoComponent) {
|
||||||
ctx = context;
|
ctx = context;
|
||||||
|
crypto = cryptoComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdenticonKey(byte[] key) {
|
||||||
|
this.identiconKey = key;
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,18 +130,23 @@ class ConversationAdapter extends
|
|||||||
} else {
|
} else {
|
||||||
ui.status.setImageResource(R.drawable.message_stored);
|
ui.status.setImageResource(R.drawable.message_stored);
|
||||||
}
|
}
|
||||||
} else if (!header.isRead()) {
|
} else {
|
||||||
int left = ui.layout.getPaddingLeft();
|
if (identiconKey != null)
|
||||||
int top = ui.layout.getPaddingTop();
|
ui.avatar.setImageDrawable(
|
||||||
int right = ui.layout.getPaddingRight();
|
new IdenticonDrawable(crypto, identiconKey));
|
||||||
int bottom = ui.layout.getPaddingBottom();
|
if (!header.isRead()) {
|
||||||
|
int left = ui.layout.getPaddingLeft();
|
||||||
|
int top = ui.layout.getPaddingTop();
|
||||||
|
int right = ui.layout.getPaddingRight();
|
||||||
|
int bottom = ui.layout.getPaddingBottom();
|
||||||
|
|
||||||
// show unread messages in different color to not miss them
|
// show unread messages in different color to not miss them
|
||||||
ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
|
ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
|
||||||
|
|
||||||
// re-apply the previous padding due to bug in some Android versions
|
// re-apply the previous padding due to bug in some Android versions
|
||||||
// see: https://code.google.com/p/android/issues/detail?id=17885
|
// see: https://code.google.com/p/android/issues/detail?id=17885
|
||||||
ui.layout.setPadding(left, top, right, bottom);
|
ui.layout.setPadding(left, top, right, bottom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getBody() == null) {
|
if (item.getBody() == null) {
|
||||||
@@ -186,6 +202,7 @@ class ConversationAdapter extends
|
|||||||
public TextView body;
|
public TextView body;
|
||||||
public TextView date;
|
public TextView date;
|
||||||
public ImageView status;
|
public ImageView status;
|
||||||
|
public ImageView avatar;
|
||||||
|
|
||||||
public MessageHolder(View v, int type) {
|
public MessageHolder(View v, int type) {
|
||||||
super(v);
|
super(v);
|
||||||
@@ -197,6 +214,8 @@ class ConversationAdapter extends
|
|||||||
// outgoing message (local)
|
// outgoing message (local)
|
||||||
if (type == MSG_OUT) {
|
if (type == MSG_OUT) {
|
||||||
status = (ImageView) v.findViewById(R.id.msgStatus);
|
status = (ImageView) v.findViewById(R.id.msgStatus);
|
||||||
|
} else {
|
||||||
|
avatar = (ImageView) v.findViewById(R.id.msgAvatar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.briarproject.android.util.ElasticHorizontalSpace;
|
|||||||
import org.briarproject.android.util.HorizontalBorder;
|
import org.briarproject.android.util.HorizontalBorder;
|
||||||
import org.briarproject.android.util.ListLoadingProgressBar;
|
import org.briarproject.android.util.ListLoadingProgressBar;
|
||||||
import org.briarproject.api.android.AndroidNotificationManager;
|
import org.briarproject.api.android.AndroidNotificationManager;
|
||||||
|
import org.briarproject.api.android.ReferenceManager;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.NoSuchMessageException;
|
import org.briarproject.api.db.NoSuchMessageException;
|
||||||
import org.briarproject.api.db.NoSuchSubscriptionException;
|
import org.briarproject.api.db.NoSuchSubscriptionException;
|
||||||
@@ -70,6 +71,8 @@ public class ForumActivity extends BriarActivity implements EventListener,
|
|||||||
private ListLoadingProgressBar loading = null;
|
private ListLoadingProgressBar loading = null;
|
||||||
private ImageButton composeButton = null, shareButton = null;
|
private ImageButton composeButton = null, shareButton = null;
|
||||||
|
|
||||||
|
@Inject private ReferenceManager referenceManager;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
// Fields that are accessed from background threads must be volatile
|
||||||
@Inject private volatile ForumManager forumManager;
|
@Inject private volatile ForumManager forumManager;
|
||||||
@Inject private volatile EventBus eventBus;
|
@Inject private volatile EventBus eventBus;
|
||||||
@@ -366,7 +369,8 @@ public class ForumActivity extends BriarActivity implements EventListener,
|
|||||||
i.putExtra("briar.FORUM_NAME", forum.getName());
|
i.putExtra("briar.FORUM_NAME", forum.getName());
|
||||||
i.putExtra("briar.MESSAGE_ID", header.getId().getBytes());
|
i.putExtra("briar.MESSAGE_ID", header.getId().getBytes());
|
||||||
Author author = header.getAuthor();
|
Author author = header.getAuthor();
|
||||||
if (author != null) i.putExtra("briar.AUTHOR_NAME", author.getName());
|
if (author != null) i.putExtra("briar.AUTHOR_HANDLE",
|
||||||
|
referenceManager.putReference(author, Author.class));
|
||||||
i.putExtra("briar.AUTHOR_STATUS", header.getAuthorStatus().name());
|
i.putExtra("briar.AUTHOR_STATUS", header.getAuthorStatus().name());
|
||||||
i.putExtra("briar.CONTENT_TYPE", header.getContentType());
|
i.putExtra("briar.CONTENT_TYPE", header.getContentType());
|
||||||
i.putExtra("briar.TIMESTAMP", header.getTimestamp());
|
i.putExtra("briar.TIMESTAMP", header.getTimestamp());
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ class ForumAdapter extends ArrayAdapter<ForumItem> {
|
|||||||
AuthorView authorView = new AuthorView(ctx);
|
AuthorView authorView = new AuthorView(ctx);
|
||||||
authorView.setLayoutParams(WRAP_WRAP_1);
|
authorView.setLayoutParams(WRAP_WRAP_1);
|
||||||
Author author = header.getAuthor();
|
Author author = header.getAuthor();
|
||||||
if (author == null) authorView.init(null, header.getAuthorStatus());
|
authorView.init(author, header.getAuthorStatus());
|
||||||
else authorView.init(author.getName(), header.getAuthorStatus());
|
|
||||||
headerLayout.addView(authorView);
|
headerLayout.addView(authorView);
|
||||||
|
|
||||||
TextView date = new TextView(ctx);
|
TextView date = new TextView(ctx);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.briarproject.android.util.AuthorView;
|
|||||||
import org.briarproject.android.util.ElasticHorizontalSpace;
|
import org.briarproject.android.util.ElasticHorizontalSpace;
|
||||||
import org.briarproject.android.util.HorizontalBorder;
|
import org.briarproject.android.util.HorizontalBorder;
|
||||||
import org.briarproject.android.util.LayoutUtils;
|
import org.briarproject.android.util.LayoutUtils;
|
||||||
|
import org.briarproject.api.android.ReferenceManager;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.NoSuchMessageException;
|
import org.briarproject.api.db.NoSuchMessageException;
|
||||||
import org.briarproject.api.forum.ForumManager;
|
import org.briarproject.api.forum.ForumManager;
|
||||||
@@ -56,6 +57,8 @@ implements OnClickListener {
|
|||||||
private TextView content = null;
|
private TextView content = null;
|
||||||
private int position = -1;
|
private int position = -1;
|
||||||
|
|
||||||
|
@Inject private ReferenceManager referenceManager;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
// Fields that are accessed from background threads must be volatile
|
||||||
@Inject private volatile ForumManager forumManager;
|
@Inject private volatile ForumManager forumManager;
|
||||||
private volatile MessageId messageId = null;
|
private volatile MessageId messageId = null;
|
||||||
@@ -82,7 +85,9 @@ implements OnClickListener {
|
|||||||
if (minTimestamp == -1) throw new IllegalStateException();
|
if (minTimestamp == -1) throw new IllegalStateException();
|
||||||
position = i.getIntExtra("briar.POSITION", -1);
|
position = i.getIntExtra("briar.POSITION", -1);
|
||||||
if (position == -1) throw new IllegalStateException();
|
if (position == -1) throw new IllegalStateException();
|
||||||
String authorName = i.getStringExtra("briar.AUTHOR_NAME");
|
long authorHandle = i.getLongExtra("briar.AUTHOR_HANDLE", -1);
|
||||||
|
if (authorHandle == -1) throw new IllegalStateException();
|
||||||
|
Author author = referenceManager.removeReference(authorHandle, Author.class);
|
||||||
String s = i.getStringExtra("briar.AUTHOR_STATUS");
|
String s = i.getStringExtra("briar.AUTHOR_STATUS");
|
||||||
if (s == null) throw new IllegalStateException();
|
if (s == null) throw new IllegalStateException();
|
||||||
Author.Status authorStatus = Author.Status.valueOf(s);
|
Author.Status authorStatus = Author.Status.valueOf(s);
|
||||||
@@ -102,10 +107,10 @@ implements OnClickListener {
|
|||||||
header.setOrientation(HORIZONTAL);
|
header.setOrientation(HORIZONTAL);
|
||||||
header.setGravity(CENTER_VERTICAL);
|
header.setGravity(CENTER_VERTICAL);
|
||||||
|
|
||||||
AuthorView author = new AuthorView(this);
|
AuthorView authorView = new AuthorView(this);
|
||||||
author.setLayoutParams(WRAP_WRAP_1);
|
authorView.setLayoutParams(WRAP_WRAP_1);
|
||||||
author.init(authorName, authorStatus);
|
authorView.init(author, authorStatus);
|
||||||
header.addView(author);
|
header.addView(authorView);
|
||||||
|
|
||||||
int pad = LayoutUtils.getPadding(this);
|
int pad = LayoutUtils.getPadding(this);
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
left.addRule(CENTER_VERTICAL);
|
left.addRule(CENTER_VERTICAL);
|
||||||
header.addView(from, left);
|
header.addView(from, left);
|
||||||
|
|
||||||
adapter = new LocalAuthorSpinnerAdapter(this, true);
|
adapter = new LocalAuthorSpinnerAdapter(this, crypto, true);
|
||||||
spinner = new Spinner(this);
|
spinner = new Spinner(this);
|
||||||
spinner.setId(2);
|
spinner.setId(2);
|
||||||
spinner.setAdapter(adapter);
|
spinner.setAdapter(adapter);
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
package org.briarproject.android.identity;
|
package org.briarproject.android.identity;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.SpinnerAdapter;
|
import android.widget.SpinnerAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.util.LayoutUtils;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static android.text.TextUtils.TruncateAt.END;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
import static org.briarproject.android.identity.LocalAuthorItem.ANONYMOUS;
|
import static org.briarproject.android.identity.LocalAuthorItem.ANONYMOUS;
|
||||||
import static org.briarproject.android.identity.LocalAuthorItem.NEW;
|
import static org.briarproject.android.identity.LocalAuthorItem.NEW;
|
||||||
|
|
||||||
@@ -23,11 +26,14 @@ public class LocalAuthorSpinnerAdapter extends BaseAdapter
|
|||||||
implements SpinnerAdapter {
|
implements SpinnerAdapter {
|
||||||
|
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
|
private final CryptoComponent crypto;
|
||||||
private final boolean includeAnonymous;
|
private final boolean includeAnonymous;
|
||||||
private final List<LocalAuthorItem> list = new ArrayList<LocalAuthorItem>();
|
private final List<LocalAuthorItem> list = new ArrayList<LocalAuthorItem>();
|
||||||
|
|
||||||
public LocalAuthorSpinnerAdapter(Context ctx, boolean includeAnonymous) {
|
public LocalAuthorSpinnerAdapter(Context ctx,
|
||||||
|
CryptoComponent crypto, boolean includeAnonymous) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
this.crypto = crypto;
|
||||||
this.includeAnonymous = includeAnonymous;
|
this.includeAnonymous = includeAnonymous;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,17 +55,33 @@ implements SpinnerAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public View getDropDownView(int position, View convertView,
|
public View getDropDownView(int position, View convertView,
|
||||||
ViewGroup parent) {
|
ViewGroup parent) {
|
||||||
TextView name = new TextView(ctx);
|
View view;
|
||||||
name.setTextSize(18);
|
if (convertView == null) {
|
||||||
name.setSingleLine();
|
LayoutInflater inflater =
|
||||||
name.setEllipsize(END);
|
(LayoutInflater) ctx
|
||||||
int pad = LayoutUtils.getPadding(ctx);
|
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
name.setPadding(pad, pad, pad, pad);
|
view = inflater.inflate(R.layout.dropdown_author, parent, false);
|
||||||
|
} else
|
||||||
|
view = convertView;
|
||||||
|
|
||||||
|
TextView name = (TextView) view.findViewById(R.id.nameView);
|
||||||
|
ImageView avatar =
|
||||||
|
(ImageView) view.findViewById(R.id.avatarView);
|
||||||
|
|
||||||
LocalAuthorItem item = getItem(position);
|
LocalAuthorItem item = getItem(position);
|
||||||
if (item == ANONYMOUS) name.setText(R.string.anonymous);
|
if (item == ANONYMOUS) {
|
||||||
else if (item == NEW) name.setText(R.string.new_identity_item);
|
name.setText(R.string.anonymous);
|
||||||
else name.setText(item.getLocalAuthor().getName());
|
avatar.setVisibility(View.INVISIBLE);
|
||||||
return name;
|
} else if (item == NEW) {
|
||||||
|
name.setText(R.string.new_identity_item);
|
||||||
|
avatar.setVisibility(View.INVISIBLE);
|
||||||
|
} else {
|
||||||
|
name.setText(item.getLocalAuthor().getName());
|
||||||
|
avatar.setVisibility(View.VISIBLE);
|
||||||
|
avatar.setImageDrawable(new IdenticonDrawable(crypto,
|
||||||
|
item.getLocalAuthor().getId().getBytes()));
|
||||||
|
}
|
||||||
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalAuthorItem getItem(int position) {
|
public LocalAuthorItem getItem(int position) {
|
||||||
@@ -78,15 +100,7 @@ implements SpinnerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
TextView name = new TextView(ctx);
|
return getDropDownView(position, convertView, parent);
|
||||||
name.setTextSize(18);
|
|
||||||
name.setSingleLine();
|
|
||||||
name.setEllipsize(END);
|
|
||||||
LocalAuthorItem item = getItem(position);
|
|
||||||
if (item == ANONYMOUS) name.setText(R.string.anonymous);
|
|
||||||
else if (item == NEW) name.setText(R.string.new_identity_item);
|
|
||||||
else name.setText(item.getLocalAuthor().getName());
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,11 +16,16 @@ import org.briarproject.android.identity.CreateIdentityActivity;
|
|||||||
import org.briarproject.android.identity.LocalAuthorItem;
|
import org.briarproject.android.identity.LocalAuthorItem;
|
||||||
import org.briarproject.android.identity.LocalAuthorItemComparator;
|
import org.briarproject.android.identity.LocalAuthorItemComparator;
|
||||||
import org.briarproject.android.identity.LocalAuthorSpinnerAdapter;
|
import org.briarproject.android.identity.LocalAuthorSpinnerAdapter;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import roboguice.RoboGuice;
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||||
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
||||||
import static org.briarproject.android.identity.LocalAuthorItem.NEW;
|
import static org.briarproject.android.identity.LocalAuthorItem.NEW;
|
||||||
@@ -30,6 +35,7 @@ import static org.briarproject.android.invitation.AddContactActivity.REQUEST_CRE
|
|||||||
class ChooseIdentityView extends AddContactView
|
class ChooseIdentityView extends AddContactView
|
||||||
implements OnItemSelectedListener, OnClickListener {
|
implements OnItemSelectedListener, OnClickListener {
|
||||||
|
|
||||||
|
@Inject private CryptoComponent crypto;
|
||||||
private LocalAuthorSpinnerAdapter adapter = null;
|
private LocalAuthorSpinnerAdapter adapter = null;
|
||||||
private Spinner spinner = null;
|
private Spinner spinner = null;
|
||||||
|
|
||||||
@@ -40,6 +46,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
void populate() {
|
void populate() {
|
||||||
removeAllViews();
|
removeAllViews();
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
|
RoboGuice.injectMembers(ctx, this);
|
||||||
|
|
||||||
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
|
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
|
||||||
(Context.LAYOUT_INFLATER_SERVICE);
|
(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
@@ -50,7 +57,7 @@ implements OnItemSelectedListener, OnClickListener {
|
|||||||
TextView step = (TextView) view.findViewById(R.id.stepView);
|
TextView step = (TextView) view.findViewById(R.id.stepView);
|
||||||
step.setText(String.format(ctx.getString(R.string.step), 1, 3));
|
step.setText(String.format(ctx.getString(R.string.step), 1, 3));
|
||||||
|
|
||||||
adapter = new LocalAuthorSpinnerAdapter(ctx, false);
|
adapter = new LocalAuthorSpinnerAdapter(ctx, crypto, false);
|
||||||
spinner = (Spinner) view.findViewById(R.id.spinner);
|
spinner = (Spinner) view.findViewById(R.id.spinner);
|
||||||
spinner.setAdapter(adapter);
|
spinner.setAdapter(adapter);
|
||||||
spinner.setOnItemSelectedListener(this);
|
spinner.setOnItemSelectedListener(this);
|
||||||
|
|||||||
@@ -1,42 +1,69 @@
|
|||||||
package org.briarproject.android.util;
|
package org.briarproject.android.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
|
|
||||||
import static android.text.TextUtils.TruncateAt.END;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class AuthorView extends RelativeLayout {
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
import roboguice.RoboGuice;
|
||||||
|
|
||||||
|
public class AuthorView extends FrameLayout {
|
||||||
|
|
||||||
|
@Inject private CryptoComponent crypto;
|
||||||
|
private ImageView avatarView;
|
||||||
|
private TextView nameView;
|
||||||
|
private ImageView statusView;
|
||||||
|
|
||||||
public AuthorView(Context ctx) {
|
public AuthorView(Context ctx) {
|
||||||
super(ctx);
|
super(ctx);
|
||||||
|
|
||||||
|
initViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(String name, Author.Status status) {
|
public AuthorView(Context context, AttributeSet attrs) {
|
||||||
Context ctx = getContext();
|
super(context, attrs);
|
||||||
int pad = LayoutUtils.getPadding(ctx);
|
|
||||||
|
|
||||||
TextView nameView = new TextView(ctx);
|
initViews();
|
||||||
nameView.setId(1);
|
}
|
||||||
nameView.setTextSize(18);
|
|
||||||
nameView.setSingleLine();
|
public AuthorView(Context context, AttributeSet attrs,
|
||||||
nameView.setEllipsize(END);
|
int defStyle) {
|
||||||
nameView.setPadding(pad, pad, pad, pad);
|
super(context, attrs, defStyle);
|
||||||
if (name == null) nameView.setText(R.string.anonymous);
|
|
||||||
else nameView.setText(name);
|
initViews();
|
||||||
LayoutParams leftOf = CommonLayoutParams.relative();
|
}
|
||||||
leftOf.addRule(ALIGN_PARENT_LEFT);
|
|
||||||
leftOf.addRule(CENTER_VERTICAL);
|
private void initViews() {
|
||||||
leftOf.addRule(LEFT_OF, 2);
|
RoboGuice.injectMembers(getContext(), this);
|
||||||
addView(nameView, leftOf);
|
if (isInEditMode())
|
||||||
|
return;
|
||||||
|
|
||||||
|
View v = LayoutInflater.from(getContext()).inflate(
|
||||||
|
R.layout.author_view, this, true);
|
||||||
|
|
||||||
|
avatarView = (ImageView) v.findViewById(R.id.avatarView);
|
||||||
|
nameView = (TextView) v.findViewById(R.id.nameView);
|
||||||
|
statusView = (ImageView) v.findViewById(R.id.statusView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Author author, Author.Status status) {
|
||||||
|
if (author == null) nameView.setText(R.string.anonymous);
|
||||||
|
else {
|
||||||
|
avatarView.setImageDrawable(
|
||||||
|
new IdenticonDrawable(crypto, author.getId().getBytes()));
|
||||||
|
nameView.setText(author.getName());
|
||||||
|
}
|
||||||
|
|
||||||
ImageView statusView = new ImageView(ctx);
|
|
||||||
statusView.setId(2);
|
|
||||||
statusView.setPadding(0, pad, pad, pad);
|
|
||||||
switch(status) {
|
switch(status) {
|
||||||
case ANONYMOUS:
|
case ANONYMOUS:
|
||||||
statusView.setImageResource(R.drawable.identity_anonymous);
|
statusView.setImageResource(R.drawable.identity_anonymous);
|
||||||
@@ -51,9 +78,5 @@ public class AuthorView extends RelativeLayout {
|
|||||||
statusView.setImageResource(R.drawable.identity_verified);
|
statusView.setImageResource(R.drawable.identity_verified);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
LayoutParams right = CommonLayoutParams.relative();
|
|
||||||
right.addRule(ALIGN_PARENT_RIGHT);
|
|
||||||
right.addRule(CENTER_VERTICAL);
|
|
||||||
addView(statusView, right);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user