for v2.5.0 Merge branch 'master' into develop

This commit is contained in:
Martin/Geno 2019-04-29 20:07:33 +02:00
commit a15756d4ff
No known key found for this signature in database
GPG Key ID: 9D7D3C6BFF600C6A
77 changed files with 1868 additions and 617 deletions

View File

@ -12,3 +12,10 @@ file_filter = src/quicksy/res/values-<lang>/strings.xml
source_file = src/quicksy/res/values/strings.xml
source_lang = en
type = ANDROID
[conversations.conversations-strings]
file_filter = src/conversations/res/values-<lang>/strings.xml
source_file = src/conversations/res/values/strings.xml
source_lang = en
type = ANDROID

View File

@ -1,5 +1,10 @@
# Changelog
### Version 2.5.0
* Added channel search via search.jabbercat.org
* Reworked onboarding screens
* Warn when trying to enter domain address or channel address in Add Contact dialog
### Version 2.4.3
* Fixed display of private messages sent from another client
* Fixed backup creation on long time installations

View File

@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.android.tools.build:gradle:3.4.0'
}
}
@ -64,6 +64,9 @@ dependencies {
implementation 'org.conscrypt:conscrypt-android:1.3.0'
implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation "com.leinardi.android:speed-dial:2.0.1"
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.google.guava:guava:27.1-android'
quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.10.1'
}
@ -78,8 +81,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 323
versionName "2.4.3"
versionCode 326
versionName "2.5.0"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId

View File

@ -1,5 +1,6 @@
#Wed Apr 24 10:50:09 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2-all.zip

2
proguard-rules.pro vendored
View File

@ -18,3 +18,5 @@
-dontwarn org.bouncycastle.cert.dane.**
-dontwarn rocks.xmpp.addr.**
-dontwarn com.google.firebase.analytics.connector.AnalyticsConnector
-dontwarn java.lang.**
-dontwarn javax.lang.**

View File

@ -12,9 +12,13 @@
android:name=".ui.WelcomeActivity"
android:label="@string/app_name"
android:launchMode="singleTask"/>
<activity
android:name=".ui.PickServerActivity"
android:label="@string/create_new_account"
android:launchMode="singleTask"/>
<activity
android:name=".ui.MagicCreateActivity"
android:label="@string/create_account"
android:label="@string/create_new_account"
android:launchMode="singleTask"/>
<activity
android:name=".ui.ImportBackupActivity"

View File

