Merge tag '2.8.8' into develop
This commit is contained in:
commit
0bca11bdeb
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### Version 2.8.8
|
||||||
|
* Fixed notifications not showing up under certain conditions
|
||||||
|
* Fixed compatibility issues and crashes related to A/V calls
|
||||||
|
|
||||||
### Version 2.8.7
|
### Version 2.8.7
|
||||||
|
|
||||||
* Show help button if A/V call fails
|
* Show help button if A/V call fails
|
||||||
|
|
|
@ -119,7 +119,7 @@ images.each do |source_filename, settings|
|
||||||
else
|
else
|
||||||
path = "../src/#{output_parts[0]}/res/drawable-#{resolution}/#{output_parts[1]}.png"
|
path = "../src/#{output_parts[0]}/res/drawable-#{resolution}/#{output_parts[1]}.png"
|
||||||
end
|
end
|
||||||
execute_cmd "#{inkscape} -f #{source_filename} -z -C -w #{width} -h #{height} -e #{path}"
|
execute_cmd "#{inkscape} #{source_filename} -C -w #{width} -h #{height} -o #{path}"
|
||||||
|
|
||||||
top = []
|
top = []
|
||||||
right = []
|
right = []
|
||||||
|
|
|
@ -96,8 +96,8 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 393
|
versionCode 394
|
||||||
versionName "2.8.7"
|
versionName "2.8.8"
|
||||||
archivesBaseName += "-$versionName"
|
archivesBaseName += "-$versionName"
|
||||||
applicationId "eu.sum7.conversations"
|
applicationId "eu.sum7.conversations"
|
||||||
resValue "string", "applicationId", applicationId
|
resValue "string", "applicationId", applicationId
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
* Show help button if A/V call fails
|
||||||
|
* Fixed some annoying crashes
|
||||||
|
* Fixed Jingle connections (file transfer + calls) with bare JIDs
|
|
@ -1,3 +1,2 @@
|
||||||
• Show help button if A/V call fails
|
• Fixed notifications not showing up under certain conditions
|
||||||
• Fixed some annoying crashes
|
• Fixed compatibility issues and crashes related to A/V calls
|
||||||
• Fixed Jingle connections (file transfer + calls) with bare JIDs
|
|
||||||
|
|
|
@ -38,6 +38,40 @@
|
||||||
<data android:mimeType="application/vnd.conversations.backup" />
|
<data android:mimeType="application/vnd.conversations.backup" />
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="content" />
|
||||||
|
<data android:host="*" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
<data android:pathPattern=".*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="file" />
|
||||||
|
<data android:host="*" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
<data android:pathPattern=".*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.ceb" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package eu.siacs.conversations.entities;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
|
public class AccountConfiguration {
|
||||||
|
|
||||||
|
private static final Gson GSON = new GsonBuilder().create();
|
||||||
|
|
||||||
|
public Protocol protocol;
|
||||||
|
public String address;
|
||||||
|
public String password;
|
||||||
|
|
||||||
|
public Jid getJid() {
|
||||||
|
return Jid.ofEscaped(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountConfiguration parse(final String input) {
|
||||||
|
final AccountConfiguration c;
|
||||||
|
try {
|
||||||
|
c = GSON.fromJson(input, AccountConfiguration.class);
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new IllegalArgumentException("Not a valid JSON string", e);
|
||||||
|
}
|
||||||
|
Preconditions.checkArgument(
|
||||||
|
c.protocol == Protocol.XMPP,
|
||||||
|
"Protocol must be XMPP"
|
||||||
|
);
|
||||||
|
Preconditions.checkArgument(
|
||||||
|
c.address != null && c.getJid().isBareJid() && !c.getJid().isDomainJid(),
|
||||||
|
"Invalid XMPP address"
|
||||||
|
);
|
||||||
|
Preconditions.checkArgument(
|
||||||
|
c.password != null && c.password.length() > 0,
|
||||||
|
"No password specified"
|
||||||
|
);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Protocol {
|
||||||
|
@SerializedName("xmpp") XMPP,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.databinding.DataBindingUtil;
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.security.KeyChain;
|
import android.security.KeyChain;
|
||||||
import android.security.KeyChainAliasCallback;
|
import android.security.KeyChainAliasCallback;
|
||||||
|
@ -25,6 +26,7 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
|
import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
import eu.siacs.conversations.utils.InstallReferrerUtils;
|
import eu.siacs.conversations.utils.InstallReferrerUtils;
|
||||||
import eu.siacs.conversations.utils.SignupUtils;
|
import eu.siacs.conversations.utils.SignupUtils;
|
||||||
import eu.siacs.conversations.utils.XmppUri;
|
import eu.siacs.conversations.utils.XmppUri;
|
||||||
|
@ -46,35 +48,37 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
||||||
activity.overridePendingTransition(0, 0);
|
activity.overridePendingTransition(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onInstallReferrerDiscovered(final String referrer) {
|
public void onInstallReferrerDiscovered(final Uri referrer) {
|
||||||
Log.d(Config.LOGTAG, "welcome activity: on install referrer discovered " + referrer);
|
Log.d(Config.LOGTAG, "welcome activity: on install referrer discovered " + referrer);
|
||||||
if (referrer != null) {
|
if ("xmpp".equalsIgnoreCase(referrer.getScheme())) {
|
||||||
final XmppUri xmppUri = new XmppUri(referrer);
|
final XmppUri xmppUri = new XmppUri(referrer);
|
||||||
runOnUiThread(() -> processXmppUri(xmppUri));
|
runOnUiThread(() -> processXmppUri(xmppUri));
|
||||||
|
} else {
|
||||||
|
Log.i(Config.LOGTAG, "install referrer was not an XMPP uri");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processXmppUri(final XmppUri xmppUri) {
|
private void processXmppUri(final XmppUri xmppUri) {
|
||||||
if (xmppUri.isValidJid()) {
|
if (!xmppUri.isValidJid()) {
|
||||||
final String preauth = xmppUri.getParameter("preauth");
|
return;
|
||||||
final Jid jid = xmppUri.getJid();
|
|
||||||
final Intent intent;
|
|
||||||
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
|
||||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preauth);
|
|
||||||
} else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter("ibr"))) {
|
|
||||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preauth);
|
|
||||||
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
|
||||||
} else {
|
|
||||||
intent = null;
|
|
||||||
}
|
|
||||||
if (intent != null) {
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.inviteUri = xmppUri;
|
|
||||||
}
|
}
|
||||||
return false;
|
final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
|
||||||
|
final Jid jid = xmppUri.getJid();
|
||||||
|
final Intent intent;
|
||||||
|
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
||||||
|
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
|
||||||
|
} else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
|
||||||
|
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
|
||||||
|
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
||||||
|
} else {
|
||||||
|
intent = null;
|
||||||
|
}
|
||||||
|
if (intent != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.inviteUri = xmppUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -143,10 +147,12 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
getMenuInflater().inflate(R.menu.welcome_menu, menu);
|
getMenuInflater().inflate(R.menu.welcome_menu, menu);
|
||||||
final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
|
final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
|
||||||
scan.setVisible(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA));
|
scan.setVisible(Compatibility.hasFeatureCamera(this));
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
|
@ -156,7 +162,7 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.action_scan_qr_code:
|
case R.id.action_scan_qr_code:
|
||||||
UriHandlerActivity.scan(this);
|
UriHandlerActivity.scan(this, true);
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_account_with_cert:
|
case R.id.action_add_account_with_cert:
|
||||||
addAccountFromKey();
|
addAccountFromKey();
|
||||||
|
@ -183,7 +189,7 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
|
||||||
@Override
|
@Override
|
||||||
public void onAccountCreated(final Account account) {
|
public void onAccountCreated(final Account account) {
|
||||||
final Intent intent = new Intent(this, EditAccountActivity.class);
|
final Intent intent = new Intent(this, EditAccountActivity.class);
|
||||||
intent.putExtra("jid", account.getJid().asBareJid().toString());
|
intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
|
||||||
intent.putExtra("init", true);
|
intent.putExtra("init", true);
|
||||||
addInviteUri(intent);
|
addInviteUri(intent);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.entities.AccountConfiguration;
|
||||||
|
import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
|
import eu.siacs.conversations.ui.EditAccountActivity;
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
|
public class ProvisioningUtils {
|
||||||
|
|
||||||
|
public static void provision(final Activity activity, final String json) {
|
||||||
|
final AccountConfiguration accountConfiguration;
|
||||||
|
try {
|
||||||
|
accountConfiguration = AccountConfiguration.parse(json);
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
Toast.makeText(activity, R.string.improperly_formatted_provisioning, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Jid jid = accountConfiguration.getJid();
|
||||||
|
final List<Jid> accounts = DatabaseBackend.getInstance(activity).getAccountJids(true);
|
||||||
|
if (accounts.contains(jid)) {
|
||||||
|
Toast.makeText(activity, R.string.account_already_exists, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Intent serviceIntent = new Intent(activity, XmppConnectionService.class);
|
||||||
|
serviceIntent.setAction(XmppConnectionService.ACTION_PROVISION_ACCOUNT);
|
||||||
|
serviceIntent.putExtra("address", jid.asBareJid().toEscapedString());
|
||||||
|
serviceIntent.putExtra("password", accountConfiguration.password);
|
||||||
|
Compatibility.startService(activity, serviceIntent);
|
||||||
|
final Intent intent = new Intent(activity, EditAccountActivity.class);
|
||||||
|
intent.putExtra("jid", jid.asBareJid().toEscapedString());
|
||||||
|
intent.putExtra("init", true);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,4 +3,6 @@
|
||||||
<string name="pick_a_server">Выберите своего XMPP-провайдера</string>
|
<string name="pick_a_server">Выберите своего XMPP-провайдера</string>
|
||||||
<string name="use_chat.sum7.eu">Использовать chat.sum7.eu</string>
|
<string name="use_chat.sum7.eu">Использовать chat.sum7.eu</string>
|
||||||
<string name="create_new_account">Создать новый аккаунт</string>
|
<string name="create_new_account">Создать новый аккаунт</string>
|
||||||
</resources>
|
<string name="do_you_have_an_account">У вас есть аккаунт XMPP? Если вы использовали Conversations или другой XMPP-клиент в прошлом, то скорее всего, он у вас есть. Если у вас нет аккаунта, вы можете создать его прямо сейчас.\nНекоторые провайдеры электронной почты также регистрируют аккаунты XMPP. </string>
|
||||||
|
<string name="server_select_text">XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор.\nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на chat.sum7.eu, сервер, специально предназначеный для работы с приложением Conv6sations.</string>
|
||||||
|
</resources>
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
<string name="magic_create_text_on_x">You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
<string name="magic_create_text_on_x">You have been invited to %1$s. We will guide you through the process of creating an account.\nWhen picking %1$s as a provider you will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
||||||
<string name="magic_create_text_fixed">You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
<string name="magic_create_text_fixed">You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address.</string>
|
||||||
<string name="your_server_invitation">Your server invitation</string>
|
<string name="your_server_invitation">Your server invitation</string>
|
||||||
|
<string name="improperly_formatted_provisioning">Improperly formatted provisioning code</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -9,6 +10,7 @@ import android.util.Log;
|
||||||
import com.android.installreferrer.api.InstallReferrerClient;
|
import com.android.installreferrer.api.InstallReferrerClient;
|
||||||
import com.android.installreferrer.api.InstallReferrerStateListener;
|
import com.android.installreferrer.api.InstallReferrerStateListener;
|
||||||
import com.android.installreferrer.api.ReferrerDetails;
|
import com.android.installreferrer.api.ReferrerDetails;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.ui.WelcomeActivity;
|
import eu.siacs.conversations.ui.WelcomeActivity;
|
||||||
|
@ -49,8 +51,11 @@ public class InstallReferrerUtils implements InstallReferrerStateListener {
|
||||||
try {
|
try {
|
||||||
final ReferrerDetails referrerDetails = installReferrerClient.getInstallReferrer();
|
final ReferrerDetails referrerDetails = installReferrerClient.getInstallReferrer();
|
||||||
final String referrer = referrerDetails.getInstallReferrer();
|
final String referrer = referrerDetails.getInstallReferrer();
|
||||||
welcomeActivity.onInstallReferrerDiscovered(referrer);
|
if (Strings.isNullOrEmpty(referrer)) {
|
||||||
} catch (final RemoteException e) {
|
return;
|
||||||
|
}
|
||||||
|
welcomeActivity.onInstallReferrerDiscovered(Uri.parse(referrer));
|
||||||
|
} catch (final RemoteException | IllegalArgumentException e) {
|
||||||
Log.d(Config.LOGTAG, "unable to get install referrer", e);
|
Log.d(Config.LOGTAG, "unable to get install referrer", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.google.common.collect.ComparisonChain;
|
import com.google.common.collect.ComparisonChain;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -169,6 +170,22 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String findMostRecentRemoteDisplayableId() {
|
||||||
|
final boolean multi = mode == Conversation.MODE_MULTI;
|
||||||
|
synchronized (this.messages) {
|
||||||
|
for(final Message message : Lists.reverse(this.messages)) {
|
||||||
|
if (message.getStatus() == Message.STATUS_RECEIVED) {
|
||||||
|
final String serverMsgId = message.getServerMsgId();
|
||||||
|
if (serverMsgId != null && multi) {
|
||||||
|
return serverMsgId;
|
||||||
|
}
|
||||||
|
return message.getRemoteMsgId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Message findUnsentMessageWithUuid(String uuid) {
|
public Message findUnsentMessageWithUuid(String uuid) {
|
||||||
synchronized (this.messages) {
|
synchronized (this.messages) {
|
||||||
for (final Message message : this.messages) {
|
for (final Message message : this.messages) {
|
||||||
|
|
|
@ -837,9 +837,9 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
|
if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
|
||||||
final String action = child.getName();
|
final String action = child.getName();
|
||||||
final String sessionId = child.getAttribute("id");
|
final String sessionId = child.getAttribute("id");
|
||||||
if (sessionId == null) {
|
if (sessionId == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
if (serverMsgId == null) {
|
if (serverMsgId == null) {
|
||||||
serverMsgId = extractStanzaId(account, packet);
|
serverMsgId = extractStanzaId(account, packet);
|
||||||
|
@ -952,7 +952,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
final String id = displayed.getAttribute("id");
|
final String id = displayed.getAttribute("id");
|
||||||
final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
|
final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
|
||||||
if (packet.fromAccount(account) && !selfAddressed) {
|
if (packet.fromAccount(account) && !selfAddressed) {
|
||||||
dismissNotification(account, counterpart, query);
|
dismissNotification(account, counterpart, query, id);
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
activateGracePeriod(account);
|
activateGracePeriod(account);
|
||||||
}
|
}
|
||||||
|
@ -993,7 +993,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
message = message.prev();
|
message = message.prev();
|
||||||
}
|
}
|
||||||
if (displayedMessage != null && selfAddressed) {
|
if (displayedMessage != null && selfAddressed) {
|
||||||
dismissNotification(account, counterpart, query);
|
dismissNotification(account, counterpart, query, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1018,10 +1018,15 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query) {
|
private void dismissNotification(Account account, Jid counterpart, MessageArchiveService.Query query, final String id) {
|
||||||
Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid());
|
final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid());
|
||||||
if (conversation != null && (query == null || query.isCatchup())) {
|
if (conversation != null && (query == null || query.isCatchup())) {
|
||||||
mXmppConnectionService.markRead(conversation); //TODO only mark messages read that are older than timestamp
|
final String displayableId = conversation.findMostRecentRemoteDisplayableId();
|
||||||
|
if (displayableId != null && displayableId.equals(id)) {
|
||||||
|
mXmppConnectionService.markRead(conversation);
|
||||||
|
} else {
|
||||||
|
Log.w(Config.LOGTAG, account.getJid().asBareJid() + ": received dismissing display marker that did not match our last id in that conversation");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -602,9 +602,6 @@ public class NotificationService {
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
Log.d(Config.LOGTAG, "unable to use custom notification sound " + uri.toString());
|
Log.d(Config.LOGTAG, "unable to use custom notification sound " + uri.toString());
|
||||||
}
|
}
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
|
|
||||||
}
|
|
||||||
mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
setNotificationColor(mBuilder);
|
setNotificationColor(mBuilder);
|
||||||
mBuilder.setLights(LED_COLOR, 2000, 3000);
|
mBuilder.setLights(LED_COLOR, 2000, 3000);
|
||||||
|
|
|
@ -170,6 +170,7 @@ public class XmppConnectionService extends Service {
|
||||||
public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received";
|
public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received";
|
||||||
public static final String ACTION_DISMISS_CALL = "dismiss_call";
|
public static final String ACTION_DISMISS_CALL = "dismiss_call";
|
||||||
public static final String ACTION_END_CALL = "end_call";
|
public static final String ACTION_END_CALL = "end_call";
|
||||||
|
public static final String ACTION_PROVISION_ACCOUNT = "provision_account";
|
||||||
private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
|
private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
|
||||||
|
|
||||||
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
|
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
|
||||||
|
@ -659,6 +660,15 @@ public class XmppConnectionService extends Service {
|
||||||
mJingleConnectionManager.endRtpSession(sessionId);
|
mJingleConnectionManager.endRtpSession(sessionId);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ACTION_PROVISION_ACCOUNT: {
|
||||||
|
final String address = intent.getStringExtra("address");
|
||||||
|
final String password = intent.getStringExtra("password");
|
||||||
|
if (QuickConversationsService.isQuicksy() || Strings.isNullOrEmpty(address) || Strings.isNullOrEmpty(password)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
provisionAccount(address, password);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ACTION_DISMISS_ERROR_NOTIFICATIONS:
|
case ACTION_DISMISS_ERROR_NOTIFICATIONS:
|
||||||
dismissErrorNotifications();
|
dismissErrorNotifications();
|
||||||
break;
|
break;
|
||||||
|
@ -2180,6 +2190,14 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void provisionAccount(final String address, final String password) {
|
||||||
|
final Jid jid = Jid.ofEscaped(address);
|
||||||
|
final Account account = new Account(jid, password);
|
||||||
|
account.setOption(Account.OPTION_DISABLED, true);
|
||||||
|
Log.d(Config.LOGTAG,jid.asBareJid().toEscapedString()+": provisioning account");
|
||||||
|
createAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
|
public void createAccountFromKey(final String alias, final OnAccountCreated callback) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -900,7 +900,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
|
|
||||||
|
|
||||||
private static boolean anyNeedsExternalStoragePermission(final Collection<Attachment> attachments) {
|
private static boolean anyNeedsExternalStoragePermission(final Collection<Attachment> attachments) {
|
||||||
for(final Attachment attachment : attachments) {
|
for (final Attachment attachment : attachments) {
|
||||||
if (attachment.getType() != Attachment.Type.LOCATION) {
|
if (attachment.getType() != Attachment.Type.LOCATION) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1346,7 +1346,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
|
|
||||||
final Contact contact = conversation.getContact();
|
final Contact contact = conversation.getContact();
|
||||||
if (contact.getPresences().anySupport(Namespace.JINGLE_MESSAGE)) {
|
if (contact.getPresences().anySupport(Namespace.JINGLE_MESSAGE)) {
|
||||||
triggerRtpSession(contact.getAccount(),contact.getJid().asBareJid(),action);
|
triggerRtpSession(contact.getAccount(), contact.getJid().asBareJid(), action);
|
||||||
} else {
|
} else {
|
||||||
final RtpCapability.Capability capability;
|
final RtpCapability.Capability capability;
|
||||||
if (action.equals(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL)) {
|
if (action.equals(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL)) {
|
||||||
|
@ -1436,6 +1436,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attachFile(final int attachmentChoice) {
|
public void attachFile(final int attachmentChoice) {
|
||||||
|
attachFile(attachmentChoice, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachFile(final int attachmentChoice, final boolean updateRecentlyUsed) {
|
||||||
if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
|
if (attachmentChoice == ATTACHMENT_CHOICE_RECORD_VOICE) {
|
||||||
if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO)) {
|
if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1449,12 +1453,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
if (updateRecentlyUsed) {
|
||||||
activity.getPreferences().edit()
|
storeRecentlyUsedQuickAction(attachmentChoice);
|
||||||
.putString(RECENTLY_USED_QUICK_ACTION, SendButtonAction.of(attachmentChoice).toString())
|
|
||||||
.apply();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
//just do not save
|
|
||||||
}
|
}
|
||||||
final int encryption = conversation.getNextEncryption();
|
final int encryption = conversation.getNextEncryption();
|
||||||
final int mode = conversation.getMode();
|
final int mode = conversation.getMode();
|
||||||
|
@ -1502,6 +1502,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void storeRecentlyUsedQuickAction(final int attachmentChoice) {
|
||||||
|
try {
|
||||||
|
activity.getPreferences().edit()
|
||||||
|
.putString(RECENTLY_USED_QUICK_ACTION, SendButtonAction.of(attachmentChoice).toString())
|
||||||
|
.apply();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
//just do not save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
if (grantResults.length > 0) {
|
if (grantResults.length > 0) {
|
||||||
|
@ -2134,7 +2144,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
return this.binding != null && scrolledToBottom(this.binding.messagesView);
|
return this.binding != null && scrolledToBottom(this.binding.messagesView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processExtras(Bundle extras) {
|
private void processExtras(final Bundle extras) {
|
||||||
final String downloadUuid = extras.getString(ConversationsActivity.EXTRA_DOWNLOAD_UUID);
|
final String downloadUuid = extras.getString(ConversationsActivity.EXTRA_DOWNLOAD_UUID);
|
||||||
final String text = extras.getString(Intent.EXTRA_TEXT);
|
final String text = extras.getString(Intent.EXTRA_TEXT);
|
||||||
final String nick = extras.getString(ConversationsActivity.EXTRA_NICK);
|
final String nick = extras.getString(ConversationsActivity.EXTRA_NICK);
|
||||||
|
@ -2180,7 +2190,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ConversationsActivity.POST_ACTION_RECORD_VOICE.equals(postInitAction)) {
|
if (ConversationsActivity.POST_ACTION_RECORD_VOICE.equals(postInitAction)) {
|
||||||
attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
|
attachFile(ATTACHMENT_CHOICE_RECORD_VOICE, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Message message = downloadUuid == null ? null : conversation.findMessageWithFileAndUuid(downloadUuid);
|
final Message message = downloadUuid == null ? null : conversation.findMessageWithFileAndUuid(downloadUuid);
|
||||||
|
@ -2189,7 +2199,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Uri> extractUris(Bundle extras) {
|
private List<Uri> extractUris(final Bundle extras) {
|
||||||
final List<Uri> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
|
final List<Uri> uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
|
||||||
if (uris != null) {
|
if (uris != null) {
|
||||||
return uris;
|
return uris;
|
||||||
|
@ -2202,7 +2212,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Uri> cleanUris(List<Uri> uris) {
|
private List<Uri> cleanUris(final List<Uri> uris) {
|
||||||
Iterator<Uri> iterator = uris.iterator();
|
Iterator<Uri> iterator = uris.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
final Uri uri = iterator.next();
|
final Uri uri = iterator.next();
|
||||||
|
@ -2878,13 +2888,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
private void clearPending() {
|
private void clearPending() {
|
||||||
if (postponedActivityResult.clear()) {
|
if (postponedActivityResult.clear()) {
|
||||||
Log.e(Config.LOGTAG, "cleared pending intent with unhandled result left");
|
Log.e(Config.LOGTAG, "cleared pending intent with unhandled result left");
|
||||||
|
if (pendingTakePhotoUri.clear()) {
|
||||||
|
Log.e(Config.LOGTAG, "cleared pending photo uri");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pendingScrollState.clear()) {
|
if (pendingScrollState.clear()) {
|
||||||
Log.e(Config.LOGTAG, "cleared scroll state");
|
Log.e(Config.LOGTAG, "cleared scroll state");
|
||||||
}
|
}
|
||||||
if (pendingTakePhotoUri.clear()) {
|
|
||||||
Log.e(Config.LOGTAG, "cleared pending photo uri");
|
|
||||||
}
|
|
||||||
if (pendingConversationsUuid.clear()) {
|
if (pendingConversationsUuid.clear()) {
|
||||||
Log.e(Config.LOGTAG, "cleared pending conversations uuid");
|
Log.e(Config.LOGTAG, "cleared pending conversations uuid");
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,7 +276,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
private void releaseProximityWakeLock() {
|
private void releaseProximityWakeLock() {
|
||||||
if (this.mProximityWakeLock != null && mProximityWakeLock.isHeld()) {
|
if (this.mProximityWakeLock != null && mProximityWakeLock.isHeld()) {
|
||||||
Log.d(Config.LOGTAG, "releasing proximity wake lock");
|
Log.d(Config.LOGTAG, "releasing proximity wake lock");
|
||||||
this.mProximityWakeLock.release();
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
this.mProximityWakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
|
||||||
|
} else {
|
||||||
|
this.mProximityWakeLock.release();
|
||||||
|
}
|
||||||
this.mProximityWakeLock = null;
|
this.mProximityWakeLock = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -402,10 +406,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get();
|
final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get();
|
||||||
if (jingleRtpConnection != null) {
|
if (jingleRtpConnection != null) {
|
||||||
releaseVideoTracks(jingleRtpConnection);
|
releaseVideoTracks(jingleRtpConnection);
|
||||||
} else if (!isChangingConfigurations()) {
|
|
||||||
if (xmppConnectionService != null) {
|
|
||||||
retractSessionProposal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
releaseProximityWakeLock();
|
releaseProximityWakeLock();
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
@ -424,17 +424,20 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
endCall();
|
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
|
endCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUserLeaveHint() {
|
public void onUserLeaveHint() {
|
||||||
|
super.onUserLeaveHint();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && deviceSupportsPictureInPicture()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && deviceSupportsPictureInPicture()) {
|
||||||
if (shouldBePictureInPicture()) {
|
if (shouldBePictureInPicture()) {
|
||||||
startPictureInPicture();
|
startPictureInPicture();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
retractSessionProposal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
@ -445,7 +448,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
.setAspectRatio(new Rational(10, 16))
|
.setAspectRatio(new Rational(10, 16))
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
} catch (IllegalStateException e) {
|
} catch (final IllegalStateException e) {
|
||||||
//this sometimes happens on Samsung phones (possibly when Knox is enabled)
|
//this sometimes happens on Samsung phones (possibly when Knox is enabled)
|
||||||
Log.w(Config.LOGTAG, "unable to enter picture in picture mode", e);
|
Log.w(Config.LOGTAG, "unable to enter picture in picture mode", e);
|
||||||
}
|
}
|
||||||
|
@ -467,7 +470,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
||||||
RtpEndUserState.CONNECTING,
|
RtpEndUserState.CONNECTING,
|
||||||
RtpEndUserState.CONNECTED
|
RtpEndUserState.CONNECTED
|
||||||
).contains(rtpConnection.getEndUserState());
|
).contains(rtpConnection.getEndUserState());
|
||||||
} catch (IllegalStateException e) {
|
} catch (final IllegalStateException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,16 @@ import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.persistance.DatabaseBackend;
|
import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||||
|
import eu.siacs.conversations.services.QuickConversationsService;
|
||||||
|
import eu.siacs.conversations.utils.ProvisioningUtils;
|
||||||
import eu.siacs.conversations.utils.SignupUtils;
|
import eu.siacs.conversations.utils.SignupUtils;
|
||||||
import eu.siacs.conversations.utils.XmppUri;
|
import eu.siacs.conversations.utils.XmppUri;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
@ -24,29 +28,45 @@ import eu.siacs.conversations.xmpp.Jid;
|
||||||
public class UriHandlerActivity extends AppCompatActivity {
|
public class UriHandlerActivity extends AppCompatActivity {
|
||||||
|
|
||||||
public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
|
public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
|
||||||
|
private static final String EXTRA_ALLOW_PROVISIONING = "extra_allow_provisioning";
|
||||||
private static final int REQUEST_SCAN_QR_CODE = 0x1234;
|
private static final int REQUEST_SCAN_QR_CODE = 0x1234;
|
||||||
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
|
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
|
||||||
private static final Pattern VCARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
|
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION = 0x6790;
|
||||||
|
private static final Pattern V_CARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
|
||||||
private boolean handled = false;
|
private boolean handled = false;
|
||||||
|
|
||||||
public static void scan(Activity activity) {
|
public static void scan(final Activity activity) {
|
||||||
|
scan(activity, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void scan(final Activity activity, final boolean provisioning) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||||
Intent intent = new Intent(activity, UriHandlerActivity.class);
|
final Intent intent = new Intent(activity, UriHandlerActivity.class);
|
||||||
intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
|
intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
|
||||||
|
if (provisioning) {
|
||||||
|
intent.putExtra(EXTRA_ALLOW_PROVISIONING, true);
|
||||||
|
}
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSIONS_TO_SCAN);
|
activity.requestPermissions(
|
||||||
|
new String[]{Manifest.permission.CAMERA},
|
||||||
|
provisioning ? REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION : REQUEST_CAMERA_PERMISSIONS_TO_SCAN
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onRequestPermissionResult(Activity activity, int requestCode, int[] grantResults) {
|
public static void onRequestPermissionResult(Activity activity, int requestCode, int[] grantResults) {
|
||||||
if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN) {
|
if (requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN && requestCode != REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (grantResults.length > 0) {
|
if (grantResults.length > 0) {
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
scan(activity);
|
if (requestCode == REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION) {
|
||||||
|
scan(activity, true);
|
||||||
|
} else {
|
||||||
|
scan(activity);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(activity, R.string.qr_code_scanner_needs_access_to_camera, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.qr_code_scanner_needs_access_to_camera, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
@ -88,19 +108,19 @@ public class UriHandlerActivity extends AppCompatActivity {
|
||||||
final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
|
final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
|
||||||
|
|
||||||
if (SignupUtils.isSupportTokenRegistry() && xmppUri.isValidJid()) {
|
if (SignupUtils.isSupportTokenRegistry() && xmppUri.isValidJid()) {
|
||||||
final String preauth = xmppUri.getParameter("preauth");
|
final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
|
||||||
final Jid jid = xmppUri.getJid();
|
final Jid jid = xmppUri.getJid();
|
||||||
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
|
||||||
if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
|
if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
|
||||||
Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preauth);
|
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter("ibr"))) {
|
if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
|
||||||
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preauth);
|
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
|
||||||
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return;
|
return;
|
||||||
|
@ -194,22 +214,38 @@ public class UriHandlerActivity extends AppCompatActivity {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean allowProvisioning() {
|
||||||
|
final Intent launchIntent = getIntent();
|
||||||
|
return launchIntent != null && launchIntent.getBooleanExtra(EXTRA_ALLOW_PROVISIONING, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
|
||||||
super.onActivityResult(requestCode, requestCode, intent);
|
super.onActivityResult(requestCode, requestCode, intent);
|
||||||
if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
|
if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
|
||||||
String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
|
final String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
|
||||||
if (result != null) {
|
if (Strings.isNullOrEmpty(result)) {
|
||||||
if (result.startsWith("BEGIN:VCARD\n")) {
|
finish();
|
||||||
Matcher matcher = VCARD_XMPP_PATTERN.matcher(result);
|
return;
|
||||||
if (matcher.find()) {
|
|
||||||
result = matcher.group(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Uri uri = Uri.parse(result);
|
|
||||||
handleUri(uri, true);
|
|
||||||
}
|
}
|
||||||
|
if (result.startsWith("BEGIN:VCARD\n")) {
|
||||||
|
final Matcher matcher = V_CARD_XMPP_PATTERN.matcher(result);
|
||||||
|
if (matcher.find()) {
|
||||||
|
handleUri(Uri.parse(matcher.group(2)), true);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
} else if (QuickConversationsService.isConversations() && looksLikeJsonObject(result) && allowProvisioning()) {
|
||||||
|
ProvisioningUtils.provision(this, result);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleUri(Uri.parse(result), true);
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean looksLikeJsonObject(final String input) {
|
||||||
|
return input.charAt(0) == '{' && input.charAt(input.length() - 1) == '}';
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.siacs.conversations.utils;
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -139,4 +140,15 @@ public class Compatibility {
|
||||||
Log.d(Config.LOGTAG, context.getClass().getSimpleName() + " was unable to start service");
|
Log.d(Config.LOGTAG, context.getClass().getSimpleName() + " was unable to start service");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||||
|
public static boolean hasFeatureCamera(final Context context) {
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
||||||
|
} else {
|
||||||
|
return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ public class XmppUri {
|
||||||
public static final String ACTION_MESSAGE = "message";
|
public static final String ACTION_MESSAGE = "message";
|
||||||
public static final String ACTION_REGISTER = "register";
|
public static final String ACTION_REGISTER = "register";
|
||||||
public static final String ACTION_ROSTER = "roster";
|
public static final String ACTION_ROSTER = "roster";
|
||||||
|
public static final String PARAMETER_PRE_AUTH = "preauth";
|
||||||
|
public static final String PARAMETER_IBR = "ibr";
|
||||||
private static final String OMEMO_URI_PARAM = "omemo-sid-";
|
private static final String OMEMO_URI_PARAM = "omemo-sid-";
|
||||||
protected Uri uri;
|
protected Uri uri;
|
||||||
protected String jid;
|
protected String jid;
|
||||||
|
|
|
@ -39,6 +39,7 @@ import eu.siacs.conversations.services.AppRTCAudioManager;
|
||||||
import eu.siacs.conversations.utils.IP;
|
import eu.siacs.conversations.utils.IP;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
||||||
|
@ -47,7 +48,6 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
|
||||||
|
|
||||||
public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback {
|
public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback {
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
|
|
||||||
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
|
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
|
||||||
private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>();
|
private final ArrayDeque<Set<Map.Entry<String, RtpContentMap.DescriptionTransport>>> pendingIceCandidates = new ArrayDeque<>();
|
||||||
private final Message message;
|
private final Message message;
|
||||||
private State state = State.NULL;
|
private State state = State.NULL;
|
||||||
private StateTransitionException stateTransitionException;
|
private StateTransitionException stateTransitionException;
|
||||||
|
@ -237,13 +237,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e);
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final RtpContentMap rtpContentMap = isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap;
|
final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
|
||||||
final Group originalGroup = rtpContentMap != null ? rtpContentMap.group : null;
|
if (this.state == State.SESSION_ACCEPTED) {
|
||||||
final List<String> identificationTags = originalGroup == null ? Collections.emptyList() : originalGroup.getIdentificationTags();
|
processCandidates(candidates);
|
||||||
if (identificationTags.size() == 0) {
|
} else {
|
||||||
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
|
pendingIceCandidates.push(candidates);
|
||||||
}
|
}
|
||||||
receiveCandidates(identificationTags, contentMap.contents.entrySet());
|
|
||||||
} else {
|
} else {
|
||||||
if (isTerminated()) {
|
if (isTerminated()) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
|
@ -255,7 +254,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveCandidates(final List<String> identificationTags, final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
|
private void processCandidates(final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
|
||||||
|
final RtpContentMap rtpContentMap = isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap;
|
||||||
|
final Group originalGroup = rtpContentMap.group;
|
||||||
|
final List<String> identificationTags = originalGroup == null ? rtpContentMap.getNames() : originalGroup.getIdentificationTags();
|
||||||
|
if (identificationTags.size() == 0) {
|
||||||
|
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
|
||||||
|
}
|
||||||
|
processCandidates(identificationTags, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processCandidates(final List<String> indices, final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
|
||||||
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) {
|
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) {
|
||||||
final String ufrag = content.getValue().transport.getAttribute("ufrag");
|
final String ufrag = content.getValue().transport.getAttribute("ufrag");
|
||||||
for (final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) {
|
for (final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) {
|
||||||
|
@ -267,15 +276,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final String sdpMid = content.getKey();
|
final String sdpMid = content.getKey();
|
||||||
final int mLineIndex = identificationTags.indexOf(sdpMid);
|
final int mLineIndex = indices.indexOf(sdpMid);
|
||||||
final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
|
final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
|
||||||
if (isInState(State.SESSION_ACCEPTED)) {
|
Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
|
||||||
Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
|
this.webRTCWrapper.addIceCandidate(iceCandidate);
|
||||||
this.webRTCWrapper.addIceCandidate(iceCandidate);
|
|
||||||
} else {
|
|
||||||
this.pendingIceCandidates.offer(iceCandidate);
|
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": put ICE candidate on backlog");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,8 +322,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
|
if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
final List<String> identificationTags = contentMap.group == null ? Collections.emptyList() : contentMap.group.getIdentificationTags();
|
pendingIceCandidates.push(contentMap.contents.entrySet());
|
||||||
receiveCandidates(identificationTags, contentMap.contents.entrySet());
|
|
||||||
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
|
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
|
||||||
sendSessionAccept();
|
sendSessionAccept();
|
||||||
|
@ -364,8 +367,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
if (transition(State.SESSION_ACCEPTED)) {
|
if (transition(State.SESSION_ACCEPTED)) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
receiveSessionAccept(contentMap);
|
receiveSessionAccept(contentMap);
|
||||||
final List<String> identificationTags = contentMap.group == null ? Collections.emptyList() : contentMap.group.getIdentificationTags();
|
final List<String> identificationTags = contentMap.group == null ? contentMap.getNames() : contentMap.group.getIdentificationTags();
|
||||||
receiveCandidates(identificationTags, contentMap.contents.entrySet());
|
processCandidates(identificationTags, contentMap.contents.entrySet());
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state));
|
Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state));
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
|
@ -451,9 +454,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
|
|
||||||
private void addIceCandidatesFromBlackLog() {
|
private void addIceCandidatesFromBlackLog() {
|
||||||
while (!this.pendingIceCandidates.isEmpty()) {
|
while (!this.pendingIceCandidates.isEmpty()) {
|
||||||
final IceCandidate iceCandidate = this.pendingIceCandidates.poll();
|
processCandidates(this.pendingIceCandidates.poll());
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added ICE candidate from back log " + iceCandidate);
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added candidates from back log");
|
||||||
this.webRTCWrapper.addIceCandidate(iceCandidate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +463,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
this.responderRtpContentMap = rtpContentMap;
|
this.responderRtpContentMap = rtpContentMap;
|
||||||
this.transitionOrThrow(State.SESSION_ACCEPTED);
|
this.transitionOrThrow(State.SESSION_ACCEPTED);
|
||||||
final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
|
final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
|
||||||
Log.d(Config.LOGTAG, sessionAccept.toString());
|
|
||||||
send(sessionAccept);
|
send(sessionAccept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -889,6 +890,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void rejectCall() {
|
public synchronized void rejectCall() {
|
||||||
|
if (isTerminated()) {
|
||||||
|
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": received rejectCall() when session has already been terminated. nothing to do");
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case PROPOSED:
|
case PROPOSED:
|
||||||
rejectCallFromProposed();
|
rejectCallFromProposed();
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.google.common.base.Function;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
@ -13,6 +14,7 @@ import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
|
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -23,7 +25,6 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
|
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
||||||
|
|
||||||
public class RtpContentMap {
|
public class RtpContentMap {
|
||||||
|
@ -59,6 +60,10 @@ public class RtpContentMap {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getNames() {
|
||||||
|
return ImmutableList.copyOf(contents.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
void requireContentDescriptions() {
|
void requireContentDescriptions() {
|
||||||
if (this.contents.size() == 0) {
|
if (this.contents.size() == 0) {
|
||||||
throw new IllegalStateException("No contents available");
|
throw new IllegalStateException("No contents available");
|
||||||
|
|
|
@ -168,8 +168,10 @@ public class SessionDescription {
|
||||||
}
|
}
|
||||||
formatBuilder.add(payloadType.getIntId());
|
formatBuilder.add(payloadType.getIntId());
|
||||||
mediaAttributes.put("rtpmap", payloadType.toSdpAttribute());
|
mediaAttributes.put("rtpmap", payloadType.toSdpAttribute());
|
||||||
List<RtpDescription.Parameter> parameters = payloadType.getParameters();
|
final List<RtpDescription.Parameter> parameters = payloadType.getParameters();
|
||||||
if (parameters.size() > 0) {
|
if (parameters.size() == 1) {
|
||||||
|
mediaAttributes.put("fmtp", RtpDescription.Parameter.toSdpString(id, parameters.get(0)));
|
||||||
|
} else if (parameters.size() > 0) {
|
||||||
mediaAttributes.put("fmtp", RtpDescription.Parameter.toSdpString(id, parameters));
|
mediaAttributes.put("fmtp", RtpDescription.Parameter.toSdpString(id, parameters));
|
||||||
}
|
}
|
||||||
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : payloadType.getFeedbackNegotiations()) {
|
for (RtpDescription.FeedbackNegotiation feedbackNegotiation : payloadType.getFeedbackNegotiations()) {
|
||||||
|
|
|
@ -214,9 +214,13 @@ public class WebRTCWrapper {
|
||||||
PeerConnectionFactory.InitializationOptions.builder(service).createInitializationOptions()
|
PeerConnectionFactory.InitializationOptions.builder(service).createInitializationOptions()
|
||||||
);
|
);
|
||||||
} catch (final UnsatisfiedLinkError e) {
|
} catch (final UnsatisfiedLinkError e) {
|
||||||
throw new InitializationException(e);
|
throw new InitializationException("Unable to initialize PeerConnectionFactory", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.eglBase = EglBase.create();
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
throw new InitializationException("Unable to create EGL base", e);
|
||||||
}
|
}
|
||||||
this.eglBase = EglBase.create();
|
|
||||||
this.context = service;
|
this.context = service;
|
||||||
this.toneManager = service.getJingleConnectionManager().toneManager;
|
this.toneManager = service.getJingleConnectionManager().toneManager;
|
||||||
mainHandler.post(() -> {
|
mainHandler.post(() -> {
|
||||||
|
@ -589,8 +593,8 @@ public class WebRTCWrapper {
|
||||||
|
|
||||||
static class InitializationException extends Exception {
|
static class InitializationException extends Exception {
|
||||||
|
|
||||||
private InitializationException(final Throwable throwable) {
|
private InitializationException(final String message, final Throwable throwable) {
|
||||||
super(throwable);
|
super(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InitializationException(final String message) {
|
private InitializationException(final String message) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.siacs.conversations.xmpp.jingle.stanzas;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
@ -369,7 +370,7 @@ public class RtpDescription extends GenericDescription {
|
||||||
final StringBuilder stringBuilder = new StringBuilder();
|
final StringBuilder stringBuilder = new StringBuilder();
|
||||||
stringBuilder.append(id).append(' ');
|
stringBuilder.append(id).append(' ');
|
||||||
for (int i = 0; i < parameters.size(); ++i) {
|
for (int i = 0; i < parameters.size(); ++i) {
|
||||||
Parameter p = parameters.get(i);
|
final Parameter p = parameters.get(i);
|
||||||
final String name = p.getParameterName();
|
final String name = p.getParameterName();
|
||||||
Preconditions.checkArgument(name != null, String.format("parameter for %s must have a name", id));
|
Preconditions.checkArgument(name != null, String.format("parameter for %s must have a name", id));
|
||||||
SessionDescription.checkNoWhitespace(name, String.format("parameter names for %s must not contain whitespaces", id));
|
SessionDescription.checkNoWhitespace(name, String.format("parameter names for %s must not contain whitespaces", id));
|
||||||
|
@ -386,11 +387,23 @@ public class RtpDescription extends GenericDescription {
|
||||||
return stringBuilder.toString();
|
return stringBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pair<String, List<Parameter>> ofSdpString(final String sdp) {
|
public static String toSdpString(final String id, final Parameter parameter) {
|
||||||
|
final String name = parameter.getParameterName();
|
||||||
|
final String value = parameter.getParameterValue();
|
||||||
|
Preconditions.checkArgument(value != null, String.format("parameter for %s must have a value", id));
|
||||||
|
SessionDescription.checkNoWhitespace(value, String.format("parameter values for %s must not contain whitespaces", id));
|
||||||
|
if (Strings.isNullOrEmpty(name)) {
|
||||||
|
return String.format("%s %s", id, value);
|
||||||
|
} else {
|
||||||
|
return String.format("%s %s=%s", id, name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Pair<String, List<Parameter>> ofSdpString(final String sdp) {
|
||||||
final String[] pair = sdp.split(" ");
|
final String[] pair = sdp.split(" ");
|
||||||
if (pair.length == 2) {
|
if (pair.length == 2) {
|
||||||
final String id = pair[0];
|
final String id = pair[0];
|
||||||
ImmutableList.Builder<Parameter> builder = new ImmutableList.Builder<>();
|
final ImmutableList.Builder<Parameter> builder = new ImmutableList.Builder<>();
|
||||||
for (final String parameter : pair[1].split(";")) {
|
for (final String parameter : pair[1].split(";")) {
|
||||||
final String[] parts = parameter.split("=", 2);
|
final String[] parts = parameter.split("=", 2);
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
<string name="crash_report_message">Надсиланням звіту про відмову ви допомагаєте удосконаленню месенджера.\n<b>Увага:</b> Звіти надсилатимуться розробниками з вашого облікового запису XMPP.</string>
|
<string name="crash_report_message">Надсиланням звіту про відмову ви допомагаєте удосконаленню месенджера.\n<b>Увага:</b> Звіти надсилатимуться розробниками з вашого облікового запису XMPP.</string>
|
||||||
<string name="send_now">Надіслати зараз</string>
|
<string name="send_now">Надіслати зараз</string>
|
||||||
<string name="send_never">Ніколи не питати знову</string>
|
<string name="send_never">Ніколи не питати знову</string>
|
||||||
<string name="problem_connecting_to_account">Не можу з\'єднатися з обліковим записом</string>
|
<string name="problem_connecting_to_account">Неможливо з\'єднатися з обліковим записом</string>
|
||||||
<string name="problem_connecting_to_accounts">Не можу увімкнути режим багатьох облікових записів</string>
|
<string name="problem_connecting_to_accounts">Не можу увімкнути режим багатьох облікових записів</string>
|
||||||
<string name="touch_to_fix">Перейти для керування обліковими записами</string>
|
<string name="touch_to_fix">Перейти для керування обліковими записами</string>
|
||||||
<string name="attach_file">Долучити файл</string>
|
<string name="attach_file">Долучити файл</string>
|
||||||
|
@ -149,14 +149,14 @@
|
||||||
<string name="account_status_disabled">Тимчасово вимкнено</string>
|
<string name="account_status_disabled">Тимчасово вимкнено</string>
|
||||||
<string name="account_status_online">У мережі</string>
|
<string name="account_status_online">У мережі</string>
|
||||||
<string name="account_status_connecting">З\'єднання\u2026</string>
|
<string name="account_status_connecting">З\'єднання\u2026</string>
|
||||||
<string name="account_status_offline">Не в мережі</string>
|
<string name="account_status_offline">Поза мережею</string>
|
||||||
<string name="account_status_unauthorized">Не авторизовано</string>
|
<string name="account_status_unauthorized">Не авторизовано</string>
|
||||||
<string name="account_status_not_found">Сервер не знайдено</string>
|
<string name="account_status_not_found">Сервер не знайдено</string>
|
||||||
<string name="account_status_no_internet">Немає зв\'язку із мережею</string>
|
<string name="account_status_no_internet">Немає зв\'язку із мережею</string>
|
||||||
<string name="account_status_regis_fail">Не вдалося зареєструватися</string>
|
<string name="account_status_regis_fail">Не вдалося зареєструватися</string>
|
||||||
<string name="account_status_regis_conflict">Ім\'я користувача вже використовується</string>
|
<string name="account_status_regis_conflict">Ім\'я користувача вже використовується</string>
|
||||||
<string name="account_status_regis_success">Реєстрацію виконано</string>
|
<string name="account_status_regis_success">Реєстрацію виконано</string>
|
||||||
<string name="account_status_regis_not_sup">Сервер не підтримує режстрацію</string>
|
<string name="account_status_regis_not_sup">Сервер не підтримує реєстрацію</string>
|
||||||
<string name="account_status_regis_invalid_token">Неправильний реєстраційний токен</string>
|
<string name="account_status_regis_invalid_token">Неправильний реєстраційний токен</string>
|
||||||
<string name="account_status_tls_error">Узгодження TLS не відбулося</string>
|
<string name="account_status_tls_error">Узгодження TLS не відбулося</string>
|
||||||
<string name="account_status_policy_violation">Порушення політики</string>
|
<string name="account_status_policy_violation">Порушення політики</string>
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
<string name="block_jabber_id">Заблокувати адресу XMPP</string>
|
<string name="block_jabber_id">Заблокувати адресу XMPP</string>
|
||||||
<string name="account_settings_example_jabber_id">username@example.com</string>
|
<string name="account_settings_example_jabber_id">username@example.com</string>
|
||||||
<string name="password">Пароль</string>
|
<string name="password">Пароль</string>
|
||||||
<string name="invalid_jid">Це неправильна адреса XMPP</string>
|
<string name="invalid_jid">Це недійсна адреса XMPP</string>
|
||||||
<string name="error_out_of_memory">Пам\'ять вичерпано. Завелике зображення.</string>
|
<string name="error_out_of_memory">Пам\'ять вичерпано. Завелике зображення.</string>
|
||||||
<string name="add_phone_book_text">Бажаєте додати %s до своєї книги контактів?</string>
|
<string name="add_phone_book_text">Бажаєте додати %s до своєї книги контактів?</string>
|
||||||
<string name="server_info_show_more">Інформація про сервер</string>
|
<string name="server_info_show_more">Інформація про сервер</string>
|
||||||
|
@ -196,10 +196,10 @@
|
||||||
<string name="server_info_pep">XEP-0163: Персональне (піктограми користувачів, OMEMO)</string>
|
<string name="server_info_pep">XEP-0163: Персональне (піктограми користувачів, OMEMO)</string>
|
||||||
<string name="server_info_http_upload">XEP-0363: Обмін файлами через HTTP</string>
|
<string name="server_info_http_upload">XEP-0363: Обмін файлами через HTTP</string>
|
||||||
<string name="server_info_push">XEP-0357: Push-повідомлення</string>
|
<string name="server_info_push">XEP-0357: Push-повідомлення</string>
|
||||||
<string name="server_info_available">є</string>
|
<string name="server_info_available">так</string>
|
||||||
<string name="server_info_unavailable">нема</string>
|
<string name="server_info_unavailable">ні</string>
|
||||||
<string name="missing_public_keys">Не вистачає повідомлення публічного ключа.</string>
|
<string name="missing_public_keys">Бракує інформації про публічний ключ</string>
|
||||||
<string name="last_seen_now">у мережі</string>
|
<string name="last_seen_now">зараз у мережі</string>
|
||||||
<string name="last_seen_min">у мережі 1 хвилину тому</string>
|
<string name="last_seen_min">у мережі 1 хвилину тому</string>
|
||||||
<string name="last_seen_mins">у мережі %d хвилин тому</string>
|
<string name="last_seen_mins">у мережі %d хвилин тому</string>
|
||||||
<string name="last_seen_hour">у мережі 1 годину тому</string>
|
<string name="last_seen_hour">у мережі 1 годину тому</string>
|
||||||
|
@ -368,7 +368,7 @@
|
||||||
<string name="disable_all_accounts">Вимкнути всі облікові записи</string>
|
<string name="disable_all_accounts">Вимкнути всі облікові записи</string>
|
||||||
<string name="perform_action_with">Здійснити дію з</string>
|
<string name="perform_action_with">Здійснити дію з</string>
|
||||||
<string name="no_affiliation">Непов\'язаний</string>
|
<string name="no_affiliation">Непов\'язаний</string>
|
||||||
<string name="no_role">Не в мережі</string>
|
<string name="no_role">Поза мережею</string>
|
||||||
<string name="outcast">Вигнанець</string>
|
<string name="outcast">Вигнанець</string>
|
||||||
<string name="member">Учасник</string>
|
<string name="member">Учасник</string>
|
||||||
<string name="advanced_mode">Розширений режим</string>
|
<string name="advanced_mode">Розширений режим</string>
|
||||||
|
@ -669,7 +669,7 @@
|
||||||
<string name="received_message_from_stranger">Отримано повідомлення від незнайомця</string>
|
<string name="received_message_from_stranger">Отримано повідомлення від незнайомця</string>
|
||||||
<string name="block_stranger">Заблокувати невідомий контакт</string>
|
<string name="block_stranger">Заблокувати невідомий контакт</string>
|
||||||
<string name="block_entire_domain">Заблокувати весь домен</string>
|
<string name="block_entire_domain">Заблокувати весь домен</string>
|
||||||
<string name="online_right_now">у мережі просто зараз</string>
|
<string name="online_right_now">зараз у мережі</string>
|
||||||
<string name="retry_decryption">Спробувати знову розшифрувати</string>
|
<string name="retry_decryption">Спробувати знову розшифрувати</string>
|
||||||
<string name="session_failure">Помилка сесії</string>
|
<string name="session_failure">Помилка сесії</string>
|
||||||
<string name="sasl_downgrade">Застарілий механізм SASL</string>
|
<string name="sasl_downgrade">Застарілий механізм SASL</string>
|
||||||
|
@ -838,7 +838,7 @@
|
||||||
<string name="ebook">Електронна книга</string>
|
<string name="ebook">Електронна книга</string>
|
||||||
<string name="video_original">Оригінал (не стиснений)</string>
|
<string name="video_original">Оригінал (не стиснений)</string>
|
||||||
<string name="open_with">Відкрити</string>
|
<string name="open_with">Відкрити</string>
|
||||||
<string name="set_profile_picture">Фото профілю чату</string>
|
<string name="set_profile_picture">Світлана профілю</string>
|
||||||
<string name="choose_account">Виберіть обліковий запис</string>
|
<string name="choose_account">Виберіть обліковий запис</string>
|
||||||
<string name="restore_backup">Відновити з резервної копії</string>
|
<string name="restore_backup">Відновити з резервної копії</string>
|
||||||
<string name="restore">Відновити</string>
|
<string name="restore">Відновити</string>
|
||||||
|
@ -902,7 +902,7 @@
|
||||||
<string name="pref_channel_discovery_summary">Більшість користувачів вибирають \'jabber.network\' як одну з кращих пропозицій зі всіх публічних середовищ XMPP.</string>
|
<string name="pref_channel_discovery_summary">Більшість користувачів вибирають \'jabber.network\' як одну з кращих пропозицій зі всіх публічних середовищ XMPP.</string>
|
||||||
<string name="pref_channel_discovery">Спосіб пошуку каналів</string>
|
<string name="pref_channel_discovery">Спосіб пошуку каналів</string>
|
||||||
<string name="backup">Резервне копіювання</string>
|
<string name="backup">Резервне копіювання</string>
|
||||||
<string name="category_about">Про</string>
|
<string name="category_about">Про застосунок</string>
|
||||||
<string name="please_enable_an_account">Будь ласка, активуйте обліковий запис</string>
|
<string name="please_enable_an_account">Будь ласка, активуйте обліковий запис</string>
|
||||||
<string name="make_call">Здійснити виклик</string>
|
<string name="make_call">Здійснити виклик</string>
|
||||||
<string name="rtp_state_incoming_call">Вхідний виклик</string>
|
<string name="rtp_state_incoming_call">Вхідний виклик</string>
|
||||||
|
@ -917,6 +917,7 @@
|
||||||
<string name="rtp_state_ringing">Викликаю</string>
|
<string name="rtp_state_ringing">Викликаю</string>
|
||||||
<string name="rtp_state_declined_or_busy">Зайнято</string>
|
<string name="rtp_state_declined_or_busy">Зайнято</string>
|
||||||
<string name="rtp_state_connectivity_error">Неможливо здійснити виклик</string>
|
<string name="rtp_state_connectivity_error">Неможливо здійснити виклик</string>
|
||||||
|
<string name="rtp_state_connectivity_lost_error">З\'єднання втрачено</string>
|
||||||
<string name="rtp_state_retracted">Відхилені виклики</string>
|
<string name="rtp_state_retracted">Відхилені виклики</string>
|
||||||
<string name="rtp_state_application_failure">Помилка застосунку</string>
|
<string name="rtp_state_application_failure">Помилка застосунку</string>
|
||||||
<string name="hang_up">Завершити</string>
|
<string name="hang_up">Завершити</string>
|
||||||
|
@ -930,6 +931,7 @@
|
||||||
<string name="missed_call">Пропущені виклики</string>
|
<string name="missed_call">Пропущені виклики</string>
|
||||||
<string name="audio_call">Голосовий виклик</string>
|
<string name="audio_call">Голосовий виклик</string>
|
||||||
<string name="video_call">Відеовиклик</string>
|
<string name="video_call">Відеовиклик</string>
|
||||||
|
<string name="help">Допомога</string>
|
||||||
<string name="microphone_unavailable">Недоступний мікрофон</string>
|
<string name="microphone_unavailable">Недоступний мікрофон</string>
|
||||||
<string name="only_one_call_at_a_time">Водночас можливо здійснювати лише один виклик.</string>
|
<string name="only_one_call_at_a_time">Водночас можливо здійснювати лише один виклик.</string>
|
||||||
<string name="return_to_ongoing_call">Назад до активного виклику</string>
|
<string name="return_to_ongoing_call">Назад до активного виклику</string>
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="action_settings">设置</string>
|
<string name="action_settings">设置</string>
|
||||||
<string name="action_add">新聊天</string>
|
<string name="action_add">新对话</string>
|
||||||
<string name="action_accounts">管理账户</string>
|
<string name="action_accounts">管理账户</string>
|
||||||
<string name="action_account">管理账户</string>
|
<string name="action_account">管理账户</string>
|
||||||
<string name="action_end_conversation">关闭聊天</string>
|
<string name="action_end_conversation">关闭对话</string>
|
||||||
<string name="action_contact_details">联系人详情</string>
|
<string name="action_contact_details">联系人详情</string>
|
||||||
<string name="action_muc_details">群聊详情</string>
|
<string name="action_muc_details">群聊详情</string>
|
||||||
<string name="channel_details">频道详情</string>
|
<string name="channel_details">频道详情</string>
|
||||||
<string name="action_secure">加密聊天</string>
|
<string name="action_secure">加密对话</string>
|
||||||
<string name="action_add_account">添加账户</string>
|
<string name="action_add_account">添加账户</string>
|
||||||
<string name="action_edit_contact">编辑名称</string>
|
<string name="action_edit_contact">编辑名称</string>
|
||||||
<string name="action_add_phone_book">添加到通讯录</string>
|
<string name="action_add_phone_book">添加到通讯录</string>
|
||||||
<string name="action_delete_contact">从畅聊通讯录中删除</string>
|
<string name="action_delete_contact">从通讯录中删除</string>
|
||||||
<string name="action_block_contact">封禁联系人</string>
|
<string name="action_block_contact">封禁联系人</string>
|
||||||
<string name="action_unblock_contact">解封联系人</string>
|
<string name="action_unblock_contact">解封联系人</string>
|
||||||
<string name="action_block_domain">封禁域名</string>
|
<string name="action_block_domain">封禁域名</string>
|
||||||
|
@ -21,24 +21,24 @@
|
||||||
<string name="action_unblock_participant">解封成员</string>
|
<string name="action_unblock_participant">解封成员</string>
|
||||||
<string name="title_activity_manage_accounts">管理账户</string>
|
<string name="title_activity_manage_accounts">管理账户</string>
|
||||||
<string name="title_activity_settings">设置</string>
|
<string name="title_activity_settings">设置</string>
|
||||||
<string name="title_activity_sharewith">通过畅聊分享</string>
|
<string name="title_activity_sharewith">通过Conversations分享</string>
|
||||||
<string name="title_activity_start_conversation">开始聊天</string>
|
<string name="title_activity_start_conversation">开始对话</string>
|
||||||
<string name="title_activity_choose_contact">选择联系人</string>
|
<string name="title_activity_choose_contact">选择联系人</string>
|
||||||
<string name="title_activity_choose_contacts">选择联系人</string>
|
<string name="title_activity_choose_contacts">选择联系人</string>
|
||||||
<string name="title_activity_share_via_account">通过帐户分享</string>
|
<string name="title_activity_share_via_account">通过帐户分享</string>
|
||||||
<string name="title_activity_block_list">封禁列表</string>
|
<string name="title_activity_block_list">封禁列表</string>
|
||||||
<string name="just_now">刚刚</string>
|
<string name="just_now">刚刚</string>
|
||||||
<string name="minute_ago">一分钟前</string>
|
<string name="minute_ago">1分钟前</string>
|
||||||
<string name="minutes_ago">%d分钟前</string>
|
<string name="minutes_ago">%d分钟前</string>
|
||||||
<string name="x_unread_conversations">%d条未读消息</string>
|
<string name="x_unread_conversations">%d条未读消息</string>
|
||||||
<string name="sending">发送中…</string>
|
<string name="sending">发送中…</string>
|
||||||
<string name="message_decrypting">解密中。请稍候…</string>
|
<string name="message_decrypting">正在解密信息。请稍候……</string>
|
||||||
<string name="pgp_message">OpenPGP加密的信息</string>
|
<string name="pgp_message">OpenPGP加密的信息</string>
|
||||||
<string name="nick_in_use">用户名已存在</string>
|
<string name="nick_in_use">用户名已存在</string>
|
||||||
<string name="invalid_muc_nick">无效用户名</string>
|
<string name="invalid_muc_nick">无效用户名</string>
|
||||||
<string name="admin">管理员</string>
|
<string name="admin">管理员</string>
|
||||||
<string name="owner">所有者</string>
|
<string name="owner">所有者</string>
|
||||||
<string name="moderator">版主</string>
|
<string name="moderator">群主</string>
|
||||||
<string name="participant">成员</string>
|
<string name="participant">成员</string>
|
||||||
<string name="visitor">访客</string>
|
<string name="visitor">访客</string>
|
||||||
<string name="remove_contact_text">将%s从XMPP联系人中移除?与该联系人的会话消息不会清除。</string>
|
<string name="remove_contact_text">将%s从XMPP联系人中移除?与该联系人的会话消息不会清除。</string>
|
||||||
|
@ -66,8 +66,8 @@
|
||||||
<string name="unblock">解封</string>
|
<string name="unblock">解封</string>
|
||||||
<string name="save">保存</string>
|
<string name="save">保存</string>
|
||||||
<string name="ok">完成</string>
|
<string name="ok">完成</string>
|
||||||
<string name="crash_report_title">畅聊已崩溃</string>
|
<string name="crash_report_title">Conversations已崩溃</string>
|
||||||
<string name="crash_report_message">通过您的账户发送堆栈跟踪,可以帮助畅聊持续发展。</string>
|
<string name="crash_report_message">使用您的XMPP帐户发送堆栈跟踪信息有助于Conversations的持续发展。</string>
|
||||||
<string name="send_now">立即发送</string>
|
<string name="send_now">立即发送</string>
|
||||||
<string name="send_never">不再询问</string>
|
<string name="send_never">不再询问</string>
|
||||||
<string name="problem_connecting_to_account">账户无法连接</string>
|
<string name="problem_connecting_to_account">账户无法连接</string>
|
||||||
|
@ -97,16 +97,16 @@
|
||||||
<string name="send_unencrypted">不加密发送</string>
|
<string name="send_unencrypted">不加密发送</string>
|
||||||
<string name="decryption_failed">解密失败,可能是私钥不正确。</string>
|
<string name="decryption_failed">解密失败,可能是私钥不正确。</string>
|
||||||
<string name="openkeychain_required">OpenKeychain</string>
|
<string name="openkeychain_required">OpenKeychain</string>
|
||||||
<string name="openkeychain_required_long">畅聊使用了第三方程序<b>OpenKeychain</b>来加密、解密信息并管理您的密钥。\n\nOpenKeychain遵循GPLv3并且可以在F-Droid 和Google Play上获取。\n\n<small>(之后请重启畅聊)</small></string>
|
<string name="openkeychain_required_long">Conversations使用<b>OpenKeychain</b>加密和解密消息以及管理您的公钥。\n\nOpenKeychain遵循GPLv3许可并且可在F-Droid和Google Play上获取。\n\n<small>(请稍后重启Conversations。)</small></string>
|
||||||
<string name="restart">重启</string>
|
<string name="restart">重启</string>
|
||||||
<string name="install">安装</string>
|
<string name="install">安装</string>
|
||||||
<string name="openkeychain_not_installed">请安装OpenKeychain以解密</string>
|
<string name="openkeychain_not_installed">请安装OpenKeychain以解密</string>
|
||||||
<string name="offering">提供…</string>
|
<string name="offering">提供…</string>
|
||||||
<string name="waiting">等待…</string>
|
<string name="waiting">等待…</string>
|
||||||
<string name="no_pgp_key">无OpenPGP密钥</string>
|
<string name="no_pgp_key">无OpenPGP密钥</string>
|
||||||
<string name="contact_has_no_pgp_key">因您的联系人未公布公钥,畅聊未能成功加密您的信息。\n\n<small>请通知对方设置OpenPGP。</small></string>
|
<string name="contact_has_no_pgp_key">因您的联系人未公布其公钥,无法加密您的信息。\n\n<small>请通知您的联系人设置OpenPGP。</small></string>
|
||||||
<string name="no_pgp_keys">无OpenPGP密钥</string>
|
<string name="no_pgp_keys">无OpenPGP密钥</string>
|
||||||
<string name="contacts_have_no_pgp_keys">因您的联系人未公布公钥,畅聊未能成功加密您的信息。\n\n<small>请通知对方设置OpenPGP.</small></string>
|
<string name="contacts_have_no_pgp_keys">因您的联系人未公布其公钥,无法加密您的信息。\n\n<small>请通知您的联系人设置OpenPGP。</small></string>
|
||||||
<string name="pref_general">常规</string>
|
<string name="pref_general">常规</string>
|
||||||
<string name="pref_accept_files">接收文件</string>
|
<string name="pref_accept_files">接收文件</string>
|
||||||
<string name="pref_accept_files_summary">自动接收小于此大小的文件</string>
|
<string name="pref_accept_files_summary">自动接收小于此大小的文件</string>
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
<string name="pref_notification_grace_period_summary">在其他设备上检测到活动之后,通知在此时间段内将被静音。</string>
|
<string name="pref_notification_grace_period_summary">在其他设备上检测到活动之后,通知在此时间段内将被静音。</string>
|
||||||
<string name="pref_advanced_options">高级</string>
|
<string name="pref_advanced_options">高级</string>
|
||||||
<string name="pref_never_send_crash">从不发送崩溃报告</string>
|
<string name="pref_never_send_crash">从不发送崩溃报告</string>
|
||||||
<string name="pref_never_send_crash_summary">通过发送堆栈跟踪,您可以帮助畅聊持续发展</string>
|
<string name="pref_never_send_crash_summary">通过发送堆栈跟踪,您可以帮助Conversations持续发展</string>
|
||||||
<string name="pref_confirm_messages">确认消息</string>
|
<string name="pref_confirm_messages">确认消息</string>
|
||||||
<string name="pref_confirm_messages_summary">让对方知道你收到并阅读了他们的消息</string>
|
<string name="pref_confirm_messages_summary">让对方知道你收到并阅读了他们的消息</string>
|
||||||
<string name="pref_ui_options">用户界面</string>
|
<string name="pref_ui_options">用户界面</string>
|
||||||
|
@ -408,15 +408,15 @@
|
||||||
<string name="video">视频</string>
|
<string name="video">视频</string>
|
||||||
<string name="image">图片</string>
|
<string name="image">图片</string>
|
||||||
<string name="pdf_document">PDF文档</string>
|
<string name="pdf_document">PDF文档</string>
|
||||||
<string name="apk">Android程序</string>
|
<string name="apk">Android App</string>
|
||||||
<string name="vcard">联系人</string>
|
<string name="vcard">联系人</string>
|
||||||
<string name="avatar_has_been_published">头像已经发布!</string>
|
<string name="avatar_has_been_published">头像已经发布!</string>
|
||||||
<string name="sending_x_file">正在发送%s</string>
|
<string name="sending_x_file">正在发送%s</string>
|
||||||
<string name="offering_x_file">正在提供%s</string>
|
<string name="offering_x_file">正在提供%s</string>
|
||||||
<string name="hide_offline">隐藏离线联系人</string>
|
<string name="hide_offline">隐藏离线联系人</string>
|
||||||
<string name="contact_is_typing">%s正在输入</string>
|
<string name="contact_is_typing">%s正在输入……</string>
|
||||||
<string name="contact_has_stopped_typing">%s已停止输入</string>
|
<string name="contact_has_stopped_typing">%s已停止输入</string>
|
||||||
<string name="contacts_are_typing">%s正在输入</string>
|
<string name="contacts_are_typing">%s正在输入……</string>
|
||||||
<string name="contacts_have_stopped_typing">%s已停止输入</string>
|
<string name="contacts_have_stopped_typing">%s已停止输入</string>
|
||||||
<string name="pref_chat_states">输入通知</string>
|
<string name="pref_chat_states">输入通知</string>
|
||||||
<string name="pref_chat_states_summary">让对方知道你正在输入</string>
|
<string name="pref_chat_states_summary">让对方知道你正在输入</string>
|
||||||
|
@ -499,8 +499,8 @@
|
||||||
<string name="shared_image_with_x">图片已分享给%s</string>
|
<string name="shared_image_with_x">图片已分享给%s</string>
|
||||||
<string name="shared_images_with_x">图片已分享给%s</string>
|
<string name="shared_images_with_x">图片已分享给%s</string>
|
||||||
<string name="shared_text_with_x">文本已分享给%s</string>
|
<string name="shared_text_with_x">文本已分享给%s</string>
|
||||||
<string name="no_storage_permission">允许畅聊访问外部储存</string>
|
<string name="no_storage_permission">允许Conversations访问外部存储</string>
|
||||||
<string name="no_camera_permission">允许畅聊使用摄像头</string>
|
<string name="no_camera_permission">允许Conversations使用摄像头</string>
|
||||||
<string name="sync_with_contacts">同步联系人</string>
|
<string name="sync_with_contacts">同步联系人</string>
|
||||||
<string name="sync_with_contacts_long">将服务器端联系人与本地联系人匹配可以显示联系人的全名与头像。\n\n此应用只在本地读取并匹配联系人。\n\n现在应用将请求联系人权限。</string>
|
<string name="sync_with_contacts_long">将服务器端联系人与本地联系人匹配可以显示联系人的全名与头像。\n\n此应用只在本地读取并匹配联系人。\n\n现在应用将请求联系人权限。</string>
|
||||||
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy可以匹配您的通讯录以确定哪些人已经在使用此应用。<br><br>我们并不储存这些号码。\n\n更多信息请阅读<a href="https://quicksy.im/#privacy">隐私政策</a>。接下来将请求通讯录权限。]]></string>
|
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy可以匹配您的通讯录以确定哪些人已经在使用此应用。<br><br>我们并不储存这些号码。\n\n更多信息请阅读<a href="https://quicksy.im/#privacy">隐私政策</a>。接下来将请求通讯录权限。]]></string>
|
||||||
|
@ -513,8 +513,8 @@
|
||||||
<string name="always">总是</string>
|
<string name="always">总是</string>
|
||||||
<string name="large_images_only">仅大图片</string>
|
<string name="large_images_only">仅大图片</string>
|
||||||
<string name="battery_optimizations_enabled">已启用节电模式</string>
|
<string name="battery_optimizations_enabled">已启用节电模式</string>
|
||||||
<string name="battery_optimizations_enabled_explained">你的设备正在为畅聊进行电池优化,这可能导致通知的延迟甚至消息的丢失。\n建议禁用电池优化。</string>
|
<string name="battery_optimizations_enabled_explained">你的设备正在对Conversations进行电池优化,这可能会导致通知延迟甚至消息丢失。\n建议禁用电池优化。</string>
|
||||||
<string name="battery_optimizations_enabled_dialog">你的设备正在为畅聊进行电池优化,这可能导致通知的延迟甚至消息的丢失。\n你将会被提示禁用该功能。</string>
|
<string name="battery_optimizations_enabled_dialog">你的设备正在对Conversations进行电池优化,这可能会导致通知延迟甚至消息丢失。\n你将会被提示禁用该功能。</string>
|
||||||
<string name="disable">禁用</string>
|
<string name="disable">禁用</string>
|
||||||
<string name="selection_too_large">选择区域过大</string>
|
<string name="selection_too_large">选择区域过大</string>
|
||||||
<string name="no_accounts">(没有启用的账户)</string>
|
<string name="no_accounts">(没有启用的账户)</string>
|
||||||
|
@ -553,7 +553,7 @@
|
||||||
<string name="gp_medium">中</string>
|
<string name="gp_medium">中</string>
|
||||||
<string name="gp_long">长</string>
|
<string name="gp_long">长</string>
|
||||||
<string name="pref_broadcast_last_activity">广播使用应用的时间</string>
|
<string name="pref_broadcast_last_activity">广播使用应用的时间</string>
|
||||||
<string name="pref_broadcast_last_activity_summary">让联系人知道你使用畅聊的时间</string>
|
<string name="pref_broadcast_last_activity_summary">让你的联系人知道你使用Conversations的时间</string>
|
||||||
<string name="pref_privacy">隐私</string>
|
<string name="pref_privacy">隐私</string>
|
||||||
<string name="pref_theme_options">主题</string>
|
<string name="pref_theme_options">主题</string>
|
||||||
<string name="pref_theme_options_summary">选择主题色彩</string>
|
<string name="pref_theme_options_summary">选择主题色彩</string>
|
||||||
|
@ -604,7 +604,7 @@
|
||||||
<string name="blindly_trusted_omemo_keys">盲目信任OMEMO密钥,可能会有人冒充对方发送消息</string>
|
<string name="blindly_trusted_omemo_keys">盲目信任OMEMO密钥,可能会有人冒充对方发送消息</string>
|
||||||
<string name="not_trusted">不信任的</string>
|
<string name="not_trusted">不信任的</string>
|
||||||
<string name="invalid_barcode">无效二维码</string>
|
<string name="invalid_barcode">无效二维码</string>
|
||||||
<string name="pref_clean_cache_summary">清除相机缓存</string>
|
<string name="pref_clean_cache_summary">清理缓存文件夹(由相机应用使用)</string>
|
||||||
<string name="pref_clean_cache">清除缓存</string>
|
<string name="pref_clean_cache">清除缓存</string>
|
||||||
<string name="pref_clean_private_storage">清除私密存储</string>
|
<string name="pref_clean_private_storage">清除私密存储</string>
|
||||||
<string name="pref_clean_private_storage_summary">清除保存私密文件的存储 (可以从服务器上重新下载)</string>
|
<string name="pref_clean_private_storage_summary">清除保存私密文件的存储 (可以从服务器上重新下载)</string>
|
||||||
|
@ -666,7 +666,7 @@
|
||||||
<string name="message">消息</string>
|
<string name="message">消息</string>
|
||||||
<string name="private_messages_are_disabled">禁止私信</string>
|
<string name="private_messages_are_disabled">禁止私信</string>
|
||||||
<string name="huawei_protected_apps">受保护的应用</string>
|
<string name="huawei_protected_apps">受保护的应用</string>
|
||||||
<string name="huawei_protected_apps_summary">为了在屏幕关闭时也可收到消息提醒,您需要将畅聊加入受保护的应用列表。</string>
|
<string name="huawei_protected_apps_summary">为了在屏幕关闭时也可收到消息提醒,您需要将Conversations加入受保护的应用列表。</string>
|
||||||
<string name="mtm_accept_cert">接受未知的证书?</string>
|
<string name="mtm_accept_cert">接受未知的证书?</string>
|
||||||
<string name="mtm_trust_anchor">服务器证书未由已知证书机构签发。</string>
|
<string name="mtm_trust_anchor">服务器证书未由已知证书机构签发。</string>
|
||||||
<string name="mtm_accept_servername">接受不匹配的服务器名称?</string>
|
<string name="mtm_accept_servername">接受不匹配的服务器名称?</string>
|
||||||
|
@ -680,7 +680,7 @@
|
||||||
<string name="edit_status_message_title">编辑状态信息</string>
|
<string name="edit_status_message_title">编辑状态信息</string>
|
||||||
<string name="edit_status_message">编辑状态信息</string>
|
<string name="edit_status_message">编辑状态信息</string>
|
||||||
<string name="disable_encryption">禁用加密</string>
|
<string name="disable_encryption">禁用加密</string>
|
||||||
<string name="error_trustkey_general">畅聊无法向%1$s发送加密信息。这可能是由于您的联系人使用了无法处理OMEMO的过时服务器或客户端。</string>
|
<string name="error_trustkey_general">Conversations无法向%1$s发送加密信息。这可能是由于您的联系人使用了无法处理OMEMO的过时服务器或客户端。</string>
|
||||||
<string name="error_trustkey_device_list">无法获取设备列表</string>
|
<string name="error_trustkey_device_list">无法获取设备列表</string>
|
||||||
<string name="error_trustkey_bundle">无法获取密钥</string>
|
<string name="error_trustkey_bundle">无法获取密钥</string>
|
||||||
<string name="error_trustkey_hint_mutual">提示:某些情况下,可以将对方加入联系人列表,以解决此问题。</string>
|
<string name="error_trustkey_hint_mutual">提示:某些情况下,可以将对方加入联系人列表,以解决此问题。</string>
|
||||||
|
@ -713,7 +713,7 @@
|
||||||
<string name="share">分享</string>
|
<string name="share">分享</string>
|
||||||
<string name="unable_to_start_recording">无法开始录制</string>
|
<string name="unable_to_start_recording">无法开始录制</string>
|
||||||
<string name="please_wait">请等待…</string>
|
<string name="please_wait">请等待…</string>
|
||||||
<string name="no_microphone_permission">允许畅聊使用麦克风</string>
|
<string name="no_microphone_permission">允许Conversations使用麦克风</string>
|
||||||
<string name="search_messages">搜索消息</string>
|
<string name="search_messages">搜索消息</string>
|
||||||
<string name="gif">GIF动图</string>
|
<string name="gif">GIF动图</string>
|
||||||
<string name="view_conversation">查看聊天</string>
|
<string name="view_conversation">查看聊天</string>
|
||||||
|
@ -735,7 +735,7 @@
|
||||||
<string name="conference_destroyed">群聊已被解散</string>
|
<string name="conference_destroyed">群聊已被解散</string>
|
||||||
<string name="unable_to_save_recording">无法保存录制的文件</string>
|
<string name="unable_to_save_recording">无法保存录制的文件</string>
|
||||||
<string name="foreground_service_channel_name">前台服务</string>
|
<string name="foreground_service_channel_name">前台服务</string>
|
||||||
<string name="foreground_service_channel_description">此通知类别用于显示表明畅聊正在运行的永久通知。</string>
|
<string name="foreground_service_channel_description">此通知类别用于显示表明Conversations正在运行的永久通知。</string>
|
||||||
<string name="notification_group_status_information">状态信息</string>
|
<string name="notification_group_status_information">状态信息</string>
|
||||||
<string name="error_channel_name">连接问题</string>
|
<string name="error_channel_name">连接问题</string>
|
||||||
<string name="error_channel_description">此通知类别用于显示帐户连接问题通知。</string>
|
<string name="error_channel_description">此通知类别用于显示帐户连接问题通知。</string>
|
||||||
|
@ -797,9 +797,9 @@
|
||||||
<string name="temporarily_unavailable">暂时无法连接。请稍候再试。</string>
|
<string name="temporarily_unavailable">暂时无法连接。请稍候再试。</string>
|
||||||
<string name="no_network_connection">无网络连接</string>
|
<string name="no_network_connection">无网络连接</string>
|
||||||
<string name="try_again_in_x">请在%s后重试</string>
|
<string name="try_again_in_x">请在%s后重试</string>
|
||||||
<string name="rate_limited">频率过高</string>
|
<string name="rate_limited">你被限制速率</string>
|
||||||
<string name="too_many_attempts">尝试次数过多</string>
|
<string name="too_many_attempts">尝试次数过多</string>
|
||||||
<string name="the_app_is_out_of_date">您正在使用旧版应用。</string>
|
<string name="the_app_is_out_of_date">您正在使用此应用程序的过时版本。</string>
|
||||||
<string name="update">更新</string>
|
<string name="update">更新</string>
|
||||||
<string name="logged_in_with_another_device">此号码已在其他设备上登录。</string>
|
<string name="logged_in_with_another_device">此号码已在其他设备上登录。</string>
|
||||||
<string name="enter_your_name_instructions">请输入您的姓名。这样,对方就能知道您是谁。</string>
|
<string name="enter_your_name_instructions">请输入您的姓名。这样,对方就能知道您是谁。</string>
|
||||||
|
@ -819,7 +819,7 @@
|
||||||
<string name="restore_backup">恢复备份</string>
|
<string name="restore_backup">恢复备份</string>
|
||||||
<string name="restore">恢复</string>
|
<string name="restore">恢复</string>
|
||||||
<string name="enter_password_to_restore">输入%s的密码以恢复备份</string>
|
<string name="enter_password_to_restore">输入%s的密码以恢复备份</string>
|
||||||
<string name="restore_warning">仅在迁移或丢失设备时恢复备份。</string>
|
<string name="restore_warning">请勿使用恢复备份功能来尝试克隆安装的应用程序(同时运行)。恢复备份功能仅用于迁移或丢失原始设备的情况。</string>
|
||||||
<string name="unable_to_restore_backup">无法恢复备份。</string>
|
<string name="unable_to_restore_backup">无法恢复备份。</string>
|
||||||
<string name="unable_to_decrypt_backup">无法解密备份。密码是否正确?</string>
|
<string name="unable_to_decrypt_backup">无法解密备份。密码是否正确?</string>
|
||||||
<string name="backup_channel_name">备份与恢复</string>
|
<string name="backup_channel_name">备份与恢复</string>
|
||||||
|
@ -893,6 +893,7 @@
|
||||||
<string name="rtp_state_ringing">正在响铃</string>
|
<string name="rtp_state_ringing">正在响铃</string>
|
||||||
<string name="rtp_state_declined_or_busy">正忙</string>
|
<string name="rtp_state_declined_or_busy">正忙</string>
|
||||||
<string name="rtp_state_connectivity_error">无法接通来电</string>
|
<string name="rtp_state_connectivity_error">无法接通来电</string>
|
||||||
|
<string name="rtp_state_connectivity_lost_error">连接丢失</string>
|
||||||
<string name="rtp_state_retracted">通话已撤销</string>
|
<string name="rtp_state_retracted">通话已撤销</string>
|
||||||
<string name="rtp_state_application_failure">程序错误</string>
|
<string name="rtp_state_application_failure">程序错误</string>
|
||||||
<string name="hang_up">挂断</string>
|
<string name="hang_up">挂断</string>
|
||||||
|
@ -906,6 +907,7 @@
|
||||||
<string name="missed_call">未接电话</string>
|
<string name="missed_call">未接电话</string>
|
||||||
<string name="audio_call">语音通话</string>
|
<string name="audio_call">语音通话</string>
|
||||||
<string name="video_call">视频通话</string>
|
<string name="video_call">视频通话</string>
|
||||||
|
<string name="help">帮助</string>
|
||||||
<string name="microphone_unavailable">麦克风不可用</string>
|
<string name="microphone_unavailable">麦克风不可用</string>
|
||||||
<string name="only_one_call_at_a_time">只能同时打一通电话</string>
|
<string name="only_one_call_at_a_time">只能同时打一通电话</string>
|
||||||
<string name="return_to_ongoing_call">返回正在进行的通话</string>
|
<string name="return_to_ongoing_call">返回正在进行的通话</string>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.ui.UriHandlerActivity;
|
||||||
|
|
||||||
|
public class ProvisioningUtils {
|
||||||
|
public static void provision(UriHandlerActivity uriHandlerActivity, String result) {
|
||||||
|
throw new IllegalStateException("Quicksy does not support provisioning");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue