forked from mirror/monocles_chat_clean
add option to restrict avatar access model to contacts
This commit is contained in:
parent
a555141178
commit
ee33f3f6c9
5 changed files with 185 additions and 127 deletions
|
@ -4577,8 +4577,19 @@ public class XmppConnectionService extends Service {
|
|||
}).start();
|
||||
}
|
||||
|
||||
public void publishAvatar(final Account account, final Uri image, final OnAvatarPublication callback) {
|
||||
new Thread(() -> {
|
||||
public void publishAvatarAsync(
|
||||
final Account account,
|
||||
final Uri image,
|
||||
final boolean open,
|
||||
final OnAvatarPublication callback) {
|
||||
new Thread(() -> publishAvatar(account, image, open, callback)).start();
|
||||
}
|
||||
|
||||
private void publishAvatar(
|
||||
final Account account,
|
||||
final Uri image,
|
||||
final boolean open,
|
||||
final OnAvatarPublication callback) {
|
||||
final Bitmap.CompressFormat format = Config.AVATAR_FORMAT;
|
||||
final int size = Config.AVATAR_SIZE;
|
||||
final Avatar avatar = getFileBackend().getPepAvatar(image, size, format);
|
||||
|
@ -4588,12 +4599,10 @@ public class XmppConnectionService extends Service {
|
|||
callback.onAvatarPublicationFailed(R.string.error_saving_avatar);
|
||||
return;
|
||||
}
|
||||
publishAvatar(account, avatar, callback);
|
||||
publishAvatar(account, avatar, open, callback);
|
||||
} else {
|
||||
callback.onAvatarPublicationFailed(R.string.error_publish_avatar_converting);
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
private void publishMucAvatar(Conversation conversation, Avatar avatar, OnAvatarPublication callback) {
|
||||
|
@ -4631,10 +4640,14 @@ public class XmppConnectionService extends Service {
|
|||
});
|
||||
}
|
||||
|
||||
public void publishAvatar(Account account, final Avatar avatar, final OnAvatarPublication callback) {
|
||||
public void publishAvatar(
|
||||
final Account account,
|
||||
final Avatar avatar,
|
||||
final boolean open,
|
||||
final OnAvatarPublication callback) {
|
||||
final Bundle options;
|
||||
if (account.getXmppConnection().getFeatures().pepPublishOptions()) {
|
||||
options = PublishOptions.openAccess();
|
||||
options = open ? PublishOptions.openAccess() : PublishOptions.presenceAccess();
|
||||
} else {
|
||||
options = null;
|
||||
}
|
||||
|
@ -4706,7 +4719,7 @@ public class XmppConnectionService extends Service {
|
|||
});
|
||||
}
|
||||
|
||||
public void republishAvatarIfNeeded(Account account) {
|
||||
public void republishAvatarIfNeeded(final Account account) {
|
||||
if (account.getAxolotlService().isPepBroken()) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping republication of avatar because pep is broken");
|
||||
return;
|
||||
|
@ -4735,12 +4748,19 @@ public class XmppConnectionService extends Service {
|
|||
@Override
|
||||
public void accept(final Iq packet) {
|
||||
if (packet.getType() == Iq.Type.RESULT || errorIsItemNotFound(packet)) {
|
||||
Avatar serverAvatar = parseAvatar(packet);
|
||||
final Avatar serverAvatar = parseAvatar(packet);
|
||||
if (serverAvatar == null && account.getAvatar() != null) {
|
||||
Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar());
|
||||
final Avatar avatar =
|
||||
fileBackend.getStoredPepAvatar(account.getAvatar());
|
||||
if (avatar != null) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": avatar on server was null. republishing");
|
||||
publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null);
|
||||
// publishing as 'open' - old server (that requires
|
||||
// republication) likely doesn't support access models anyway
|
||||
publishAvatar(
|
||||
account,
|
||||
fileBackend.getStoredPepAvatar(account.getAvatar()),
|
||||
true,
|
||||
null);
|
||||
} else {
|
||||
Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": error rereading avatar");
|
||||
}
|
||||
|
|
|
@ -14,19 +14,12 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.canhub.cropper.CropImage;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.databinding.ActivityPublishProfilePictureBinding;
|
||||
|
@ -35,23 +28,21 @@ import eu.siacs.conversations.persistance.FileBackend;
|
|||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
|
||||
import eu.siacs.conversations.utils.PhoneHelper;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class PublishProfilePictureActivity extends XmppActivity implements XmppConnectionService.OnAccountUpdate, OnAvatarPublication {
|
||||
|
||||
public class PublishProfilePictureActivity extends XmppActivity
|
||||
implements XmppConnectionService.OnAccountUpdate, OnAvatarPublication {
|
||||
public static final int REQUEST_CHOOSE_PICTURE = 0x1337;
|
||||
|
||||
private ImageView avatar;
|
||||
private TextView hintOrWarning;
|
||||
private TextView secondaryHint;
|
||||
private Button cancelButton;
|
||||
private Button publishButton;
|
||||
private ActivityPublishProfilePictureBinding binding;
|
||||
private Uri avatarUri;
|
||||
private Uri defaultUri;
|
||||
private Account account;
|
||||
private boolean support = false;
|
||||
private boolean publishing = false;
|
||||
private final AtomicBoolean handledExternalUri = new AtomicBoolean(false);
|
||||
private final OnLongClickListener backToDefaultListener = new OnLongClickListener() {
|
||||
private final OnLongClickListener backToDefaultListener =
|
||||
new OnLongClickListener() {
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
|
@ -64,60 +55,70 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
|
||||
@Override
|
||||
public void onAvatarPublicationSucceeded() {
|
||||
runOnUiThread(() -> {
|
||||
runOnUiThread(
|
||||
() -> {
|
||||
if (mInitialAccountSetup) {
|
||||
Intent intent = new Intent(getApplicationContext(), StartConversationActivity.class);
|
||||
Intent intent =
|
||||
new Intent(
|
||||
getApplicationContext(), StartConversationActivity.class);
|
||||
StartConversationActivity.addInviteUri(intent, getIntent());
|
||||
intent.putExtra("init", true);
|
||||
intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
|
||||
intent.putExtra(
|
||||
EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
|
||||
startActivity(intent);
|
||||
}
|
||||
Toast.makeText(PublishProfilePictureActivity.this,
|
||||
Toast.makeText(
|
||||
PublishProfilePictureActivity.this,
|
||||
R.string.avatar_has_been_published,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvatarPublicationFailed(int res) {
|
||||
runOnUiThread(() -> {
|
||||
hintOrWarning.setText(res);
|
||||
hintOrWarning.setVisibility(View.VISIBLE);
|
||||
publishing = false;
|
||||
public void onAvatarPublicationFailed(final int res) {
|
||||
runOnUiThread(
|
||||
() -> {
|
||||
this.binding.hintOrWarning.setText(res);
|
||||
this.binding.hintOrWarning.setVisibility(View.VISIBLE);
|
||||
this.publishing = false;
|
||||
togglePublishButton(true, R.string.publish);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
ActivityPublishProfilePictureBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_publish_profile_picture);
|
||||
this.binding =
|
||||
DataBindingUtil.setContentView(this, R.layout.activity_publish_profile_picture);
|
||||
|
||||
setSupportActionBar(binding.toolbar);
|
||||
|
||||
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
|
||||
|
||||
this.avatar = findViewById(R.id.account_image);
|
||||
this.cancelButton = findViewById(R.id.cancel_button);
|
||||
this.publishButton = findViewById(R.id.publish_button);
|
||||
this.hintOrWarning = findViewById(R.id.hint_or_warning);
|
||||
this.secondaryHint = findViewById(R.id.secondary_hint);
|
||||
this.publishButton.setOnClickListener(v -> {
|
||||
if (avatarUri != null) {
|
||||
this.binding.publishButton.setOnClickListener(
|
||||
v -> {
|
||||
final boolean open = !this.binding.contactOnly.isChecked();
|
||||
final var uri = this.avatarUri;
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
publishing = true;
|
||||
togglePublishButton(false, R.string.publishing);
|
||||
xmppConnectionService.publishAvatar(account, avatarUri, this);
|
||||
}
|
||||
xmppConnectionService.publishAvatarAsync(account, uri, open, this);
|
||||
});
|
||||
this.cancelButton.setOnClickListener(
|
||||
this.binding.cancelButton.setOnClickListener(
|
||||
v -> {
|
||||
if (mInitialAccountSetup) {
|
||||
final Intent intent =
|
||||
new Intent(
|
||||
getApplicationContext(), StartConversationActivity.class);
|
||||
if (xmppConnectionService != null
|
||||
&& xmppConnectionService.getAccounts().size() == 1) {
|
||||
intent.putExtra("init", true);
|
||||
}
|
||||
StartConversationActivity.addInviteUri(intent, getIntent());
|
||||
if (account != null) {
|
||||
intent.putExtra(
|
||||
|
@ -127,11 +128,12 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
}
|
||||
finish();
|
||||
});
|
||||
this.avatar.setOnClickListener(v -> chooseAvatar(this));
|
||||
this.binding.accountImage.setOnClickListener(v -> chooseAvatar(this));
|
||||
this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
|
||||
if (savedInstanceState != null) {
|
||||
this.avatarUri = savedInstanceState.getParcelable("uri");
|
||||
this.handledExternalUri.set(savedInstanceState.getBoolean("handle_external_uri",false));
|
||||
this.handledExternalUri.set(
|
||||
savedInstanceState.getBoolean("handle_external_uri", false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,8 +146,8 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_delete_avatar) {
|
||||
if (xmppConnectionService != null && account != null) {
|
||||
xmppConnectionService.deleteAvatar(account);
|
||||
if (account != null) {
|
||||
deleteAvatar(account);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
@ -153,6 +155,22 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
}
|
||||
}
|
||||
|
||||
private void deleteAvatar(final Account account) {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.delete_avatar)
|
||||
.setMessage(R.string.delete_avatar_message)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(
|
||||
R.string.confirm,
|
||||
(d, v) -> {
|
||||
if (xmppConnectionService != null) {
|
||||
xmppConnectionService.deleteAvatar(account);
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
if (this.avatarUri != null) {
|
||||
|
@ -191,9 +209,9 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("image/*");
|
||||
activity.startActivityForResult(
|
||||
Intent.createChooser(intent, activity.getString(R.string.attach_choose_picture)),
|
||||
REQUEST_CHOOSE_PICTURE
|
||||
);
|
||||
Intent.createChooser(
|
||||
intent, activity.getString(R.string.attach_choose_picture)),
|
||||
REQUEST_CHOOSE_PICTURE);
|
||||
} else {
|
||||
CropImage.activity()
|
||||
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
|
@ -212,7 +230,9 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
}
|
||||
|
||||
private void reloadAvatar() {
|
||||
this.support = this.account.getXmppConnection() != null && this.account.getXmppConnection().getFeatures().pep();
|
||||
this.support =
|
||||
this.account.getXmppConnection() != null
|
||||
&& this.account.getXmppConnection().getFeatures().pep();
|
||||
if (this.avatarUri == null) {
|
||||
if (this.account.getAvatar() != null || this.defaultUri == null) {
|
||||
loadImageIntoPreview(null);
|
||||
|
@ -233,21 +253,22 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
|
||||
final Uri uri = intent != null ? intent.getData() : null;
|
||||
|
||||
if (uri != null && handledExternalUri.compareAndSet(false,true)) {
|
||||
if (uri != null && handledExternalUri.compareAndSet(false, true)) {
|
||||
cropUri(uri);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mInitialAccountSetup) {
|
||||
this.cancelButton.setText(R.string.skip);
|
||||
this.binding.cancelButton.setText(R.string.skip);
|
||||
}
|
||||
configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
|
||||
configureActionBar(
|
||||
getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
|
||||
}
|
||||
|
||||
public void cropUri(final Uri uri) {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
loadImageIntoPreview(uri);
|
||||
if (this.avatar.getDrawable() instanceof AnimatedImageDrawable || this.avatar.getDrawable() instanceof FileBackend.SVGDrawable) {
|
||||
if (this.binding.accountImage.getDrawable() instanceof AnimatedImageDrawable || this.binding.accountImage.getDrawable() instanceof FileBackend.SVGDrawable) {
|
||||
this.avatarUri = uri;
|
||||
return;
|
||||
}
|
||||
|
@ -259,7 +280,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
.start(this);
|
||||
}
|
||||
|
||||
protected void loadImageIntoPreview(Uri uri) {
|
||||
protected void loadImageIntoPreview(final Uri uri) {
|
||||
|
||||
Drawable bm = null;
|
||||
if (uri == null) {
|
||||
|
@ -267,46 +288,46 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
|
|||
} else {
|
||||
try {
|
||||
bm = xmppConnectionService.getFileBackend().cropCenterSquareDrawable(uri, (int) getResources().getDimension(R.dimen.publish_avatar_size));
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
Log.d(Config.LOGTAG, "unable to load bitmap into image view", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (bm == null) {
|
||||
togglePublishButton(false, R.string.publish);
|
||||
this.hintOrWarning.setVisibility(View.VISIBLE);
|
||||
this.hintOrWarning.setText(R.string.error_publish_avatar_converting);
|
||||
this.binding.hintOrWarning.setVisibility(View.VISIBLE);
|
||||
this.binding.hintOrWarning.setText(R.string.error_publish_avatar_converting);
|
||||
return;
|
||||
}
|
||||
this.avatar.setImageDrawable(bm);
|
||||
this.binding.accountImage.setImageDrawable(bm);
|
||||
if (Build.VERSION.SDK_INT >= 28 && bm instanceof AnimatedImageDrawable) {
|
||||
((AnimatedImageDrawable) bm).start();
|
||||
}
|
||||
if (support) {
|
||||
togglePublishButton(uri != null, R.string.publish);
|
||||
this.hintOrWarning.setVisibility(View.INVISIBLE);
|
||||
this.binding.hintOrWarning.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
togglePublishButton(false, R.string.publish);
|
||||
this.hintOrWarning.setVisibility(View.VISIBLE);
|
||||
this.binding.hintOrWarning.setVisibility(View.VISIBLE);
|
||||
if (account.getStatus() == Account.State.ONLINE) {
|
||||
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
|
||||
this.binding.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
|
||||
} else {
|
||||
this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
|
||||
this.binding.hintOrWarning.setText(R.string.error_publish_avatar_offline);
|
||||
}
|
||||
}
|
||||
if (this.defaultUri == null || this.defaultUri.equals(uri)) {
|
||||
this.secondaryHint.setVisibility(View.INVISIBLE);
|
||||
this.avatar.setOnLongClickListener(null);
|
||||
this.binding.secondaryHint.setVisibility(View.INVISIBLE);
|
||||
this.binding.accountImage.setOnLongClickListener(null);
|
||||
} else if (this.defaultUri != null) {
|
||||
this.secondaryHint.setVisibility(View.VISIBLE);
|
||||
this.avatar.setOnLongClickListener(this.backToDefaultListener);
|
||||
this.binding.secondaryHint.setVisibility(View.VISIBLE);
|
||||
this.binding.accountImage.setOnLongClickListener(this.backToDefaultListener);
|
||||
}
|
||||
}
|
||||
|
||||
protected void togglePublishButton(boolean enabled, @StringRes int res) {
|
||||
final boolean status = enabled && !publishing;
|
||||
this.publishButton.setText(publishing ? R.string.publishing : res);
|
||||
this.publishButton.setEnabled(status);
|
||||
this.binding.publishButton.setText(publishing ? R.string.publishing : res);
|
||||
this.binding.publishButton.setEnabled(status);
|
||||
}
|
||||
|
||||
public void refreshUiReal() {
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package eu.siacs.conversations.xmpp.pep;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
|
||||
public class PublishOptions {
|
||||
|
||||
private PublishOptions() {
|
||||
|
||||
}
|
||||
private PublishOptions() {}
|
||||
|
||||
public static Bundle openAccess() {
|
||||
final Bundle options = new Bundle();
|
||||
|
@ -18,6 +15,12 @@ public class PublishOptions {
|
|||
return options;
|
||||
}
|
||||
|
||||
public static Bundle presenceAccess() {
|
||||
final Bundle options = new Bundle();
|
||||
options.putString("pubsub#access_model", "presence");
|
||||
return options;
|
||||
}
|
||||
|
||||
public static Bundle persistentWhitelistAccess() {
|
||||
final Bundle options = new Bundle();
|
||||
options.putString("pubsub#persist_items", "true");
|
||||
|
@ -32,14 +35,15 @@ public class PublishOptions {
|
|||
options.putString("pubsub#send_last_published_item", "never");
|
||||
options.putString("pubsub#max_items", "max");
|
||||
options.putString("pubsub#notify_delete", "true");
|
||||
options.putString("pubsub#notify_retract", "true"); //one could also set notify=true on the retract
|
||||
options.putString(
|
||||
"pubsub#notify_retract", "true"); // one could also set notify=true on the retract
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public static boolean preconditionNotMet(Iq response) {
|
||||
final Element error = response.getType() == Iq.Type.ERROR ? response.findChild("error") : null;
|
||||
final Element error =
|
||||
response.getType() == Iq.Type.ERROR ? response.findChild("error") : null;
|
||||
return error != null && error.hasChild("precondition-not-met", Namespace.PUBSUB_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -19,18 +20,19 @@
|
|||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/app_bar_layout"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginBottom="@dimen/activity_vertical_margin">
|
||||
android:layout_above="@+id/button_bar"
|
||||
android:layout_below="@id/app_bar_layout">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginTop="@dimen/activity_vertical_margin"
|
||||
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginBottom="@dimen/activity_vertical_margin"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -62,17 +64,26 @@
|
|||
android:text="@string/or_long_press_for_default"
|
||||
android:textAppearance="?textAppearanceBodyMedium" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/contact_only"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:text="@string/show_to_contacts_only" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hint_or_warning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:textAppearance="?textAppearanceBodyMedium"
|
||||
android:textColor="?colorError" />
|
||||
android:textColor="?colorError"
|
||||
tools:text="@string/error_saving_avatar" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/button_bar"
|
||||
|
|
|
@ -1408,4 +1408,6 @@
|
|||
<string name="pref_prefer_ipv6">Prefer IPv6</string>
|
||||
<string name="pref_use_colored_muc_names">Use colored user names</string>
|
||||
<string name="pref_use_colored_muc_names_summary">Use colored user names in group chats</string>
|
||||
<string name="delete_avatar_message">Would you like to delete your avatar? Some clients might continue to display a cached copy of your avatar.</string>
|
||||
<string name="show_to_contacts_only">Show to contacts only</string>
|
||||
</resources>
|
Loading…
Reference in a new issue