@ -0,0 +1,104 @@
package eu.siacs.conversations.ui;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityPickServerBinding;
import eu.siacs.conversations.entities.Account;
public class PickServerActivity extends XmppActivity {
@Override
protected void refreshUiReal() {
}
@Override
void onBackendConnected() {
}
@Override
public void onStart() {
super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == android.R.id.home) {
startActivity(new Intent(this, WelcomeActivity.class));
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
startActivity(new Intent(this, WelcomeActivity.class));
super.onBackPressed();
}
@Override
public void onNewIntent(Intent intent) {
if (intent != null) {
setIntent(intent);
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
if (getResources().getBoolean(R.bool.portrait_only)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
super.onCreate(savedInstanceState);
ActivityPickServerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_pick_server);
setSupportActionBar((Toolbar) binding.toolbar);
configureActionBar(getSupportActionBar());
binding.useCim.setOnClickListener(v -> {
final Intent intent = new Intent(this, MagicCreateActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
addInviteUri(intent);
startActivity(intent);
});
binding.useOwnProvider.setOnClickListener(v -> {
List<Account> accounts = xmppConnectionService.getAccounts();
Intent intent = new Intent(this, EditAccountActivity.class);
intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, true);
if (accounts.size() == 1) {
intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
intent.putExtra("init", true);
} else if (accounts.size() >= 1) {
intent = new Intent(this, ManageAccountActivity.class);
}
addInviteUri(intent);
startActivity(intent);
});
}
public void addInviteUri(Intent intent) {
StartConversationActivity.addInviteUri(intent, getIntent());
}
public static void launch(AppCompatActivity activity) {
Intent intent = new Intent(activity, PickServerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(intent);
activity.overridePendingTransition(0, 0);
}
}

View File

@ -2,18 +2,19 @@ package eu.siacs.conversations.ui;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.Toast;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
import eu.siacs.conversations.entities.Account;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
@ -55,24 +56,18 @@ public class WelcomeActivity extends XmppActivity {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
setSupportActionBar(findViewById(R.id.toolbar));
final ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayShowHomeEnabled(false);
ab.setDisplayHomeAsUpEnabled(false);
}
final Button createAccount = findViewById(R.id.create_account);
createAccount.setOnClickListener(v -> {
final Intent intent = new Intent(WelcomeActivity.this, MagicCreateActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_welcome);
setSupportActionBar((Toolbar) binding.toolbar);
configureActionBar(getSupportActionBar(), false);
binding.registerNewAccount.setOnClickListener(v -> {
final Intent intent = new Intent(this, PickServerActivity.class);
addInviteUri(intent);
startActivity(intent);
});
final Button useOwnProvider = findViewById(R.id.use_own_provider);
useOwnProvider.setOnClickListener(v -> {
binding.useExisting.setOnClickListener(v -> {
List<Account> accounts = xmppConnectionService.getAccounts();
Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class);
intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER,false);
if (accounts.size() == 1) {
intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
intent.putExtra("init", true);

View File

@ -9,13 +9,23 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.EditAccountActivity;
import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.ui.PickServerActivity;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.ui.WelcomeActivity;
public class SignupUtils {
public static Intent getSignUpIntent(final Activity activity) {
Intent intent = new Intent(activity, WelcomeActivity.class);
return getSignUpIntent(activity, false);
}
public static Intent getSignUpIntent(final Activity activity, final boolean toServerChooser) {
Intent intent;
if (toServerChooser) {
intent = new Intent(activity, PickServerActivity.class);
} else {
intent = new Intent(activity, WelcomeActivity.class);
}
StartConversationActivity.addInviteUri(intent, activity.getIntent());
return intent;
}
@ -27,6 +37,9 @@ public class SignupUtils {
if (pendingAccount != null) {
intent = new Intent(activity, EditAccountActivity.class);
intent.putExtra("jid", pendingAccount.getJid().asBareJid().toString());
if (!pendingAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, pendingAccount.isOptionSet(Account.OPTION_REGISTER));
}
} else {
if (service.getAccounts().size() == 0) {
if (Config.X509_VERIFICATION) {

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include android:id="@+id/toolbar" layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_background_primary">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:minHeight="256dp"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="10dp">
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pick_a_server"
android:textAppearance="@style/TextAppearance.Conversations.Title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/server_select_text"
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
<Button
android:id="@+id/use_cim"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/use_conversations.im"
android:textColor="?colorAccent" />
<Button
android:id="@+id/use_own_provider"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/use_own_provider"
android:textColor="?android:textColorSecondary" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/linearLayout"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="8dp"
android:src="@drawable/main_logo" />
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</LinearLayout>
</layout>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/color_background_primary">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:minHeight="256dp"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="10dp">
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_header"
android:textAppearance="@style/TextAppearance.Conversations.Title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/do_you_have_an_account"
android:textAppearance="@style/TextAppearance.Conversations.Body1" />
<Button
android:id="@+id/register_new_account"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/create_new_account"
android:textColor="?colorAccent" />
<Button
android:id="@+id/use_existing"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/i_already_have_an_account"
android:textColor="?android:textColorSecondary" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/linearLayout"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="8dp"
android:src="@drawable/main_logo" />
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</LinearLayout>
</layout>

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<ScrollView android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_background_primary">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:minHeight="256dp"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_header"
android:textAppearance="@style/TextAppearance.Conversations.Title"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/welcome_text"
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
<Button
android:id="@+id/create_account"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/create_account"
android:textColor="?colorAccent"/>
<Button
android:id="@+id/use_own_provider"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/use_own_provider"
android:textColor="?android:textColorSecondary"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/linearLayout"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="8dp"
android:src="@drawable/main_logo"/>
</RelativeLayout>
</RelativeLayout>
</ScrollView>
</LinearLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Wähle deinen XMPP-Provider</string>
<string name="use_conversations.im">Benutze conversations.im</string>
<string name="create_new_account">Neues Konto erstellen</string>
<string name="do_you_have_an_account">Hast du bereits ein XMPP-Konto? Dies kann der Fall sein, wenn du bereits einen anderen XMPP-Client verwendest oder bereits Conversations verwendet hast. Wenn nicht, kannst du jetzt ein neues XMPP-Konto erstellen.\nHinweis: Einige E-Mail-Anbieter bieten auch XMPP-Konten an.</string>
<string name="server_select_text">XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diesen Client mit jedem beliebigen XMPP-Server nutzen.\nUm es dir leicht zu machen, haben wir die Möglichkeit geschaffen, ein Konto auf conversations.im¹ anzulegen; ein Anbieter, der speziell für die Verwendung mit Conversations geeignet ist.</string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Elige tu proveedor XMPP</string>
<string name="use_conversations.im">Usa conversations.im</string>
<string name="create_new_account">Crear nueva cuenta</string>
<string name="do_you_have_an_account">¿Ya tienes una cuenta XMPP? Este puede ser el caso si ya estás usando un cliente XMPP diferente o has usado Conversations anteriormente. Si no es así, puedes crear una nueva cuenta XMPP ahora mismo.\nConsejo: Algunos proveedores de email también ofrecen una cuenta XMPP.</string>
<string name="server_select_text">XMPP es una red de mensajería instantánea independiente del proveedor. Puedes usar este cliente con cualquier servidor XMPP que elijas.\nSin embargo, para tu conveniencia, hacemos de forma sencilla la creación de una cuenta en conversations.im¹; un proveedor especializado para el uso con Conversations </string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Choisissez votre fournisseur XMPP</string>
<string name="use_conversations.im">Utiliser conversations.im</string>
<string name="create_new_account">Créer un nouveau compte</string>
<string name="do_you_have_an_account">Avez-vous déjà un compte XMPP ? Cela peut être le cas si vous utilisez déjà un autre client XMPP ou si vous avez déjà utilisé Conversations auparavant. Sinon, vous pouvez créer un nouveau compte XMPP dès maintenant. Remarque : Certains fournisseurs de messagerie proposent également des comptes XMPP.</string>
<string name="server_select_text">XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec n\'importe quel serveur XMPP de votre choix. Toutefois, pour votre commodité, nous avons facilité la création d\'un compte sur conversations.im¹ ; un fournisseur spécialement conçu pour l\'utilisation avec Conversations.</string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Válassza ki az XMPP szolgáltatóját</string>
<string name="use_conversations.im">conversations.im használata</string>
<string name="create_new_account">Új fiók létrehozása</string>
<string name="do_you_have_an_account">Már rendelkezik XMPP fiókkal? Ez az eset állhat fenn, ha már egy másik XMPP klienst használ vagy ha már korábban használta a Conversations-t. Ha nem, akkor most létrehozhat egy új XMPP fiókot.\nTipp: Egyes e-mail szolgáltatók is biztosítanak XMPP fiókokat is.</string>
<string name="server_select_text">Az XMPP egy szolgáltató független, azonnali üzenetküldő hálózat. Ezt az ügyfél programot bármelyik XMPP szerverhez használhatja.\nDe a könnyebbség kedvéért létrehozhat egy fiókot a conversations.im¹ szolgáltatón is; ami kifejezetten a Conversations programmal való használatra lett tervezve.</string>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Scegli il tuo provider XMPP</string>
<string name="use_conversations.im">Usa conversations.im</string>
<string name="create_new_account">Crea un nuovo account</string>
<string name="do_you_have_an_account">Possiedi già un account XMPP? Questo succede se stai già usando un diverso client XMPP o hai già usato prima Conversations. In caso negativo puoi creare un account XMPP adesso.
Suggerimento: alcuni provider di email forniscono anche un account XMPP.</string>
<string name="server_select_text">XMPP è una rete di instant messaging indipendente dal provider. Puoi usare questo client con qualsiasi server XMPP.
In ogni caso per facilitare puoi creare facilmente un account su conversations.im, un provider pensato apposta per essere usato con Conversations.</string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Kies je XMPP-dienst</string>
<string name="use_conversations.im">Conversations.im gebruiken</string>
<string name="create_new_account">Nieuwe account registreren</string>
<string name="do_you_have_an_account">Heb je al een XMPP-account? Als je al een andere XMPP-cliënt gebruikt, of Conversations vroeger al eens hebt gebruikt, is dit waarschijnlijk het geval. Zo niet, kan je nu een nieuwe XMPP-account aanmaken.\nTip: sommige e-mailproviders bieden ook XMPP-accounts aan.</string>
<string name="server_select_text">XMPP is een provider-onafhankelijk berichtennetwerk. Je kan deze cliënt gebruiken met eender welke XMPP-server.\nOm het je gemakkelijker te maken kun je simpelweg een account aanmaken op conversations.im¹; een provider speciaal geschikt voor Conversations.</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Selecione o seu provedor XMPP</string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Alegeți-vă furnizorul XMPP</string>
<string name="use_conversations.im">Folosește conversations.im</string>
<string name="create_new_account">Creează un cont nou</string>
<string name="do_you_have_an_account">Aveți deja un cont XMPP? S-ar putea să fie așa dacă deja utilizați un alt client XMPP sau dacă ați folosit Conversations în trecut. Dacă nu, puteți crea un cont nou XMPP chiar acum.\nIdee: Unii furnizori de e-mail oferă de asemenea și conturi XMPP.</string>
<string name="server_select_text">XMPP este o rețea de mesagerie instant ce nu depinde de un anumit furnizor. Aveți posibilitatea să utilizați acest client cu orice server XMPP doriți.\nTotuși, pentru confortul dumneavoastră, am facilitat crearea unui cont pe conversations.im¹; un furnizor potrivit pentru utilizarea cu aplicația Conversations.</string>
</resources>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Pick your XMPP provider</string>
<string name="use_conversations.im">Use conversations.im</string>
<string name="create_new_account">Create new account</string>
<string name="do_you_have_an_account">Do you already have an XMPP account? This might be the case if you are already using a different XMPP client or have used Conversations before. If not you can create a new XMPP account right now.\nHint: Some email providers also provide XMPP accounts.</string>
<string name="server_select_text">XMPP is a provider independent instant messaging network. You can use this client with what ever XMPP server you choose.\nHowever for your convenience we made it easy to create an account on conversations.im¹; a provider specially suited for the use with Conversations.</string>
</resources>

View File

@ -283,6 +283,7 @@
<activity
android:name=".ui.MucUsersActivity"
android:label="@string/group_chat_members" />
<activity android:label="@string/discover_channels" android:name=".ui.ChannelDiscoveryActivity"/>
</application>
</manifest>

View File

@ -40,6 +40,9 @@ public final class Config {
public static final String DOMAIN_LOCK = null; //only allow account creation for this domain
public static final String MAGIC_CREATE_DOMAIN = "chat.sum7.eu";
public static final String QUICKSY_DOMAIN = "quicksy.im";
public static final String CHANNEL_DISCOVERY = "https://search.jabbercat.org";
public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
public static final boolean USE_RANDOM_RESOURCE_ON_EVERY_BIND = false;

View File

@ -1326,7 +1326,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
final boolean success;
if (message.getType() == Message.TYPE_PRIVATE) {
if (message.isPrivateMessage()) {
success = buildHeader(axolotlMessage, message.getTrueCounterpart());
} else {
success = buildHeader(axolotlMessage, (Conversation) message.getConversation());

View File

@ -60,6 +60,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private static final String ATTRIBUTE_NEXT_MESSAGE_TIMESTAMP = "next_message_timestamp";
private static final String ATTRIBUTE_CRYPTO_TARGETS = "crypto_targets";
private static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
private static final String ATTRIBUTE_CORRECTING_MESSAGE = "correcting_message";
static final String ATTRIBUTE_MEMBERS_ONLY = "members_only";
static final String ATTRIBUTE_MODERATED = "moderated";
static final String ATTRIBUTE_NON_ANONYMOUS = "non_anonymous";
@ -82,7 +83,6 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
private ChatState mOutgoingChatState = Config.DEFAULT_CHATSTATE;
private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
private String mFirstMamReference = null;
private Message correctingMessage;
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
@ -425,12 +425,13 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
public boolean setCorrectingMessage(Message correctingMessage) {
this.correctingMessage = correctingMessage;
setAttribute(ATTRIBUTE_CORRECTING_MESSAGE,correctingMessage == null ? null : correctingMessage.getUuid());
return correctingMessage == null && draftMessage != null;
}
public Message getCorrectingMessage() {
return this.correctingMessage;
final String uuid = getAttribute(ATTRIBUTE_CORRECTING_MESSAGE);
return uuid == null ? null : findSentMessageWithUuid(uuid);
}
public boolean withSelf() {
@ -485,7 +486,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
final Message message = messages.get(i);
if (message.getStatus() <= Message.STATUS_RECEIVED
&& (message.markable || isPrivateAndNonAnonymousMuc)
&& message.getType() != Message.TYPE_PRIVATE) {
&& !message.isPrivateMessage()) {
return message;
}
}
@ -747,7 +748,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
synchronized (this.messages) {
for (int i = this.messages.size() - 1; i >= 0; --i) {
final Message message = this.messages.get(i);
if (message.getType() == Message.TYPE_PRIVATE) {
if (message.isPrivateMessage()) {
continue; //it's unsafe to use private messages as anchor. They could be coming from user archive
}
if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon() || message.getServerMsgId() != null) {

View File

@ -57,6 +57,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
public static final int TYPE_FILE = 2;
public static final int TYPE_STATUS = 3;
public static final int TYPE_PRIVATE = 4;
public static final int TYPE_PRIVATE_FILE = 5;
public static final String CONVERSATION = "conversationUuid";
public static final String COUNTERPART = "counterpart";
@ -495,7 +496,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
boolean similar(Message message) {
if (type != TYPE_PRIVATE && this.serverMsgId != null && message.getServerMsgId() != null) {
if (!isPrivateMessage() && this.serverMsgId != null && message.getServerMsgId() != null) {
return this.serverMsgId.equals(message.getServerMsgId()) || Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId());
} else if (Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId())) {
return true;
@ -837,8 +838,12 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
this.mPreviousMessage = null;
}
public boolean isPrivateMessage() {
return type == TYPE_PRIVATE || type == TYPE_PRIVATE_FILE;
}
public boolean isFileOrImage() {
return type == TYPE_FILE || type == TYPE_IMAGE;
return type == TYPE_FILE || type == TYPE_IMAGE || type == TYPE_PRIVATE_FILE;
}
public boolean hasFileOnRemoteHost() {
@ -915,4 +920,31 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
return encryption;
}
public static boolean configurePrivateMessage(final Message message) {
return configurePrivateMessage(message, false);
}
public static boolean configurePrivateFileMessage(final Message message) {
return configurePrivateMessage(message, true);
}
private static boolean configurePrivateMessage(final Message message, final boolean isFile) {
final Conversation conversation;
if (message.conversation instanceof Conversation) {
conversation = (Conversation) message.conversation;
} else {
return false;
}
if (conversation.getMode() == Conversation.MODE_MULTI) {
final Jid nextCounterpart = conversation.getNextCounterpart();
if (nextCounterpart != null) {
message.setCounterpart(nextCounterpart);
message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(nextCounterpart));
message.setType(isFile ? Message.TYPE_PRIVATE_FILE : Message.TYPE_PRIVATE);
return true;
}
}
return false;
}
}

View File

@ -415,7 +415,7 @@ public class IqGenerator extends AbstractGenerator {
register.setFrom(account.getJid().asBareJid());
register.setTo(Jid.of(account.getServer()));
register.setId(id);
Element query = register.query("jabber:iq:register");
Element query = register.query(Namespace.REGISTER);
if (data != null) {
query.addChild(data);
}

View File

@ -1,7 +1,5 @@
package eu.siacs.conversations.generator;
import android.util.Log;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -13,7 +11,6 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.http.P1S3UrlStreamHandler;
@ -43,7 +40,7 @@ public class MessageGenerator extends AbstractGenerator {
if (this.mXmppConnectionService.indicateReceived() && !isWithSelf) {
packet.addChild("request", "urn:xmpp:receipts");
}
} else if (message.getType() == Message.TYPE_PRIVATE) { //TODO files and images might be private as well
} else if (message.isPrivateMessage()) {
packet.setTo(message.getCounterpart());
packet.setType(MessagePacket.TYPE_CHAT);
packet.addChild("x", "http://jabber.org/protocol/muc#user");
@ -54,7 +51,7 @@ public class MessageGenerator extends AbstractGenerator {
packet.setTo(message.getCounterpart().asBareJid());
packet.setType(MessagePacket.TYPE_GROUPCHAT);
}
if (conversation.isSingleOrPrivateAndNonAnonymous() && message.getType() != Message.TYPE_PRIVATE) {
if (conversation.isSingleOrPrivateAndNonAnonymous() && !message.isPrivateMessage()) {
packet.addChild("markable", "urn:xmpp:chat-markers:0");
}
packet.setFrom(account.getJid());

View File

@ -442,7 +442,8 @@ public class HttpDownloadConnection implements Transferable {
}
private void updateImageBounds() {
message.setType(Message.TYPE_FILE);
final boolean privateMessage = message.isPrivateMessage();
message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : Message.TYPE_FILE);
final URL url;
final String ref = mUrl.getRef();
if (method == Method.P1_S3) {

View File

@ -216,7 +216,9 @@ public class HttpUploadConnection implements Transferable {
mXmppConnectionService.getFileBackend().updateFileParams(message, get);
mXmppConnectionService.getFileBackend().updateMediaScanner(file);
finish();
message.setCounterpart(message.getConversation().getJid().asBareJid());
if (!message.isPrivateMessage()) {
message.setCounterpart(message.getConversation().getJid().asBareJid());
}
mXmppConnectionService.resendMessage(message, delayed);
} else {
Log.d(Config.LOGTAG,"http upload failed because response code was "+code);

View File

@ -0,0 +1,98 @@
package eu.siacs.conversations.http.services;
import com.google.common.base.Objects;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.utils.UIHelper;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import rocks.xmpp.addr.Jid;
public interface MuclumbusService {
@GET("/api/1.0/rooms/unsafe")
Call<Rooms> getRooms(@Query("p") int page);
@POST("/api/1.0/search")
Call<SearchResult> search(@Body SearchRequest searchRequest);
class Rooms {
int page;
int total;
int pages;
public List<Room> items;
}
class Room implements AvatarService.Avatarable {
public String address;
public String name;
public String description;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Jid getRoom() {
try {
return Jid.of(address);
} catch (IllegalArgumentException e) {
return null;
}
}
@Override
public int getAvatarBackgroundColor() {
Jid room = getRoom();
return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Room room = (Room) o;
return Objects.equal(address, room.address) &&
Objects.equal(name, room.name) &&
Objects.equal(description, room.description);
}
@Override
public int hashCode() {
return Objects.hashCode(address, name, description);
}
}
class SearchRequest {
public Set<String> keywords;
public SearchRequest(String keyword) {
this.keywords = Collections.singleton(keyword);
}
}
class SearchResult {
public Result result;
}
class Result {
public List<Room> items;
}
}

View File

@ -551,7 +551,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
mXmppConnectionService.updateMessage(replacedMessage, uuid);
if (mXmppConnectionService.confirmMessages()
&& replacedMessage.getStatus() == Message.STATUS_RECEIVED
&& (replacedMessage.trusted() || replacedMessage.getType() == Message.TYPE_PRIVATE)
&& (replacedMessage.trusted() || replacedMessage.isPrivateMessage()) //TODO do we really want to send receipts for all PMs?
&& remoteMsgId != null
&& !selfAddressed
&& !isTypeGroupChat) {
@ -577,7 +577,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
boolean checkForDuplicates = (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay"))
|| message.getType() == Message.TYPE_PRIVATE
|| message.isPrivateMessage()
|| message.getServerMsgId() != null
|| (query == null && mXmppConnectionService.getMessageArchiveService().isCatchupInProgress(conversation));
if (checkForDuplicates) {
@ -637,7 +637,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (mXmppConnectionService.confirmMessages()
&& message.getStatus() == Message.STATUS_RECEIVED
&& (message.trusted() || message.getType() == Message.TYPE_PRIVATE)
&& (message.trusted() || message.isPrivateMessage())
&& remoteMsgId != null
&& !selfAddressed
&& !isTypeGroupChat) {

View File

@ -811,14 +811,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (internal) {
final String name = file.getName();
if (name.endsWith(".pgp")) {
selection = "(" + Message.RELATIVE_FILE_PATH + " IN(?,?) OR (" + Message.RELATIVE_FILE_PATH + "=? and encryption in(1,4))) and type in (1,2)";
selection = "(" + Message.RELATIVE_FILE_PATH + " IN(?,?) OR (" + Message.RELATIVE_FILE_PATH + "=? and encryption in(1,4))) and type in (1,2,5)";
selectionArgs = new String[]{file.getAbsolutePath(), name, name.substring(0, name.length() - 4)};
} else {
selection = Message.RELATIVE_FILE_PATH + " IN(?,?) and type in (1,2)";
selection = Message.RELATIVE_FILE_PATH + " IN(?,?) and type in (1,2,5)";
selectionArgs = new String[]{file.getAbsolutePath(), name};
}
} else {
selection = Message.RELATIVE_FILE_PATH + "=? and type in (1,2)";
selection = Message.RELATIVE_FILE_PATH + "=? and type in (1,2,5)";
selectionArgs = new String[]{file.getAbsolutePath()};
}
final List<String> uuids = new ArrayList<>();
@ -861,7 +861,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<FilePathInfo> getFilePathInfo() {
final SQLiteDatabase db = this.getReadableDatabase();
final Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID, Message.RELATIVE_FILE_PATH, Message.DELETED}, "type in (1,2) and "+Message.RELATIVE_FILE_PATH+" is not null", null, null, null, null);
final Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID, Message.RELATIVE_FILE_PATH, Message.DELETED}, "type in (1,2,5) and "+Message.RELATIVE_FILE_PATH+" is not null", null, null, null, null);
final List<FilePathInfo> list = new ArrayList<>();
while (cursor != null && cursor.moveToNext()) {
list.add(new FilePathInfo(cursor.getString(0), cursor.getString(1), cursor.getInt(2) > 0));
@ -874,7 +874,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<FilePath> getRelativeFilePaths(String account, Jid jid, int limit) {
SQLiteDatabase db = this.getReadableDatabase();
final String SQL = "select uuid,relativeFilePath from messages where type in (1,2) and deleted=0 and "+Message.RELATIVE_FILE_PATH+" is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
final String SQL = "select uuid,relativeFilePath from messages where type in (1,2,5) and deleted=0 and "+Message.RELATIVE_FILE_PATH+" is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
final String[] args = {account, jid.toEscapedString(), jid.toEscapedString() + "/%"};
Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + String.valueOf(limit) : ""), args);
List<FilePath> filesPaths = new ArrayList<>();

View File

@ -1178,6 +1178,7 @@ public class FileBackend {
public void updateFileParams(Message message, URL url) {
DownloadableFile file = getFile(message);
final String mime = file.getMimeType();
final boolean privateMessage = message.isPrivateMessage();
final boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
final boolean video = mime != null && mime.startsWith("video/");
final boolean audio = mime != null && mime.startsWith("audio/");
@ -1201,7 +1202,7 @@ public class FileBackend {
}
message.setBody(body.toString());
message.setDeleted(false);
message.setType(image ? Message.TYPE_IMAGE : Message.TYPE_FILE);
message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE));
}

View File

@ -3,7 +3,6 @@ package eu.siacs.conversations.services;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
@ -14,7 +13,6 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.text.TextUtils;
@ -39,10 +37,10 @@ import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.pep.Avatar;
import rocks.xmpp.addr.Jid;
public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
@ -82,11 +80,22 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
return get((ListItem) avatarable, size, cachedOnly);
} else if (avatarable instanceof MucOptions.User) {
return get((MucOptions.User) avatarable, size, cachedOnly);
} else if (avatarable instanceof MuclumbusService.Room) {
return get((MuclumbusService.Room) avatarable, size, cachedOnly);
}
throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName());
}
private Bitmap get(final MuclumbusService.Room result, final int size, boolean cacheOnly) {
final Jid room = result.getRoom();
Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null;
if (conversation != null) {
return get(conversation,size,cacheOnly);
}
return get(result.getName(), room != null ? room.asBareJid().toEscapedString() : result.getName(), size, cacheOnly);
}
private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
if (contact.isSelf()) {
return get(contact.getAccount(), size, cachedOnly);

View File

@ -0,0 +1,124 @@
package eu.siacs.conversations.services;
import android.util.Log;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.http.services.MuclumbusService;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ChannelDiscoveryService {
private final XmppConnectionService service;
private MuclumbusService muclumbusService;
private final Cache<String, List<MuclumbusService.Room>> cache;
public ChannelDiscoveryService(XmppConnectionService service) {
this.service = service;
this.cache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
}
public void initializeMuclumbusService() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (service.useTorToConnect()) {
try {
builder.proxy(HttpConnectionManager.getProxy());
} catch (IOException e) {
throw new RuntimeException("Unable to use Tor proxy", e);
}
}
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.baseUrl(Config.CHANNEL_DISCOVERY)
.addConverterFactory(GsonConverterFactory.create())
.callbackExecutor(Executors.newSingleThreadExecutor())
.build();
this.muclumbusService = retrofit.create(MuclumbusService.class);
}
public void discover(String query, OnChannelSearchResultsFound onChannelSearchResultsFound) {
final boolean all = query == null || query.trim().isEmpty();
Log.d(Config.LOGTAG, "discover channels. query=" + query);
List<MuclumbusService.Room> result = cache.getIfPresent(all ? "" : query);
if (result != null) {
onChannelSearchResultsFound.onChannelSearchResultsFound(result);
return;
}
if (all) {
discoverChannels(onChannelSearchResultsFound);
} else {
discoverChannels(query, onChannelSearchResultsFound);
}
}
private void discoverChannels(OnChannelSearchResultsFound listener) {
Call<MuclumbusService.Rooms> call = muclumbusService.getRooms(1);
try {
call.enqueue(new Callback<MuclumbusService.Rooms>() {
@Override
public void onResponse(Call<MuclumbusService.Rooms> call, Response<MuclumbusService.Rooms> response) {
final MuclumbusService.Rooms body = response.body();
if (body == null) {
return;
}
cache.put("", body.items);
listener.onChannelSearchResultsFound(body.items);
}
@Override
public void onFailure(Call<MuclumbusService.Rooms> call, Throwable throwable) {
Log.d(Config.LOGTAG, "Unable to query muclumbus on "+Config.CHANNEL_DISCOVERY, throwable);
listener.onChannelSearchResultsFound(Collections.emptyList());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void discoverChannels(final String query, OnChannelSearchResultsFound listener) {
Call<MuclumbusService.SearchResult> searchResultCall = muclumbusService.search(new MuclumbusService.SearchRequest(query));
searchResultCall.enqueue(new Callback<MuclumbusService.SearchResult>() {
@Override
public void onResponse(Call<MuclumbusService.SearchResult> call, Response<MuclumbusService.SearchResult> response) {
System.out.println(response.message());
MuclumbusService.SearchResult body = response.body();
if (body == null) {
return;
}
cache.put(query, body.result.items);
listener.onChannelSearchResultsFound(body.result.items);
}
@Override
public void onFailure(Call<MuclumbusService.SearchResult> call, Throwable throwable) {
Log.d(Config.LOGTAG, "Unable to query muclumbus on "+Config.CHANNEL_DISCOVERY, throwable);
listener.onChannelSearchResultsFound(Collections.emptyList());
}
});
}
public interface OnChannelSearchResultsFound {
void onChannelSearchResultsFound(List<MuclumbusService.Room> results);
}
}

View File

@ -192,7 +192,7 @@ public class ExportBackupService extends Service {
final String value = cursor.getString(i);
if (value == null) {
builder.append("NULL");
} else if (value.matches("\\d+")) {
} else if (value.matches("[0-9]+")) {
builder.append(value);
} else {
DatabaseUtils.appendEscapedSQLString(builder, value);
@ -349,4 +349,4 @@ public class ExportBackupService extends Service {
return builder.build();
}
}
}
}

View File

@ -868,7 +868,7 @@ public class NotificationService {
return false;
}
final Matcher m = highlight.matcher(message.getBody());
return (m.find() || message.getType() == Message.TYPE_PRIVATE);
return (m.find() || message.isPrivateMessage());
} else {
return false;
}

View File

@ -68,6 +68,9 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@ -100,6 +103,7 @@ import eu.siacs.conversations.generator.MessageGenerator;
import eu.siacs.conversations.generator.PresenceGenerator;
import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.http.CustomURLStreamHandlerFactory;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.parser.AbstractParser;
import eu.siacs.conversations.parser.IqParser;
import eu.siacs.conversations.parser.MessageParser;
@ -151,6 +155,11 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import me.leolin.shortcutbadger.ShortcutBadger;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import rocks.xmpp.addr.Jid;
public class XmppConnectionService extends Service {
@ -200,6 +209,7 @@ public class XmppConnectionService extends Service {
private FileBackend fileBackend = new FileBackend(this);
private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService(this);
private ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this);
private ShortcutService mShortcutService = new ShortcutService(this);
private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
private AtomicBoolean mForceForegroundService = new AtomicBoolean(false);
@ -477,9 +487,7 @@ public class XmppConnectionService extends Service {
encryption = Message.ENCRYPTION_DECRYPTED;
}
Message message = new Message(conversation, uri.toString(), encryption);
if (conversation.getNextCounterpart() != null) {
message.setCounterpart(conversation.getNextCounterpart());
}
Message.configurePrivateMessage(message);
if (encryption == Message.ENCRYPTION_DECRYPTED) {
getPgpEngine().encrypt(message, callback);
} else {
@ -495,8 +503,12 @@ public class XmppConnectionService extends Service {
} else {
message = new Message(conversation, "", conversation.getNextEncryption());
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_FILE);
}
Log.d(Config.LOGTAG,"attachFile: type="+message.getType());
Log.d(Config.LOGTAG,"counterpart="+message.getCounterpart());
final AttachFileToConversationRunnable runnable = new AttachFileToConversationRunnable(this, uri, type, message, callback);
if (runnable.isVideoMessage()) {
mVideoCompressionExecutor.execute(runnable);
@ -523,8 +535,11 @@ public class XmppConnectionService extends Service {
} else {
message = new Message(conversation, "", conversation.getNextEncryption());
}
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_IMAGE);
if (!Message.configurePrivateFileMessage(message)) {
message.setCounterpart(conversation.getNextCounterpart());
message.setType(Message.TYPE_IMAGE);
}
Log.d(Config.LOGTAG,"attachImage: type="+message.getType());
mFileAddingExecutor.execute(() -> {
try {
getFileBackend().copyImageToPrivateStorage(message, uri);
@ -795,6 +810,14 @@ public class XmppConnectionService extends Service {
return pingNow;
}
public void reinitializeMuclumbusService() {
mChannelDiscoveryService.initializeMuclumbusService();
}
public void discoverChannels(String query, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) {
mChannelDiscoveryService.discover(query, onChannelSearchResultsFound);
}
public boolean isDataSaverDisabled() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
@ -920,7 +943,7 @@ public class XmppConnectionService extends Service {
}
}
if (account.setShowErrorNotification(true)) {
databaseBackend.updateAccount(account);
mDatabaseWriterExecutor.execute(() -> databaseBackend.updateAccount(account));
}
}
mNotificationService.updateErrorNotification();
@ -931,7 +954,7 @@ public class XmppConnectionService extends Service {
if (account.hasErrorStatus()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": dismissing error notification");
if (account.setShowErrorNotification(false)) {
databaseBackend.updateAccount(account);
mDatabaseWriterExecutor.execute(() -> databaseBackend.updateAccount(account));
}
}
}
@ -978,6 +1001,7 @@ public class XmppConnectionService extends Service {
if (Compatibility.runsTwentySix()) {
mNotificationService.initializeChannels();
}
mChannelDiscoveryService.initializeMuclumbusService();
mForceDuringOnCreate.set(Compatibility.runsAndTargetsTwentySix(this));
toggleForegroundService();
this.destroyed = false;
@ -1412,7 +1436,7 @@ public class XmppConnectionService extends Service {
}
boolean mucMessage = conversation.getMode() == Conversation.MODE_MULTI && message.getType() != Message.TYPE_PRIVATE;
boolean mucMessage = conversation.getMode() == Conversation.MODE_MULTI && !message.isPrivateMessage();
if (mucMessage) {
message.setCounterpart(conversation.getMucOptions().getSelf().getFullJid());
}
@ -1447,6 +1471,7 @@ public class XmppConnectionService extends Service {
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
}
}
Log.d(Config.LOGTAG,packet.toString());
sendMessagePacket(account, packet);
}
}

View File

@ -73,7 +73,8 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
getString(R.string.block),
null,
account.getJid().asBareJid().toString(),
true
true,
false
);
dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {

View File

@ -0,0 +1,218 @@
package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.services.ChannelDiscoveryService;
import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.utils.AccountUtils;
import rocks.xmpp.addr.Jid;
public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.OnActionExpandListener, TextView.OnEditorActionListener, ChannelDiscoveryService.OnChannelSearchResultsFound, ChannelSearchResultAdapter.OnChannelSearchResultSelected {
private static final String CHANNEL_DISCOVERY_OPT_IN = "channel_discovery_opt_in";
private final ChannelSearchResultAdapter adapter = new ChannelSearchResultAdapter();
private ActivityChannelDiscoveryBinding binding;
private final PendingItem<String> mInitialSearchValue = new PendingItem<>();
private MenuItem mMenuSearchView;
private EditText mSearchEditText;
private boolean optedIn = false;
@Override
protected void refreshUiReal() {
}
@Override
void onBackendConnected() {
if (optedIn) {
String query;
if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
query = mSearchEditText.getText().toString();
} else {
query = mInitialSearchValue.peek();
}
xmppConnectionService.discoverChannels(query, this);
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery);
setSupportActionBar((Toolbar) binding.toolbar);
configureActionBar(getSupportActionBar(), true);
binding.list.setAdapter(this.adapter);
this.adapter.setOnChannelSearchResultSelectedListener(this);
optedIn = getPreferences().getBoolean(CHANNEL_DISCOVERY_OPT_IN, false);
final String search = savedInstanceState == null ? null : savedInstanceState.getString("search");
if (search != null) {
mInitialSearchValue.push(search);
}
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.muc_users_activity, menu);
mMenuSearchView = menu.findItem(R.id.action_search);
final View mSearchView = mMenuSearchView.getActionView();
mSearchEditText = mSearchView.findViewById(R.id.search_field);
mSearchEditText.setHint(R.string.search_channels);
String initialSearchValue = mInitialSearchValue.pop();
if (initialSearchValue != null) {
mMenuSearchView.expandActionView();
mSearchEditText.append(initialSearchValue);
mSearchEditText.requestFocus();
if (optedIn) {
xmppConnectionService.discoverChannels(initialSearchValue, this);
}
}
mSearchEditText.setOnEditorActionListener(this);
mMenuSearchView.setOnActionExpandListener(this);
return true;
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
mSearchEditText.post(() -> {
mSearchEditText.requestFocus();
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
});
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
mSearchEditText.setText("");
toggleLoadingScreen();
if (optedIn) {
xmppConnectionService.discoverChannels(null, this);
}
return true;
}
private void toggleLoadingScreen() {
adapter.submitList(Collections.emptyList());
binding.progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onStart() {
super.onStart();
if (!optedIn) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.channel_discovery_opt_in_title);
builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message)));
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn());
builder.setOnCancelListener(dialog -> finish());
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null);
}
super.onSaveInstanceState(savedInstanceState);
}
private void optIn() {
SharedPreferences preferences = getPreferences();
preferences.edit().putBoolean(CHANNEL_DISCOVERY_OPT_IN, true).apply();
optedIn = true;
xmppConnectionService.discoverChannels(null, this);
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (optedIn) {
xmppConnectionService.discoverChannels(v.getText().toString(), this);
}
toggleLoadingScreen();
SoftKeyboardUtils.hideSoftKeyboard(this);
return true;
}
@Override
public void onChannelSearchResultsFound(List<MuclumbusService.Room> results) {
runOnUiThread(() -> {
adapter.submitList(results);
binding.list.setVisibility(View.VISIBLE);
binding.progressBar.setVisibility(View.GONE);
});
}
@Override
public void onChannelSearchResult(final MuclumbusService.Room result) {
List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
if (accounts.size() == 1) {
joinChannelSearchResult(accounts.get(0), result);
} else if (accounts.size() > 0) {
final AtomicReference<String> account = new AtomicReference<>(accounts.get(0));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.choose_account);
builder.setSingleChoiceItems(accounts.toArray(new CharSequence[0]), 0, (dialog, which) -> account.set(accounts.get(which)));
builder.setPositiveButton(R.string.join, (dialog, which) -> joinChannelSearchResult(account.get(), result));
builder.setNegativeButton(R.string.cancel, null);
builder.create().show();
}
}
public void joinChannelSearchResult(String accountJid, MuclumbusService.Room result) {
final boolean syncAutojoin = getBooleanPreference("autojoin", R.bool.autojoin);
Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid));
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);
if (conversation.getBookmark() != null) {
if (!conversation.getBookmark().autojoin() && syncAutojoin) {
conversation.getBookmark().setAutojoin(true);
xmppConnectionService.pushBookmarks(account);
}
} else {
final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid());
bookmark.setAutojoin(syncAutojoin);
account.getBookmarks().add(bookmark);
xmppConnectionService.pushBookmarks(account);
}
switchToConversation(conversation);
}
}

View File

@ -313,7 +313,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
getString(R.string.select),
jid == null ? null : jid.asBareJid().toString(),
getIntent().getStringExtra(EXTRA_ACCOUNT),
true
true,
false
);
dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {

View File

@ -432,6 +432,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
conversation.setDraftMessage(null);
} else if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.setNextCounterpart(null);
binding.textinput.setText("");
} else {
binding.textinput.setText("");
}
updateChatMsgHint();
updateSendButton();
@ -455,11 +458,11 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
private static ConversationFragment findConversationFragment(Activity activity) {
Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
if (fragment != null && fragment instanceof ConversationFragment) {
if (fragment instanceof ConversationFragment) {
return (ConversationFragment) fragment;
}
fragment = activity.getFragmentManager().findFragmentById(R.id.secondary_fragment);
if (fragment != null && fragment instanceof ConversationFragment) {
if (fragment instanceof ConversationFragment) {
return (ConversationFragment) fragment;
}
return null;
@ -725,14 +728,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final Message message;
if (conversation.getCorrectingMessage() == null) {
message = new Message(conversation, body, conversation.getNextEncryption());
if (conversation.getMode() == Conversation.MODE_MULTI) {
final Jid nextCounterpart = conversation.getNextCounterpart();
if (nextCounterpart != null) {
message.setCounterpart(nextCounterpart);
message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(nextCounterpart));
message.setType(Message.TYPE_PRIVATE);
}
}
Message.configurePrivateMessage(message);
} else {
message = conversation.getCorrectingMessage();
message.setBody(body);
@ -778,14 +774,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
public void updateChatMsgHint() {
final boolean multi = conversation.getMode() == Conversation.MODE_MULTI;
if (conversation.getCorrectingMessage() != null) {
this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(R.string.send_corrected_message);
} else if (multi && conversation.getNextCounterpart() != null) {
this.binding.textinput.setHint(getString(
this.binding.textinput.setHint(R.string.send_unencrypted_message);
this.binding.textInputHint.setVisibility(View.VISIBLE);
this.binding.textInputHint.setText(getString(
R.string.send_private_message_to,
conversation.getNextCounterpart().getResource()));
} else if (multi && !conversation.getMucOptions().participating()) {
this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(R.string.you_are_not_participating);
} else {
this.binding.textInputHint.setVisibility(View.GONE);
this.binding.textinput.setHint(UIHelper.getMessageHint(getActivity(), conversation));
getActivity().invalidateOptionsMenu();
}
@ -876,7 +877,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
mediaPreviewAdapter.notifyDataSetChanged();
toggleInputMethod();
};
if (conversation == null || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(getActivity(), attachments, getMaxHttpUploadSize(conversation))) {
if (conversation == null
|| conversation.getMode() == Conversation.MODE_MULTI
|| Attachment.canBeSendInband(attachments)
|| (conversation.getAccount().httpUploadAvailable() && FileBackend.allFilesUnderSize(getActivity(), attachments, getMaxHttpUploadSize(conversation)))) {
callback.onPresenceSelected();
} else {
activity.selectPresence(conversation, callback);
@ -1328,7 +1332,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@Override
public void success(Contact contact) {
selectPresenceToAttachFile(attachmentChoice);
invokeAttachFileIntent(attachmentChoice);
}
@Override
@ -1342,19 +1346,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
warning.show();
}
selectPresenceToAttachFile(attachmentChoice);
invokeAttachFileIntent(attachmentChoice);
} else {
showNoPGPKeyDialog(false, (dialog, which) -> {
conversation.setNextEncryption(Message.ENCRYPTION_NONE);
activity.xmppConnectionService.updateConversation(conversation);
selectPresenceToAttachFile(attachmentChoice);
invokeAttachFileIntent(attachmentChoice);
});
}
} else {
activity.showInstallPgpDialog();
}
} else {
selectPresenceToAttachFile(attachmentChoice);
invokeAttachFileIntent(attachmentChoice);
}
}
@ -1508,62 +1512,54 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
getActivity().invalidateOptionsMenu();
}
protected void selectPresenceToAttachFile(final int attachmentChoice) {
final Account account = conversation.getAccount();
final PresenceSelector.OnPresenceSelected callback = () -> {
Intent intent = new Intent();
boolean chooser = false;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
intent.setType("image/*");
chooser = true;
break;
case ATTACHMENT_CHOICE_RECORD_VIDEO:
intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
final Uri uri = activity.xmppConnectionService.getFileBackend().getTakePhotoUri();
pendingTakePhotoUri.push(uri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
break;
case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true;
intent.setType("*/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
intent = new Intent(getActivity(), RecordingActivity.class);
break;
case ATTACHMENT_CHOICE_LOCATION:
intent = GeoHelper.getFetchIntent(activity);
break;
}
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
attachmentChoice);
} else {
startActivityForResult(intent, attachmentChoice);
protected void invokeAttachFileIntent(final int attachmentChoice) {
Intent intent = new Intent();
boolean chooser = false;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
intent.setType("image/*");
chooser = true;
break;
case ATTACHMENT_CHOICE_RECORD_VIDEO:
intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
break;
case ATTACHMENT_CHOICE_TAKE_PHOTO:
final Uri uri = activity.xmppConnectionService.getFileBackend().getTakePhotoUri();
pendingTakePhotoUri.push(uri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
break;
case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true;
intent.setType("*/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
case ATTACHMENT_CHOICE_RECORD_VOICE:
intent = new Intent(getActivity(), RecordingActivity.class);
break;
case ATTACHMENT_CHOICE_LOCATION:
intent = GeoHelper.getFetchIntent(activity);
break;
}
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
attachmentChoice);
} else {
startActivityForResult(intent, attachmentChoice);
}
};
if (account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) {
conversation.setNextCounterpart(null);
callback.onPresenceSelected();
} else {
activity.selectPresence(conversation, callback);
}
}
@ -2283,110 +2279,102 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textSendButton.setImageResource(SendButtonTool.getSendButtonImageResource(getActivity(), action, status));
}
protected void updateDateSeparators() {
synchronized (this.messageList) {
DateSeparator.addAll(this.messageList);
}
}
protected void updateStatusMessages() {
updateDateSeparators();
synchronized (this.messageList) {
if (showLoadMoreMessages(conversation)) {
this.messageList.add(0, Message.createLoadMoreMessage(conversation));
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
ChatState state = conversation.getIncomingChatState();
if (state == ChatState.COMPOSING) {
this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
} else if (state == ChatState.PAUSED) {
this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
} else {
for (int i = this.messageList.size() - 1; i >= 0; --i) {
final Message message = this.messageList.get(i);
if (message.getType() != Message.TYPE_STATUS) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
return;
} else {
if (message.getStatus() == Message.STATUS_SEND_DISPLAYED) {
this.messageList.add(i + 1,
Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName())));
return;
}
}
}
}
}
DateSeparator.addAll(this.messageList);
if (showLoadMoreMessages(conversation)) {
this.messageList.add(0, Message.createLoadMoreMessage(conversation));
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
ChatState state = conversation.getIncomingChatState();
if (state == ChatState.COMPOSING) {
this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_is_typing, conversation.getName())));
} else if (state == ChatState.PAUSED) {
this.messageList.add(Message.createStatusMessage(conversation, getString(R.string.contact_has_stopped_typing, conversation.getName())));
} else {
final MucOptions mucOptions = conversation.getMucOptions();
final List<MucOptions.User> allUsers = mucOptions.getUsers();
final Set<ReadByMarker> addedMarkers = new HashSet<>();
ChatState state = ChatState.COMPOSING;
List<MucOptions.User> users = conversation.getMucOptions().getUsersWithChatState(state, 5);
if (users.size() == 0) {
state = ChatState.PAUSED;
users = conversation.getMucOptions().getUsersWithChatState(state, 5);
}
if (mucOptions.isPrivateAndNonAnonymous()) {
for (int i = this.messageList.size() - 1; i >= 0; --i) {
final Set<ReadByMarker> markersForMessage = messageList.get(i).getReadByMarkers();
final List<MucOptions.User> shownMarkers = new ArrayList<>();
for (ReadByMarker marker : markersForMessage) {
if (!ReadByMarker.contains(marker, addedMarkers)) {
addedMarkers.add(marker); //may be put outside this condition. set should do dedup anyway
MucOptions.User user = mucOptions.findUser(marker);
if (user != null && !users.contains(user)) {
shownMarkers.add(user);
}
}
}
final ReadByMarker markerForSender = ReadByMarker.from(messageList.get(i));
final Message statusMessage;
final int size = shownMarkers.size();
if (size > 1) {
final String body;
if (size <= 4) {
body = getString(R.string.contacts_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers));
} else if (ReadByMarker.allUsersRepresented(allUsers, markersForMessage, markerForSender)) {
body = getString(R.string.everyone_has_read_up_to_this_point);
} else {
body = getString(R.string.contacts_and_n_more_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers, 3), size - 3);
}
statusMessage = Message.createStatusMessage(conversation, body);
statusMessage.setCounterparts(shownMarkers);
} else if (size == 1) {
statusMessage = Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, UIHelper.getDisplayName(shownMarkers.get(0))));
statusMessage.setCounterpart(shownMarkers.get(0).getFullJid());
statusMessage.setTrueCounterpart(shownMarkers.get(0).getRealJid());
for (int i = this.messageList.size() - 1; i >= 0; --i) {
final Message message = this.messageList.get(i);
if (message.getType() != Message.TYPE_STATUS) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
return;
} else {
statusMessage = null;
}
if (statusMessage != null) {
this.messageList.add(i + 1, statusMessage);
}
addedMarkers.add(markerForSender);
if (ReadByMarker.allUsersRepresented(allUsers, addedMarkers)) {
break;
if (message.getStatus() == Message.STATUS_SEND_DISPLAYED) {
this.messageList.add(i + 1,
Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, conversation.getName())));
return;
}
}
}
}
if (users.size() > 0) {
Message statusMessage;
if (users.size() == 1) {
MucOptions.User user = users.get(0);
int id = state == ChatState.COMPOSING ? R.string.contact_is_typing : R.string.contact_has_stopped_typing;
statusMessage = Message.createStatusMessage(conversation, getString(id, UIHelper.getDisplayName(user)));
statusMessage.setTrueCounterpart(user.getRealJid());
statusMessage.setCounterpart(user.getFullJid());
} else {
int id = state == ChatState.COMPOSING ? R.string.contacts_are_typing : R.string.contacts_have_stopped_typing;
statusMessage = Message.createStatusMessage(conversation, getString(id, UIHelper.concatNames(users)));
statusMessage.setCounterparts(users);
}
this.messageList.add(statusMessage);
}
}
} else {
final MucOptions mucOptions = conversation.getMucOptions();
final List<MucOptions.User> allUsers = mucOptions.getUsers();
final Set<ReadByMarker> addedMarkers = new HashSet<>();
ChatState state = ChatState.COMPOSING;
List<MucOptions.User> users = conversation.getMucOptions().getUsersWithChatState(state, 5);
if (users.size() == 0) {
state = ChatState.PAUSED;
users = conversation.getMucOptions().getUsersWithChatState(state, 5);
}
if (mucOptions.isPrivateAndNonAnonymous()) {
for (int i = this.messageList.size() - 1; i >= 0; --i) {
final Set<ReadByMarker> markersForMessage = messageList.get(i).getReadByMarkers();
final List<MucOptions.User> shownMarkers = new ArrayList<>();
for (ReadByMarker marker : markersForMessage) {
if (!ReadByMarker.contains(marker, addedMarkers)) {
addedMarkers.add(marker); //may be put outside this condition. set should do dedup anyway
MucOptions.User user = mucOptions.findUser(marker);
if (user != null && !users.contains(user)) {
shownMarkers.add(user);
}
}
}
final ReadByMarker markerForSender = ReadByMarker.from(messageList.get(i));
final Message statusMessage;
final int size = shownMarkers.size();
if (size > 1) {
final String body;
if (size <= 4) {
body = getString(R.string.contacts_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers));
} else if (ReadByMarker.allUsersRepresented(allUsers, markersForMessage, markerForSender)) {
body = getString(R.string.everyone_has_read_up_to_this_point);
} else {
body = getString(R.string.contacts_and_n_more_have_read_up_to_this_point, UIHelper.concatNames(shownMarkers, 3), size - 3);
}
statusMessage = Message.createStatusMessage(conversation, body);
statusMessage.setCounterparts(shownMarkers);
} else if (size == 1) {
statusMessage = Message.createStatusMessage(conversation, getString(R.string.contact_has_read_up_to_this_point, UIHelper.getDisplayName(shownMarkers.get(0))));
statusMessage.setCounterpart(shownMarkers.get(0).getFullJid());
statusMessage.setTrueCounterpart(shownMarkers.get(0).getRealJid());
} else {
statusMessage = null;
}
if (statusMessage != null) {
this.messageList.add(i + 1, statusMessage);
}
addedMarkers.add(markerForSender);
if (ReadByMarker.allUsersRepresented(allUsers, addedMarkers)) {
break;
}
}
}
if (users.size() > 0) {
Message statusMessage;
if (users.size() == 1) {
MucOptions.User user = users.get(0);
int id = state == ChatState.COMPOSING ? R.string.contact_is_typing : R.string.contact_has_stopped_typing;
statusMessage = Message.createStatusMessage(conversation, getString(id, UIHelper.getDisplayName(user)));
statusMessage.setTrueCounterpart(user.getRealJid());
statusMessage.setCounterpart(user.getFullJid());
} else {
int id = state == ChatState.COMPOSING ? R.string.contacts_are_typing : R.string.contacts_have_stopped_typing;
statusMessage = Message.createStatusMessage(conversation, getString(id, UIHelper.concatNames(users)));
statusMessage.setCounterparts(users);
}
this.messageList.add(statusMessage);
}
}
}
@ -2592,7 +2580,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) {
service.sendChatState(conversation);
}
updateSendButton();
runOnUiThread(this::updateSendButton);
}
@Override
@ -2618,15 +2606,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
service.sendChatState(conversation);
}
if (storeNextMessage()) {
activity.onConversationsListItemUpdated();
runOnUiThread(() -> activity.onConversationsListItemUpdated());
}
updateSendButton();
runOnUiThread(this::updateSendButton);
}
@Override
public void onTextChanged() {
if (conversation != null && conversation.getCorrectingMessage() != null) {
updateSendButton();
runOnUiThread(this::updateSendButton);
}
}

