Minor bug fixes and improvements, added caching to inventory
This commit is contained in:
parent
6ec25cfae7
commit
6f6a2e4e6c
@ -188,6 +188,16 @@ public class MainActivity extends AppCompatActivity
|
|||||||
.withTag(identity)
|
.withTag(identity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (profiles.isEmpty()){
|
||||||
|
// Create an initial identity
|
||||||
|
BitmessageAddress identity = Singleton.getIdentity(this);
|
||||||
|
profiles.add(new ProfileDrawerItem()
|
||||||
|
.withIcon(new Identicon(identity))
|
||||||
|
.withName(identity.toString())
|
||||||
|
.withEmail(identity.getAddress())
|
||||||
|
.withTag(identity)
|
||||||
|
);
|
||||||
|
}
|
||||||
profiles.add(new ProfileSettingDrawerItem()
|
profiles.add(new ProfileSettingDrawerItem()
|
||||||
.withName("Add Identity")
|
.withName("Add Identity")
|
||||||
.withDescription("Create new identity")
|
.withDescription("Create new identity")
|
||||||
|
@ -100,7 +100,8 @@ public class SubscriptionDetailFragment extends Fragment {
|
|||||||
TextView address = (TextView) rootView.findViewById(R.id.address);
|
TextView address = (TextView) rootView.findViewById(R.id.address);
|
||||||
address.setText(item.getAddress());
|
address.setText(item.getAddress());
|
||||||
address.setSelected(true);
|
address.setSelected(true);
|
||||||
((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity().getString(R.string.stream_number, item.getStream()));
|
((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity()
|
||||||
|
.getString(R.string.stream_number, item.getStream()));
|
||||||
Switch active = (Switch) rootView.findViewById(R.id.active);
|
Switch active = (Switch) rootView.findViewById(R.id.active);
|
||||||
active.setChecked(item.isSubscribed());
|
active.setChecked(item.isSubscribed());
|
||||||
active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
@ -109,6 +110,14 @@ public class SubscriptionDetailFragment extends Fragment {
|
|||||||
item.setSubscribed(isChecked);
|
item.setSubscribed(isChecked);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ImageView pubkeyAvailableImg = (ImageView) rootView.findViewById(R.id.pubkey_available);
|
||||||
|
if (item.getPubkey() == null) {
|
||||||
|
pubkeyAvailableImg.setAlpha(0.3f);
|
||||||
|
TextView pubkeyAvailableDesc = (TextView) rootView.findViewById(R.id
|
||||||
|
.pubkey_available_desc);
|
||||||
|
pubkeyAvailableDesc.setText(R.string.pubkey_not_available);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
|
@ -33,10 +33,18 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static ch.dissem.apps.abit.repository.SqlHelper.join;
|
import static ch.dissem.apps.abit.repository.SqlHelper.join;
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
||||||
import static ch.dissem.bitmessage.utils.UnixTime.now;
|
import static ch.dissem.bitmessage.utils.UnixTime.now;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,47 +63,64 @@ public class AndroidInventory implements Inventory {
|
|||||||
|
|
||||||
private final SqlHelper sql;
|
private final SqlHelper sql;
|
||||||
|
|
||||||
|
private final Map<Long, Map<InventoryVector, Long>> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public AndroidInventory(SqlHelper sql) {
|
public AndroidInventory(SqlHelper sql) {
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<InventoryVector> getInventory(long... streams) {
|
public List<InventoryVector> getInventory(long... streams) {
|
||||||
return getInventory(false, streams);
|
List<InventoryVector> result = new LinkedList<>();
|
||||||
|
long now = now();
|
||||||
|
for (long stream : streams) {
|
||||||
|
for (Map.Entry<InventoryVector, Long> e : getCache(stream).entrySet()) {
|
||||||
|
if (e.getValue() > now) {
|
||||||
|
result.add(e.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<InventoryVector> getInventory(boolean includeExpired, long... streams) {
|
private Map<InventoryVector, Long> getCache(long stream) {
|
||||||
// Define a projection that specifies which columns from the database
|
Map<InventoryVector, Long> result = cache.get(stream);
|
||||||
// you will actually use after this query.
|
if (result == null) {
|
||||||
String[] projection = {
|
synchronized (cache) {
|
||||||
COLUMN_HASH
|
if (cache.get(stream) == null) {
|
||||||
};
|
result = new ConcurrentHashMap<>();
|
||||||
|
cache.put(stream, result);
|
||||||
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
String[] projection = {
|
||||||
Cursor c = db.query(
|
COLUMN_HASH, COLUMN_EXPIRES
|
||||||
TABLE_NAME, projection,
|
};
|
||||||
(includeExpired ? "" : "expires > " + now() + " AND ") + "stream IN (" + join
|
|
||||||
(streams) + ")",
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
null, null, null, null
|
try (Cursor c = db.query(
|
||||||
);
|
TABLE_NAME, projection,
|
||||||
List<InventoryVector> result = new LinkedList<>();
|
"stream = " + stream,
|
||||||
try {
|
null, null, null, null
|
||||||
c.moveToFirst();
|
)) {
|
||||||
while (!c.isAfterLast()) {
|
c.moveToFirst();
|
||||||
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
|
while (!c.isAfterLast()) {
|
||||||
result.add(new InventoryVector(blob));
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
|
||||||
c.moveToNext();
|
long expires = c.getLong(c.getColumnIndex(COLUMN_EXPIRES));
|
||||||
|
result.put(new InventoryVector(blob), expires);
|
||||||
|
c.moveToNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.info("Stream #" + stream + " inventory size: " + result.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
}
|
||||||
LOG.info("Inventory size: " + result.size());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
|
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
|
||||||
offer.removeAll(getInventory(true, streams));
|
for (long stream : streams) {
|
||||||
|
offer.removeAll(getCache(stream).keySet());
|
||||||
|
}
|
||||||
LOG.info(offer.size() + " objects missing.");
|
LOG.info(offer.size() + " objects missing.");
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
@ -110,12 +135,11 @@ public class AndroidInventory implements Inventory {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor c = db.query(
|
try (Cursor c = db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
"hash = X'" + vector + "'",
|
"hash = X'" + vector + "'",
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
)) {
|
||||||
try {
|
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
if (c.isAfterLast()) {
|
if (c.isAfterLast()) {
|
||||||
LOG.info("Object requested that we don't have. IV: " + vector);
|
LOG.info("Object requested that we don't have. IV: " + vector);
|
||||||
@ -125,8 +149,6 @@ public class AndroidInventory implements Inventory {
|
|||||||
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
||||||
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
return Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob.length);
|
return Factory.getObjectMessage(version, new ByteArrayInputStream(blob), blob.length);
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,13 +172,12 @@ public class AndroidInventory implements Inventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
Cursor c = db.query(
|
List<ObjectMessage> result = new LinkedList<>();
|
||||||
|
try (Cursor c = db.query(
|
||||||
TABLE_NAME, projection,
|
TABLE_NAME, projection,
|
||||||
where.toString(),
|
where.toString(),
|
||||||
null, null, null, null
|
null, null, null, null
|
||||||
);
|
)) {
|
||||||
List<ObjectMessage> result = new LinkedList<>();
|
|
||||||
try {
|
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
while (!c.isAfterLast()) {
|
while (!c.isAfterLast()) {
|
||||||
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
|
||||||
@ -165,8 +186,6 @@ public class AndroidInventory implements Inventory {
|
|||||||
blob.length));
|
blob.length));
|
||||||
c.moveToNext();
|
c.moveToNext();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -174,6 +193,10 @@ public class AndroidInventory implements Inventory {
|
|||||||
@Override
|
@Override
|
||||||
public void storeObject(ObjectMessage object) {
|
public void storeObject(ObjectMessage object) {
|
||||||
InventoryVector iv = object.getInventoryVector();
|
InventoryVector iv = object.getInventoryVector();
|
||||||
|
|
||||||
|
if (getCache(object.getStream()).containsKey(iv))
|
||||||
|
return;
|
||||||
|
|
||||||
LOG.trace("Storing object " + iv);
|
LOG.trace("Storing object " + iv);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -188,6 +211,8 @@ public class AndroidInventory implements Inventory {
|
|||||||
values.put(COLUMN_VERSION, object.getVersion());
|
values.put(COLUMN_VERSION, object.getVersion());
|
||||||
|
|
||||||
db.insertOrThrow(TABLE_NAME, null, values);
|
db.insertOrThrow(TABLE_NAME, null, values);
|
||||||
|
|
||||||
|
getCache(object.getStream()).put(iv, object.getExpiresTime());
|
||||||
} catch (SQLiteConstraintException e) {
|
} catch (SQLiteConstraintException e) {
|
||||||
LOG.trace(e.getMessage(), e);
|
LOG.trace(e.getMessage(), e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -197,22 +222,22 @@ public class AndroidInventory implements Inventory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(ObjectMessage object) {
|
public boolean contains(ObjectMessage object) {
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
return getCache(object.getStream()).keySet().contains(object.getInventoryVector());
|
||||||
Cursor c = db.query(
|
|
||||||
TABLE_NAME, new String[]{COLUMN_STREAM},
|
|
||||||
"hash = X'" + object.getInventoryVector() + "'",
|
|
||||||
null, null, null, null
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
return c.getCount() > 0;
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
|
long fiveMinutesAgo = now() - 5 * MINUTE;
|
||||||
SQLiteDatabase db = sql.getWritableDatabase();
|
SQLiteDatabase db = sql.getWritableDatabase();
|
||||||
db.delete(TABLE_NAME, "expires < " + (now() - 300), null);
|
db.delete(TABLE_NAME, "expires < " + fiveMinutesAgo, null);
|
||||||
|
|
||||||
|
for (Map<InventoryVector, Long> c : cache.values()) {
|
||||||
|
Iterator<Map.Entry<InventoryVector, Long>> iterator = c.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
if (iterator.next().getValue() < fiveMinutesAgo) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class BitmessageService extends Service {
|
|||||||
private static Messenger messenger;
|
private static Messenger messenger;
|
||||||
|
|
||||||
public static boolean isRunning() {
|
public static boolean isRunning() {
|
||||||
return running;
|
return running && bmc.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,8 +102,6 @@ public class BitmessageService extends Service {
|
|||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case MSG_CREATE_IDENTITY: {
|
case MSG_CREATE_IDENTITY: {
|
||||||
BitmessageAddress identity = bmc.createIdentity(false);
|
BitmessageAddress identity = bmc.createIdentity(false);
|
||||||
identity.setAlias(service.get().getString(R.string.alias_default_identity));
|
|
||||||
bmc.addresses().save(identity);
|
|
||||||
if (msg.replyTo != null) {
|
if (msg.replyTo != null) {
|
||||||
try {
|
try {
|
||||||
Message message = Message.obtain(this, MSG_CREATE_IDENTITY);
|
Message message = Message.obtain(this, MSG_CREATE_IDENTITY);
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.R;
|
||||||
import ch.dissem.apps.abit.adapter.AndroidCryptography;
|
import ch.dissem.apps.abit.adapter.AndroidCryptography;
|
||||||
import ch.dissem.apps.abit.adapter.SwitchingProofOfWorkEngine;
|
import ch.dissem.apps.abit.adapter.SwitchingProofOfWorkEngine;
|
||||||
import ch.dissem.apps.abit.listener.MessageListener;
|
import ch.dissem.apps.abit.listener.MessageListener;
|
||||||
@ -92,10 +93,15 @@ public class Singleton {
|
|||||||
if (identity == null) {
|
if (identity == null) {
|
||||||
synchronized (Singleton.class) {
|
synchronized (Singleton.class) {
|
||||||
if (identity == null) {
|
if (identity == null) {
|
||||||
List<BitmessageAddress> identities = getBitmessageContext(ctx).addresses()
|
BitmessageContext bmc = getBitmessageContext(ctx);
|
||||||
|
List<BitmessageAddress> identities = bmc.addresses()
|
||||||
.getIdentities();
|
.getIdentities();
|
||||||
if (identities.size() > 0) {
|
if (identities.size() > 0) {
|
||||||
identity = identities.get(0);
|
identity = identities.get(0);
|
||||||
|
} else {
|
||||||
|
identity = bmc.createIdentity(false);
|
||||||
|
identity.setAlias(ctx.getString(R.string.alias_default_identity));
|
||||||
|
bmc.addresses().save(identity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
app/src/main/res/drawable/public_key.xml
Normal file
8
app/src/main/res/drawable/public_key.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#000" android:pathData="M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10A2,2 0 0,1 6,8H15V6A3,3 0 0,0 12,3A3,3 0 0,0 9,6H7A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,17A2,2 0 0,0 14,15A2,2 0 0,0 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17Z" />
|
||||||
|
</vector>
|
@ -16,65 +16,90 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_margin="16dp"
|
||||||
android:src="@color/colorAccent"
|
android:src="@color/colorAccent"
|
||||||
android:layout_margin="16dp"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Name"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignTop="@+id/avatar"
|
android:layout_alignTop="@+id/avatar"
|
||||||
android:layout_toRightOf="@+id/avatar"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_toEndOf="@+id/avatar"
|
android:layout_toEndOf="@+id/avatar"
|
||||||
android:layout_marginRight="16dp"
|
android:text=""
|
||||||
android:layout_marginEnd="16dp"
|
android:inputType="textPersonName"
|
||||||
android:layout_alignParentRight="true"
|
tools:ignore="LabelFor"/>
|
||||||
android:layout_alignParentEnd="true"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/address"
|
android:id="@+id/address"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Address"
|
android:layout_below="@+id/avatar"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:paddingLeft="16dp"
|
||||||
android:layout_below="@+id/avatar"
|
android:paddingRight="16dp"
|
||||||
android:paddingLeft="16dp"
|
android:text="BM-XyYxXyYxXyYxXyYxXyYx"
|
||||||
android:paddingRight="16dp"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
/>
|
tools:ignore="HardcodedText"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/stream_number"
|
android:id="@+id/stream_number"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Stream #"
|
android:layout_below="@+id/address"
|
||||||
android:lines="1"
|
android:ellipsize="end"
|
||||||
android:ellipsize="end"
|
android:lines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:paddingLeft="16dp"
|
||||||
android:paddingLeft="16dp"
|
android:paddingRight="16dp"
|
||||||
android:paddingRight="16dp"
|
android:text="Stream #"
|
||||||
android:layout_below="@+id/address"/>
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
tools:ignore="HardcodedText"/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/active"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/subscribed"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/active"
|
android:layout_below="@+id/stream_number"
|
||||||
android:paddingTop="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingLeft="16dp"
|
android:paddingRight="16dp"
|
||||||
android:paddingRight="16dp"
|
android:paddingTop="16dp"
|
||||||
android:layout_below="@+id/stream_number"/>
|
android:text="@string/subscribed"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pubkey_available"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_below="@+id/active"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:src="@drawable/public_key"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pubkey_available_desc"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:layout_alignBottom="@id/pubkey_available"
|
||||||
|
android:layout_toEndOf="@id/pubkey_available"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:text="@string/pubkey_available"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -56,4 +56,6 @@
|
|||||||
<string name="alias_default_identity">Ich</string>
|
<string name="alias_default_identity">Ich</string>
|
||||||
<string name="title_activity_status">Debugging</string>
|
<string name="title_activity_status">Debugging</string>
|
||||||
<string name="status_summary">Technische Infos</string>
|
<string name="status_summary">Technische Infos</string>
|
||||||
|
<string name="pubkey_available">Öffentlicher Schlüssel verfügbar</string>
|
||||||
|
<string name="pubkey_not_available">Öffentlicher Schlüssel noch nicht verfügbar</string>
|
||||||
</resources>
|
</resources>
|
@ -59,4 +59,6 @@
|
|||||||
<string name="status">Debugging</string>
|
<string name="status">Debugging</string>
|
||||||
<string name="status_summary">Technical information</string>
|
<string name="status_summary">Technical information</string>
|
||||||
<string name="alias_default_identity">Me</string>
|
<string name="alias_default_identity">Me</string>
|
||||||
|
<string name="pubkey_available">Public key available</string>
|
||||||
|
<string name="pubkey_not_available">Public key not yet available</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user