mirror of
https://codeberg.org/monocles/monocles_chat.git
synced 2025-01-29 00:14:12 +01:00
Basic link previews + include opengraph metadata about HTML links + Links in a quote are not really in this message + OpenGraph library not using tor right now, so disable when we are + disable Gradle daemon + Better defaults for OpenGraphParser + Eventually we need to move on + Empty result as an error instead of hanging + re-activate map preview (Experimental) + modify mime type detection for shared files
This commit is contained in:
parent
d8118ad945
commit
db78906a61
26 changed files with 304 additions and 113 deletions
|
@ -128,8 +128,9 @@ dependencies {
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
implementation 'com.nineoldandroids:library:2.4.0'
|
implementation 'com.nineoldandroids:library:2.4.0'
|
||||||
implementation "androidx.core:core-ktx:1.12.0"
|
implementation "androidx.core:core-ktx:1.12.0"
|
||||||
implementation "androidx.compose.material3:material3-android:1.2.0-beta02"
|
implementation "androidx.compose.material3:material3-android:1.2.0-rc01"
|
||||||
implementation "androidx.emoji2:emoji2-emojipicker:1.4.0"
|
implementation "androidx.emoji2:emoji2-emojipicker:1.4.0"
|
||||||
|
implementation 'com.github.Priyansh-Kedia:OpenGraphParser:2.5.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
|
|
@ -15,19 +15,6 @@
|
||||||
"versionName": "1.7.8.8",
|
"versionName": "1.7.8.8",
|
||||||
"outputFile": "monocles chat-1.7.8.8-git-universal-release.apk"
|
"outputFile": "monocles chat-1.7.8.8-git-universal-release.apk"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "ONE_OF_MANY",
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"filterType": "ABI",
|
|
||||||
"value": "armeabi-v7a"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": [],
|
|
||||||
"versionCode": 15901,
|
|
||||||
"versionName": "1.7.8.8",
|
|
||||||
"outputFile": "monocles chat-1.7.8.8-git-armeabi-v7a-release.apk"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
"filters": [
|
"filters": [
|
||||||
|
@ -41,6 +28,19 @@
|
||||||
"versionName": "1.7.8.8",
|
"versionName": "1.7.8.8",
|
||||||
"outputFile": "monocles chat-1.7.8.8-git-arm64-v8a-release.apk"
|
"outputFile": "monocles chat-1.7.8.8-git-arm64-v8a-release.apk"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "ONE_OF_MANY",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"filterType": "ABI",
|
||||||
|
"value": "armeabi-v7a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 15901,
|
||||||
|
"versionName": "1.7.8.8",
|
||||||
|
"outputFile": "monocles chat-1.7.8.8-git-armeabi-v7a-release.apk"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
"filters": [
|
"filters": [
|
||||||
|
|
|
@ -18,4 +18,5 @@ android.enableJetifier=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.gradle.gradle-args=--max-workers=32
|
org.gradle.gradle-args=--max-workers=32
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
android.nonFinalResIds=false
|
android.nonFinalResIds=false
|
||||||
|
org.gradle.daemon=false
|
|
@ -15,6 +15,8 @@ import android.util.Log;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.ui.util.MyLinkify;
|
||||||
import eu.siacs.conversations.utils.Compatibility;
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
|
@ -918,7 +920,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
String body, otherBody;
|
String body, otherBody;
|
||||||
if (this.hasFileOnRemoteHost()) {
|
if (this.hasFileOnRemoteHost() && (this.body == null || "".equals(this.body))) {
|
||||||
body = getFileParams().url;
|
body = getFileParams().url;
|
||||||
otherBody = message.body == null ? null : message.body.trim();
|
otherBody = message.body == null ? null : message.body.trim();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1203,6 +1205,19 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<URI> getLinks() {
|
||||||
|
SpannableStringBuilder text = new SpannableStringBuilder(
|
||||||
|
getBody().replaceAll("^>.*", "") // Remove quotes
|
||||||
|
);
|
||||||
|
return MyLinkify.extractLinks(text).stream().map((url) -> {
|
||||||
|
try {
|
||||||
|
return new URI(url);
|
||||||
|
} catch (final URISyntaxException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).filter(x -> x != null).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public URI getOob() {
|
public URI getOob() {
|
||||||
final String url = getFileParams().url;
|
final String url = getFileParams().url;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -150,17 +150,19 @@ public class MessageGenerator extends AbstractGenerator {
|
||||||
if (message.hasFileOnRemoteHost()) {
|
if (message.hasFileOnRemoteHost()) {
|
||||||
final Message.FileParams fileParams = message.getFileParams();
|
final Message.FileParams fileParams = message.getFileParams();
|
||||||
|
|
||||||
if (message.getBody().equals("")) {
|
if (message.getFallbacks(Namespace.OOB).isEmpty()) {
|
||||||
message.setBody(fileParams.url);
|
if (message.getBody().equals("")) {
|
||||||
packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB)
|
message.setBody(fileParams.url);
|
||||||
.addChild("body", "urn:xmpp:fallback:0");
|
packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB)
|
||||||
} else {
|
.addChild("body", "urn:xmpp:fallback:0");
|
||||||
long start = message.getQuoteableBody().length();
|
} else {
|
||||||
message.appendBody(fileParams.url);
|
long start = message.getQuoteableBody().length();
|
||||||
packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB)
|
message.appendBody(fileParams.url);
|
||||||
.addChild("body", "urn:xmpp:fallback:0")
|
packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB)
|
||||||
.setAttribute("start", String.valueOf(start))
|
.addChild("body", "urn:xmpp:fallback:0")
|
||||||
.setAttribute("end", String.valueOf(start + fileParams.url.length()));
|
.setAttribute("start", String.valueOf(start))
|
||||||
|
.setAttribute("end", String.valueOf(start + fileParams.url.length()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.addChild("x", Namespace.OOB).addChild("url").setContent(fileParams.url);
|
packet.addChild("x", Namespace.OOB).addChild("url").setContent(fileParams.url);
|
||||||
|
|
|
@ -129,11 +129,11 @@ public class HttpConnectionManager extends AbstractConnectionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OkHttpClient buildHttpClient(final HttpUrl url, final Account account, boolean interactive) {
|
public OkHttpClient buildHttpClient(final HttpUrl url, final Account account, boolean interactive) {
|
||||||
return buildHttpClient(url, account, 30, interactive);
|
return buildHttpClient(url, account, 30, interactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
OkHttpClient buildHttpClient(final HttpUrl url, final Account account, int readTimeout, boolean interactive) {
|
public OkHttpClient buildHttpClient(final HttpUrl url, final Account account, int readTimeout, boolean interactive) {
|
||||||
final String slotHostname = url.host();
|
final String slotHostname = url.host();
|
||||||
final boolean onionSlot = slotHostname.endsWith(".onion");
|
final boolean onionSlot = slotHostname.endsWith(".onion");
|
||||||
final boolean I2PSlot = slotHostname.endsWith(".i2p");
|
final boolean I2PSlot = slotHostname.endsWith(".i2p");
|
||||||
|
@ -182,4 +182,31 @@ public class HttpConnectionManager extends AbstractConnectionManager {
|
||||||
}
|
}
|
||||||
return body.byteStream();
|
return body.byteStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String extractFilenameFromResponse(okhttp3.Response response) {
|
||||||
|
String filename = null;
|
||||||
|
|
||||||
|
// Try to extract filename from the Content-Disposition header
|
||||||
|
String contentDisposition = response.header("Content-Disposition");
|
||||||
|
if (contentDisposition != null && contentDisposition.contains("filename=")) {
|
||||||
|
String[] parts = contentDisposition.split(";");
|
||||||
|
for (String part : parts) {
|
||||||
|
if (part.trim().startsWith("filename=")) {
|
||||||
|
filename = part.substring("filename=".length()).trim().replace("\"", "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If filename is not found in the Content-Disposition header, try to get it from the URL
|
||||||
|
if (filename == null || filename.isEmpty()) {
|
||||||
|
HttpUrl httpUrl = response.request().url();
|
||||||
|
List<String> pathSegments = httpUrl.pathSegments();
|
||||||
|
if (!pathSegments.isEmpty()) {
|
||||||
|
filename = pathSegments.get(pathSegments.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -232,7 +232,6 @@ public class HttpDownloadConnection implements Transferable {
|
||||||
message.setDeleted(true);
|
message.setDeleted(true);
|
||||||
}
|
}
|
||||||
message.setTransferable(null);
|
message.setTransferable(null);
|
||||||
if (cb != null) cb.accept(file);
|
|
||||||
mXmppConnectionService.updateMessage(message);
|
mXmppConnectionService.updateMessage(message);
|
||||||
mHttpConnectionManager.finishConnection(this);
|
mHttpConnectionManager.finishConnection(this);
|
||||||
final boolean notifyAfterScan = notify;
|
final boolean notifyAfterScan = notify;
|
||||||
|
@ -419,6 +418,7 @@ public class HttpDownloadConnection implements Transferable {
|
||||||
decryptIfNeeded();
|
decryptIfNeeded();
|
||||||
finish();
|
finish();
|
||||||
updateImageBounds();
|
updateImageBounds();
|
||||||
|
if (cb != null) cb.accept(file);
|
||||||
} catch (final SSLHandshakeException e) {
|
} catch (final SSLHandshakeException e) {
|
||||||
changeStatus(STATUS_OFFER);
|
changeStatus(STATUS_OFFER);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
|
|
|
@ -23,12 +23,17 @@ import android.graphics.drawable.AnimatedImageDrawable;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
|
||||||
|
import com.kedia.ogparser.JsoupProxy;
|
||||||
|
import com.kedia.ogparser.OpenGraphCallback;
|
||||||
|
import com.kedia.ogparser.OpenGraphParser;
|
||||||
|
import com.kedia.ogparser.OpenGraphResult;
|
||||||
|
|
||||||
import eu.siacs.conversations.ui.ConversationsActivity;
|
import eu.siacs.conversations.ui.ConversationsActivity;
|
||||||
import eu.siacs.conversations.utils.FileUtils;
|
import eu.siacs.conversations.utils.FileUtils;
|
||||||
import eu.siacs.conversations.persistance.UnifiedPushDatabase;
|
import eu.siacs.conversations.persistance.UnifiedPushDatabase;
|
||||||
import eu.siacs.conversations.xmpp.OnGatewayResult;
|
import eu.siacs.conversations.xmpp.OnGatewayResult;
|
||||||
import eu.siacs.conversations.utils.Consumer;
|
import eu.siacs.conversations.utils.Consumer;
|
||||||
|
import java.net.URI;
|
||||||
import static eu.siacs.conversations.utils.Compatibility.s;
|
import static eu.siacs.conversations.utils.Compatibility.s;
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
@ -138,6 +143,7 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
@ -232,6 +238,9 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||||
import io.ipfs.cid.Cid;
|
import io.ipfs.cid.Cid;
|
||||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
public class XmppConnectionService extends Service {
|
public class XmppConnectionService extends Service {
|
||||||
|
|
||||||
|
@ -2097,10 +2106,10 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessage(final Message message) {
|
public void sendMessage(final Message message) {
|
||||||
sendMessage(message, false, false);
|
sendMessage(message, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(final Message message, final boolean resend, final boolean delay) {
|
private void sendMessage(final Message message, final boolean resend, final boolean previewedLinks, final boolean delay) {
|
||||||
if (resend) {
|
if (resend) {
|
||||||
message.setTime(System.currentTimeMillis());
|
message.setTime(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
@ -2140,7 +2149,108 @@ public class XmppConnectionService extends Service {
|
||||||
message.setCounterpart(message.getConversation().getJid().asBareJid());
|
message.setCounterpart(message.getConversation().getJid().asBareJid());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.isOnlineAndConnected() && !inProgressJoin) {
|
boolean waitForPreview = false;
|
||||||
|
if (getPreferences().getBoolean("send_link_previews", true) && !previewedLinks && !message.needsUploading()) {
|
||||||
|
final List<URI> links = message.getLinks();
|
||||||
|
if (!links.isEmpty()) {
|
||||||
|
waitForPreview = true;
|
||||||
|
if (account.isOnlineAndConnected()) {
|
||||||
|
FILE_ATTACHMENT_EXECUTOR.execute(() -> {
|
||||||
|
for (URI link : links) {
|
||||||
|
if ("https".equals(link.getScheme())) {
|
||||||
|
try {
|
||||||
|
HttpUrl url = HttpUrl.parse(link.toString());
|
||||||
|
OkHttpClient http = getHttpConnectionManager().buildHttpClient(url, account, false);
|
||||||
|
okhttp3.Response response = http.newCall(new okhttp3.Request.Builder().url(url).head().build()).execute();
|
||||||
|
final String mimeType = response.header("Content-Type") == null ? "" : response.header("Content-Type");
|
||||||
|
final boolean image = mimeType.startsWith("image/");
|
||||||
|
final boolean audio = mimeType.startsWith("audio/");
|
||||||
|
final boolean video = mimeType.startsWith("video/");
|
||||||
|
final boolean pdf = mimeType.equals("application/pdf");
|
||||||
|
final boolean html = mimeType.startsWith("text/html") || mimeType.startsWith("application/xhtml+xml");
|
||||||
|
if (response.isSuccessful() && (image || audio || video || pdf)) {
|
||||||
|
Message.FileParams params = message.getFileParams();
|
||||||
|
params.url = url.toString();
|
||||||
|
if (response.header("Content-Length") != null) params.size = Long.parseLong(response.header("Content-Length"), 10);
|
||||||
|
if (!Message.configurePrivateFileMessage(message)) {
|
||||||
|
message.setType(image ? Message.TYPE_IMAGE : Message.TYPE_FILE);
|
||||||
|
}
|
||||||
|
params.setName(HttpConnectionManager.extractFilenameFromResponse(response));
|
||||||
|
|
||||||
|
if (link.toString().equals(message.getQuoteableBody())) {
|
||||||
|
Element fallback = new Element("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB);
|
||||||
|
fallback.addChild("body", "urn:xmpp:fallback:0");
|
||||||
|
message.addPayload(fallback);
|
||||||
|
} else if (message.getQuoteableBody().indexOf(link.toString()) >= 0) {
|
||||||
|
// Part of the real body, not just a fallback
|
||||||
|
Element fallback = new Element("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB);
|
||||||
|
fallback.addChild("body", "urn:xmpp:fallback:0")
|
||||||
|
.setAttribute("start", "0")
|
||||||
|
.setAttribute("end", "0");
|
||||||
|
message.addPayload(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHttpConnectionManager().createNewDownloadConnection(message, false, (file) -> {
|
||||||
|
synchronized (message.getConversation()) {
|
||||||
|
if (message.getStatus() == Message.STATUS_WAITING) sendMessage(message, true, true, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (response.isSuccessful() && html && !useI2PToConnect()) {
|
||||||
|
Semaphore waiter = new Semaphore(0);
|
||||||
|
OpenGraphParser.Builder openGraphBuilder = new OpenGraphParser.Builder(new OpenGraphCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPostResponse(OpenGraphResult result) {
|
||||||
|
Element rdf = new Element("Description", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
|
||||||
|
rdf.setAttribute("xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
|
||||||
|
rdf.setAttribute("rdf:about", link.toString());
|
||||||
|
if (result.getTitle() != null && !"".equals(result.getTitle())) {
|
||||||
|
rdf.addChild("title", "https://ogp.me/ns#").setContent(result.getTitle());
|
||||||
|
}
|
||||||
|
if (result.getDescription() != null && !"".equals(result.getDescription())) {
|
||||||
|
rdf.addChild("description", "https://ogp.me/ns#").setContent(result.getDescription());
|
||||||
|
}
|
||||||
|
if (result.getUrl() != null) {
|
||||||
|
rdf.addChild("url", "https://ogp.me/ns#").setContent(result.getUrl());
|
||||||
|
}
|
||||||
|
if (result.getImage() != null) {
|
||||||
|
rdf.addChild("image", "https://ogp.me/ns#").setContent(result.getImage());
|
||||||
|
}
|
||||||
|
if (result.getType() != null) {
|
||||||
|
rdf.addChild("type", "https://ogp.me/ns#").setContent(result.getType());
|
||||||
|
}
|
||||||
|
if (result.getSiteName() != null) {
|
||||||
|
rdf.addChild("site_name", "https://ogp.me/ns#").setContent(result.getSiteName());
|
||||||
|
}
|
||||||
|
message.addPayload(rdf);
|
||||||
|
waiter.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onError(String error) {
|
||||||
|
waiter.release();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.showNullOnEmpty(true)
|
||||||
|
.maxBodySize(4000)
|
||||||
|
.timeout(5000);
|
||||||
|
if (useTorToConnect()) {
|
||||||
|
openGraphBuilder = openGraphBuilder.jsoupProxy(new JsoupProxy("127.0.0.1", 8118));
|
||||||
|
}
|
||||||
|
openGraphBuilder.build().parse(link.toString());
|
||||||
|
waiter.tryAcquire(10L, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
} catch (final IOException | InterruptedException e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronized (message.getConversation()) {
|
||||||
|
if (message.getStatus() == Message.STATUS_WAITING) sendMessage(message, true, true, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.isOnlineAndConnected() && !inProgressJoin && !waitForPreview) {
|
||||||
switch (message.getEncryption()) {
|
switch (message.getEncryption()) {
|
||||||
case Message.ENCRYPTION_NONE:
|
case Message.ENCRYPTION_NONE:
|
||||||
if (message.needsUploading()) {
|
if (message.needsUploading()) {
|
||||||
|
@ -2317,11 +2427,9 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendUnsentMessages(final Conversation conversation) {
|
private void sendUnsentMessages(final Conversation conversation) {
|
||||||
final Runnable runnable = () -> {
|
synchronized (conversation) {
|
||||||
conversation.findWaitingMessages(message -> resendMessage(message, true));
|
conversation.findWaitingMessages(message -> resendMessage(message, true));
|
||||||
};
|
}
|
||||||
mDatabaseWriterExecutor.execute((runnable));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
private void resendFailedMessages(final Conversation conversation) {
|
private void resendFailedMessages(final Conversation conversation) {
|
||||||
final Runnable runnable = () -> {
|
final Runnable runnable = () -> {
|
||||||
|
@ -2348,7 +2456,7 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resendMessage(final Message message, final boolean delay) {
|
public void resendMessage(final Message message, final boolean delay) {
|
||||||
sendMessage(message, true, delay);
|
sendMessage(message, true, false, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestEasyOnboardingInvite(final Account account, final EasyOnboardingInvite.OnInviteRequested callback) {
|
public void requestEasyOnboardingInvite(final Account account, final EasyOnboardingInvite.OnInviteRequested callback) {
|
||||||
|
|
|
@ -88,6 +88,8 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
|
||||||
Log.d(Config.LOGTAG, "mime=" + mime);
|
Log.d(Config.LOGTAG, "mime=" + mime);
|
||||||
if (mime == null) {
|
if (mime == null) {
|
||||||
attr = R.attr.media_preview_unknown;
|
attr = R.attr.media_preview_unknown;
|
||||||
|
} else if (mime.equals("audio/x-m4b")) {
|
||||||
|
attr = R.attr.media_preview_audiobook;
|
||||||
} else if (mime.startsWith("audio/")) {
|
} else if (mime.startsWith("audio/")) {
|
||||||
attr = R.attr.media_preview_audio;
|
attr = R.attr.media_preview_audio;
|
||||||
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
|
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
|
||||||
|
|
|
@ -168,7 +168,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
|
static class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
private final MediaPreviewBinding binding;
|
private final MediaPreviewBinding binding;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
|
@ -262,6 +264,7 @@ public final class MimeUtils {
|
||||||
add("audio/mpeg", "mpega");
|
add("audio/mpeg", "mpega");
|
||||||
add("audio/mpeg", "mp2");
|
add("audio/mpeg", "mp2");
|
||||||
add("audio/mp4", "m4a");
|
add("audio/mp4", "m4a");
|
||||||
|
add("audio/x-m4b", "m4b");
|
||||||
add("audio/mpegurl", "m3u");
|
add("audio/mpegurl", "m3u");
|
||||||
add("audio/ogg", "ogg");
|
add("audio/ogg", "ogg");
|
||||||
add("audio/ogg", "oga");
|
add("audio/ogg", "oga");
|
||||||
|
@ -427,6 +430,8 @@ public final class MimeUtils {
|
||||||
"text/plain"
|
"text/plain"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// mime types that are more reliant by path
|
||||||
|
private static final Collection<String> PATH_PRECEDENCE_MIME_TYPE = Arrays.asList("audio/x-m4b");
|
||||||
|
|
||||||
private static void add(String mimeType, String extension) {
|
private static void add(String mimeType, String extension) {
|
||||||
// If we have an existing x -> y mapping, we do not want to
|
// If we have an existing x -> y mapping, we do not want to
|
||||||
|
@ -552,46 +557,49 @@ public final class MimeUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String guessMimeTypeFromUriAndMime(final Context context, final Uri uri, final String mime) {
|
public static String guessMimeTypeFromUriAndMime(final Context context, final Uri uri, final String mime) {
|
||||||
Log.d(Config.LOGTAG, "guessMimeTypeFromUriAndMime " + uri + " and mime=" + mime);
|
Log.d(Config.LOGTAG, "guessMimeTypeFromUriAndMime(" + uri + "," + mime+")");
|
||||||
if (mime == null || mime.equals("application/octet-stream")) {
|
final String mimeFromUri = guessMimeTypeFromUri(context, uri);
|
||||||
final String guess = guessMimeTypeFromUri(context, uri);
|
Log.d(Config.LOGTAG,"mimeFromUri:"+mimeFromUri);
|
||||||
if (guess != null) {
|
if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeFromUri)) {
|
||||||
return guess;
|
return mimeFromUri;
|
||||||
} else {
|
} else if (mime == null || mime.equals("application/octet-stream")) {
|
||||||
return mime;
|
return mimeFromUri;
|
||||||
}
|
} else {
|
||||||
|
return mime;
|
||||||
}
|
}
|
||||||
return guessMimeTypeFromUri(context, uri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String guessMimeTypeFromUri(Context context, Uri uri) {
|
public static String guessMimeTypeFromUri(final Context context, final Uri uri) {
|
||||||
// try the content resolver
|
final String mimeTypeContentResolver = guessFromContentResolver(context, uri);
|
||||||
String mimeType;
|
final String mimeTypeFromQueryParameter = uri.getQueryParameter("mimeType");
|
||||||
|
final String name = "content".equals(uri.getScheme()) ? getDisplayName(context, uri) : null;
|
||||||
|
final String mimeTypeFromName = Strings.isNullOrEmpty(name) ? null : guessFromPath(name);
|
||||||
|
final String path = uri.getPath();
|
||||||
|
final String mimeTypeFromPath = Strings.isNullOrEmpty(path) ? null : guessFromPath(path);
|
||||||
|
if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeTypeFromName)) {
|
||||||
|
return mimeTypeFromName;
|
||||||
|
}
|
||||||
|
if (PATH_PRECEDENCE_MIME_TYPE.contains(mimeTypeFromPath)) {
|
||||||
|
return mimeTypeFromPath;
|
||||||
|
}
|
||||||
|
if (mimeTypeContentResolver != null && !"application/octet-stream".equals(mimeTypeContentResolver)) {
|
||||||
|
return mimeTypeContentResolver;
|
||||||
|
}
|
||||||
|
if (mimeTypeFromName != null) {
|
||||||
|
return mimeTypeFromName;
|
||||||
|
}
|
||||||
|
if (mimeTypeFromQueryParameter != null) {
|
||||||
|
return mimeTypeFromQueryParameter;
|
||||||
|
}
|
||||||
|
return mimeTypeFromPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String guessFromContentResolver(final Context context, final Uri uri) {
|
||||||
try {
|
try {
|
||||||
mimeType = context.getContentResolver().getType(uri);
|
return context.getContentResolver().getType(uri);
|
||||||
} catch (final Throwable throwable) {
|
} catch (final Throwable e) {
|
||||||
mimeType = null;
|
return null;
|
||||||
}
|
}
|
||||||
// try the extension
|
|
||||||
if (mimeType == null || mimeType.equals("application/octet-stream")) {
|
|
||||||
final String path = uri.getPath();
|
|
||||||
if (path != null) {
|
|
||||||
mimeType = guessFromPath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mimeType == null && "content".equals(uri.getScheme())) {
|
|
||||||
final String name = getDisplayName(context, uri);
|
|
||||||
if (name != null) {
|
|
||||||
mimeType = guessFromPath(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// sometimes this works (as with the commit content api)
|
|
||||||
if (mimeType == null) {
|
|
||||||
try {
|
|
||||||
mimeType = uri.getQueryParameter("mimeType");
|
|
||||||
} catch (final Throwable throwable) { }
|
|
||||||
}
|
|
||||||
return mimeType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDisplayName(final Context context, final Uri uri) {
|
private static String getDisplayName(final Context context, final Uri uri) {
|
||||||
|
|
|
@ -541,6 +541,8 @@ public class UIHelper {
|
||||||
return context.getString(R.string.file);
|
return context.getString(R.string.file);
|
||||||
} else if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
|
} else if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
|
||||||
return context.getString(R.string.multimedia_file);
|
return context.getString(R.string.multimedia_file);
|
||||||
|
} else if (mime.equals("audio/x-m4b")) {
|
||||||
|
return context.getString(R.string.audiobook);
|
||||||
} else if (mime.startsWith("audio/")) {
|
} else if (mime.startsWith("audio/")) {
|
||||||
return context.getString(R.string.audio);
|
return context.getString(R.string.audio);
|
||||||
} else if (mime.startsWith("video/")) {
|
} else if (mime.startsWith("video/")) {
|
||||||
|
|
6
src/main/res/drawable/ic_play_lesson_black_24.xml
Normal file
6
src/main/res/drawable/ic_play_lesson_black_24.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<vector android:height="48dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M18,11c0.34,0 0.67,0.03 1,0.08V4c0,-1.1 -0.9,-2 -2,-2H5C3.9,2 3,2.9 3,4v16c0,1.1 0.9,2 2,2h7.26C11.47,20.87 11,19.49 11,18C11,14.13 14.13,11 18,11zM7,11V4h5v7L9.5,9.5L7,11z"/>
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M18,13c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S20.76,13 18,13zM16.75,20.5v-5l4,2.5L16.75,20.5z"/>
|
||||||
|
</vector>
|
6
src/main/res/drawable/ic_play_lesson_white_48dp.xml
Normal file
6
src/main/res/drawable/ic_play_lesson_white_48dp.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M18,11c0.34,0 0.67,0.03 1,0.08V4c0,-1.1 -0.9,-2 -2,-2H5C3.9,2 3,2.9 3,4v16c0,1.1 0.9,2 2,2h7.26C11.47,20.87 11,19.49 11,18C11,14.13 14.13,11 18,11zM7,11V4h5v7L9.5,9.5L7,11z"/>
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M18,13c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S20.76,13 18,13zM16.75,20.5v-5l4,2.5L16.75,20.5z"/>
|
||||||
|
</vector>
|
|
@ -2,13 +2,13 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="2dp"
|
android:topLeftRadius="2dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="15dp"
|
android:bottomRightRadius="20dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="2dp"
|
android:bottom="0dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="2dp" />
|
android:top="2dp" />
|
||||||
<solid android:color="@color/lightblue" />
|
<solid android:color="@color/lightblue" />
|
||||||
</shape>
|
</shape>
|
|
@ -3,13 +3,13 @@
|
||||||
<stroke android:width="2dp" android:color="@color/accent"/>
|
<stroke android:width="2dp" android:color="@color/accent"/>
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="2dp"
|
android:topLeftRadius="2dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="15dp"
|
android:bottomRightRadius="20dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="2dp"
|
android:bottom="0dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="2dp" />
|
android:top="2dp" />
|
||||||
<solid android:color="@color/lightblue" />
|
<solid android:color="@color/lightblue" />
|
||||||
</shape>
|
</shape>
|
|
@ -2,13 +2,13 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="2dp"
|
android:topLeftRadius="2dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="15dp"
|
android:bottomRightRadius="20dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="4dp"
|
android:bottom="2dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="4dp" />
|
android:top="4dp" />
|
||||||
<solid android:color="@color/lightred" />
|
<solid android:color="@color/lightred" />
|
||||||
</shape>
|
</shape>
|
|
@ -3,13 +3,13 @@
|
||||||
<stroke android:width="2dp" android:color="@color/accent"/>
|
<stroke android:width="2dp" android:color="@color/accent"/>
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="2dp"
|
android:topLeftRadius="2dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="15dp"
|
android:bottomRightRadius="20dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="4dp"
|
android:bottom="2dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="4dp" />
|
android:top="4dp" />
|
||||||
<solid android:color="@color/lightred" />
|
<solid android:color="@color/lightred" />
|
||||||
</shape>
|
</shape>
|
|
@ -1,14 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="15dp"
|
android:topLeftRadius="20dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="2dp"
|
android:bottomRightRadius="2dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="4dp"
|
android:bottom="2dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="4dp" />
|
android:top="4dp" />
|
||||||
<solid android:color="@color/darkblue" />
|
<solid android:color="@color/darkblue" />
|
||||||
</shape>
|
</shape>
|
|
@ -2,14 +2,14 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
<stroke android:width="2dp" android:color="@color/accent"/>
|
<stroke android:width="2dp" android:color="@color/accent"/>
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="15dp"
|
android:topLeftRadius="20dp"
|
||||||
android:topRightRadius="15dp"
|
android:topRightRadius="20dp"
|
||||||
android:bottomRightRadius="2dp"
|
android:bottomRightRadius="2dp"
|
||||||
android:bottomLeftRadius="15dp" />
|
android:bottomLeftRadius="20dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="4dp"
|
android:bottom="2dp"
|
||||||
android:left="6dp"
|
android:left="10dp"
|
||||||
android:right="6dp"
|
android:right="10dp"
|
||||||
android:top="4dp" />
|
android:top="4dp" />
|
||||||
<solid android:color="@color/darkblue" />
|
<solid android:color="@color/darkblue" />
|
||||||
</shape>
|
</shape>
|
|
@ -409,6 +409,7 @@
|
||||||
<string name="image">Bild</string>
|
<string name="image">Bild</string>
|
||||||
<string name="pdf_document">PDF-Dokument</string>
|
<string name="pdf_document">PDF-Dokument</string>
|
||||||
<string name="apk">Android App</string>
|
<string name="apk">Android App</string>
|
||||||
|
<string name="audiobook">Audiobuch</string>
|
||||||
<string name="vcard">Kontakt</string>
|
<string name="vcard">Kontakt</string>
|
||||||
<string name="avatar_has_been_published">Profilbild wurde gespeichert</string>
|
<string name="avatar_has_been_published">Profilbild wurde gespeichert</string>
|
||||||
<string name="sending_x_file">%s wird gesendet</string>
|
<string name="sending_x_file">%s wird gesendet</string>
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
<attr name="media_preview_tour" format="reference" />
|
<attr name="media_preview_tour" format="reference" />
|
||||||
<attr name="media_preview_contact" format="reference" />
|
<attr name="media_preview_contact" format="reference" />
|
||||||
<attr name="media_preview_app" format="reference" />
|
<attr name="media_preview_app" format="reference" />
|
||||||
|
<attr name="media_preview_audiobook" format="reference" />
|
||||||
<attr name="media_preview_calendar" format="reference" />
|
<attr name="media_preview_calendar" format="reference" />
|
||||||
<attr name="media_preview_archive" format="reference" />
|
<attr name="media_preview_archive" format="reference" />
|
||||||
<attr name="media_preview_ebook" format="reference" />
|
<attr name="media_preview_ebook" format="reference" />
|
||||||
|
|
|
@ -10,4 +10,5 @@
|
||||||
<bool name="set_text_collapsable">false</bool>
|
<bool name="set_text_collapsable">false</bool>
|
||||||
<bool name="enforce_dane">false</bool>
|
<bool name="enforce_dane">false</bool>
|
||||||
<bool name="hide_donation_snackbar">false</bool>
|
<bool name="hide_donation_snackbar">false</bool>
|
||||||
|
<bool name="send_link_previews">true</bool>
|
||||||
</resources>
|
</resources>
|
|
@ -392,6 +392,7 @@
|
||||||
<string name="image">image</string>
|
<string name="image">image</string>
|
||||||
<string name="pdf_document">PDF document</string>
|
<string name="pdf_document">PDF document</string>
|
||||||
<string name="apk">Android App</string>
|
<string name="apk">Android App</string>
|
||||||
|
<string name="audiobook">Audiobook</string>
|
||||||
<string name="vcard">Contact</string>
|
<string name="vcard">Contact</string>
|
||||||
<string name="avatar_has_been_published">Avatar has been published!</string>
|
<string name="avatar_has_been_published">Avatar has been published!</string>
|
||||||
<string name="sending_x_file">Sending %s</string>
|
<string name="sending_x_file">Sending %s</string>
|
||||||
|
@ -1405,4 +1406,6 @@
|
||||||
<string name="grey">Grey</string>
|
<string name="grey">Grey</string>
|
||||||
<string name="blue">Blue</string>
|
<string name="blue">Blue</string>
|
||||||
<string name="green_and_blue">Green and blue</string>
|
<string name="green_and_blue">Green and blue</string>
|
||||||
|
<string name="pref_send_link_previews">Send link previews</string>
|
||||||
|
<string name="pref_send_link_previews_summary">Attach metadata about links when sending a message</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
<item name="media_preview_tour" type="reference">@drawable/baseline_tour_black_48</item>
|
<item name="media_preview_tour" type="reference">@drawable/baseline_tour_black_48</item>
|
||||||
<item name="media_preview_contact" type="reference">@drawable/ic_person_black_48dp</item>
|
<item name="media_preview_contact" type="reference">@drawable/ic_person_black_48dp</item>
|
||||||
<item name="media_preview_app" type="reference">@drawable/ic_android_black_48dp</item>
|
<item name="media_preview_app" type="reference">@drawable/ic_android_black_48dp</item>
|
||||||
|
<item name="media_preview_audiobook" type="reference">@drawable/ic_play_lesson_black_24</item>
|
||||||
<item name="media_preview_calendar" type="reference">@drawable/ic_event_black_48dp</item>
|
<item name="media_preview_calendar" type="reference">@drawable/ic_event_black_48dp</item>
|
||||||
<item name="media_preview_archive" type="reference">@drawable/ic_archive_black_48dp</item>
|
<item name="media_preview_archive" type="reference">@drawable/ic_archive_black_48dp</item>
|
||||||
<item name="media_preview_ebook" type="reference">@drawable/ic_book_black_48dp</item>
|
<item name="media_preview_ebook" type="reference">@drawable/ic_book_black_48dp</item>
|
||||||
|
@ -352,6 +353,7 @@
|
||||||
<item name="media_preview_tour" type="reference">@drawable/baseline_tour_white_48</item>
|
<item name="media_preview_tour" type="reference">@drawable/baseline_tour_white_48</item>
|
||||||
<item name="media_preview_contact" type="reference">@drawable/ic_person_white_48dp</item>
|
<item name="media_preview_contact" type="reference">@drawable/ic_person_white_48dp</item>
|
||||||
<item name="media_preview_app" type="reference">@drawable/ic_android_white_48dp</item>
|
<item name="media_preview_app" type="reference">@drawable/ic_android_white_48dp</item>
|
||||||
|
<item name="media_preview_audiobook" type="reference">@drawable/ic_play_lesson_white_48dp</item>
|
||||||
<item name="media_preview_calendar" type="reference">@drawable/ic_event_white_48dp</item>
|
<item name="media_preview_calendar" type="reference">@drawable/ic_event_white_48dp</item>
|
||||||
<item name="media_preview_archive" type="reference">@drawable/ic_archive_white_48dp</item>
|
<item name="media_preview_archive" type="reference">@drawable/ic_archive_white_48dp</item>
|
||||||
<item name="media_preview_ebook" type="reference">@drawable/ic_book_white_48dp</item>
|
<item name="media_preview_ebook" type="reference">@drawable/ic_book_white_48dp</item>
|
||||||
|
|
|
@ -482,6 +482,11 @@
|
||||||
android:key="last_activity"
|
android:key="last_activity"
|
||||||
android:summary="@string/pref_broadcast_last_activity_summary"
|
android:summary="@string/pref_broadcast_last_activity_summary"
|
||||||
android:title="@string/pref_broadcast_last_activity" />
|
android:title="@string/pref_broadcast_last_activity" />
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="@bool/send_link_previews"
|
||||||
|
android:key="send_link_previews"
|
||||||
|
android:summary="@string/pref_send_link_previews_summary"
|
||||||
|
android:title="@string/pref_send_link_previews" />
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="@bool/notifications_from_strangers"
|
android:defaultValue="@bool/notifications_from_strangers"
|
||||||
android:key="notifications_from_strangers"
|
android:key="notifications_from_strangers"
|
||||||
|
@ -502,25 +507,25 @@
|
||||||
android:key="use_internal_updater"
|
android:key="use_internal_updater"
|
||||||
android:summary="@string/pref_use_internal_updater_summary"
|
android:summary="@string/pref_use_internal_updater_summary"
|
||||||
android:title="@string/pref_use_internal_updater" />
|
android:title="@string/pref_use_internal_updater" />
|
||||||
|
<!--
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="@bool/show_links_inside"
|
android:defaultValue="@bool/show_links_inside"
|
||||||
android:key="show_links_inside"
|
android:key="show_links_inside"
|
||||||
android:summary="@string/pref_show_links_inside_summary"
|
android:summary="@string/pref_show_links_inside_summary"
|
||||||
android:title="@string/pref_show_links_inside" />
|
android:title="@string/pref_show_links_inside" />
|
||||||
|
-->
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="@bool/show_maps_inside"
|
android:defaultValue="@bool/show_maps_inside"
|
||||||
android:disableDependentsState="false"
|
android:disableDependentsState="false"
|
||||||
android:key="show_maps_inside"
|
android:key="show_maps_inside"
|
||||||
android:summary="@string/pref_show_mappreview_inside_summary"
|
android:summary="@string/pref_show_mappreview_inside_summary"
|
||||||
android:title="@string/pref_show_mappreview_inside" />
|
android:title="@string/pref_show_mappreview_inside" />
|
||||||
<!--
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue="@string/mappreview_url"
|
android:defaultValue="@string/mappreview_url"
|
||||||
android:dependency="show_maps_inside"
|
android:dependency="show_maps_inside"
|
||||||
android:key="mappreview_host"
|
android:key="mappreview_host"
|
||||||
android:summary="@string/pref_mappreview_host_summary"
|
android:summary="@string/pref_mappreview_host_summary"
|
||||||
android:title="@string/pref_mappreview_host" />
|
android:title="@string/pref_mappreview_host" />
|
||||||
-->
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="@bool/warn_unencrypted_chat"
|
android:defaultValue="@bool/warn_unencrypted_chat"
|
||||||
android:key="warn_unencrypted_chat"
|
android:key="warn_unencrypted_chat"
|
||||||
|
|
Loading…
Add table
Reference in a new issue