View File

@ -83,6 +83,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
public static final String EXTRA_OPENED_FROM_NOTIFICATION = "opened_from_notification";
public static final String EXTRA_FORCE_REGISTER = "force_register";
private static final int REQUEST_DATA_SAVER = 0xf244;
private static final int REQUEST_CHANGE_STATUS = 0xee11;
@ -92,6 +93,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private AlertDialog mCaptchaDialog = null;
private Jid jidToEdit;
private boolean mInitMode = false;
private Boolean mForceRegister = null;
private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
private boolean mShowOptions = false;
private Account mAccount;
@ -152,7 +154,12 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
return;
}
final boolean registerNewAccount = binding.accountRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
final boolean registerNewAccount;
if (mForceRegister != null) {
registerNewAccount = mForceRegister;
} else {
registerNewAccount = binding.accountRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
}
if (mUsernameMode && binding.accountJid.getText().toString().contains("@")) {
binding.accountJidLayout.setError(getString(R.string.invalid_username));
removeErrorsOnAllBut(binding.accountJidLayout);
@ -395,7 +402,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
Intent intent = SignupUtils.getSignUpIntent(this);
Intent intent = SignupUtils.getSignUpIntent(this, mForceRegister != null && mForceRegister);
startActivity(intent);
}
}
@ -676,6 +683,9 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
boolean init = intent.getBooleanExtra("init", false);
boolean openedFromNotification = intent.getBooleanExtra(EXTRA_OPENED_FROM_NOTIFICATION, false);
Log.d(Config.LOGTAG,"extras "+intent.getExtras());
this.mForceRegister = intent.hasExtra(EXTRA_FORCE_REGISTER) ? intent.getBooleanExtra(EXTRA_FORCE_REGISTER,false) : null;
Log.d(Config.LOGTAG,"force register="+mForceRegister);
this.mInitMode = init || this.jidToEdit == null;
this.messageFingerprint = intent.getStringExtra("fingerprint");
if (!mInitMode) {
@ -685,13 +695,24 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} else {
this.binding.avater.setVisibility(View.GONE);
configureActionBar(getSupportActionBar(), !(init && Config.MAGIC_CREATE_DOMAIN == null));
setTitle(R.string.action_add_account);
if (mForceRegister != null) {
if (mForceRegister) {
setTitle(R.string.register_new_account);
} else {
setTitle(R.string.add_existing_account);
}
} else {
setTitle(R.string.action_add_account);
}
}
}
SharedPreferences preferences = getPreferences();
mUseTor = QuickConversationsService.isConversations() && preferences.getBoolean("use_tor", getResources().getBoolean(R.bool.use_tor));
this.mShowOptions = mUseTor || (QuickConversationsService.isConversations() && preferences.getBoolean("show_connection_options", getResources().getBoolean(R.bool.show_connection_options)));
this.binding.namePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
if (mForceRegister != null) {
this.binding.accountRegisterNew.setVisibility(View.GONE);
}
}
@Override
@ -967,7 +988,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
}
this.binding.accountRegisterNew.setVisibility(View.GONE);
} else if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
} else if (this.mAccount.isOptionSet(Account.OPTION_REGISTER) && mForceRegister == null) {
this.binding.accountRegisterNew.setVisibility(View.VISIBLE);
} else {
this.binding.accountRegisterNew.setVisibility(View.GONE);

View File

@ -1,24 +1,20 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
import android.app.Dialog;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.app.Dialog;
import android.util.Log;
import android.view.KeyEvent;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import eu.siacs.conversations.Config;
@ -29,7 +25,10 @@ import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.util.DelayedHintHelper;
import rocks.xmpp.addr.Jid;
public class EnterJidDialog extends DialogFragment implements OnBackendConnected {
public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
private static final List<String> SUSPICIOUS_DOMAINS = Arrays.asList("conference","muc","room","rooms","chat");
private OnEnterJidDialogPositiveListener mListener = null;
@ -39,12 +38,21 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
private static final String ACCOUNT_KEY = "account";
private static final String ALLOW_EDIT_JID_KEY = "allow_edit_jid";
private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
private static final String SANITY_CHECK_JID = "sanity_check_jid";
private KnownHostsAdapter knownHostsAdapter;
private EnterJidDialogBinding binding;
private AlertDialog dialog;
private boolean sanityCheckJid = false;
private boolean issuedWarning = false;
public static EnterJidDialog newInstance(final List<String> activatedAccounts,
final String title, final String positiveButton,
final String prefilledJid, final String account, boolean allowEditJid) {
final String prefilledJid, final String account,
boolean allowEditJid, final boolean sanity_check_jid) {
EnterJidDialog dialog = new EnterJidDialog();
Bundle bundle = new Bundle();
bundle.putString(TITLE_KEY, title);
@ -53,6 +61,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
bundle.putString(ACCOUNT_KEY, account);
bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
dialog.setArguments(bundle);
return dialog;
}
@ -77,9 +86,10 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getArguments().getString(TITLE_KEY));
EnterJidDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false);
binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false);
this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item);
binding.jid.setAdapter(this.knownHostsAdapter);
binding.jid.addTextChangedListener(this);
String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
if (prefilledJid != null) {
binding.jid.append(prefilledJid);
@ -90,6 +100,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
binding.jid.setCursorVisible(false);
}
}
sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
@ -110,14 +121,14 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
builder.setView(binding.getRoot());
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
AlertDialog dialog = builder.create();
this.dialog = builder.create();
View.OnClickListener dialogOnClick = v -> {
handleEnter(binding, account, dialog);
handleEnter(binding, account);
};
binding.jid.setOnEditorActionListener((v, actionId, event) -> {
handleEnter(binding, account, dialog);
handleEnter(binding, account);
return true;
});
@ -126,7 +137,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
return dialog;
}
private void handleEnter(EnterJidDialogBinding binding, String account, Dialog dialog) {
private void handleEnter(EnterJidDialogBinding binding, String account) {
final Jid accountJid;
if (!binding.account.isEnabled() && account == null) {
return;
@ -148,6 +159,21 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
return;
}
if (!issuedWarning && sanityCheckJid) {
if (contactJid.isDomainJid()) {
binding.jid.setError(getActivity().getString(R.string.this_looks_like_a_domain));
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
issuedWarning = true;
return;
}
if (suspiciousSubDomain(contactJid.getDomain())) {
binding.jid.setError(getActivity().getString(R.string.this_looks_like_channel));
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
issuedWarning = true;
return;
}
}
if (mListener != null) {
try {
if (mListener.onEnterJidDialogPositive(accountJid, contactJid)) {
@ -176,6 +202,24 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (issuedWarning) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
issuedWarning = false;
}
}
public interface OnEnterJidDialogPositiveListener {
boolean onEnterJidDialogPositive(Jid account, Jid contact) throws EnterJidDialog.JidError;
}
@ -200,4 +244,9 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
}
super.onDestroyView();
}
private static boolean suspiciousSubDomain(String domain) {
final String[] parts = domain.split("\\.");
return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
}
}

View File

@ -384,6 +384,7 @@ public class SettingsActivity extends XmppActivity implements
reconnectAccounts();
} else if (name.equals("use_tor")) {
reconnectAccounts();
xmppConnectionService.reinitializeMuclumbusService();
} else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
xmppConnectionService.expireOldMessages(true);
} else if (name.equals(THEME)) {

View File

@ -98,6 +98,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
private List<String> mActivatedAccounts = new ArrayList<>();
private EditText mSearchEditText;
private AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false);
private AtomicBoolean mOpenedFab = new AtomicBoolean(false);
private boolean mHideOfflineContacts = false;
private boolean createdByViewIntent = false;
private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
@ -304,6 +305,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
mInitialSearchValue.push("");
}
mRequestedContactsPermission.set(savedInstanceState != null && savedInstanceState.getBoolean("requested_contacts_permission",false));
mOpenedFab.set(savedInstanceState != null && savedInstanceState.getBoolean("opened_fab",false));
binding.speedDial.setOnActionSelectedListener(actionItem -> {
final String searchString = mSearchEditText != null ? mSearchEditText.getText().toString() : null;
final String prefilled;
@ -313,6 +315,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
prefilled = null;
}
switch (actionItem.getId()) {
case R.id.discover_public_channels:
startActivity(new Intent(this, ChannelDiscoveryActivity.class));
break;
case R.id.join_public_channel:
showJoinConferenceDialog(prefilled);
break;
@ -344,6 +349,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
Intent pendingIntent = pendingViewIntent.peek();
savedInstanceState.putParcelable("intent", pendingIntent != null ? pendingIntent : getIntent());
savedInstanceState.putBoolean("requested_contacts_permission",mRequestedContactsPermission.get());
savedInstanceState.putBoolean("opened_fab",mOpenedFab.get());
savedInstanceState.putBoolean("created_by_view_intent",createdByViewIntent);
if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null);
@ -489,7 +495,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
getString(R.string.add),
prefilledJid,
null,
invite == null || !invite.hasFingerprints()
invite == null || !invite.hasFingerprints(),
true
);
dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
@ -781,15 +788,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
this.mPostponedActivityResult = null;
}
this.mActivatedAccounts.clear();
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
if (Config.DOMAIN_LOCK != null) {
this.mActivatedAccounts.add(account.getJid().getLocal());
} else {
this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
}
}
}
this.mActivatedAccounts.addAll(AccountUtils.getEnabledAccounts(xmppConnectionService));
configureHomeButton();
Intent intent = pendingViewIntent.pop();
if (intent != null && processViewIntent(intent)) {
@ -809,6 +808,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (QuickConversationsService.isQuicksy()) {
setRefreshing(xmppConnectionService.getQuickConversationsService().isSynchronizing());
}
if (QuickConversationsService.isConversations() && AccountUtils.hasEnabledAccounts(xmppConnectionService) && this.contacts.size() == 0 && this.conferences.size() == 0 && mOpenedFab.compareAndSet(false,true)) {
binding.speedDial.open();
}
}
protected boolean processViewIntent(@NonNull Intent intent) {

View File

@ -0,0 +1,78 @@
package eu.siacs.conversations.ui.adapter;
import android.databinding.DataBindingUtil;
import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.ListAdapter;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.SearchResultItemBinding;
import eu.siacs.conversations.http.services.MuclumbusService;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
public class ChannelSearchResultAdapter extends ListAdapter<MuclumbusService.Room, ChannelSearchResultAdapter.ViewHolder> {
private OnChannelSearchResultSelected listener;
private static final DiffUtil.ItemCallback<MuclumbusService.Room> DIFF = new DiffUtil.ItemCallback<MuclumbusService.Room>() {
@Override
public boolean areItemsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) {
return a.address != null && a.address.equals(b.address);
}
@Override
public boolean areContentsTheSame(@NonNull MuclumbusService.Room a, @NonNull MuclumbusService.Room b) {
return a.equals(b);
}
};
public ChannelSearchResultAdapter() {
super(DIFF);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.search_result_item,viewGroup,false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final MuclumbusService.Room searchResult = getItem(position);
viewHolder.binding.name.setText(searchResult.getName());
final String description = searchResult.getDescription();
if (TextUtils.isEmpty(description)) {
viewHolder.binding.description.setVisibility(View.GONE);
} else {
viewHolder.binding.description.setText(description);
viewHolder.binding.description.setVisibility(View.VISIBLE);
}
viewHolder.binding.room.setText(searchResult.getRoom().asBareJid().toString());
AvatarWorkerTask.loadAvatar(searchResult, viewHolder.binding.avatar, R.dimen.avatar);
viewHolder.binding.getRoot().setOnClickListener(v -> listener.onChannelSearchResult(searchResult));
}
public void setOnChannelSearchResultSelectedListener(OnChannelSearchResultSelected listener) {
this.listener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final SearchResultItemBinding binding;
private ViewHolder(SearchResultItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
public interface OnChannelSearchResultSelected {
void onChannelSearchResult(MuclumbusService.Room result);
}
}

View File

@ -1,6 +1,5 @@
package eu.siacs.conversations.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.databinding.DataBindingUtil;
@ -8,7 +7,6 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -17,7 +15,6 @@ import android.widget.ImageView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
@ -26,7 +23,6 @@ import eu.siacs.conversations.databinding.MediaPreviewBinding;
import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.StyledAttributes;
public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {

View File

@ -32,6 +32,8 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.base.Strings;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
@ -70,6 +72,7 @@ import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.mam.MamReference;
import rocks.xmpp.addr.Jid;
public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {
@ -438,7 +441,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
body.setSpan(new DividerSpan(true), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
boolean startsWithQuote = handleTextQuotes(body, darkBackground);
if (message.getType() != Message.TYPE_PRIVATE) {
if (!message.isPrivateMessage()) {
if (hasMeCommand) {
body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
@ -448,13 +451,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
if (message.getStatus() <= Message.STATUS_RECEIVED) {
privateMarker = activity.getString(R.string.private_message);
} else {
final String to;
if (message.getCounterpart() != null) {
to = message.getCounterpart().getResource();
} else {
to = "";
}
privateMarker = activity.getString(R.string.private_message_to, to);
Jid cp = message.getCounterpart();
privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource()));
}
body.insert(0, privateMarker);
int privateMarkerIndex = privateMarker.length();
@ -505,27 +503,27 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
}
private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(text);
viewHolder.download_button.setOnClickListener(v -> ConversationFragment.downloadFile(activity, message));
}
private void displayOpenableMessage(ViewHolder viewHolder, final Message message) {
private void displayOpenableMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
viewHolder.download_button.setOnClickListener(v -> openDownloadable(message));
}
private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
private void displayLocationMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(R.string.show_location);
@ -533,8 +531,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
private void displayAudioMessage(ViewHolder viewHolder, Message message, boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.GONE);
final RelativeLayout audioPlayer = viewHolder.audioPlayer;
audioPlayer.setVisibility(View.VISIBLE);
@ -542,9 +540,9 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
this.audioPlayer.init(audioPlayer, message);
}
private void displayImageMessage(ViewHolder viewHolder, final Message message) {
private void displayImageMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.download_button.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
FileParams params = message.getFileParams();
@ -571,6 +569,25 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
viewHolder.image.setOnClickListener(v -> openDownloadable(message));
}
private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
if (message.isPrivateMessage()) {
final String privateMarker;
if (message.getStatus() <= Message.STATUS_RECEIVED) {
privateMarker = activity.getString(R.string.private_message);
} else {
Jid cp = message.getCounterpart();
privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource()));
}
final SpannableString body = new SpannableString(privateMarker);
body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
viewHolder.messageBody.setText(body);
viewHolder.messageBody.setVisibility(View.VISIBLE);
} else {
viewHolder.messageBody.setVisibility(View.GONE);
}
}
private void loadMoreMessages(Conversation conversation) {
conversation.setLastClearHistory(0, null);
activity.xmppConnectionService.updateConversation(conversation);
@ -721,19 +738,19 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
final Transferable transferable = message.getTransferable();
if (message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) {
if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) {
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)));
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), darkBackground);
} else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)));
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), darkBackground);
} else {
displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground);
}
} else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
if (message.getFileParams().width > 0 && message.getFileParams().height > 0) {
displayImageMessage(viewHolder, message);
displayImageMessage(viewHolder, message, darkBackground);
} else if (message.getFileParams().runtime > 0) {
displayAudioMessage(viewHolder, message, darkBackground);
} else {
displayOpenableMessage(viewHolder, message);
displayOpenableMessage(viewHolder, message, darkBackground);
}
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
if (account.isPgpDecryptionServiceConnected()) {
@ -755,7 +772,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
displayInfoMessage(viewHolder, activity.getString(R.string.omemo_decryption_failed), darkBackground);
} else {
if (message.isGeoUri()) {
displayLocationMessage(viewHolder, message);
displayLocationMessage(viewHolder, message, darkBackground);
} else if (message.bodyIsOnlyEmojis() && message.getType() != Message.TYPE_PRIVATE) {
displayEmojiMessage(viewHolder, message.getBody().trim(), darkBackground);
} else if (message.treatAsDownloadable()) {
@ -765,19 +782,22 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
displayDownloadableMessage(viewHolder,
message,
activity.getString(R.string.check_x_filesize,
UIHelper.getFileDescriptionString(activity, message)));
UIHelper.getFileDescriptionString(activity, message)),
darkBackground);
} else {
displayDownloadableMessage(viewHolder,
message,
activity.getString(R.string.check_x_filesize_on_host,
UIHelper.getFileDescriptionString(activity, message),
url.getHost()));
url.getHost()),
darkBackground);
}
} catch (Exception e) {
displayDownloadableMessage(viewHolder,
message,
activity.getString(R.string.check_x_filesize,
UIHelper.getFileDescriptionString(activity, message)));
UIHelper.getFileDescriptionString(activity, message)),
darkBackground);
}
} else {
displayTextMessage(viewHolder, message, darkBackground, type);

View File

@ -111,6 +111,15 @@ public class Attachment implements Parcelable {
this.uuid = UUID.randomUUID();
}
public static boolean canBeSendInband(final List<Attachment> attachments) {
for(Attachment attachment : attachments) {
if (attachment.type != Type.LOCATION) {
return false;
}
}
return true;
}
public static List<Attachment> of(final Context context, Uri uri, Type type) {
final String mime = type == Type.LOCATION ?null :MimeUtils.guessMimeTypeFromUri(context, uri);
return Collections.singletonList(new Attachment(uri, type, mime));

View File

@ -179,7 +179,11 @@ public final class MucDetailsContextMenuHelper {
activity.privateMsgInMuc(conversation, user.getName());
return true;
case R.id.invite:
activity.xmppConnectionService.directInvite(conversation, jid);
if (user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) {
activity.xmppConnectionService.directInvite(conversation, jid);
} else {
activity.xmppConnectionService.invite(conversation, jid);
}
return true;
default:
return false;

View File

@ -19,176 +19,184 @@ import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
public class EditMessage extends EmojiWrapperEditText {
private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source;
protected Handler mTypingHandler = new Handler();
protected KeyboardListener keyboardListener;
private OnCommitContentListener mCommitContentListener = null;
private String[] mimeTypes = null;
private boolean isUserTyping = false;
protected Runnable mTypingTimeout = new Runnable() {
@Override
public void run() {
if (isUserTyping && keyboardListener != null) {
keyboardListener.onTypingStopped();
isUserTyping = false;
}
}
};
private boolean lastInputWasTab = false;
private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
protected Handler mTypingHandler = new Handler();
protected KeyboardListener keyboardListener;
private OnCommitContentListener mCommitContentListener = null;
private String[] mimeTypes = null;
private boolean isUserTyping = false;
private final Runnable mTypingTimeout = new Runnable() {
@Override
public void run() {
if (isUserTyping && keyboardListener != null) {
keyboardListener.onTypingStopped();
isUserTyping = false;
}
}
};
private boolean lastInputWasTab = false;
public EditMessage(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditMessage(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditMessage(Context context) {
super(context);
}
public EditMessage(Context context) {
super(context);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent e) {
if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
lastInputWasTab = false;
if (keyboardListener != null && keyboardListener.onEnterPressed()) {
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
lastInputWasTab = true;
return true;
}
} else {
lastInputWasTab = false;
}
return super.onKeyDown(keyCode, e);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent e) {
if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
lastInputWasTab = false;
if (keyboardListener != null && keyboardListener.onEnterPressed()) {
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
lastInputWasTab = true;
return true;
}
} else {
lastInputWasTab = false;
}
return super.onKeyDown(keyCode, e);
}
@Override
public int getAutofillType() {
return AUTOFILL_TYPE_NONE;
}
@Override
public int getAutofillType() {
return AUTOFILL_TYPE_NONE;
}
@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
lastInputWasTab = false;
if (this.mTypingHandler != null && this.keyboardListener != null) {
this.mTypingHandler.removeCallbacks(mTypingTimeout);
this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
final int length = text.length();
if (!isUserTyping && length > 0) {
this.isUserTyping = true;
this.keyboardListener.onTypingStarted();
} else if (length == 0) {
this.isUserTyping = false;
this.keyboardListener.onTextDeleted();
}
this.keyboardListener.onTextChanged();
}
}
public void setKeyboardListener(KeyboardListener listener) {
this.keyboardListener = listener;
if (listener != null) {
this.isUserTyping = false;
}
}
@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
lastInputWasTab = false;
if (this.mTypingHandler != null && this.keyboardListener != null) {
executor.execute(() -> triggerKeyboardEvents(text.length()));
}
}
@Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
} else {
Editable editable = getEditableText();
InputFilter[] filters = editable.getFilters();
InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
if (filters != null) {
System.arraycopy(filters, 0, tempFilters, 1, filters.length);
}
tempFilters[0] = SPAN_FILTER;
editable.setFilters(tempFilters);
try {
return super.onTextContextMenuItem(id);
} finally {
editable.setFilters(filters);
}
}
} else {
return super.onTextContextMenuItem(id);
}
}
private void triggerKeyboardEvents(final int length) {
this.mTypingHandler.removeCallbacks(mTypingTimeout);
this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
if (!isUserTyping && length > 0) {
this.isUserTyping = true;
this.keyboardListener.onTypingStarted();
} else if (length == 0) {
this.isUserTyping = false;
this.keyboardListener.onTextDeleted();
}
this.keyboardListener.onTextChanged();
}
public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
this.mimeTypes = mimeTypes;
this.mCommitContentListener = listener;
}
public void setKeyboardListener(KeyboardListener listener) {
this.keyboardListener = listener;
if (listener != null) {
this.isUserTyping = false;
}
}
public void insertAsQuote(String text) {
text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", "");
Editable editable = getEditableText();
int position = getSelectionEnd();
if (position == -1) position = editable.length();
if (position > 0 && editable.charAt(position - 1) != '\n') {
editable.insert(position++, "\n");
}
editable.insert(position, text);
position += text.length();
editable.insert(position++, "\n");
if (position < editable.length() && editable.charAt(position) != '\n') {
editable.insert(position, "\n");
}
setSelection(position);
}
@Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
} else {
Editable editable = getEditableText();
InputFilter[] filters = editable.getFilters();
InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
if (filters != null) {
System.arraycopy(filters, 0, tempFilters, 1, filters.length);
}
tempFilters[0] = SPAN_FILTER;
editable.setFilters(tempFilters);
try {
return super.onTextContextMenuItem(id);
} finally {
editable.setFilters(filters);
}
}
} else {
return super.onTextContextMenuItem(id);
}
}
@Override
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
final InputConnection ic = super.onCreateInputConnection(editorInfo);
public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
this.mimeTypes = mimeTypes;
this.mCommitContentListener = listener;
}
if (mimeTypes != null && mCommitContentListener != null && ic != null) {
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes));
} else {
return ic;
}
}
public void insertAsQuote(String text) {
text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", "");
Editable editable = getEditableText();
int position = getSelectionEnd();
if (position == -1) position = editable.length();
if (position > 0 && editable.charAt(position - 1) != '\n') {
editable.insert(position++, "\n");
}
editable.insert(position, text);
position += text.length();
editable.insert(position++, "\n");
if (position < editable.length() && editable.charAt(position) != '\n') {
editable.insert(position, "\n");
}
setSelection(position);
}
public void refreshIme() {
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext());
final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key));
final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
@Override
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
final InputConnection ic = super.onCreateInputConnection(editorInfo);
if (usingEnterKey && enterIsSend) {
setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else if (usingEnterKey) {
setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else {
setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
}
}
if (mimeTypes != null && mCommitContentListener != null && ic != null) {
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes));
} else {
return ic;
}
}
public interface OnCommitContentListener {
boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
}
public void refreshIme() {
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext());
final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key));
final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
public interface KeyboardListener {
boolean onEnterPressed();
if (usingEnterKey && enterIsSend) {
setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else if (usingEnterKey) {
setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
} else {
setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
}
}
void onTypingStarted();
public interface OnCommitContentListener {
boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
}
void onTypingStopped();
public interface KeyboardListener {
boolean onEnterPressed();
void onTextDeleted();
void onTypingStarted();
void onTextChanged();
void onTypingStopped();
boolean onTabPressed(boolean repeated);
}
void onTextDeleted();
void onTextChanged();
boolean onTabPressed(boolean repeated);
}
}

View File

@ -6,8 +6,10 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
@ -22,6 +24,30 @@ public class AccountUtils {
}
public static boolean hasEnabledAccounts(final XmppConnectionService service) {
final List<Account> accounts = service.getAccounts();
for(Account account : accounts) {
if (account.isOptionSet(Account.OPTION_DISABLED)) {
return false;
}
}
return false;
}
public static List<String> getEnabledAccounts(final XmppConnectionService service) {
ArrayList<String> accounts = new ArrayList<>();
for (Account account : service.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
if (Config.DOMAIN_LOCK != null) {
accounts.add(account.getJid().getLocal());
} else {
accounts.add(account.getJid().asBareJid().toString());
}
}
}
return accounts;
}
public static Account getFirstEnabled(XmppConnectionService service) {
final List<Account> accounts = service.getAccounts();
for(Account account : accounts) {

View File

@ -914,7 +914,7 @@ public class XmppConnection implements Runnable {
private void sendRegistryRequest() {
final IqPacket register = new IqPacket(IqPacket.TYPE.GET);
register.query("jabber:iq:register");
register.query(Namespace.REGISTER);
register.setTo(Jid.of(account.getServer()));
sendUnmodifiedIqPacket(register, (account, packet) -> {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
@ -923,12 +923,12 @@ public class XmppConnection implements Runnable {
if (packet.getType() == IqPacket.TYPE.ERROR) {
throw new StateChangingError(Account.State.REGISTRATION_FAILED);
}
final Element query = packet.query("jabber:iq:register");
final Element query = packet.query(Namespace.REGISTER);
if (query.hasChild("username") && (query.hasChild("password"))) {
final IqPacket register1 = new IqPacket(IqPacket.TYPE.SET);
final Element username = new Element("username").setContent(account.getUsername());
final Element password = new Element("password").setContent(account.getPassword());
register1.query("jabber:iq:register").addChild(username);
register1.query(Namespace.REGISTER).addChild(username);
register1.query().addChild(password);
register1.setFrom(account.getJid().asBareJid());
sendUnmodifiedIqPacket(register1, registrationResponseListener, true);

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="?attr/color_background_primary"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:layout_anchor="@+id/list" />
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_background_primary">
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_background_primary"
android:orientation="vertical"
android:scrollbars="vertical"
android:visibility="gone"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
</layout>

View File

@ -2,8 +2,7 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_background_secondary">
@ -14,6 +13,7 @@
android:layout_height="wrap_content"
android:layout_above="@+id/snackbar"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="?attr/color_background_secondary"
android:divider="@null"
@ -21,23 +21,21 @@
android:listSelector="@android:color/transparent"
android:stackFromBottom="true"
android:transcriptMode="normal"
tools:listitem="@layout/message_sent"
android:layout_alignParentLeft="true">
</ListView>
tools:listitem="@layout/message_sent"></ListView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/scroll_to_bottom_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignBottom="@+id/messages_view"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:alpha="0.85"
app:backgroundTint="?attr/color_background_primary"
android:src="?attr/icon_scroll_down"
app:fabSize="mini"
android:visibility="gone"
app:useCompatPadding="true"
android:layout_alignParentRight="true" />
app:backgroundTint="?attr/color_background_primary"
app:fabSize="mini"
app:useCompatPadding="true" />
<eu.siacs.conversations.ui.widget.UnreadCountCustomView
android:id="@+id/unread_count_custom_view"
@ -45,75 +43,86 @@
android:layout_height="?attr/IconSize"
android:layout_alignTop="@+id/scroll_to_bottom_button"
android:layout_alignEnd="@+id/scroll_to_bottom_button"
android:layout_alignRight="@+id/scroll_to_bottom_button"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:elevation="8dp"
android:visibility="gone"
app:backgroundColor="?attr/unread_count"
android:layout_alignRight="@+id/scroll_to_bottom_button"
tools:ignore="RtlCompat"
android:layout_marginRight="8dp" />
tools:ignore="RtlCompat" />
<RelativeLayout
android:id="@+id/textsend"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="?attr/color_background_primary"
android:layout_alignParentLeft="true">
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:background="?attr/color_background_primary">
<android.support.v7.widget.RecyclerView
android:visibility="gone"
android:id="@+id/media_preview"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/textSendButton"
tools:listitem="@layout/media_preview"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:paddingTop="8dp"
android:requiresFadingEdge="horizontal"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/textSendButton">
</android.support.v7.widget.RecyclerView>
<eu.siacs.conversations.ui.widget.EditMessage
android:id="@+id/textinput"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/textSendButton"
android:background="?attr/color_background_primary"
android:ems="10"
style="@style/Widget.Conversations.EditText"
android:imeOptions="flagNoExtractUi|actionSend"
android:inputType="textShortMessage|textMultiLine|textCapSentences"
android:maxLines="8"
android:minHeight="48dp"
android:minLines="1"
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/textSendButton">
android:layout_toLeftOf="@+id/textSendButton"
android:orientation="vertical">
<requestFocus/>
</eu.siacs.conversations.ui.widget.EditMessage>
<TextView
android:id="@+id/text_input_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:maxLines="1"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="@style/TextAppearance.Conversations.Caption.Highlight"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="@+id/media_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp"
android:requiresFadingEdge="horizontal"
android:visibility="gone"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="@layout/media_preview">
</android.support.v7.widget.RecyclerView>
<eu.siacs.conversations.ui.widget.EditMessage
android:id="@+id/textinput"
style="@style/Widget.Conversations.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/color_background_primary"
android:ems="10"
android:imeOptions="flagNoExtractUi|actionSend"
android:inputType="textShortMessage|textMultiLine|textCapSentences"
android:maxLines="8"
android:minHeight="48dp"
android:minLines="1"
android:padding="8dp">
<requestFocus />
</eu.siacs.conversations.ui.widget.EditMessage>
</LinearLayout>
<ImageButton
android:id="@+id/textSendButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="?attr/color_background_primary"
android:contentDescription="@string/send_message"
android:src="?attr/ic_send_text_offline"
android:layout_alignParentRight="true" />
android:src="?attr/ic_send_text_offline" />
</RelativeLayout>
<RelativeLayout
@ -121,9 +130,9 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/textsend"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
android:background="@drawable/snackbar"
android:minHeight="48dp"
android:visibility="gone">
@ -133,28 +142,28 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/snackbar_action"
android:layout_toLeftOf="@+id/snackbar_action"
android:paddingStart="24dp"
android:textAppearance="@style/TextAppearance.Conversations.Body1.OnDark"
android:layout_alignParentLeft="true"
android:paddingLeft="24dp"
android:layout_toLeftOf="@+id/snackbar_action" />
android:textAppearance="@style/TextAppearance.Conversations.Body1.OnDark" />
<TextView
android:id="@+id/snackbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingBottom="16dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="16dp"
android:paddingRight="24dp"
android:paddingBottom="16dp"
android:textAllCaps="true"
android:textAppearance="@style/TextAppearance.Conversations.Body1.OnDark"
android:textStyle="bold"
android:layout_alignParentRight="true" />
android:textStyle="bold" />
</RelativeLayout>
</RelativeLayout>

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<eu.siacs.conversations.ui.widget.CopyTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:longClickable="true"
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
<ImageView
android:id="@+id/message_image"
android:layout_width="wrap_content"
@ -12,14 +20,6 @@
android:longClickable="true"
android:scaleType="centerCrop"/>
<eu.siacs.conversations.ui.widget.CopyTextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:longClickable="true"
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
<Button
android:id="@+id/download_button"
style="?android:attr/buttonStyleSmall"

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:padding="@dimen/list_padding">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/avatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:scaleType="centerCrop"
app:riv_corner_radius="2dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/avatar"
android:orientation="vertical"
android:layout_marginLeft="@dimen/avatar_item_distance">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Conversations.Subhead"/>
<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
<TextView
android:id="@+id/room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"/>
</LinearLayout>
</RelativeLayout>
</layout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
app:actionLayout="@layout/actionview_search"
android:icon="?attr/icon_search"
app:showAsAction="collapseActionView|always"
android:title="@string/search"/>
</menu>

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/discover_public_channels"
android:title="@string/discover_channels"
android:icon="@drawable/ic_search_white_24dp"/>
<item
android:id="@+id/join_public_channel"
android:title="@string/join_public_channel"

View File

@ -501,9 +501,6 @@ missatges.\n\nAra se us demanarà que desactiveu-las.</string>
<string name="security_error_invalid_file_access">Error de seguretat: accés al fitxer no vàlid</string>
<string name="no_application_to_share_uri">No s\'ha trobat cap aplicació que comparteixi URI</string>
<string name="share_uri_with">Comparteix l\'URI amb…</string>
<string name="welcome_text">Jabber és una xarxa de missatgeria instantània independent del proveïdor. Podeu
utilitzar aquest client amb el servidor de Jabber que trieu.\nPerò
per a la seva comoditat hem facilitat crear un compte a converses.im¹; un proveïdor especialment indicat per a l\'ús amb Conversations.</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy és un spin off de les populars Converses client Jabber/XMPP amb detecció automàtica de contactes. <br> <br> Vostè es registra amb el seu número de telèfon i Quicksy es basarà automàticament en els números de telèfon de la seva llibreta d\'adreces per suggerir-li possibles contactes. <br> <br> En registrar-se vostè accepta la nostra <a href="https://quicksy.im/#privacy"> política de privadesa </a>. ]]></string>
<string name="magic_create_text">Us guiarem pel procés de creació d\'un compte a chat.sum7.eu.\nQuan
trieu chat.sum7.eu com a proveïdor, podreu comunicar-vos amb els usuaris

View File

@ -526,7 +526,6 @@
<string name="security_error_invalid_file_access">Sicherheitsfehler: Dateizugriff nicht erlaubt</string>
<string name="no_application_to_share_uri">Keine Anwendung zum Teilen der URI gefunden</string>
<string name="share_uri_with">Teile URI mit…</string>
<string name="welcome_text">XMPP ist ein anbieterunabhängiges Instant Messaging Netzwerk. Du kannst diesen Client mit jedem beliebigen XMPP-Server nutzen.\nUm es dir leicht zu machen, haben wir die Möglichkeit geschaffen, ein Konto auf chat.sum7.eu anzulegen; ein Anbieter, der speziell für die Verwendung mit Conversations geeignet ist.</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy ist ein Ableger des beliebten XMPP-Clients Conversations mit automatischer Kontakterkennung.<br><br>Du registrierst dich mit deiner Telefonnummer und Quicksy wird automatisch auf der Grundlage der Telefonnummern in deinem Adressbuch mögliche Kontakte vorschlagen.<br><br>Mit der Anmeldung erklärst du dich mit unserer <a href="https://quicksy.im/#privacy">Datenschutzerklärung</a> einverstanden.]]></string>
<string name="agree_and_continue">Zustimmen &amp; fortfahren</string>
<string name="magic_create_text">Wir führen dich durch den Prozess der Kontoerstellung auf chat.sum7.eu.\nWenn du chat.sum7.eu als Provider wählst, kannst du mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst.</string>
@ -704,7 +703,7 @@
<string name="medium">Mittel</string>
<string name="large">Groß</string>
<string name="not_encrypted_for_this_device">Die Nachricht wurde nicht für dieses Gerät verschlüsselt.</string>
<string name="omemo_decryption_failed">Die OMEMO-Nachricht konnte nicht entschlüsselt werden.</string>
<string name="omemo_decryption_failed">OMEMO-Nachricht konnte nicht entschlüsselt werden.</string>
<string name="undo">rückgängig</string>
<string name="location_disabled">Standort teilen ist deaktiviert</string>
<string name="action_fix_to_location">Position fixieren</string>
@ -754,7 +753,7 @@
<string name="view_users">Teilnehmer anzeigen</string>
<string name="group_chat_members">Teilnehmer</string>
<string name="media_browser">Medienbrowser</string>
<string name="security_violation_not_attaching_file">Die Datei wurde aufgrund von Sicherheitsverletzungen ausgelassen.</string>
<string name="security_violation_not_attaching_file">Datei wurde aufgrund von Sicherheitsverletzungen ausgelassen.</string>
<string name="pref_video_compression">Videoqualität</string>
<string name="pref_video_compression_summary">Geringere Qualität ermöglicht kleinere Dateien</string>
<string name="video_360p">Mittel (360p)</string>
@ -853,4 +852,14 @@
<string name="search_participants">Teilnehmer suchen</string>
<string name="file_too_large">Datei ist zu groß</string>
<string name="attach">Hinzufügen</string>
<string name="discover_channels">Channels entdecken</string>
<string name="search_channels">Channels suchen</string>
<string name="channel_discovery_opt_in_title">Mögliche Datenschutzverletzung!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Die Channel-Entdeckung verwendet einen Drittanbieterservice namens <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der <a href="https://search.jabbercat.org/privacy">Datenschutzerklärung</a>.]]></string>
<string name="i_already_have_an_account">Ich habe bereits ein Konto</string>
<string name="add_existing_account">Vorhandenes Konto hinzufügen</string>
<string name="register_new_account">Neues Konto erstellen</string>
<string name="this_looks_like_a_domain">Dies sieht aus wie eine Domain-Adresse</string>
<string name="add_anway">Trotzdem hinzufügen</string>
<string name="this_looks_like_channel">Dies sieht aus wie eine Channel-Adresse</string>
</resources>

View File

@ -522,8 +522,6 @@
<string name="pref_theme_options_summary">Επιλογή παλέτας χρωμάτων</string>
<string name="pref_theme_light">Ανοιχτόχρωμο θέμα</string>
<string name="pref_theme_dark">Σκουρόχρωμο θέμα</string>
<string name="pref_use_green_background">Πράσινο φόντο</string>
<string name="pref_use_green_background_summary">Χρήση πράσινου φόντου για εισερχόμενα μηνύματα</string>
<string name="unable_to_connect_to_keychain">Αδυναμία σύνδεσης στο OpenKeychain</string>
<string name="this_device_is_no_longer_in_use">Αυτή η συσκευή δεν χρησιμοποιείται πλέον</string>
<string name="type_pc">Υπολογιστής</string>

View File

@ -526,7 +526,6 @@
<string name="security_error_invalid_file_access">Error de seguridad: Acceso a fichero inválido</string>
<string name="no_application_to_share_uri">No se ha encontrado ninguna aplicación para compartir la URI</string>
<string name="share_uri_with">Compartir URI con...</string>
<string name="welcome_text">XMPP es una red de mensajería instantánea independiente del proveedor. Puedes usar este cliente con cualquier servidor XMPP que elijas.\nSin embargo, para tu conveniencia, hacemos de forma sencilla la creación de una cuenta en chat.sum7.eu; un proveedor especialmente especializado para el uso con Conversations</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy es un derivado del popular cliente XMPP Conversations con detección automática de contactos.<br><br>El registro se realiza con tu número de teléfono y Quicksy automáticamente—basado en los teléfonos de tu agenda de contactos—te sugerirá posibles contactos.<br><br>Registrándote en Quicksy aceptas nuestra <a href="https://quicksy.im/#privacy">política de privacidad</a>.]]> </string>
<string name="agree_and_continue">Aceptar &amp; continuar</string>
<string name="magic_create_text">Te guiaremos durante el proceso de creación de la cuenta en chat.sum7.eu.\nCuando selecciones chat.sum7.eu como proveedor podrás comunicarte con usuarios de otros servidores proporcionándoles tu dirección XMPP completa. </string>
@ -853,4 +852,14 @@
<string name="search_participants">Buscar participantes</string>
<string name="file_too_large">Archivo demasiado grande</string>
<string name="attach">Adjuntar</string>
<string name="discover_channels">Descubrir canales</string>
<string name="search_channels">Buscar canales</string>
<string name="channel_discovery_opt_in_title">¡Posible violación de privacidad!</string>
<string name="channel_discover_opt_in_message"><![CDATA[La búsqueda de canales usa un servicio de terceros llamado search.jabbercat.org<a href="https://search.jabbercat.org">. <br><br>Usando esta funcionalidad transmitirás tu dirección IP y los términos buscados a este servicio. Ver su <a href="https://search.jabbercat.org/privacy">Política de Privacidad</a> para más información.]]></string>
<string name="i_already_have_an_account">Ya tengo una cuenta</string>
<string name="add_existing_account">Añadir una cuenta existente</string>
<string name="register_new_account">Registrar una cuenta nueva</string>
<string name="this_looks_like_a_domain">Esto parece una dirección de dominio</string>
<string name="add_anway">Añadir de todas formas</string>
<string name="this_looks_like_channel">Esto parece una dirección de un canal</string>
</resources>

View File

@ -502,6 +502,7 @@
<string name="no_storage_permission">Conversationsek kanpoko biltegirako sarbidea behar du</string>
<string name="no_camera_permission">Conversationsek kamerarako sarbidea behar du</string>
<string name="sync_with_contacts">Kontaktuekin sinkronizatu</string>
<string name="sync_with_contacts_long">Conversationsek zerbitzariko zure kontaktuen zerrenda eta zure helbide liburu lokala uztartu nahi ditu haien izenak eta argazkiak erakusteko.\n\nConversationsek zure kontaktuak modu lokalean soilik irakurri eta uztartuko ditu, zure zerbitzarira kargatu gabe.\n\nJarraian baimenak eskatuko zaizkizu zure kontaktuetara sartu ahal izateko.</string>
<string name="notify_on_all_messages">Mezu guztiak jakinarazi</string>
<string name="notify_only_when_highlighted">Jakinarazi aipatua izaterakoan soilik</string>
<string name="notify_never">Jakinarazpenak ezgaituta</string>
@ -525,6 +526,7 @@
<string name="no_application_to_share_uri">Ez da aplikaziorik aurkitu URIa partekatzeko</string>
<string name="share_uri_with">URIa honekin partekatu...</string>
<string name="agree_and_continue">Onartu eta jarraitu</string>
<string name="magic_create_text">conversations.im-en kontu bat sortzeko prozesuan zehar gidatuko zaitugu.\nconversations.im¹ hornitzaile bezala aukeratzerakoan beste hornitzaileen erabiltzaileekin komunikatzeko gai izango zara haiei zure XMPP helbide osoa emanez.</string>
<string name="your_full_jid_will_be">Zure XMPP helbide osoa hau izango da: %s</string>
<string name="create_account">Kontua sortu</string>
<string name="use_own_provider">Nire hornitzale propioa erabili</string>
@ -815,6 +817,7 @@
<string name="restore_backup">Babes-kopia berrezarri</string>
<string name="restore">Berrezarri</string>
<string name="enter_password_to_restore">Sartu %s kontuaren pasahitza babes-kopia berrezartzeko.</string>
<string name="restore_warning">Ez erabili babes-kopiak berrezartzeko ezaugarria instalazio bat klonatzeko (aldi berean exekutatzeko). Babes-kopia bat berrezartzea migrazioetarako edo jatorrizko gailua galdu duzunerako da soilik.</string>
<string name="unable_to_restore_backup">Ezin izan da babes-kopia berrezarri.</string>
<string name="unable_to_decrypt_backup">Ezin izan da babes-kopia desenkriptatu. Pasahitza zuzena da?</string>
<string name="backup_channel_name">Babes-kopiak egin eta berrezarri</string>
@ -846,4 +849,5 @@
<string name="manage_permission">Baimenak kudeatu</string>
<string name="search_participants">Parte-hartzaileak bilatu</string>
<string name="file_too_large">Fitxategia handiegia da</string>
<string name="attach">Erantsi</string>
</resources>

View File

@ -229,7 +229,7 @@
<string name="delete_bookmark">Supprimer le favori</string>
<string name="destroy_room">Détruire le chat en groupe</string>
<string name="destroy_channel">Détruire le canal</string>
<string name="destroy_room_dialog">Voulez-vous vraiment détruire cette discussion de groupe? \ N \ n<b>Avertissement:</b> la discussion de groupe sera complètement supprimée du serveur.</string>
<string name="destroy_room_dialog">Voulez-vous vraiment détruire cette discussion de groupe?\n\n<b>Avertissement:</b> la discussion de groupe sera complètement supprimée du serveur.</string>
<string name="destroy_channel_dialog">Êtes-vous sûr de vouloir détruire ce canal public? \ On \ <b>Avertissement:</b> le canal sera complètement supprimé du serveur.</string>
<string name="could_not_destroy_room">Impossible de détruire le chat en groupe</string>
<string name="could_not_destroy_channel">Impossible de détruire le canal</string>
@ -502,8 +502,8 @@
<string name="no_storage_permission">Conversations a besoin d\'accéder au stockage externe</string>
<string name="no_camera_permission">Conversations a besoin d\'accéder à l\'appareil photo</string>
<string name="sync_with_contacts">Synchroniser avec contacts</string>
<string name="sync_with_contacts_long">Conversations veut faire correspondre votre liste de contacts côté serveur avec votre carnet d\'adresses local pour afficher les noms complets et les avatars de vos contacts. \ N \ nLes conversions liront uniquement vos contacts et les feront correspondre localement sans les télécharger sur votre serveur. \ N \ nVous allez maintenant être invité à donner la permission d\'accéder à vos contacts.</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy a besoin d\'accéder aux numéros de téléphone de vos contacts pour vous suggérer des contacts éventuels déjà connectés.<br><br> Nous ne stockerons pas de copie de ces numéros. \ N \ nPour plus d\'informations, lisez notre <a href="https://quicksy.im/#privacy">politique de confidentialité</a>. <br><br>maintenant être invité à donner la permission d\'accéder à vos contacts.]]></string>
<string name="sync_with_contacts_long">Conversations veut faire correspondre votre liste de contacts côté serveur avec votre carnet d\'adresses local pour afficher les noms complets et les avatars de vos contacts.\n\nLes conversions liront uniquement vos contacts et les feront correspondre localement sans les télécharger sur votre serveur.\n\nVous allez maintenant être invité à donner la permission d\'accéder à vos contacts.</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy a besoin d\'accéder aux numéros de téléphone de vos contacts pour vous suggérer des contacts éventuels déjà connectés.<br><br> Nous ne stockerons pas de copie de ces numéros.\n\nPour plus d\'informations, lisez notre <a href="https://quicksy.im/#privacy">politique de confidentialité</a>. <br><br>maintenant être invité à donner la permission d\'accéder à vos contacts.]]></string>
<string name="notify_on_all_messages">Notifier pour tous les messages</string>
<string name="notify_only_when_highlighted">Notifier seulement en cas de mention</string>
<string name="notify_never">Notifications désactivées</string>
@ -526,10 +526,9 @@
<string name="security_error_invalid_file_access">Erreur de sécurité : accès fichier non-valide</string>
<string name="no_application_to_share_uri">Aucune application trouvée pour partager l\'URI</string>
<string name="share_uri_with">Partager l\'URI avec...</string>
<string name="welcome_text">XMPP est un réseau de messagerie instantanée indépendant du fournisseur. Vous pouvez utiliser ce client avec le serveur XMPP de votre choix. \ NCependant, pour votre commodité, nous avons facilité la création d\'un compte pour les chat.sum7.eu; un fournisseur spécialement adapté à l\'utilisation avec Conversations.</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy est une version du populaire client XMPP Conversations avec découverte automatique des contacts.<br><br>Vous vous inscrivez avec votre numéro de téléphone et Quicksy va automatiquement, en fonction des numéros de votre carnet dadresses, vous suggérer déventuels contacts.<br><br> en vous inscrivant, vous acceptez notre <a href="https://quicksy.im/#privacy">politique de confidentialité</a>.]]></string>
<string name="agree_and_continue">Accepter &amp; continuer</string>
<string name="magic_create_text">Nous vous guiderons tout au long du processus de création d\'un compte sur chat.sum7.eu. \nLorsque vous sélectionnerez chat.sum7.eu en tant que fournisseur, vous pourrez communiquer avec les utilisateurs d\'autres fournisseurs en leur fournissant votre adresse XMPP complète.</string>
<string name="magic_create_text">Nous vous guiderons tout au long du processus de création d\'un compte sur chat.sum7.eu.\nLorsque vous sélectionnerez chat.sum7.eu en tant que fournisseur, vous pourrez communiquer avec les utilisateurs d\'autres fournisseurs en leur fournissant votre adresse XMPP complète.</string>
<string name="your_full_jid_will_be">Votre adresse XMPP complète sera : %s</string>
<string name="create_account">Créer un compte</string>
<string name="use_own_provider">Utiliser votre propre fournisseur</string>
@ -852,4 +851,15 @@
<string name="manage_permission">Gérer les privilèges</string>
<string name="search_participants">Rechercher des participants</string>
<string name="file_too_large">Fichier trop volumineux</string>
<string name="attach">Joindre</string>
<string name="discover_channels">Découverte des canaux</string>
<string name="search_channels">Recherche des canaux</string>
<string name="channel_discovery_opt_in_title">Violation possible de la confidentialité !</string>
<string name="channel_discover_opt_in_message"><![CDATA[La découverte de chaînes utilise un service tiers appelé <a href="https://search.jabbercat.org">search.jabbercat.org</a> qui transmet votre adresse IP et les termes de recherche à ce service. Voir leur <a href="https://search.jabbercat.org/privacy">politique de confidentialité</a> pour plus d\'informations]]>.</string>
<string name="i_already_have_an_account">J\'ai déjà un compte</string>
<string name="add_existing_account">Ajouter un compte existant</string>
<string name="register_new_account">Enregistrer un nouveau compte</string>
<string name="this_looks_like_a_domain">Ceci ressemble à une adresse de domaine</string>
<string name="add_anway">Ajouter quand même</string>
<string name="this_looks_like_channel">Ceci ressemble à une adresse de canal</string>
</resources>

View File

@ -526,7 +526,6 @@
<string name="security_error_invalid_file_access">Fallo na seguridade: Acceso non válido ao ficheiro</string>
<string name="no_application_to_share_uri">Non se atopou un aplicativo para compartir URI</string>
<string name="share_uri_with">Compartir URI con...</string>
<string name="welcome_text">XMPP é un fornecedor de mensaxería instantánea independente da rede. Pode utilizar este cliente con calquera servidor XMPP que escolla.\nPorén, para a súa conveniencia, puxémolo fácil para crear unha conta en chat.sum7.eu; un fornecedor especialmente axeitado para o uso con Conversations.</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy é un derivado do popular cliente XMPP Conversations con descubrimento automático de contactos.<br><br>Pode rexistrarse co seu número de teléfono e Quicksy suxeriralle automáticamente —tomando os números da súa libreta de enderezos como referencia— posibles contactos para vostede.<br><br>Ao rexistarse vostede acepta a nosa <a href="https://quicksy.im/#privacy">política de intimidade</a>.]]></string>
<string name="agree_and_continue">Aceptar &amp; continuar</string>
<string name="magic_create_text">Guiarémola a través do proceso de creación de unha conta en chat.sum7.eu.\nAo escoller a chat.sum7.eu como fornecedor poderá comunicar con usuarias de outros fornecedores proporcionándolles o seu enderezo XMPP completo.</string>
@ -853,4 +852,14 @@
<string name="search_participants">Buscar participantes</string>
<string name="file_too_large">Ficheiro demasiado grande</string>
<string name="attach">Anexar</string>
<string name="discover_channels">Descubrir canles</string>
<string name="search_channels">Buscar canles</string>
<string name="channel_discovery_opt_in_title">Posible intrusión na intimidade!</string>
<string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canles utiliza un servizo de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao utilizar esta característica transmitirá o seu enderezo IP e os termos de busca a ese servizo. Lea a súa <a href="https://search.jabbercat.org/privacy">Política de Intimidade</a> para máis información.]]></string>
<string name="i_already_have_an_account">Xa teño unha conta</string>
<string name="add_existing_account">Engadir conta existente</string>
<string name="register_new_account">Rexistrar unha nova conta</string>
<string name="this_looks_like_a_domain">Esto semella un enderezo de dominio</string>
<string name="add_anway">Engadir igualmente</string>
<string name="this_looks_like_channel">Esto semella o enderezo de unha canle</string>
</resources>

View File

@ -526,7 +526,6 @@
<string name="security_error_invalid_file_access">Biztonsági hiba: Érvénytelen fájl hozzáférés</string>
<string name="no_application_to_share_uri">Nem találtunk URI-t használó alkalmazást</string>
<string name="share_uri_with">Megosztás URI-ként</string>
<string name="welcome_text">Az XMPP egy szolgáltató független, azonnali üzenetküldő hálózat. Ezt az ügyfél programot bármelyik XMPP szerverhez használhatja.\nDe a könnyebbség kedvéért létrehozhat egy fiókot a chat.sum7.eu szolgáltatón is; ami kifejezetten a Conversations programmal való használatra lett tervezve.</string>
<string name="welcome_text_quicksy"><![CDATA[A Quicksy a népszerű Conversations XMPP kliens mellékhajtása automatikus kapcsolat felfedezéssel.<br><br>Regisztráljon a telefonszámával, és a Quicksy automatikusan a címjegyzékében szereplő telefonszámok alapján jelzi a lehetséges kapcsolatokat.<br><br>A regisztrációval elfogadja az<a href="https://quicksy.im/#privacy">adatvédelmi irányelveinket</a>.]]></string>
<string name="agree_and_continue">Egyetért &amp; folytatás</string>
<string name="magic_create_text">Végigvezetjük a chat.sum7.eu-en való fiók létrehozás folyamatán.\nHa a chat.sum7.eu-et választja szolgáltatójának, akkor képes lesz kommunikálni a más szolgáltatóknál lévő felhasználókkal, ha megadja nekik a teljes XMPP címét.</string>
@ -852,4 +851,15 @@
<string name="manage_permission">Jogosultságok kezelése</string>
<string name="search_participants">Résztvevők keresése</string>
<string name="file_too_large">A fájl túl nagy</string>
<string name="attach">Csatol</string>
<string name="discover_channels">Csatornák felderítése</string>
<string name="search_channels">Csatornák keresése</string>
<string name="channel_discovery_opt_in_title">Magánélet lehetséges megsértése!</string>
<string name="channel_discover_opt_in_message"><![CDATA[A csatorna felderítés egy harmadik fél által biztosított szolgáltatást használ: <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ezen funkció használata során át fog kerülni az IP címe és a keresési kifejezés ahhoz a szolgáltatáshoz. További információért tekintse meg az <a href="https://search.jabbercat.org/privacy">Adatvédelmi Irányelveiket</a>.]]></string>
<string name="i_already_have_an_account">Már rendelkezem fiókkal</string>
<string name="add_existing_account">Már létező fiók hozzáadása</string>
<string name="register_new_account">Új fiók létrehozása</string>
<string name="this_looks_like_a_domain">Ez egy domain címnek tűnik</string>
<string name="add_anway">Akkor is adja hozzá</string>
<string name="this_looks_like_channel">Ez egy csatorna címnek tűnik</string>
</resources>

View File

@ -526,7 +526,6 @@
<string name="security_error_invalid_file_access">Errore di sicurezza: accesso file non valido</string>
<string name="no_application_to_share_uri">Nessuna applicazione trovata per condividere l\'URI</string>
<string name="share_uri_with">Condividi l\'URI con...</string>
<string name="welcome_text">XMPP è una rete di messaggeria istantanea indipendente dal provider. Puoi usare questo client con qualsiasi server XMPP scelto.\nComunque, per tua comodità, abbiamo semplificato la creazione di un account su chat.sum7.eu; un provider particolarmente adatto all\'uso con Conversations.</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy è una variante del popolare client XMPP Conversations con ricerca automatica dei contatti.<br><br>Ti registri con il tuo numero di telefono e Quicksy ti suggerirà—in base ai numeri di telefono nella tua rubrica—automaticamente i possibili contatti.<br><br>Registrandoti accetti la nostra <a href="https://quicksy.im/#privacy">politica sulla privacy</a>.]]></string>
<string name="agree_and_continue">Accetta e continua</string>
<string name="magic_create_text">Ti guideremo nel processo di creazione di un account su chat.sum7.eu.\nQuando scegli chat.sum7.eu come provider potrai comunicare con utenti di altri provider dando il tuo indirizzo XMPP completo.</string>
@ -852,4 +851,15 @@
<string name="manage_permission">Gestisci i privilegi</string>
<string name="search_participants">Cerca partecipanti</string>
<string name="file_too_large">File troppo grande</string>
<string name="attach">Allega</string>
<string name="discover_channels">Individua i canali</string>
<string name="search_channels">Cerca i canali</string>
<string name="channel_discovery_opt_in_title">Possibile violazione della privacy!</string>
<string name="channel_discover_opt_in_message"><![CDATA[La ricerca dei canali usa un servizio di terze parti chiamato <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Usando questa opzione trasmetterai il tuo indirizzo IP e la stringa di ricerca al servizio. Controlla la loro <a href="https://search.jabbercat.org/privacy">Policy per la privacy</a> per maggiori informazioni.]]></string>
<string name="i_already_have_an_account">Ho già un account</string>
<string name="add_existing_account">Aggiungi un account pre-esistente</string>
<string name="register_new_account">Registra un nuovo account</string>
<string name="this_looks_like_a_domain">Questo sembra un indirizzo di dominio</string>
<string name="add_anway">Aggiungere comunque</string>
<string name="this_looks_like_channel">Questo sembra un indirizzo di canale</string>
</resources>

View File

@ -323,7 +323,7 @@
<string name="notification_backup_created_subtitle">De back-upbestanden zijn opgeslagen in %s</string>
<string name="restoring_backup">Bezig met herstellen van back-up...</string>
<string name="notification_restored_backup_title">Je back-up is hersteld</string>
<string name="notification_restored_backup_subtitle">Vergeet niet om het account in te schakelen.</string>
<string name="notification_restored_backup_subtitle">Vergeet niet om de account in te schakelen.</string>
<string name="choose_file">Bestand kiezen</string>
<string name="receiving_x_file">Ontvangen van %1$s (%2$d%% voltooid)</string>
<string name="download_x_file">%s downloaden</string>
@ -851,4 +851,14 @@
<string name="search_participants">Deelnemers zoeken</string>
<string name="file_too_large">Bestand te groot</string>
<string name="attach">Bijvoegen</string>
<string name="discover_channels">Kanalen ontdekken</string>
<string name="search_channels">Kanalen doorzoeken</string>
<string name="channel_discovery_opt_in_title">Mogelijke privacyschending!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Kanaalontdekking maakt gebruik van een derdepartijdienst genaamd <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Door deze functie te gebruiken, zullen je IP-adres en zoekopdrachten naar die dienst verstuurd worden. Bekijk hun <a href="https://search.jabbercat.org/privacy">privacybeleid</a> voor meer informatie.]]></string>
<string name="i_already_have_an_account">Ik heb al een account</string>
<string name="add_existing_account">Bestaande account toevoegen</string>
<string name="register_new_account">Nieuwe account registreren</string>
<string name="this_looks_like_a_domain">Dit lijkt op een domeinadres</string>
<string name="add_anway">Tóch toevoegen</string>
<string name="this_looks_like_channel">Dit lijkt op een kanaaladres</string>
</resources>

View File

@ -71,7 +71,7 @@
<string name="problem_connecting_to_account">Nie można połączyć się z kontem</string>
<string name="problem_connecting_to_accounts">Nie można połączyć się z wieloma kontami</string>
<string name="touch_to_fix">Dotknij tutaj aby zarządzać swoimi kontami</string>
<string name="attach_file">Dołącz plik</string>
<string name="attach_file">Załącz plik</string>
<string name="not_in_roster">Kontakt nie należy do Twojej listy kontaktów. Czy chcesz go dodać?</string>
<string name="add_contact">Dodaj kontakt</string>
<string name="send_failed">wysyłanie nie powiodło się</string>
@ -868,4 +868,15 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
<string name="manage_permission">Zarządzaj uprawnieniami</string>
<string name="search_participants">Wyszukaj uczestników</string>
<string name="file_too_large">Plik jest zbyt duży</string>
<string name="attach">Załącz</string>
<string name="discover_channels">Odkryj kanały</string>
<string name="search_channels">Wyszukaj kanał</string>
<string name="channel_discovery_opt_in_title">Możliwe naruszenie prywatności!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Odkrywanie kanałów używa usługi firmy trzeciej <a href="https://search.jabbercat.org">search.jabbercat.org</a>. <br><br>Używając tej funkcji twój adres IP oraz kryteria wyszukiwania zostaną wysłane do tej usługi. Sprawdź <a href="https://search.jabbercat.org/privacy">Politykę Prywatności</a> aby uzyskać więcej informacji.]]></string>
<string name="i_already_have_an_account">Już mam konto</string>
<string name="add_existing_account">Dodaj istniejące konto</string>
<string name="register_new_account">Zarejestruj nowe konto</string>
<string name="this_looks_like_a_domain">To wygląda jak nazwa domeny</string>
<string name="add_anway">Dodaj i tak</string>
<string name="this_looks_like_channel">To wygląda jak adres kanału</string>
</resources>

View File

@ -850,4 +850,15 @@
<string name="manage_permission">Gerenciar privilégios</string>
<string name="search_participants">Pesquisar participantes</string>
<string name="file_too_large">Arquivo muito grande</string>
<string name="attach">Anexar</string>
<string name="discover_channels">Descobrir canais</string>
<string name="search_channels">Pesquisar canais</string>
<string name="channel_discovery_opt_in_title">Provável violação de privacidade!</string>
<string name="channel_discover_opt_in_message"><![CDATA[A descoberta de canais utiliza um serviço de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao usar esse recurso, você enviará o seu endereço IP e termos de pesquisa para esse serviço. Veja sua <a href="https://search.jabbercat.org/privacy">Política de Privacidade</a> para maiores informações.]]></string>
<string name="i_already_have_an_account">Eu já tenho uma conta.</string>
<string name="add_existing_account">Adicionar uma conta já existente</string>
<string name="register_new_account">Registrar uma nova conta</string>
<string name="this_looks_like_a_domain">Isso se parece com um endereço de domínio</string>
<string name="add_anway">Adicionar mesmo assim</string>
<string name="this_looks_like_channel">Isso se parece com um endereço de canal</string>
</resources>

View File

@ -861,4 +861,14 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați
<string name="search_participants">Caută participanți</string>
<string name="file_too_large">Fișier prea mare</string>
<string name="attach">Atașează</string>
<string name="discover_channels">Descoperă canale publice</string>
<string name="search_channels">Caută canale publice</string>
<string name="channel_discovery_opt_in_title">Posibilă încălcare a intimității!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Descoperirea de canale publice folosește un serviciu terț numit <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Folosind această funcție se va transmite adresa dumneavoastră IP și cuvintele căutate către acest serviciu. Pentru mai multe informații citiți <a href="https://search.jabbercat.org/privacy">Politica de confidențialitate</a> a serviciului.]]></string>
<string name="i_already_have_an_account">Eu am deja un cont</string>
<string name="add_existing_account">Adaugă un cont existent</string>
<string name="register_new_account">Înregistrează un cont nou</string>
<string name="this_looks_like_a_domain">Aceasta pare să fie o adresă de domeniu</string>
<string name="add_anway">Adaugă oricum</string>
<string name="this_looks_like_channel">Aceasta pare o adresă de canal</string>
</resources>

View File

@ -506,10 +506,6 @@
<string name="security_error_invalid_file_access">Помилка безпеки: Недійсний доступ до файлу</string>
<string name="no_application_to_share_uri">Не знайдено програми, щоб поділитися URI</string>
<string name="share_uri_with">Поділитися URI з…</string>
<string name="welcome_text">XMPP є незалежною мережею обміну миттєвими повідомленнями. .
Ви можете використовувати цей клієнт з будь-яким XMPP-сервером.
\nДля Вашої зручності Ви можете використовувати Conversations з сервісом chat.sum7.eu; який є оптимізований для використання з Conversations.
</string>
<string name="agree_and_continue">Прийняти й продовжити</string>
<string name="create_account">Створити обліковий запис</string>
<string name="use_own_provider">Використати мого власного провайдера</string>

View File

@ -527,7 +527,6 @@
<string name="no_application_to_share_uri">No application found to share URI</string>
<string name="share_uri_with">Share URI with…</string>
<string name="welcome_header" translatable="false">Join the Conversation</string>
<string name="welcome_text">XMPP is a provider independent instant messaging network. You can use this client with what ever XMPP server you choose.\nHowever for your convenience we made it easy to create an account on chat.sum7.eu; a provider specially suited for the use with Conversations.</string>
<string name="welcome_header_quicksy" translatable="false">Have some Quick Conversations</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy is a spin off of the popular XMPP client Conversations with automatic contact discovery.<br><br>You sign up with your phone number and Quicksy will automatically—based on the phone numbers in your address book—suggest possible contacts to you.<br><br>By signing up you agree to our <a href="https://quicksy.im/#privacy">privacy policy</a>.]]></string>
<string name="agree_and_continue">Agree &amp; continue</string>
@ -855,4 +854,14 @@
<string name="search_participants">Search participants</string>
<string name="file_too_large">File too large</string>
<string name="attach">Attach</string>
<string name="discover_channels">Discover channels</string>
<string name="search_channels">Search channels</string>
<string name="channel_discovery_opt_in_title">Possible privacy violation!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Channel discovery uses a third party service called <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Using this feature will transmit your IP address and search terms to that service. See their <a href="https://search.jabbercat.org/privacy">Privacy Policy</a> for more information.]]></string>
<string name="i_already_have_an_account">I already have an account</string>
<string name="add_existing_account">Add existing account</string>
<string name="register_new_account">Register new account</string>
<string name="this_looks_like_a_domain">This looks like a domain address</string>
<string name="add_anway">Add anyway</string>
<string name="this_looks_like_channel">This looks like a channel address</string>
</resources>

View File

@ -16,9 +16,12 @@ import eu.siacs.conversations.ui.VerifyActivity;
public class SignupUtils {
public static Intent getSignUpIntent(Activity activity, boolean ignored) {
return getSignUpIntent(activity);
}
public static Intent getSignUpIntent(Activity activity) {
final Intent intent = new Intent(activity, EnterPhoneNumberActivity.class);
return intent;
return new Intent(activity, EnterPhoneNumberActivity.class);
}
public static Intent getRedirectionIntent(ConversationsActivity activity) {