apply Material 3 theme to all activites

This commit is contained in:
Daniel Gultsch 2024-04-04 11:38:27 +02:00
parent 4968bde774
commit 6e43248135
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
1427 changed files with 5148 additions and 5856 deletions

View file

@ -74,7 +74,7 @@ dependencies {
implementation 'org.hsluv:hsluv:0.2' implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:2.5.2' implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'me.drakeet.support:toastcompat:1.1.0' implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation "com.leinardi.android:speed-dial:3.2.0" implementation "com.leinardi.android:speed-dial:3.3.0"
implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0"

View file

@ -5,6 +5,7 @@
<activity <activity
android:name=".ui.ManageAccountActivity" android:name=".ui.ManageAccountActivity"
android:label="@string/title_activity_manage_accounts" android:label="@string/title_activity_manage_accounts"
android:theme="@style/Theme.Conversations3"
android:launchMode="singleTask" /> android:launchMode="singleTask" />
<activity <activity
android:name=".ui.WelcomeActivity" android:name=".ui.WelcomeActivity"

View file

@ -225,7 +225,7 @@ public class ImportBackupService extends Service {
NotificationCompat.Builder mBuilder = NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(getBaseContext(), "backup"); new NotificationCompat.Builder(getBaseContext(), "backup");
mBuilder.setContentTitle(getString(R.string.restoring_backup)) mBuilder.setContentTitle(getString(R.string.restoring_backup))
.setSmallIcon(R.drawable.ic_unarchive_white_24dp) .setSmallIcon(R.drawable.ic_unarchive_24dp)
.setProgress(max, progress, max == 1 && progress == 0); .setProgress(max, progress, max == 1 && progress == 0);
return mBuilder.build(); return mBuilder.build();
} }
@ -415,7 +415,7 @@ public class ImportBackupService extends Service {
? PendingIntent.FLAG_IMMUTABLE ? PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_UPDATE_CURRENT
: PendingIntent.FLAG_UPDATE_CURRENT)) : PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_unarchive_white_24dp); .setSmallIcon(R.drawable.ic_unarchive_24dp);
notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
} }

View file

@ -2,6 +2,7 @@ package eu.siacs.conversations.ui;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Point; import android.graphics.Point;
import android.os.Bundle; import android.os.Bundle;
@ -11,8 +12,10 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -23,19 +26,20 @@ import eu.siacs.conversations.services.BarcodeProvider;
import eu.siacs.conversations.utils.EasyOnboardingInvite; import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOnboardingInvite.OnInviteRequested { public class EasyOnboardingInviteActivity extends XmppActivity
implements EasyOnboardingInvite.OnInviteRequested {
private ActivityEasyInviteBinding binding; private ActivityEasyInviteBinding binding;
private EasyOnboardingInvite easyOnboardingInvite; private EasyOnboardingInvite easyOnboardingInvite;
@Override @Override
public void onCreate(final Bundle bundle) { public void onCreate(final Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_easy_invite); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_easy_invite);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar(), true); configureActionBar(getSupportActionBar(), true);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
this.binding.shareButton.setOnClickListener(v -> share()); this.binding.shareButton.setOnClickListener(v -> share());
if (bundle != null && bundle.containsKey("invite")) { if (bundle != null && bundle.containsKey("invite")) {
this.easyOnboardingInvite = bundle.getParcelable("invite"); this.easyOnboardingInvite = bundle.getParcelable("invite");
@ -65,11 +69,11 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn
} }
private void share() { private void share() {
final String shareText = getString( final String shareText =
R.string.easy_invite_share_text, getString(
easyOnboardingInvite.getDomain(), R.string.easy_invite_share_text,
easyOnboardingInvite.getShareableLink() easyOnboardingInvite.getDomain(),
); easyOnboardingInvite.getShareableLink());
final Intent sendIntent = new Intent(); final Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText); sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
@ -95,16 +99,47 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn
private void showInvite(final EasyOnboardingInvite invite) { private void showInvite(final EasyOnboardingInvite invite) {
this.binding.inProgress.setVisibility(View.GONE); this.binding.inProgress.setVisibility(View.GONE);
this.binding.invite.setVisibility(View.VISIBLE); this.binding.invite.setVisibility(View.VISIBLE);
this.binding.tapToShare.setText(getString(R.string.tap_share_button_send_invite, invite.getDomain())); this.binding.tapToShare.setText(
getString(R.string.tap_share_button_send_invite, invite.getDomain()));
final Point size = new Point(); final Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size); getWindowManager().getDefaultDisplay().getSize(size);
final int width = Math.min(size.x, size.y); final int width = Math.min(size.x, size.y);
final Bitmap bitmap = BarcodeProvider.create2dBarcodeBitmap(invite.getShareableLink(), width); final boolean nightMode =
(this.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
final int black;
final int white;
if (nightMode) {
black =
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSurface,
"No surface color configured");
white =
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSurfaceInverse,
"No inverse surface color configured");
} else {
black =
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSurfaceInverse,
"No inverse surface color configured");
white =
MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSurface,
"No surface color configured");
}
final Bitmap bitmap =
BarcodeProvider.create2dBarcodeBitmap(
invite.getShareableLink(), width, black, white);
binding.qrCode.setImageBitmap(bitmap); binding.qrCode.setImageBitmap(bitmap);
} }
@Override @Override
public void onSaveInstanceState(Bundle bundle) { public void onSaveInstanceState(@NonNull Bundle bundle) {
super.onSaveInstanceState(bundle); super.onSaveInstanceState(bundle);
if (easyOnboardingInvite != null) { if (easyOnboardingInvite != null) {
bundle.putParcelable("invite", easyOnboardingInvite); bundle.putParcelable("invite", easyOnboardingInvite);
@ -141,11 +176,12 @@ public class EasyOnboardingInviteActivity extends XmppActivity implements EasyOn
@Override @Override
public void inviteRequestFailed(final String message) { public void inviteRequestFailed(final String message) {
runOnUiThread(() -> { runOnUiThread(
if (!Strings.isNullOrEmpty(message)) { () -> {
Toast.makeText(this, message, Toast.LENGTH_LONG).show(); if (!Strings.isNullOrEmpty(message)) {
} Toast.makeText(this, message, Toast.LENGTH_LONG).show();
finish(); }
}); finish();
});
} }
} }

View file

@ -18,6 +18,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import java.io.IOException; import java.io.IOException;
@ -31,7 +32,6 @@ import eu.siacs.conversations.services.ImportBackupService;
import eu.siacs.conversations.ui.adapter.BackupFileAdapter; import eu.siacs.conversations.ui.adapter.BackupFileAdapter;
import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.BackupFileHeader; import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.ThemeHelper;
public class ImportBackupActivity extends ActionBarActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed { public class ImportBackupActivity extends ActionBarActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed {
@ -46,10 +46,9 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
this.mTheme = ThemeHelper.find(this);
setTheme(this.mTheme);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_import_backup); binding = DataBindingUtil.setContentView(this, R.layout.activity_import_backup);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
setLoadingState(savedInstanceState != null && savedInstanceState.getBoolean("loading_state", false)); setLoadingState(savedInstanceState != null && savedInstanceState.getBoolean("loading_state", false));
this.backupFileAdapter = new BackupFileAdapter(); this.backupFileAdapter = new BackupFileAdapter();
@ -57,12 +56,6 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
this.backupFileAdapter.setOnItemClickedListener(this); this.backupFileAdapter.setOnItemClickedListener(this);
} }
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override @Override
public boolean onCreateOptionsMenu(final Menu menu) { public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.import_backup, menu); getMenuInflater().inflate(R.menu.import_backup, menu);
@ -80,12 +73,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = ThemeHelper.find(this); bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE);
if (this.mTheme != theme) {
recreate();
} else {
bindService(new Intent(this, ImportBackupService.class), this, Context.BIND_AUTO_CREATE);
}
final Intent intent = getIntent(); final Intent intent = getIntent();
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction()) && !this.mLoadingState) { if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction()) && !this.mLoadingState) {
Uri uri = intent.getData(); Uri uri = intent.getData();
@ -146,7 +134,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false); final DialogEnterPasswordBinding enterPasswordBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.dialog_enter_password, null, false);
Log.d(Config.LOGTAG, "attempting to import " + backupFile.getUri()); Log.d(Config.LOGTAG, "attempting to import " + backupFile.getUri());
enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString())); enterPasswordBinding.explain.setText(getString(R.string.enter_password_to_restore, backupFile.getHeader().getJid().toString()));
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setView(enterPasswordBinding.getRoot()); builder.setView(enterPasswordBinding.getRoot());
builder.setTitle(R.string.enter_password); builder.setTitle(R.string.enter_password);
builder.setNegativeButton(R.string.cancel, (dialog, which) -> { builder.setNegativeButton(R.string.cancel, (dialog, which) -> {
@ -186,6 +174,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE); binding.coordinator.setVisibility(loadingState ? View.GONE : View.VISIBLE);
binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE); binding.inProgress.setVisibility(loadingState ? View.VISIBLE : View.GONE);
setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup); setTitle(loadingState ? R.string.restoring_backup : R.string.restore_backup);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
configureActionBar(getSupportActionBar(), !loadingState); configureActionBar(getSupportActionBar(), !loadingState);
this.mLoadingState = loadingState; this.mLoadingState = loadingState;
invalidateOptionsMenu(); invalidateOptionsMenu();

View file

@ -10,45 +10,32 @@ import android.widget.Toast;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import java.security.SecureRandom;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.MagicCreateBinding; import eu.siacs.conversations.databinding.ActivityMagicCreateBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.InstallReferrerUtils; import eu.siacs.conversations.utils.InstallReferrerUtils;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import java.security.SecureRandom;
public class MagicCreateActivity extends XmppActivity implements TextWatcher { public class MagicCreateActivity extends XmppActivity implements TextWatcher {
public static final String EXTRA_DOMAIN = "domain"; public static final String EXTRA_DOMAIN = "domain";
public static final String EXTRA_PRE_AUTH = "pre_auth"; public static final String EXTRA_PRE_AUTH = "pre_auth";
public static final String EXTRA_USERNAME = "username"; public static final String EXTRA_USERNAME = "username";
private MagicCreateBinding binding; private ActivityMagicCreateBinding binding;
private String domain; private String domain;
private String username; private String username;
private String preAuth; private String preAuth;
@Override @Override
protected void refreshUiReal() { protected void refreshUiReal() {}
}
@Override @Override
void onBackendConnected() { void onBackendConnected() {}
}
@Override
public void onStart() {
super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
}
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
@ -60,7 +47,8 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.magic_create); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_magic_create);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(this.binding.toolbar); setSupportActionBar(this.binding.toolbar);
configureActionBar(getSupportActionBar(), this.domain == null); configureActionBar(getSupportActionBar(), this.domain == null);
if (username != null && domain != null) { if (username != null && domain != null) {
@ -72,51 +60,64 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
} else if (domain != null) { } else if (domain != null) {
binding.instructions.setText(getString(R.string.magic_create_text_on_x, domain)); binding.instructions.setText(getString(R.string.magic_create_text_on_x, domain));
} }
binding.createAccount.setOnClickListener(v -> { binding.createAccount.setOnClickListener(
try { v -> {
final String username = binding.username.getText().toString(); try {
final Jid jid; final String username = binding.username.getText().toString();
final boolean fixedUsername; final Jid jid;
if (this.domain != null && this.username != null) { final boolean fixedUsername;
fixedUsername = true; if (this.domain != null && this.username != null) {
jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain); fixedUsername = true;
} else if (this.domain != null) { jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain);
fixedUsername = false; } else if (this.domain != null) {
jid = Jid.ofLocalAndDomainEscaped(username, this.domain); fixedUsername = false;
} else { jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
fixedUsername = false; } else {
jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN); fixedUsername = false;
} jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN);
if (!jid.getEscapedLocal().equals(jid.getLocal()) || (this.username == null && username.length() < 3)) {
binding.username.setError(getString(R.string.invalid_username));
binding.username.requestFocus();
} else {
binding.username.setError(null);
Account account = xmppConnectionService.findAccountByJid(jid);
if (account == null) {
account = new Account(jid, CryptoHelper.createPassword(new SecureRandom()));
account.setOption(Account.OPTION_REGISTER, true);
account.setOption(Account.OPTION_DISABLED, true);
account.setOption(Account.OPTION_MAGIC_CREATE, true);
account.setOption(Account.OPTION_FIXED_USERNAME, fixedUsername);
if (this.preAuth != null) {
account.setKey(Account.KEY_PRE_AUTH_REGISTRATION_TOKEN, this.preAuth);
} }
xmppConnectionService.createAccount(account); if (!jid.getEscapedLocal().equals(jid.getLocal())
|| (this.username == null && username.length() < 3)) {
binding.usernameLayout.setError(getString(R.string.invalid_username));
binding.username.requestFocus();
} else {
binding.usernameLayout.setError(null);
Account account = xmppConnectionService.findAccountByJid(jid);
if (account == null) {
account =
new Account(
jid,
CryptoHelper.createPassword(new SecureRandom()));
account.setOption(Account.OPTION_REGISTER, true);
account.setOption(Account.OPTION_DISABLED, true);
account.setOption(Account.OPTION_MAGIC_CREATE, true);
account.setOption(Account.OPTION_FIXED_USERNAME, fixedUsername);
if (this.preAuth != null) {
account.setKey(
Account.KEY_PRE_AUTH_REGISTRATION_TOKEN, this.preAuth);
}
xmppConnectionService.createAccount(account);
}
Intent intent =
new Intent(MagicCreateActivity.this, EditAccountActivity.class);
intent.putExtra("jid", account.getJid().asBareJid().toString());
intent.putExtra("init", true);
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
Toast.makeText(
MagicCreateActivity.this,
R.string.secure_password_generated,
Toast.LENGTH_SHORT)
.show();
StartConversationActivity.addInviteUri(intent, getIntent());
startActivity(intent);
}
} catch (final IllegalArgumentException e) {
binding.usernameLayout.setError(getString(R.string.invalid_username));
binding.username.requestFocus();
} }
Intent intent = new Intent(MagicCreateActivity.this, EditAccountActivity.class); });
intent.putExtra("jid", account.getJid().asBareJid().toString());
intent.putExtra("init", true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Toast.makeText(MagicCreateActivity.this, R.string.secure_password_generated, Toast.LENGTH_SHORT).show();
StartConversationActivity.addInviteUri(intent, getIntent());
startActivity(intent);
}
} catch (IllegalArgumentException e) {
binding.username.setError(getString(R.string.invalid_username));
binding.username.requestFocus();
}
});
binding.username.addTextChangedListener(this); binding.username.addTextChangedListener(this);
} }
@ -127,14 +128,10 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
} }
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
}
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {}
}
@Override @Override
public void afterTextChanged(final Editable s) { public void afterTextChanged(final Editable s) {
@ -153,8 +150,10 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
} else { } else {
jid = Jid.ofLocalAndDomainEscaped(username, this.domain); jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
} }
binding.fullJid.setText(getString(R.string.your_full_jid_will_be, jid.toEscapedString())); binding.fullJid.setText(
} catch (IllegalArgumentException e) { getString(R.string.your_full_jid_will_be, jid.toEscapedString()));
binding.usernameLayout.setError(null);
} catch (final IllegalArgumentException e) {
binding.fullJid.setVisibility(View.INVISIBLE); binding.fullJid.setVisibility(View.INVISIBLE);
} }
} }

View file

@ -1,10 +1,14 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.security.KeyChain; import android.security.KeyChain;
import android.security.KeyChainAliasCallback; import android.security.KeyChainAliasCallback;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
@ -12,23 +16,17 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.databinding.DataBindingUtil;
import org.openintents.openpgp.util.OpenPgpApi; import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityManageAccountsBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
@ -37,10 +35,17 @@ import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted; import org.openintents.openpgp.util.OpenPgpApi;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated, AccountAdapter.OnTglAccountState { import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class ManageAccountActivity extends XmppActivity
implements OnAccountUpdate,
KeyChainAliasCallback,
XmppConnectionService.OnAccountCreated,
AccountAdapter.OnTglAccountState {
private final String STATE_SELECTED_ACCOUNT = "selected_account"; private final String STATE_SELECTED_ACCOUNT = "selected_account";
@ -50,7 +55,6 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
protected Jid selectedAccountJid = null; protected Jid selectedAccountJid = null;
protected final List<Account> accountList = new ArrayList<>(); protected final List<Account> accountList = new ArrayList<>();
protected ListView accountListView;
protected AccountAdapter mAccountAdapter; protected AccountAdapter mAccountAdapter;
protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false); protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
@ -67,7 +71,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
accountList.clear(); accountList.clear();
accountList.addAll(xmppConnectionService.getAccounts()); accountList.addAll(xmppConnectionService.getAccounts());
} }
ActionBar actionBar = getSupportActionBar(); final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
actionBar.setHomeButtonEnabled(this.accountList.size() > 0); actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0); actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
@ -81,8 +85,11 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_accounts); ActivityManageAccountsBinding binding =
setSupportActionBar(findViewById(R.id.toolbar)); DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
if (savedInstanceState != null) { if (savedInstanceState != null) {
String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT); String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
@ -95,26 +102,19 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
} }
} }
accountListView = findViewById(R.id.account_list);
this.mAccountAdapter = new AccountAdapter(this, accountList); this.mAccountAdapter = new AccountAdapter(this, accountList);
accountListView.setAdapter(this.mAccountAdapter); binding.accountList.setAdapter(this.mAccountAdapter);
accountListView.setOnItemClickListener((arg0, view, position, arg3) -> switchToAccount(accountList.get(position))); binding.accountList.setOnItemClickListener(
registerForContextMenu(accountListView); (arg0, view, position, arg3) -> switchToAccount(accountList.get(position)));
registerForContextMenu(binding.accountList);
} }
@Override
protected void onStart() {
super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
}
@Override @Override
public void onSaveInstanceState(final Bundle savedInstanceState) { public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
if (selectedAccount != null) { if (selectedAccount != null) {
savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toEscapedString()); savedInstanceState.putString(
STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toEscapedString());
} }
super.onSaveInstanceState(savedInstanceState); super.onSaveInstanceState(savedInstanceState);
} }
@ -122,8 +122,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
ManageAccountActivity.this.getMenuInflater().inflate( ManageAccountActivity.this.getMenuInflater().inflate(R.menu.manageaccounts_context, menu);
R.menu.manageaccounts_context, menu);
AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo; AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
this.selectedAccount = accountList.get(acmi.position); this.selectedAccount = accountList.get(acmi.position);
if (this.selectedAccount.isEnabled()) { if (this.selectedAccount.isEnabled()) {
@ -144,9 +143,10 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
} }
refreshUiReal(); refreshUiReal();
if (this.mPostponedActivityResult != null) { if (this.mPostponedActivityResult != null) {
this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second); this.onActivityResult(
mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
} }
if (Config.X509_VERIFICATION && this.accountList.size() == 0) { if (Config.X509_VERIFICATION && this.accountList.isEmpty()) {
if (mInvokedAddAccount.compareAndSet(false, true)) { if (mInvokedAddAccount.compareAndSet(false, true)) {
addAccountFromKey(); addAccountFromKey();
} }
@ -233,9 +233,9 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0) { if (grantResults.length > 0) {
if (allGranted(grantResults)) { if (allGranted(grantResults)) {
@ -258,13 +258,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override @Override
public boolean onNavigateUp() { public boolean onNavigateUp() {
if (xmppConnectionService.getConversations().size() == 0) { if (xmppConnectionService.getConversations().size() == 0) {
Intent contactsIntent = new Intent(this, Intent contactsIntent = new Intent(this, StartConversationActivity.class);
StartConversationActivity.class);
contactsIntent.setFlags( contactsIntent.setFlags(
// if activity exists in stack, pop the stack and go back to it // if activity exists in stack, pop the stack and go back to it
Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP
|
// otherwise, make a new task for it // otherwise, make a new task for it
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
|
// don't use the new activity animation; finish // don't use the new activity animation; finish
// animation runs instead // animation runs instead
Intent.FLAG_ACTIVITY_NO_ANIMATION); Intent.FLAG_ACTIVITY_NO_ANIMATION);
@ -286,16 +287,17 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
} }
private void addAccountFromKey() { private void addAccountFromKey() {
Log.d(Config.LOGTAG, "add account from key");
try { try {
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
} catch (ActivityNotFoundException e) { } catch (final ActivityNotFoundException e) {
Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG)
.show();
} }
} }
private void publishAvatar(Account account) { private void publishAvatar(Account account) {
Intent intent = new Intent(getApplicationContext(), Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
PublishProfilePictureActivity.class);
intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString()); intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
startActivity(intent); startActivity(intent);
} }
@ -377,7 +379,6 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
} }
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
@ -385,7 +386,8 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
if (requestCode == REQUEST_CHOOSE_PGP_ID) { if (requestCode == REQUEST_CHOOSE_PGP_ID) {
if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) { if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID)); selectedAccount.setPgpSignId(
data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
announcePgp(selectedAccount, null, null, onOpenPGPKeyPublished); announcePgp(selectedAccount, null, null, onOpenPGPKeyPublished);
} else { } else {
choosePgpSignId(selectedAccount); choosePgpSignId(selectedAccount);
@ -402,9 +404,17 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override @Override
public void alias(final String alias) { public void alias(final String alias) {
if (alias != null) { if (Strings.isNullOrEmpty(alias)) {
xmppConnectionService.createAccountFromKey(alias, this); runOnUiThread(
() ->
Toast.makeText(
this,
R.string.no_certificate_selected,
Toast.LENGTH_LONG)
.show());
return;
} }
xmppConnectionService.createAccountFromKey(alias, this);
} }
@Override @Override
@ -417,6 +427,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override @Override
public void informUser(final int r) { public void informUser(final int r) {
runOnUiThread(() -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show()); runOnUiThread(
() -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show());
} }
} }

View file

@ -26,15 +26,6 @@ public class PickServerActivity extends XmppActivity {
} }
@Override
public void onStart() {
super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
}
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
@ -53,7 +44,8 @@ public class PickServerActivity extends XmppActivity {
} }
@Override @Override
public void onNewIntent(Intent intent) { public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
if (intent != null) { if (intent != null) {
setIntent(intent); setIntent(intent);
} }
@ -66,6 +58,7 @@ public class PickServerActivity extends XmppActivity {
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ActivityPickServerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_pick_server); ActivityPickServerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_pick_server);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
binding.useCim.setOnClickListener(v -> { binding.useCim.setOnClickListener(v -> {
@ -81,7 +74,7 @@ public class PickServerActivity extends XmppActivity {
if (accounts.size() == 1) { if (accounts.size() == 1) {
intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
intent.putExtra("init", true); intent.putExtra("init", true);
} else if (accounts.size() >= 1) { } else if (!accounts.isEmpty()) {
intent = new Intent(this, ManageAccountActivity.class); intent = new Intent(this, ManageAccountActivity.class);
} }
addInviteUri(intent); addInviteUri(intent);

View file

@ -56,15 +56,6 @@ public class ShareViaAccountActivity extends XmppActivity {
}); });
} }
@Override
protected void onStart() {
super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
}
@Override @Override
void onBackendConnected() { void onBackendConnected() {
final int numAccounts = xmppConnectionService.getAccounts().size(); final int numAccounts = xmppConnectionService.getAccounts().size();

View file

@ -34,7 +34,10 @@ import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.utils.PermissionUtils.allGranted; import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted; import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
public class WelcomeActivity extends XmppActivity implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback { import com.google.common.base.Strings;
public class WelcomeActivity extends XmppActivity
implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback {
private static final int REQUEST_IMPORT_BACKUP = 0x63fb; private static final int REQUEST_IMPORT_BACKUP = 0x63fb;
@ -66,7 +69,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
final Intent intent; final Intent intent;
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) { if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth); intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
} else if (xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) { } else if (xmppUri.isAction(XmppUri.ACTION_ROSTER)
&& "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth); intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString()); intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
} else { } else {
@ -81,22 +85,14 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
} }
@Override @Override
protected void refreshUiReal() { protected void refreshUiReal() {}
}
@Override @Override
void onBackendConnected() { void onBackendConnected() {}
}
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
new InstallReferrerUtils(this); new InstallReferrerUtils(this);
} }
@ -119,42 +115,44 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ActivityWelcomeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_welcome); ActivityWelcomeBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_welcome);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar(), false); configureActionBar(getSupportActionBar(), false);
binding.registerNewAccount.setOnClickListener(v -> { setTitle(null);
final Intent intent = new Intent(this, PickServerActivity.class); binding.registerNewAccount.setOnClickListener(
addInviteUri(intent); v -> {
startActivity(intent); final Intent intent = new Intent(this, PickServerActivity.class);
}); addInviteUri(intent);
binding.useExisting.setOnClickListener(v -> { startActivity(intent);
final List<Account> accounts = xmppConnectionService.getAccounts(); });
Intent intent = new Intent(WelcomeActivity.this, EditAccountActivity.class); binding.useExisting.setOnClickListener(
intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false); v -> {
if (accounts.size() == 1) { final List<Account> accounts = xmppConnectionService.getAccounts();
intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString()); Intent intent = new Intent(this, EditAccountActivity.class);
intent.putExtra("init", true); intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, false);
} else if (accounts.size() >= 1) { if (accounts.size() == 1) {
intent = new Intent(WelcomeActivity.this, ManageAccountActivity.class); intent.putExtra("jid", accounts.get(0).getJid().asBareJid().toString());
} intent.putExtra("init", true);
addInviteUri(intent); } else if (!accounts.isEmpty()) {
startActivity(intent); intent = new Intent(this, ManageAccountActivity.class);
}); }
addInviteUri(intent);
startActivity(intent);
});
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.welcome_menu, menu); getMenuInflater().inflate(R.menu.welcome_menu, menu);
final MenuItem scan = menu.findItem(R.id.action_scan_qr_code); final MenuItem scan = menu.findItem(R.id.action_scan_qr_code);
scan.setVisible(Compatibility.hasFeatureCamera(this)); scan.setVisible(Compatibility.hasFeatureCamera(this));
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_import_backup: case R.id.action_import_backup:
if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) { if (hasStoragePermission(REQUEST_IMPORT_BACKUP)) {
@ -174,16 +172,25 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
private void addAccountFromKey() { private void addAccountFromKey() {
try { try {
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null); KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
} catch (ActivityNotFoundException e) { } catch (final ActivityNotFoundException e) {
Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG)
.show();
} }
} }
@Override @Override
public void alias(final String alias) { public void alias(final String alias) {
if (alias != null) { if (Strings.isNullOrEmpty(alias)) {
xmppConnectionService.createAccountFromKey(alias, this); runOnUiThread(
() ->
Toast.makeText(
this,
R.string.no_certificate_selected,
Toast.LENGTH_LONG)
.show());
return;
} }
xmppConnectionService.createAccountFromKey(alias, this);
} }
@Override @Override
@ -201,7 +208,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults); UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
if (grantResults.length > 0) { if (grantResults.length > 0) {
@ -211,7 +219,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
startActivity(new Intent(this, ImportBackupActivity.class)); startActivity(new Intent(this, ImportBackupActivity.class));
break; break;
} }
} else if (Arrays.asList(permissions).contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { } else if (Arrays.asList(permissions)
.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
} }
} }
@ -232,5 +241,4 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, this.inviteUri.toString()); to.putExtra(StartConversationActivity.EXTRA_INVITE_URI, this.inviteUri.toString());
} }
} }
} }

View file

@ -22,7 +22,7 @@ import java.util.List;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.AccountRowBinding; import eu.siacs.conversations.databinding.ItemAccountBinding;
import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.ImportBackupService; import eu.siacs.conversations.services.ImportBackupService;
import eu.siacs.conversations.utils.BackupFileHeader; import eu.siacs.conversations.utils.BackupFileHeader;
@ -39,7 +39,7 @@ public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.Ba
@NonNull @NonNull
@Override @Override
public BackupFileViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { public BackupFileViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new BackupFileViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.account_row, viewGroup, false)); return new BackupFileViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_account, viewGroup, false));
} }
@Override @Override
@ -73,9 +73,9 @@ public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.Ba
} }
static class BackupFileViewHolder extends RecyclerView.ViewHolder { static class BackupFileViewHolder extends RecyclerView.ViewHolder {
private final AccountRowBinding binding; private final ItemAccountBinding binding;
BackupFileViewHolder(AccountRowBinding binding) { BackupFileViewHolder(ItemAccountBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
@ -91,7 +91,7 @@ public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.Ba
private Jid jid = null; private Jid jid = null;
private final int size; private final int size;
BitmapWorkerTask(ImageView imageView) { BitmapWorkerTask(final ImageView imageView) {
imageViewReference = new WeakReference<>(imageView); imageViewReference = new WeakReference<>(imageView);
DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics(); DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics();
this.size = ((int) (48 * metrics.density)); this.size = ((int) (48 * metrics.density));
@ -146,8 +146,7 @@ public class BackupFileAdapter extends RecyclerView.Adapter<BackupFileAdapter.Ba
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) { if (imageView != null) {
final Drawable drawable = imageView.getDrawable(); final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) { if (drawable instanceof AsyncDrawable asyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask(); return asyncDrawable.getBitmapWorkerTask();
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

View file

@ -1,17 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:background="?attr/color_background_primary"
android:orientation="vertical"> android:orientation="vertical">
<include <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
layout="@layout/toolbar" /> android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout <LinearLayout
android:id="@+id/in_progress" android:id="@+id/in_progress"
@ -41,7 +47,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/tap_share_button_send_invite" android:text="@string/tap_share_button_send_invite"
android:textAppearance="@style/TextAppearance.Conversations.Body1" /> android:textAppearance="?textAppearanceBodyMedium" />
<TextView <TextView
android:id="@+id/scan_the_code" android:id="@+id/scan_the_code"
@ -50,7 +56,7 @@
android:layout_below="@+id/tap_to_share" android:layout_below="@+id/tap_to_share"
android:layout_marginTop="24sp" android:layout_marginTop="24sp"
android:text="@string/if_contact_is_nearby_use_qr" android:text="@string/if_contact_is_nearby_use_qr"
android:textAppearance="@style/TextAppearance.Conversations.Body1" /> android:textAppearance="?textAppearanceBodyMedium" />
<ImageView <ImageView
android:id="@+id/qr_code" android:id="@+id/qr_code"
@ -59,23 +65,19 @@
android:layout_above="@+id/share_button" android:layout_above="@+id/share_button"
android:layout_below="@id/scan_the_code" android:layout_below="@id/scan_the_code"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentRight="true" android:layout_alignParentEnd="true"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_margin="24sp" android:layout_margin="24sp"
android:scaleType="fitCenter" /> android:scaleType="fitCenter" />
<Button <Button
android:id="@+id/share_button" android:id="@+id/share_button"
style="@style/Widget.Conversations.Button.Borderless"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:minWidth="0dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="@string/share"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:textColor="?attr/colorAccent" /> android:layout_marginHorizontal="16dp"
android:text="@string/share" />
</RelativeLayout> </RelativeLayout>

View file

@ -2,22 +2,30 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:background="?attr/color_background_primary"
android:orientation="vertical"> android:orientation="vertical">
<include <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
layout="@layout/toolbar" /> android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout <LinearLayout
android:visibility="gone"
android:id="@+id/in_progress" android:id="@+id/in_progress"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"> android:gravity="center"
android:visibility="gone">
<ProgressBar <ProgressBar
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -25,18 +33,15 @@
</LinearLayout> </LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator" android:id="@+id/coordinator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="?attr/color_background_primary">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/list" android:id="@+id/list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/color_background_primary"
android:orientation="vertical" android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -7,7 +7,18 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include layout="@layout/toolbar" android:id="@+id/toolbar"/> <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -16,15 +27,13 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:background="?attr/color_background_primary">
<LinearLayout <LinearLayout
android:id="@+id/linearLayout" android:id="@+id/linearLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:minHeight="256dp" android:minHeight="256dp"
android:orientation="vertical" android:orientation="vertical"
@ -42,50 +51,53 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/pick_your_username" android:text="@string/pick_your_username"
android:textAppearance="@style/TextAppearance.Conversations.Title" /> android:textAppearance="?textAppearanceTitleLarge" />
<TextView <TextView
android:id="@+id/instructions" android:id="@+id/instructions"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginVertical="8dp"
android:text="@string/magic_create_text" android:text="@string/magic_create_text"
android:textAppearance="@style/TextAppearance.Conversations.Body1" /> android:textAppearance="?textAppearanceBodyMedium" />
<EditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/username" android:id="@+id/username_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:hint="@string/username_hint">
android:hint="@string/username_hint"
android:textColor="?attr/edit_text_color" <com.google.android.material.textfield.TextInputEditText
android:inputType="textNoSuggestions" /> android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:inputType="textNoSuggestions" />
</com.google.android.material.textfield.TextInputLayout>
<TextView <TextView
android:id="@+id/full_jid" android:id="@+id/full_jid"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginVertical="8dp"
android:text="@string/your_full_jid_will_be" android:text="@string/your_full_jid_will_be"
android:textAppearance="@style/TextAppearance.Conversations.Caption" android:textAppearance="?textAppearanceLabelSmall"
android:visibility="invisible" /> android:visibility="invisible" />
<Button <Button
android:id="@+id/create_account" android:id="@+id/create_account"
style="@style/Widget.Conversations.Button.Borderless" style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="end"
android:text="@string/next" android:text="@string/next" />
android:textColor="?colorAccent" />
</LinearLayout> </LinearLayout>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@+id/linearLayout" android:layout_above="@+id/linearLayout"
android:layout_alignParentStart="true" android:layout_alignParentStart="true">
android:layout_alignParentLeft="true">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -7,7 +7,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include android:id="@+id/toolbar" layout="@layout/toolbar" /> <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -16,15 +26,13 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content">
android:background="?attr/color_background_primary">
<LinearLayout <LinearLayout
android:id="@+id/linearLayout" android:id="@+id/linearLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:minHeight="256dp" android:minHeight="256dp"
android:orientation="vertical" android:orientation="vertical"
@ -41,40 +49,38 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/pick_a_server" android:text="@string/pick_a_server"
android:textAppearance="@style/TextAppearance.Conversations.Title" /> android:textAppearance="?textAppearanceTitleLarge" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:text="@string/server_select_text" android:text="@string/server_select_text"
android:textAppearance="@style/TextAppearance.Conversations.Body1" /> android:textAppearance="?textAppearanceBodyMedium" />
<Button <Button
android:id="@+id/use_cim" android:id="@+id/use_cim"
style="@style/Widget.Conversations.Button.Borderless" style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="end"
android:text="@string/use_conversations.im" android:text="@string/use_conversations.im" />
android:textColor="?colorAccent" />
<Button <Button
android:id="@+id/use_own_provider" android:id="@+id/use_own_provider"
style="@style/Widget.Conversations.Button.Borderless" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="end"
android:text="@string/use_own_provider" android:text="@string/use_own_provider" />
android:textColor="?android:textColorSecondary" />
</LinearLayout> </LinearLayout>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@+id/linearLayout" android:layout_above="@+id/linearLayout"
android:layout_alignParentStart="true" android:layout_alignParentStart="true">
android:layout_alignParentLeft="true">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -6,9 +6,18 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<include <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar" android:id="@+id/app_bar_layout"
layout="@layout/toolbar" /> android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -17,15 +26,13 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:background="?attr/color_background_primary">
<LinearLayout <LinearLayout
android:id="@+id/linearLayout" android:id="@+id/linearLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:minHeight="256dp" android:minHeight="256dp"
android:orientation="vertical" android:orientation="vertical"
@ -42,40 +49,38 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/welcome_header" android:text="@string/welcome_header"
android:textAppearance="@style/TextAppearance.Conversations.Title" /> android:textAppearance="?textAppearanceTitleLarge" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:text="@string/do_you_have_an_account" android:text="@string/do_you_have_an_account"
android:textAppearance="@style/TextAppearance.Conversations.Body1" /> android:textAppearance="?textAppearanceBodyMedium" />
<Button <Button
android:id="@+id/register_new_account" android:id="@+id/register_new_account"
style="@style/Widget.Conversations.Button.Borderless" style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="end"
android:text="@string/create_new_account" android:text="@string/create_new_account" />
android:textColor="?colorAccent" />
<Button <Button
android:id="@+id/use_existing" android:id="@+id/use_existing"
style="@style/Widget.Conversations.Button.Borderless" style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="end"
android:text="@string/i_already_have_an_account" android:text="@string/i_already_have_an_account" />
android:textColor="?android:textColorSecondary" />
</LinearLayout> </LinearLayout>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@+id/linearLayout" android:layout_above="@+id/linearLayout"
android:layout_alignParentStart="true" android:layout_alignParentStart="true">
android:layout_alignParentLeft="true">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -13,41 +13,35 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/enter_password_to_restore" android:text="@string/enter_password_to_restore"
android:textAppearance="@style/TextAppearance.Conversations.Body2"/> android:textAppearance="?textAppearanceBodyMedium" />
<TextView <TextView
android:layout_marginTop="?TextSizeBody1"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18sp"
android:text="@string/restore_warning" android:text="@string/restore_warning"
android:textAppearance="@style/TextAppearance.Conversations.Body1"/> android:textAppearance="?textAppearanceBodyMedium" />
<TextView <TextView
android:layout_marginTop="?TextSizeBody1"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="18sp"
android:text="@string/restore_warning_continued" android:text="@string/restore_warning_continued"
android:textAppearance="@style/TextAppearance.Conversations.Subhead.Bold"/> android:textAppearance="?textAppearanceBodyMedium" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/account_password_layout" android:id="@+id/account_password_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:passwordToggleDrawable="@drawable/visibility_toggle_drawable" app:endIconMode="password_toggle">
app:passwordToggleEnabled="true"
app:passwordToggleTint="?android:textColorSecondary"
app:hintTextAppearance="@style/TextAppearance.Conversations.Design.Hint"
app:errorTextAppearance="@style/TextAppearance.Conversations.Design.Error">
<eu.siacs.conversations.ui.widget.TextInputEditText <eu.siacs.conversations.ui.widget.TextInputEditText
android:id="@+id/account_password" android:id="@+id/account_password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/password" android:hint="@string/password"
android:inputType="textPassword" android:inputType="textPassword" />
android:textColor="?attr/edit_text_color"
style="@style/Widget.Conversations.EditText"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>

View file

@ -4,7 +4,7 @@
<item <item
android:id="@+id/action_share" android:id="@+id/action_share"
android:icon="?attr/icon_share" android:icon="@drawable/ic_share_24dp"
android:title="@string/invite" android:title="@string/invite"
app:showAsAction="always" /> app:showAsAction="always" />
</menu> </menu>

View file

@ -1,32 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_add_account" android:id="@+id/action_add_account"
android:icon="?attr/icon_add_person" android:icon="@drawable/ic_person_add_24dp"
app:showAsAction="always" android:title="@string/action_add_account"
android:title="@string/action_add_account"/> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_import_backup" android:id="@+id/action_import_backup"
app:showAsAction="never" android:title="@string/restore_backup"
android:title="@string/restore_backup"/> app:showAsAction="never" />
<item <item
android:id="@+id/action_add_account_with_cert" android:id="@+id/action_add_account_with_cert"
app:showAsAction="never" android:title="@string/action_add_account_with_certificate"
android:icon="?attr/icon_add_person" android:visible="true"
android:title="@string/action_add_account_with_certificate" app:showAsAction="never" />
android:visible="true"/> <item
<item android:id="@+id/action_enable_all"
android:id="@+id/action_enable_all" android:title="@string/enable_all_accounts" />
android:title="@string/enable_all_accounts"/> <item
<item android:id="@+id/action_disable_all"
android:id="@+id/action_disable_all" android:title="@string/disable_all_accounts" />
android:title="@string/disable_all_accounts"/> <item
<item android:id="@+id/action_settings"
android:id="@+id/action_settings" android:orderInCategory="100"
android:orderInCategory="100" android:title="@string/action_settings"
app:showAsAction="never" app:showAsAction="never" />
android:title="@string/action_settings"/>
</menu> </menu>

View file

@ -3,7 +3,7 @@
<item <item
android:id="@+id/action_scan_qr_code" android:id="@+id/action_scan_qr_code"
android:icon="?attr/icon_scan_qr_code" android:icon="@drawable/ic_qr_code_scanner_24dp"
android:orderInCategory="10" android:orderInCategory="10"
android:title="@string/scan_qr_code" android:title="@string/scan_qr_code"
android:visible="@bool/show_qr_code_scan" android:visible="@bool/show_qr_code_scan"

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="md_theme_light_primary">#006E1C</color>
<color name="md_theme_light_onPrimary">#FFFFFF</color>
<color name="md_theme_light_primaryContainer">#98F994</color>
<color name="md_theme_light_onPrimaryContainer">#002204</color>
<color name="md_theme_light_secondary">#52634F</color>
<color name="md_theme_light_onSecondary">#FFFFFF</color>
<color name="md_theme_light_secondaryContainer">#D5E8CF</color>
<color name="md_theme_light_onSecondaryContainer">#111F0F</color>
<color name="md_theme_light_tertiary">#38656A</color>
<color name="md_theme_light_onTertiary">#FFFFFF</color>
<color name="md_theme_light_tertiaryContainer">#BCEBF0</color>
<color name="md_theme_light_onTertiaryContainer">#002023</color>
<color name="md_theme_light_error">#BA1A1A</color>
<color name="md_theme_light_errorContainer">#FFDAD6</color>
<color name="md_theme_light_onError">#FFFFFF</color>
<color name="md_theme_light_onErrorContainer">#410002</color>
<color name="md_theme_light_background">#FCFDF6</color>
<color name="md_theme_light_onBackground">#1A1C19</color>
<color name="md_theme_light_surface">#FCFDF6</color>
<color name="md_theme_light_onSurface">#1A1C19</color>
<color name="md_theme_light_surfaceVariant">#DEE5D8</color>
<color name="md_theme_light_onSurfaceVariant">#424940</color>
<color name="md_theme_light_outline">#72796F</color>
<color name="md_theme_light_inverseOnSurface">#F0F1EB</color>
<color name="md_theme_light_inverseSurface">#2F312D</color>
<color name="md_theme_light_inversePrimary">#7DDC7A</color>
<color name="md_theme_light_shadow">#000000</color>
<color name="md_theme_light_surfaceTint">#006E1C</color>
<color name="md_theme_light_outlineVariant">#C2C9BD</color>
<color name="md_theme_light_scrim">#000000</color>
<color name="md_theme_dark_primary">#7DDC7A</color>
<color name="md_theme_dark_onPrimary">#00390A</color>
<color name="md_theme_dark_primaryContainer">#005313</color>
<color name="md_theme_dark_onPrimaryContainer">#98F994</color>
<color name="md_theme_dark_secondary">#BACCB3</color>
<color name="md_theme_dark_onSecondary">#253423</color>
<color name="md_theme_dark_secondaryContainer">#3B4B38</color>
<color name="md_theme_dark_onSecondaryContainer">#D5E8CF</color>
<color name="md_theme_dark_tertiary">#A0CFD4</color>
<color name="md_theme_dark_onTertiary">#00363B</color>
<color name="md_theme_dark_tertiaryContainer">#1F4D52</color>
<color name="md_theme_dark_onTertiaryContainer">#BCEBF0</color>
<color name="md_theme_dark_error">#FFB4AB</color>
<color name="md_theme_dark_errorContainer">#93000A</color>
<color name="md_theme_dark_onError">#690005</color>
<color name="md_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="md_theme_dark_background">#1A1C19</color>
<color name="md_theme_dark_onBackground">#E2E3DD</color>
<color name="md_theme_dark_surface">#1A1C19</color>
<color name="md_theme_dark_onSurface">#E2E3DD</color>
<color name="md_theme_dark_surfaceVariant">#424940</color>
<color name="md_theme_dark_onSurfaceVariant">#C2C9BD</color>
<color name="md_theme_dark_outline">#8C9388</color>
<color name="md_theme_dark_inverseOnSurface">#1A1C19</color>
<color name="md_theme_dark_inverseSurface">#E2E3DD</color>
<color name="md_theme_dark_inversePrimary">#006E1C</color>
<color name="md_theme_dark_shadow">#000000</color>
<color name="md_theme_dark_surfaceTint">#7DDC7A</color>
<color name="md_theme_dark_outlineVariant">#424940</color>
<color name="md_theme_dark_scrim">#000000</color>
</resources>

View file

@ -47,7 +47,7 @@
<!-- this foreground service type permission is exclusively used for import and export backup --> <!-- this foreground service type permission is exclusively used for import and export backup -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/> <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-feature <uses-feature
android:name="android.hardware.camera" android:name="android.hardware.camera"
@ -84,6 +84,7 @@
<application <application
android:name=".Conversations"
android:allowBackup="true" android:allowBackup="true"
android:appCategory="social" android:appCategory="social"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
@ -96,7 +97,7 @@
android:networkSecurityConfig="@xml/network_security_configuration" android:networkSecurityConfig="@xml/network_security_configuration"
android:preserveLegacyExternalStorage="true" android:preserveLegacyExternalStorage="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:theme="@style/ConversationsTheme" android:theme="@style/Theme.Conversations3"
tools:targetApi="tiramisu"> tools:targetApi="tiramisu">
<meta-data <meta-data
@ -132,9 +133,10 @@
</intent-filter> </intent-filter>
</service> </service>
<service android:name=".services.CallIntegrationConnectionService" <service
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" android:name=".services.CallIntegrationConnectionService"
android:exported="true"> android:exported="true"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter> <intent-filter>
<action android:name="android.telecom.ConnectionService" /> <action android:name="android.telecom.ConnectionService" />
</intent-filter> </intent-filter>
@ -180,14 +182,14 @@
<activity <activity
android:name=".ui.RecordingActivity" android:name=".ui.RecordingActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:theme="@style/ConversationsTheme.Dialog" /> android:theme="@style/Theme.Conversations3.Dialog" />
<activity <activity
android:name=".ui.ShowLocationActivity" android:name=".ui.ShowLocationActivity"
android:label="@string/title_activity_show_location" /> android:label="@string/title_activity_show_location" />
<activity <activity
android:name=".ui.ConversationActivity" android:name=".ui.ConversationActivity"
android:exported="true" android:exported="true"
android:theme="@style/SplashTheme"> android:theme="@style/Theme.Conversations3.SplashScreen">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -202,7 +204,7 @@
<activity <activity
android:name=".ui.ScanActivity" android:name=".ui.ScanActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/ConversationsTheme.FullScreen" android:theme="@style/Theme.Conversations3.FullScreen"
android:windowSoftInputMode="stateAlwaysHidden" /> android:windowSoftInputMode="stateAlwaysHidden" />
<activity <activity
android:name=".ui.UriHandlerActivity" android:name=".ui.UriHandlerActivity"
@ -263,10 +265,10 @@
</activity> </activity>
<activity <activity
android:name=".ui.ChooseContactActivity" android:name=".ui.ChooseContactActivity"
android:label="@string/title_activity_choose_contact" /> android:label="@string/title_activity_choose_contact"/>
<activity <activity
android:name=".ui.BlocklistActivity" android:name=".ui.BlocklistActivity"
android:label="@string/title_activity_block_list" /> android:label="@string/title_activity_block_list"/>
<activity <activity
android:name=".ui.ChangePasswordActivity" android:name=".ui.ChangePasswordActivity"
android:label="@string/change_password_on_server" /> android:label="@string/change_password_on_server" />
@ -351,7 +353,7 @@
<activity <activity
android:name=".ui.MediaBrowserActivity" android:name=".ui.MediaBrowserActivity"
android:label="@string/media_browser" /> android:label="@string/media_browser"/>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@ -378,10 +380,10 @@
</activity> </activity>
<activity <activity
android:name=".ui.MucUsersActivity" android:name=".ui.MucUsersActivity"
android:label="@string/group_chat_members" /> android:label="@string/group_chat_members"/>
<activity <activity
android:name=".ui.ChannelDiscoveryActivity" android:name=".ui.ChannelDiscoveryActivity"
android:label="@string/discover_channels" /> android:label="@string/discover_channels"/>
<activity <activity
android:name=".ui.RtpSessionActivity" android:name=".ui.RtpSessionActivity"
android:autoRemoveFromRecents="true" android:autoRemoveFromRecents="true"

View file

@ -45,8 +45,6 @@ public final class Config {
public static final Jid BUG_REPORTS = Jid.of("bugs@conversations.im"); public static final Jid BUG_REPORTS = Jid.of("bugs@conversations.im");
public static final Uri HELP = Uri.parse("https://help.conversations.im"); public static final Uri HELP = Uri.parse("https://help.conversations.im");
public static final String DOMAIN_LOCK = null; // only allow account creation for this domain
public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; public static final String MAGIC_CREATE_DOMAIN = "conversations.im";
public static final Jid QUICKSY_DOMAIN = Jid.of("quicksy.im"); public static final Jid QUICKSY_DOMAIN = Jid.of("quicksy.im");

View file

@ -0,0 +1,67 @@
package eu.siacs.conversations;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.appcompat.app.AppCompatDelegate;
import com.google.android.material.color.DynamicColors;
import com.google.android.material.color.DynamicColorsOptions;
public class Conversations extends Application {
@Override
public void onCreate() {
super.onCreate();
applyThemeSettings();
}
public void applyThemeSettings() {
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences == null) {
return;
}
applyThemeSettings(sharedPreferences);
}
private void applyThemeSettings(final SharedPreferences sharedPreferences) {
AppCompatDelegate.setDefaultNightMode(getDesiredNightMode(this, sharedPreferences));
var dynamicColorsOptions =
new DynamicColorsOptions.Builder()
.setPrecondition((activity, t) -> isDynamicColorsDesired(activity))
.build();
DynamicColors.applyToActivitiesIfAvailable(this, dynamicColorsOptions);
}
public static int getDesiredNightMode(final Context context) {
final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences == null) {
return AppCompatDelegate.getDefaultNightMode();
}
return getDesiredNightMode(context, sharedPreferences);
}
public static boolean isDynamicColorsDesired(final Context context) {
final var preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getBoolean("dynamic_colors", false);
}
private static int getDesiredNightMode(
final Context context, final SharedPreferences sharedPreferences) {
final String theme =
sharedPreferences.getString("theme", context.getString(R.string.theme));
return getDesiredNightMode(theme);
}
public static int getDesiredNightMode(final String theme) {
if ("automatic".equals(theme)) {
return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
} else if ("light".equals(theme)) {
return AppCompatDelegate.MODE_NIGHT_NO;
} else {
return AppCompatDelegate.MODE_NIGHT_YES;
}
}
}

View file

@ -41,18 +41,18 @@ public class RtpSessionStatus {
return new RtpSessionStatus(made, duration); return new RtpSessionStatus(made, duration);
} }
public static @DrawableRes int getDrawable(final boolean received, final boolean successful, final boolean darkTheme) { public static @DrawableRes int getDrawable(final boolean received, final boolean successful) {
if (received) { if (received) {
if (successful) { if (successful) {
return darkTheme ? R.drawable.ic_call_received_white_18dp : R.drawable.ic_call_received_black_18dp; return R.drawable.ic_call_received_24dp;
} else { } else {
return darkTheme ? R.drawable.ic_call_missed_white_18dp : R.drawable.ic_call_missed_black_18dp; return R.drawable.ic_call_missed_24db;
} }
} else { } else {
if (successful) { if (successful) {
return darkTheme ? R.drawable.ic_call_made_white_18dp : R.drawable.ic_call_made_black_18dp; return R.drawable.ic_call_made_24dp;
} else { } else {
return darkTheme ? R.drawable.ic_call_missed_outgoing_white_18dp : R.drawable.ic_call_missed_outgoing_black_18dp; return R.drawable.ic_call_missed_outgoing_24dp;
} }
} }
} }

View file

@ -48,7 +48,11 @@ public class BarcodeProvider extends ContentProvider implements ServiceConnectio
return Uri.parse("content://" + packageId + AUTHORITY + "/" + account.getJid().asBareJid() + ".png"); return Uri.parse("content://" + packageId + AUTHORITY + "/" + account.getJid().asBareJid() + ".png");
} }
public static Bitmap create2dBarcodeBitmap(String input, int size) { public static Bitmap create2dBarcodeBitmap(final String input, final int size) {
return create2dBarcodeBitmap(input, size, Color.BLACK, Color.WHITE);
}
public static Bitmap create2dBarcodeBitmap(final String input, final int size, final int black, final int white) {
try { try {
final QRCodeWriter barcodeWriter = new QRCodeWriter(); final QRCodeWriter barcodeWriter = new QRCodeWriter();
final Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
@ -61,14 +65,14 @@ public class BarcodeProvider extends ContentProvider implements ServiceConnectio
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
final int offset = y * width; final int offset = y * width;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; pixels[offset + x] = result.get(x, y) ? black : white;
} }
} }
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height); bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap; return bitmap;
} catch (final Exception e) { } catch (final Exception e) {
e.printStackTrace(); Log.e(Config.LOGTAG,"could not generate QR code image",e);
return null; return null;
} }
} }

View file

@ -266,7 +266,7 @@ public class ExportBackupService extends Service {
NotificationCompat.Builder mBuilder = NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(getBaseContext(), "backup"); new NotificationCompat.Builder(getBaseContext(), "backup");
mBuilder.setContentTitle(getString(R.string.notification_create_backup_title)) mBuilder.setContentTitle(getString(R.string.notification_create_backup_title))
.setSmallIcon(R.drawable.ic_archive_white_24dp) .setSmallIcon(R.drawable.ic_archive_24dp)
.setProgress(1, 0, false); .setProgress(1, 0, false);
startForeground(NOTIFICATION_ID, mBuilder.build()); startForeground(NOTIFICATION_ID, mBuilder.build());
int count = 0; int count = 0;
@ -420,11 +420,11 @@ public class ExportBackupService extends Service {
.getAbsolutePath()))) .getAbsolutePath())))
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(openFolderIntent) .setContentIntent(openFolderIntent)
.setSmallIcon(R.drawable.ic_archive_white_24dp); .setSmallIcon(R.drawable.ic_archive_24dp);
if (shareFilesIntent != null) { if (shareFilesIntent != null) {
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_share_white_24dp, R.drawable.ic_share_24dp,
getString(R.string.share_backup_files), getString(R.string.share_backup_files),
shareFilesIntent); shareFilesIntent);
} }

View file

@ -475,7 +475,7 @@ public class NotificationService {
new Builder(mXmppConnectionService, "delivery_failed") new Builder(mXmppConnectionService, "delivery_failed")
.setContentTitle(conversation.getName()) .setContentTitle(conversation.getName())
.setAutoCancel(true) .setAutoCancel(true)
.setSmallIcon(R.drawable.ic_error_white_24dp) .setSmallIcon(R.drawable.ic_error_24dp)
.setContentText( .setContentText(
mXmppConnectionService mXmppConnectionService
.getResources() .getResources()
@ -495,7 +495,7 @@ public class NotificationService {
.getQuantityText( .getQuantityText(
R.plurals.some_messages_could_not_be_delivered, R.plurals.some_messages_could_not_be_delivered,
1024)) 1024))
.setSmallIcon(R.drawable.ic_error_white_24dp) .setSmallIcon(R.drawable.ic_error_24dp)
.setGroup("delivery_failed") .setGroup("delivery_failed")
.setGroupSummary(true) .setGroupSummary(true)
.setAutoCancel(true) .setAutoCancel(true)
@ -569,11 +569,11 @@ public class NotificationService {
new NotificationCompat.Builder( new NotificationCompat.Builder(
mXmppConnectionService, INCOMING_CALLS_NOTIFICATION_CHANNEL); mXmppConnectionService, INCOMING_CALLS_NOTIFICATION_CHANNEL);
if (media.contains(Media.VIDEO)) { if (media.contains(Media.VIDEO)) {
builder.setSmallIcon(R.drawable.ic_videocam_white_24dp); builder.setSmallIcon(R.drawable.ic_videocam_24dp);
builder.setContentTitle( builder.setContentTitle(
mXmppConnectionService.getString(R.string.rtp_state_incoming_video_call)); mXmppConnectionService.getString(R.string.rtp_state_incoming_video_call));
} else { } else {
builder.setSmallIcon(R.drawable.ic_call_white_24dp); builder.setSmallIcon(R.drawable.ic_call_24dp);
builder.setContentTitle( builder.setContentTitle(
mXmppConnectionService.getString(R.string.rtp_state_incoming_call)); mXmppConnectionService.getString(R.string.rtp_state_incoming_call));
} }
@ -596,7 +596,7 @@ public class NotificationService {
builder.setOngoing(true); builder.setOngoing(true);
builder.addAction( builder.addAction(
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_call_end_white_48dp, R.drawable.ic_call_end_24dp,
mXmppConnectionService.getString(R.string.dismiss_call), mXmppConnectionService.getString(R.string.dismiss_call),
createCallAction( createCallAction(
id.sessionId, id.sessionId,
@ -605,7 +605,7 @@ public class NotificationService {
.build()); .build());
builder.addAction( builder.addAction(
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_call_white_24dp, R.drawable.ic_call_24dp,
mXmppConnectionService.getString(R.string.answer_call), mXmppConnectionService.getString(R.string.answer_call),
createPendingRtpSession( createPendingRtpSession(
id, RtpSessionActivity.ACTION_ACCEPT_CALL, 103)) id, RtpSessionActivity.ACTION_ACCEPT_CALL, 103))
@ -622,7 +622,7 @@ public class NotificationService {
final NotificationCompat.Builder builder = final NotificationCompat.Builder builder =
new NotificationCompat.Builder(mXmppConnectionService, "ongoing_calls"); new NotificationCompat.Builder(mXmppConnectionService, "ongoing_calls");
if (ongoingCall.media.contains(Media.VIDEO)) { if (ongoingCall.media.contains(Media.VIDEO)) {
builder.setSmallIcon(R.drawable.ic_videocam_white_24dp); builder.setSmallIcon(R.drawable.ic_videocam_24dp);
if (ongoingCall.reconnecting) { if (ongoingCall.reconnecting) {
builder.setContentTitle( builder.setContentTitle(
mXmppConnectionService.getString(R.string.reconnecting_video_call)); mXmppConnectionService.getString(R.string.reconnecting_video_call));
@ -631,7 +631,7 @@ public class NotificationService {
mXmppConnectionService.getString(R.string.ongoing_video_call)); mXmppConnectionService.getString(R.string.ongoing_video_call));
} }
} else { } else {
builder.setSmallIcon(R.drawable.ic_call_white_24dp); builder.setSmallIcon(R.drawable.ic_call_24dp);
if (ongoingCall.reconnecting) { if (ongoingCall.reconnecting) {
builder.setContentTitle( builder.setContentTitle(
mXmppConnectionService.getString(R.string.reconnecting_call)); mXmppConnectionService.getString(R.string.reconnecting_call));
@ -647,7 +647,7 @@ public class NotificationService {
builder.setOngoing(true); builder.setOngoing(true);
builder.addAction( builder.addAction(
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_call_end_white_48dp, R.drawable.ic_call_end_24dp,
mXmppConnectionService.getString(R.string.hang_up), mXmppConnectionService.getString(R.string.hang_up),
createCallAction( createCallAction(
id.sessionId, XmppConnectionService.ACTION_END_CALL, 104)) id.sessionId, XmppConnectionService.ACTION_END_CALL, 104))
@ -826,7 +826,7 @@ public class NotificationService {
} }
private void markAsReadIfHasDirectReply(final ArrayList<Message> messages) { private void markAsReadIfHasDirectReply(final ArrayList<Message> messages) {
if (messages != null && messages.size() > 0) { if (messages != null && !messages.isEmpty()) {
Message last = messages.get(messages.size() - 1); Message last = messages.get(messages.size() - 1);
if (last.getStatus() != Message.STATUS_RECEIVED) { if (last.getStatus() != Message.STATUS_RECEIVED) {
if (mXmppConnectionService.markRead((Conversation) last.getConversation(), false)) { if (mXmppConnectionService.markRead((Conversation) last.getConversation(), false)) {
@ -837,7 +837,8 @@ public class NotificationService {
} }
private void setNotificationColor(final Builder mBuilder) { private void setNotificationColor(final Builder mBuilder) {
mBuilder.setColor(ContextCompat.getColor(mXmppConnectionService, R.color.green600)); // TODO can we use themed colors?
mBuilder.setColor(ContextCompat.getColor(mXmppConnectionService, R.color.md_theme_light_primary));
} }
public void updateNotification() { public void updateNotification() {
@ -1035,7 +1036,7 @@ public class NotificationService {
if (!publicVersion) { if (!publicVersion) {
builder.setContentText(Joiner.on(", ").join(names)); builder.setContentText(Joiner.on(", ").join(names));
} }
builder.setSmallIcon(R.drawable.ic_call_missed_white_24db); builder.setSmallIcon(R.drawable.ic_call_missed_24db);
builder.setGroupSummary(true); builder.setGroupSummary(true);
builder.setGroup(MISSED_CALLS_GROUP); builder.setGroup(MISSED_CALLS_GROUP);
builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN); builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN);
@ -1085,7 +1086,7 @@ public class NotificationService {
name)); name));
builder.setContentText(name); builder.setContentText(name);
} }
builder.setSmallIcon(R.drawable.ic_call_missed_white_24db); builder.setSmallIcon(R.drawable.ic_call_missed_24db);
builder.setGroup(MISSED_CALLS_GROUP); builder.setGroup(MISSED_CALLS_GROUP);
builder.setCategory(NotificationCompat.CATEGORY_CALL); builder.setCategory(NotificationCompat.CATEGORY_CALL);
builder.setWhen(info.getLastTime()); builder.setWhen(info.getLastTime());
@ -1221,7 +1222,7 @@ public class NotificationService {
PendingIntent markAsReadPendingIntent = createReadPendingIntent(conversation); PendingIntent markAsReadPendingIntent = createReadPendingIntent(conversation);
NotificationCompat.Action markReadAction = NotificationCompat.Action markReadAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_drafts_white_24dp, R.drawable.ic_mark_chat_read_24dp,
mXmppConnectionService.getString(R.string.mark_as_read), mXmppConnectionService.getString(R.string.mark_as_read),
markAsReadPendingIntent) markAsReadPendingIntent)
.setSemanticAction( .setSemanticAction(
@ -1232,7 +1233,7 @@ public class NotificationService {
final String lastMessageUuid = Iterables.getLast(messages).getUuid(); final String lastMessageUuid = Iterables.getLast(messages).getUuid();
final NotificationCompat.Action replyAction = final NotificationCompat.Action replyAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_send_text_offline, R.drawable.ic_send_24dp,
replyLabel, replyLabel,
createReplyIntent(conversation, lastMessageUuid, false)) createReplyIntent(conversation, lastMessageUuid, false))
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
@ -1241,7 +1242,7 @@ public class NotificationService {
.build(); .build();
final NotificationCompat.Action wearReplyAction = final NotificationCompat.Action wearReplyAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_wear_reply, R.drawable.ic_reply_24dp,
replyLabel, replyLabel,
createReplyIntent(conversation, lastMessageUuid, true)) createReplyIntent(conversation, lastMessageUuid, true))
.addRemoteInput(remoteInput) .addRemoteInput(remoteInput)
@ -1260,7 +1261,7 @@ public class NotificationService {
PendingIntent pendingSnoozeIntent = createSnoozeIntent(conversation); PendingIntent pendingSnoozeIntent = createSnoozeIntent(conversation);
NotificationCompat.Action snoozeAction = NotificationCompat.Action snoozeAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_notifications_paused_white_24dp, R.drawable.ic_notifications_paused_24dp,
label, label,
pendingSnoozeIntent) pendingSnoozeIntent)
.build(); .build();
@ -1279,7 +1280,7 @@ public class NotificationService {
.getString(R.string.show_location); .getString(R.string.show_location);
NotificationCompat.Action locationAction = NotificationCompat.Action locationAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_room_white_24dp, R.drawable.ic_location_pin_24dp,
label, label,
pendingShowLocationIntent) pendingShowLocationIntent)
.build(); .build();
@ -1303,7 +1304,7 @@ public class NotificationService {
createDownloadIntent(firstDownloadableMessage); createDownloadIntent(firstDownloadableMessage);
NotificationCompat.Action downloadAction = NotificationCompat.Action downloadAction =
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
R.drawable.ic_file_download_white_24dp, R.drawable.ic_download_24dp,
label, label,
pendingDownloadIntent) pendingDownloadIntent)
.build(); .build();
@ -1761,21 +1762,21 @@ public class NotificationService {
.setPriority(Notification.PRIORITY_MIN) .setPriority(Notification.PRIORITY_MIN)
.setSmallIcon( .setSmallIcon(
connected > 0 connected > 0
? R.drawable.ic_link_white_24dp ? R.drawable.ic_link_24dp
: R.drawable.ic_link_off_white_24dp) : R.drawable.ic_link_off_24dp)
.setLocalOnly(true); .setLocalOnly(true);
if (Compatibility.runsTwentySix()) { if (Compatibility.runsTwentySix()) {
mBuilder.setChannelId("foreground"); mBuilder.setChannelId("foreground");
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_logout_white_24dp, R.drawable.ic_logout_24dp,
mXmppConnectionService.getString(R.string.log_out), mXmppConnectionService.getString(R.string.log_out),
pendingServiceIntent( pendingServiceIntent(
mXmppConnectionService, mXmppConnectionService,
XmppConnectionService.ACTION_TEMPORARILY_DISABLE, XmppConnectionService.ACTION_TEMPORARILY_DISABLE,
87)); 87));
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_notifications_off_white_24dp, R.drawable.ic_notifications_off_24dp,
mXmppConnectionService.getString(R.string.hide_notification), mXmppConnectionService.getString(R.string.hide_notification),
pendingNotificationSettingsIntent(mXmppConnectionService)); pendingNotificationSettingsIntent(mXmppConnectionService));
} }
@ -1853,7 +1854,7 @@ public class NotificationService {
} }
try { try {
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_autorenew_white_24dp, R.drawable.ic_autorenew_24dp,
mXmppConnectionService.getString(R.string.try_again), mXmppConnectionService.getString(R.string.try_again),
pendingServiceIntent( pendingServiceIntent(
mXmppConnectionService, XmppConnectionService.ACTION_TRY_AGAIN, 45)); mXmppConnectionService, XmppConnectionService.ACTION_TRY_AGAIN, 45));
@ -1871,7 +1872,7 @@ public class NotificationService {
if (torNotAvailable) { if (torNotAvailable) {
if (TorServiceUtils.isOrbotInstalled(mXmppConnectionService)) { if (TorServiceUtils.isOrbotInstalled(mXmppConnectionService)) {
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_play_circle_filled_white_48dp, R.drawable.ic_play_circle_24dp,
mXmppConnectionService.getString(R.string.start_orbot), mXmppConnectionService.getString(R.string.start_orbot),
PendingIntent.getActivity( PendingIntent.getActivity(
mXmppConnectionService, mXmppConnectionService,
@ -1883,7 +1884,7 @@ public class NotificationService {
: PendingIntent.FLAG_UPDATE_CURRENT)); : PendingIntent.FLAG_UPDATE_CURRENT));
} else { } else {
mBuilder.addAction( mBuilder.addAction(
R.drawable.ic_file_download_white_24dp, R.drawable.ic_download_24dp,
mXmppConnectionService.getString(R.string.install_orbot), mXmppConnectionService.getString(R.string.install_orbot),
PendingIntent.getActivity( PendingIntent.getActivity(
mXmppConnectionService, mXmppConnectionService,
@ -1896,7 +1897,7 @@ public class NotificationService {
} }
} }
mBuilder.setVisibility(Notification.VISIBILITY_PRIVATE); mBuilder.setVisibility(Notification.VISIBILITY_PRIVATE);
mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); mBuilder.setSmallIcon(R.drawable.ic_warning_24dp);
mBuilder.setLocalOnly(true); mBuilder.setLocalOnly(true);
mBuilder.setPriority(Notification.PRIORITY_LOW); mBuilder.setPriority(Notification.PRIORITY_LOW);
final Intent intent; final Intent intent;
@ -1935,7 +1936,7 @@ public class NotificationService {
} else { } else {
builder.setProgress(100, 0, true); builder.setProgress(100, 0, true);
} }
builder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); builder.setSmallIcon(R.drawable.ic_hourglass_top_24dp);
if (message != null) { if (message != null) {
builder.setContentIntent(createContentIntent(message.getConversation())); builder.setContentIntent(createContentIntent(message.getConversation()));
} }

View file

@ -4760,9 +4760,6 @@ public class XmppConnectionService extends Service {
if (Config.QUICKSY_DOMAIN != null) { if (Config.QUICKSY_DOMAIN != null) {
hosts.remove(Config.QUICKSY_DOMAIN.toEscapedString()); //we only want to show this when we type a e164 number hosts.remove(Config.QUICKSY_DOMAIN.toEscapedString()); //we only want to show this when we type a e164 number
} }
if (Config.DOMAIN_LOCK != null) {
hosts.add(Config.DOMAIN_LOCK);
}
if (Config.MAGIC_CREATE_DOMAIN != null) { if (Config.MAGIC_CREATE_DOMAIN != null) {
hosts.add(Config.MAGIC_CREATE_DOMAIN); hosts.add(Config.MAGIC_CREATE_DOMAIN);
} }

View file

@ -1,31 +1,25 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import static eu.siacs.conversations.ui.XmppActivity.configureActionBar; import static eu.siacs.conversations.ui.XmppActivity.configureActionBar;
public class AboutActivity extends AppCompatActivity { import android.os.Bundle;
import androidx.databinding.DataBindingUtil;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityAboutBinding;
public class AboutActivity extends BaseActivity {
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setTheme(ThemeHelper.find(this)); final ActivityAboutBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_about);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setContentView(R.layout.activity_about); setSupportActionBar(binding.toolbar);
setSupportActionBar(findViewById(R.id.toolbar));
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
setTitle(getString(R.string.title_activity_about_x, getString(R.string.app_name))); setTitle(getString(R.string.title_activity_about_x, getString(R.string.app_name)));
} }

View file

@ -14,6 +14,7 @@ import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,7 +35,7 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() { private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
@Override @Override
public boolean onMenuItemActionExpand(final MenuItem item) { public boolean onMenuItemActionExpand(@NonNull final MenuItem item) {
mSearchEditText.post(() -> { mSearchEditText.post(() -> {
mSearchEditText.requestFocus(); mSearchEditText.requestFocus();
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@ -45,7 +46,7 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
} }
@Override @Override
public boolean onMenuItemActionCollapse(final MenuItem item) { public boolean onMenuItemActionCollapse(@NonNull final MenuItem item) {
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
mSearchEditText.setText(""); mSearchEditText.setText("");
@ -92,6 +93,7 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this,R.layout.activity_choose_contact); this.binding = DataBindingUtil.setContentView(this,R.layout.activity_choose_contact);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
this.binding.chooseContactList.setFastScrollEnabled(true); this.binding.chooseContactList.setFastScrollEnabled(true);

View file

@ -8,7 +8,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
public abstract class ActionBarActivity extends AppCompatActivity { public abstract class ActionBarActivity extends BaseActivity {
public static void configureActionBar(ActionBar actionBar) { public static void configureActionBar(ActionBar actionBar) {
configureActionBar(actionBar, true); configureActionBar(actionBar, true);
} }

View file

@ -0,0 +1,47 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.view.View;
import com.google.android.material.elevation.SurfaceColors;
public final class Activities {
private Activities() {}
public static void setStatusAndNavigationBarColors(final Activity activity, final View view) {
setStatusAndNavigationBarColors(activity, view, false);
}
public static void setStatusAndNavigationBarColors(
final Activity activity, final View view, final boolean raisedStatusBar) {
final var isLightMode = isLightMode(activity);
final var window = activity.getWindow();
final var flags = view.getSystemUiVisibility();
// an elevation of 4 matches the MaterialToolbar elevation
if (raisedStatusBar) {
window.setStatusBarColor(SurfaceColors.SURFACE_5.getColor(activity));
} else {
window.setStatusBarColor(SurfaceColors.SURFACE_0.getColor(activity));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
window.setNavigationBarColor(SurfaceColors.SURFACE_1.getColor(activity));
if (isLightMode) {
view.setSystemUiVisibility(
flags
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
| View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
} else if (isLightMode) {
view.setSystemUiVisibility(flags | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
private static boolean isLightMode(final Context context) {
final int nightModeFlags =
context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
return nightModeFlags != Configuration.UI_MODE_NIGHT_YES;
}
}

View file

@ -0,0 +1,53 @@
package eu.siacs.conversations.ui;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import eu.siacs.conversations.Conversations;
import eu.siacs.conversations.ui.util.SettingsUtils;
public abstract class BaseActivity extends AppCompatActivity {
private Boolean isDynamicColors;
@Override
public void onStart() {
super.onStart();
final int desiredNightMode = Conversations.getDesiredNightMode(this);
if (setDesiredNightMode(desiredNightMode)) {
return;
}
final boolean isDynamicColors = Conversations.isDynamicColorsDesired(this);
setDynamicColors(isDynamicColors);
}
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
public void setDynamicColors(final boolean isDynamicColors) {
if (this.isDynamicColors == null) {
this.isDynamicColors = isDynamicColors;
} else {
if (this.isDynamicColors != isDynamicColors) {
Log.i(
"Recreating {} because dynamic color setting has changed",
getClass().getSimpleName());
recreate();
}
}
}
public boolean setDesiredNightMode(final int desiredNightMode) {
if (desiredNightMode == AppCompatDelegate.getDefaultNightMode()) {
return false;
}
AppCompatDelegate.setDefaultNightMode(desiredNightMode);
Log.i("Recreating {} because desired night mode has changed", getClass().getSimpleName());
recreate();
return true;
}
}

View file

@ -7,6 +7,8 @@ import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.DialogBlockContactBinding; import eu.siacs.conversations.databinding.DialogBlockContactBinding;
import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Blockable;
@ -19,7 +21,7 @@ public final class BlockContactDialog {
show(xmppActivity, blockable, null); show(xmppActivity, blockable, null);
} }
public static void show(final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) { public static void show(final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) {
final AlertDialog.Builder builder = new AlertDialog.Builder(xmppActivity); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(xmppActivity);
final boolean isBlocked = blockable.isBlocked(); final boolean isBlocked = blockable.isBlocked();
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
DialogBlockContactBinding binding = DataBindingUtil.inflate(xmppActivity.getLayoutInflater(), R.layout.dialog_block_contact, null, false); DialogBlockContactBinding binding = DataBindingUtil.inflate(xmppActivity.getLayoutInflater(), R.layout.dialog_block_contact, null, false);

View file

@ -3,86 +3,84 @@ package eu.siacs.conversations.ui;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast; import android.widget.Toast;
import androidx.databinding.DataBindingUtil;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityChangePasswordBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.widget.DisabledActionModeCallback; import eu.siacs.conversations.ui.widget.DisabledActionModeCallback;
public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged { public class ChangePasswordActivity extends XmppActivity implements XmppConnectionService.OnAccountPasswordChanged {
private Button mChangePasswordButton; private ActivityChangePasswordBinding binding;
private final View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() { private final View.OnClickListener mOnChangePasswordButtonClicked = new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(final View view) {
if (mAccount != null) { final var account = mAccount;
final String currentPassword = mCurrentPassword.getText().toString(); if (account == null) {
final String newPassword = mNewPassword.getText().toString(); return;
if (!mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(mAccount.getPassword())) {
mCurrentPassword.requestFocus();
mCurrentPasswordLayout.setError(getString(R.string.account_status_unauthorized));
removeErrorsOnAllBut(mCurrentPasswordLayout);
} else if (newPassword.trim().isEmpty()) {
mNewPassword.requestFocus();
mNewPasswordLayout.setError(getString(R.string.password_should_not_be_empty));
removeErrorsOnAllBut(mNewPasswordLayout);
} else {
mCurrentPasswordLayout.setError(null);
mNewPasswordLayout.setError(null);
xmppConnectionService.updateAccountPasswordOnServer(mAccount, newPassword, ChangePasswordActivity.this);
mChangePasswordButton.setEnabled(false);
mChangePasswordButton.setText(R.string.updating);
}
} }
final String currentPassword = binding.currentPassword.getText().toString();
final String newPassword = binding.newPassword.getText().toString();
if (!account.isOptionSet(Account.OPTION_MAGIC_CREATE) && !currentPassword.equals(account.getPassword())) {
binding.currentPassword.requestFocus();
binding.currentPasswordLayout.setError(getString(R.string.account_status_unauthorized));
removeErrorsOnAllBut(binding.currentPasswordLayout);
} else if (newPassword.trim().isEmpty()) {
binding.newPassword.requestFocus();
binding.newPasswordLayout.setError(getString(R.string.password_should_not_be_empty));
removeErrorsOnAllBut(binding.newPasswordLayout);
} else {
binding.currentPasswordLayout.setError(null);
binding.newPasswordLayout.setError(null);
xmppConnectionService.updateAccountPasswordOnServer(account, newPassword, ChangePasswordActivity.this);
binding.changePasswordButton.setEnabled(false);
binding.changePasswordButton.setText(R.string.updating);
}
} }
}; };
private EditText mCurrentPassword;
private EditText mNewPassword;
private TextInputLayout mNewPasswordLayout;
private TextInputLayout mCurrentPasswordLayout;
private Account mAccount; private Account mAccount;
@Override @Override
void onBackendConnected() { void onBackendConnected() {
this.mAccount = extractAccount(getIntent()); this.mAccount = extractAccount(getIntent());
if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) { if (this.mAccount != null && this.mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
this.mCurrentPasswordLayout.setVisibility(View.GONE); this.binding.currentPasswordLayout.setVisibility(View.GONE);
} else { } else {
this.mCurrentPassword.setVisibility(View.VISIBLE); this.binding.currentPasswordLayout.setVisibility(View.VISIBLE);
} }
} }
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_password); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_change_password);
setSupportActionBar(findViewById(R.id.toolbar)); Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
Button mCancelButton = findViewById(R.id.left_button); binding.cancelButton.setOnClickListener(view -> finish());
mCancelButton.setOnClickListener(view -> finish()); binding.changePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked);
this.mChangePasswordButton = findViewById(R.id.right_button); binding.currentPassword.setCustomSelectionActionModeCallback(new DisabledActionModeCallback());
this.mChangePasswordButton.setOnClickListener(this.mOnChangePasswordButtonClicked); binding.newPassword.setCustomSelectionActionModeCallback(new DisabledActionModeCallback());
this.mCurrentPassword = findViewById(R.id.current_password);
this.mCurrentPassword.setCustomSelectionActionModeCallback(new DisabledActionModeCallback());
this.mNewPassword = findViewById(R.id.new_password);
this.mNewPassword.setCustomSelectionActionModeCallback(new DisabledActionModeCallback());
this.mCurrentPasswordLayout = findViewById(R.id.current_password_layout);
this.mNewPasswordLayout = findViewById(R.id.new_password_layout);
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
Intent intent = getIntent(); Intent intent = getIntent();
String password = intent != null ? intent.getStringExtra("password") : null; String password = intent != null ? intent.getStringExtra("password") : null;
if (password != null) { if (password != null) {
this.mNewPassword.getEditableText().clear(); binding.newPassword.getEditableText().clear();
this.mNewPassword.getEditableText().append(password); binding.newPassword.getEditableText().append(password);
} }
} }
@ -97,21 +95,21 @@ public class ChangePasswordActivity extends XmppActivity implements XmppConnecti
@Override @Override
public void onPasswordChangeFailed() { public void onPasswordChangeFailed() {
runOnUiThread(() -> { runOnUiThread(() -> {
mNewPasswordLayout.setError(getString(R.string.could_not_change_password)); binding.newPasswordLayout.setError(getString(R.string.could_not_change_password));
mChangePasswordButton.setEnabled(true); binding.changePasswordButton.setEnabled(true);
mChangePasswordButton.setText(R.string.change_password); binding.changePasswordButton.setText(R.string.change_password);
}); });
} }
private void removeErrorsOnAllBut(TextInputLayout exception) { private void removeErrorsOnAllBut(TextInputLayout exception) {
if (this.mCurrentPasswordLayout != exception) { if (this.binding.currentPasswordLayout != exception) {
this.mCurrentPasswordLayout.setErrorEnabled(false); this.binding.currentPasswordLayout.setErrorEnabled(false);
this.mCurrentPasswordLayout.setError(null); this.binding.currentPasswordLayout.setError(null);
} }
if (this.mNewPasswordLayout != exception) { if (this.binding.newPasswordLayout != exception) {
this.mNewPasswordLayout.setErrorEnabled(false); this.binding.newPasswordLayout.setErrorEnabled(false);
this.mNewPasswordLayout.setError(null); this.binding.newPasswordLayout.setError(null);
} }
} }

View file

@ -19,8 +19,11 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.util.Collections; import java.util.Collections;
@ -39,7 +42,6 @@ import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter; import eu.siacs.conversations.ui.adapter.ChannelSearchResultAdapter;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils; import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
@ -81,6 +83,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery); binding = DataBindingUtil.setContentView(this, R.layout.activity_channel_discovery);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
configureActionBar(getSupportActionBar(), true); configureActionBar(getSupportActionBar(), true);
binding.list.setAdapter(this.adapter); binding.list.setAdapter(this.adapter);
this.adapter.setOnChannelSearchResultSelectedListener(this); this.adapter.setOnChannelSearchResultSelectedListener(this);
@ -155,7 +158,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
private void toggleLoadingScreen() { private void toggleLoadingScreen() {
adapter.submitList(Collections.emptyList()); adapter.submitList(Collections.emptyList());
binding.progressBar.setVisibility(View.VISIBLE); binding.progressBar.setVisibility(View.VISIBLE);
binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary)); binding.list.setBackgroundColor(MaterialColors.getColor(binding.list, com.google.android.material.R.attr.colorSurface));
} }
@Override @Override
@ -163,13 +166,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
super.onStart(); super.onStart();
this.method = getMethod(this); this.method = getMethod(this);
if (!optedIn && method == ChannelDiscoveryService.Method.JABBER_NETWORK) { if (!optedIn && method == ChannelDiscoveryService.Method.JABBER_NETWORK) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.channel_discovery_opt_in_title); builder.setTitle(R.string.channel_discovery_opt_in_title);
builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message))); builder.setMessage(Html.fromHtml(getString(R.string.channel_discover_opt_in_message)));
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish()); builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn()); builder.setPositiveButton(R.string.confirm, (dialog, which) -> optIn());
builder.setOnCancelListener(dialog -> finish()); builder.setOnCancelListener(dialog -> finish());
final AlertDialog dialog = builder.create(); final androidx.appcompat.app.AlertDialog dialog = builder.create();
dialog.setOnShowListener(d -> { dialog.setOnShowListener(d -> {
final TextView textView = dialog.findViewById(android.R.id.message); final TextView textView = dialog.findViewById(android.R.id.message);
if (textView == null) { if (textView == null) {
@ -186,7 +189,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
private void holdLoading() { private void holdLoading() {
adapter.submitList(Collections.emptyList()); adapter.submitList(Collections.emptyList());
binding.progressBar.setVisibility(View.GONE); binding.progressBar.setVisibility(View.GONE);
binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary)); binding.list.setBackgroundColor(MaterialColors.getColor(binding.list, com.google.android.material.R.attr.colorSurface));
} }
@Override @Override
@ -220,10 +223,10 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
runOnUiThread(() -> { runOnUiThread(() -> {
adapter.submitList(results); adapter.submitList(results);
binding.progressBar.setVisibility(View.GONE); binding.progressBar.setVisibility(View.GONE);
if (results.size() == 0) { if (results.isEmpty()) {
binding.list.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_primary_background_no_results)); binding.list.setBackground(ContextCompat.getDrawable(this,R.drawable.background_no_results));
} else { } else {
binding.list.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_primary)); binding.list.setBackgroundColor(MaterialColors.getColor(binding.list, com.google.android.material.R.attr.colorSurface));
} }
}); });
@ -234,11 +237,11 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
final List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService); final List<String> accounts = AccountUtils.getEnabledAccounts(xmppConnectionService);
if (accounts.size() == 1) { if (accounts.size() == 1) {
joinChannelSearchResult(accounts.get(0), result); joinChannelSearchResult(accounts.get(0), result);
} else if (accounts.size() == 0) { } else if (accounts.isEmpty()) {
Toast.makeText(this, R.string.please_enable_an_account, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.please_enable_an_account, Toast.LENGTH_LONG).show();
} else { } else {
final AtomicReference<String> account = new AtomicReference<>(accounts.get(0)); final AtomicReference<String> account = new AtomicReference<>(accounts.get(0));
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.choose_account); builder.setTitle(R.string.choose_account);
builder.setSingleChoiceItems(accounts.toArray(new CharSequence[0]), 0, (dialog, which) -> account.set(accounts.get(which))); 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.setPositiveButton(R.string.join, (dialog, which) -> joinChannelSearchResult(account.get(), result));
@ -271,10 +274,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
} }
public void joinChannelSearchResult(final String selectedAccount, final Room result) { public void joinChannelSearchResult(final String selectedAccount, final Room result) {
final Jid jid = final Jid jid = Jid.ofEscaped(selectedAccount);
Config.DOMAIN_LOCK == null
? Jid.ofEscaped(selectedAccount)
: Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK);
final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin); final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin);
final Account account = xmppConnectionService.findAccountByJid(jid); final Account account = xmppConnectionService.findAccountByJid(jid);
final Conversation conversation = final Conversation conversation =

View file

@ -3,20 +3,21 @@ package eu.siacs.conversations.ui;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.widget.ListView;
import android.widget.Toast; import android.widget.Toast;
import androidx.databinding.DataBindingUtil;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityManageAccountsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.ui.adapter.AccountAdapter;
public class ChooseAccountForProfilePictureActivity extends XmppActivity { public class ChooseAccountForProfilePictureActivity extends XmppActivity {
protected final List<Account> accountList = new ArrayList<>(); protected final List<Account> accountList = new ArrayList<>();
protected ListView accountListView;
protected AccountAdapter mAccountAdapter; protected AccountAdapter mAccountAdapter;
@Override @Override
@ -28,25 +29,21 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_accounts); final ActivityManageAccountsBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts);
setSupportActionBar(findViewById(R.id.toolbar)); Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar(), false); configureActionBar(getSupportActionBar(), false);
accountListView = findViewById(R.id.account_list);
this.mAccountAdapter = new AccountAdapter(this, accountList, false); this.mAccountAdapter = new AccountAdapter(this, accountList, false);
accountListView.setAdapter(this.mAccountAdapter); binding.accountList.setAdapter(this.mAccountAdapter);
accountListView.setOnItemClickListener((arg0, view, position, arg3) -> { binding.accountList.setOnItemClickListener((arg0, view, position, arg3) -> {
final Account account = accountList.get(position); final Account account = accountList.get(position);
goToProfilePictureActivity(account); goToProfilePictureActivity(account);
}); });
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
} }
@Override @Override

View file

@ -9,6 +9,7 @@ import android.view.ActionMode;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.SoundEffectConstants;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AbsListView.MultiChoiceModeListener;
@ -51,7 +52,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
public static final String EXTRA_SHOW_ENTER_JID = "extra_show_enter_jid"; public static final String EXTRA_SHOW_ENTER_JID = "extra_show_enter_jid";
public static final String EXTRA_CONVERSATION = "extra_conversation"; public static final String EXTRA_CONVERSATION = "extra_conversation";
private static final String EXTRA_FILTERED_CONTACTS = "extra_filtered_contacts"; private static final String EXTRA_FILTERED_CONTACTS = "extra_filtered_contacts";
private final List<String> mActivatedAccounts = new ArrayList<>(); private final ArrayList<String> mActivatedAccounts = new ArrayList<>();
private final Set<String> selected = new HashSet<>(); private final Set<String> selected = new HashSet<>();
private Set<String> filterContacts; private Set<String> filterContacts;
@ -130,7 +131,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
if (this.showEnterJid) { if (this.showEnterJid) {
this.binding.fab.show(); this.binding.fab.show();
} else { } else {
binding.fab.setImageResource(R.drawable.ic_forward_white_24dp); binding.fab.setImageResource(R.drawable.ic_navigate_next_24dp);
} }
final SharedPreferences preferences = getPreferences(); final SharedPreferences preferences = getPreferences();
@ -139,7 +140,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
} }
private void onFabClicked(View v) { private void onFabClicked(View v) {
if (selected.size() == 0) { if (selected.isEmpty()) {
showEnterJidDialog(null); showEnterJidDialog(null);
} else { } else {
submitSelection(); submitSelection();
@ -154,7 +155,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
@Override @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTitle(getTitleFromIntent()); mode.setTitle(getTitleFromIntent());
binding.fab.setImageResource(R.drawable.ic_forward_white_24dp); binding.chooseContactList.setFastScrollEnabled(false);
binding.fab.setImageResource(R.drawable.ic_navigate_next_24dp);
binding.fab.show(); binding.fab.show();
final View view = getSearchEditText(); final View view = getSearchEditText();
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
@ -166,12 +168,13 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
@Override @Override
public void onDestroyActionMode(ActionMode mode) { public void onDestroyActionMode(ActionMode mode) {
this.binding.fab.setImageResource(R.drawable.ic_person_add_white_24dp); this.binding.fab.setImageResource(R.drawable.ic_person_add_24dp);
if (this.showEnterJid) { if (this.showEnterJid) {
this.binding.fab.show(); this.binding.fab.show();
} else { } else {
this.binding.fab.hide(); this.binding.fab.hide();
} }
binding.chooseContactList.setFastScrollEnabled(true);
selected.clear(); selected.clear();
} }
@ -199,8 +202,9 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
@Override @Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
if (selected.size() != 0) { if (selected.size() != 0) {
getListView().playSoundEffect(0); getListView().playSoundEffect(SoundEffectConstants.CLICK);
} }
getListItemAdapter().notifyDataSetChanged();
Contact item = (Contact) getListItems().get(position); Contact item = (Contact) getListItems().get(position);
if (checked) { if (checked) {
selected.add(item.getJid().toString()); selected.add(item.getJid().toString());
@ -361,13 +365,9 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
void onBackendConnected() { void onBackendConnected() {
filterContacts(); filterContacts();
this.mActivatedAccounts.clear(); this.mActivatedAccounts.clear();
for (Account account : xmppConnectionService.getAccounts()) { for (final Account account : xmppConnectionService.getAccounts()) {
if (account.isEnabled()) { if (account.isEnabled()) {
if (Config.DOMAIN_LOCK != null) { this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
this.mActivatedAccounts.add(account.getJid().getEscapedLocal());
} else {
this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
}
} }
} }
ActivityResult activityResult = this.postponedActivityResult.pop(); ActivityResult activityResult = this.postponedActivityResult.pop();

View file

@ -55,6 +55,8 @@ import me.drakeet.support.toast.ToastCompat;
import static eu.siacs.conversations.entities.Bookmark.printableValue; import static eu.siacs.conversations.entities.Bookmark.printableValue;
import static eu.siacs.conversations.utils.StringUtils.changed; import static eu.siacs.conversations.utils.StringUtils.changed;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnConfigurationPushed, XmppConnectionService.OnRoomDestroy, TextWatcher, OnMediaLoaded { public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnConfigurationPushed, XmppConnectionService.OnRoomDestroy, TextWatcher, OnMediaLoaded {
public static final String ACTION_VIEW_MUC = "view_muc"; public static final String ACTION_VIEW_MUC = "view_muc";
@ -97,7 +99,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private final OnClickListener mNotifyStatusClickListener = new OnClickListener() { private final OnClickListener mNotifyStatusClickListener = new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ConferenceDetailsActivity.this);
builder.setTitle(R.string.pref_notification_settings); builder.setTitle(R.string.pref_notification_settings);
String[] choices = { String[] choices = {
getString(R.string.notify_on_all_messages), getString(R.string.notify_on_all_messages),
@ -130,7 +132,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final MucOptions mucOptions = mConversation.getMucOptions(); final MucOptions mucOptions = mConversation.getMucOptions();
AlertDialog.Builder builder = new AlertDialog.Builder(ConferenceDetailsActivity.this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(ConferenceDetailsActivity.this);
MucConfiguration configuration = MucConfiguration.get(ConferenceDetailsActivity.this, mAdvancedMode, mucOptions); MucConfiguration configuration = MucConfiguration.get(ConferenceDetailsActivity.this, mAdvancedMode, mucOptions);
builder.setTitle(configuration.title); builder.setTitle(configuration.title);
final boolean[] values = configuration.values; final boolean[] values = configuration.values;
@ -168,6 +170,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_details);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings); this.binding.changeConferenceButton.setOnClickListener(this.mChangeConferenceSettings);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
@ -216,12 +219,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
recreate();
}
binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE); binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE);
} }
@ -277,7 +276,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
final MucOptions mucOptions = mConversation.getMucOptions(); final MucOptions mucOptions = mConversation.getMucOptions();
this.binding.mucEditor.setVisibility(View.VISIBLE); this.binding.mucEditor.setVisibility(View.VISIBLE);
this.binding.mucDisplay.setVisibility(View.GONE); this.binding.mucDisplay.setVisibility(View.GONE);
this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_cancel, R.drawable.ic_cancel_black_24dp)); this.binding.editMucNameButton.setImageResource(R.drawable.ic_cancel_24dp);
final String name = mucOptions.getName(); final String name = mucOptions.getName();
this.binding.mucEditTitle.setText(""); this.binding.mucEditTitle.setText("");
final boolean owner = mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER); final boolean owner = mucOptions.getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER);
@ -311,7 +310,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private void hideEditor() { private void hideEditor() {
this.binding.mucEditor.setVisibility(View.GONE); this.binding.mucEditor.setVisibility(View.GONE);
this.binding.mucDisplay.setVisibility(View.VISIBLE); this.binding.mucDisplay.setVisibility(View.VISIBLE);
this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_edit_body, R.drawable.ic_edit_black_24dp)); this.binding.editMucNameButton.setImageResource(R.drawable.ic_edit_24dp);
} }
private void onMucInfoUpdated(String subject, String name) { private void onMucInfoUpdated(String subject, String name) {
@ -384,7 +383,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
protected void destroyRoom() { protected void destroyRoom() {
final boolean groupChat = mConversation != null && mConversation.isPrivateAndNonAnonymous(); final boolean groupChat = mConversation != null && mConversation.isPrivateAndNonAnonymous();
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(groupChat ? R.string.destroy_room : R.string.destroy_channel); builder.setTitle(groupChat ? R.string.destroy_room : R.string.destroy_channel);
builder.setMessage(groupChat ? R.string.destroy_room_dialog : R.string.destroy_channel_dialog); builder.setMessage(groupChat ? R.string.destroy_room_dialog : R.string.destroy_channel_dialog);
builder.setPositiveButton(R.string.ok, (dialog, which) -> { builder.setPositiveButton(R.string.ok, (dialog, which) -> {
@ -434,12 +433,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
} }
final MucOptions mucOptions = mConversation.getMucOptions(); final MucOptions mucOptions = mConversation.getMucOptions();
final User self = mucOptions.getSelf(); final User self = mucOptions.getSelf();
String account; final String account = mConversation.getAccount().getJid().asBareJid().toEscapedString();
if (Config.DOMAIN_LOCK != null) {
account = mConversation.getAccount().getJid().getEscapedLocal();
} else {
account = mConversation.getAccount().getJid().asBareJid().toEscapedString();
}
setTitle(mucOptions.isPrivateAndNonAnonymous() ? R.string.action_muc_details : R.string.channel_details); setTitle(mucOptions.isPrivateAndNonAnonymous() ? R.string.action_muc_details : R.string.channel_details);
this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE); this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE);
this.binding.detailsAccount.setText(getString(R.string.using_account, account)); this.binding.detailsAccount.setText(getString(R.string.using_account, account));
@ -469,7 +463,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
StylingHelper.format(spannable, this.binding.mucSubject.getCurrentTextColor()); StylingHelper.format(spannable, this.binding.mucSubject.getCurrentTextColor());
MyLinkify.addLinks(spannable, false); MyLinkify.addLinks(spannable, false);
this.binding.mucSubject.setText(spannable); this.binding.mucSubject.setText(spannable);
this.binding.mucSubject.setTextAppearance(this, subject.length() > (hasTitle ? 128 : 196) ? R.style.TextAppearance_Conversations_Body1_Linkified : R.style.TextAppearance_Conversations_Subhead); this.binding.mucSubject.setTextAppearance( subject.length() > (hasTitle ? 128 : 196) ? com.google.android.material.R.style.TextAppearance_Material3_BodyMedium : com.google.android.material.R.style.TextAppearance_Material3_BodyLarge);
this.binding.mucSubject.setAutoLinkMask(0); this.binding.mucSubject.setAutoLinkMask(0);
this.binding.mucSubject.setVisibility(View.VISIBLE); this.binding.mucSubject.setVisibility(View.VISIBLE);
this.binding.mucSubject.setMovementMethod(LinkMovementMethod.getInstance()); this.binding.mucSubject.setMovementMethod(LinkMovementMethod.getInstance());
@ -507,24 +501,19 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.mucSettings.setVisibility(View.GONE); this.binding.mucSettings.setVisibility(View.GONE);
} }
int ic_notifications = getThemeResource(R.attr.icon_notifications, R.drawable.ic_notifications_black_24dp); final long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
int ic_notifications_off = getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
int ic_notifications_paused = getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
int ic_notifications_none = getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
long mutedTill = mConversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
if (mutedTill == Long.MAX_VALUE) { if (mutedTill == Long.MAX_VALUE) {
this.binding.notificationStatusText.setText(R.string.notify_never); this.binding.notificationStatusText.setText(R.string.notify_never);
this.binding.notificationStatusButton.setImageResource(ic_notifications_off); this.binding.notificationStatusButton.setImageResource(R.drawable.ic_notifications_off_24dp);
} else if (System.currentTimeMillis() < mutedTill) { } else if (System.currentTimeMillis() < mutedTill) {
this.binding.notificationStatusText.setText(R.string.notify_paused); this.binding.notificationStatusText.setText(R.string.notify_paused);
this.binding.notificationStatusButton.setImageResource(ic_notifications_paused); this.binding.notificationStatusButton.setImageResource(R.drawable.ic_notifications_paused_24dp);
} else if (mConversation.alwaysNotify()) { } else if (mConversation.alwaysNotify()) {
this.binding.notificationStatusText.setText(R.string.notify_on_all_messages); this.binding.notificationStatusText.setText(R.string.notify_on_all_messages);
this.binding.notificationStatusButton.setImageResource(ic_notifications); this.binding.notificationStatusButton.setImageResource(R.drawable.ic_notifications_24dp);
} else { } else {
this.binding.notificationStatusText.setText(R.string.notify_only_when_highlighted); this.binding.notificationStatusText.setText(R.string.notify_only_when_highlighted);
this.binding.notificationStatusButton.setImageResource(ic_notifications_none); this.binding.notificationStatusButton.setImageResource(R.drawable.ic_notifications_none_24dp);
} }
final List<User> users = mucOptions.getUsers(); final List<User> users = mucOptions.getUsers();
Collections.sort(users, (a, b) -> { Collections.sort(users, (a, b) -> {
@ -629,9 +618,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
boolean subjectChanged = changed(binding.mucEditSubject.getEditableText().toString(), mucOptions.getSubject()); boolean subjectChanged = changed(binding.mucEditSubject.getEditableText().toString(), mucOptions.getSubject());
boolean nameChanged = changed(binding.mucEditTitle.getEditableText().toString(), mucOptions.getName()); boolean nameChanged = changed(binding.mucEditTitle.getEditableText().toString(), mucOptions.getName());
if (subjectChanged || nameChanged) { if (subjectChanged || nameChanged) {
this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_save, R.drawable.ic_save_black_24dp)); this.binding.editMucNameButton.setImageResource(R.drawable.ic_save_24dp);
} else { } else {
this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_cancel, R.drawable.ic_cancel_black_24dp)); this.binding.editMucNameButton.setImageResource(R.drawable.ic_cancel_24dp);
} }
} }
} }

View file

@ -30,6 +30,9 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.openintents.openpgp.util.OpenPgpUtils; import org.openintents.openpgp.util.OpenPgpUtils;
import java.util.Collection; import java.util.Collection;
@ -144,7 +147,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
} else { } else {
value = jid.toEscapedString(); value = jid.toEscapedString();
} }
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(getString(R.string.action_add_phone_book)); builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text, value)); builder.setMessage(getString(R.string.add_phone_book_text, value));
builder.setNegativeButton(getString(R.string.cancel), null); builder.setNegativeButton(getString(R.string.cancel), null);
@ -215,6 +218,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
} }
this.messageFingerprint = getIntent().getStringExtra("fingerprint"); this.messageFingerprint = getIntent().getStringExtra("fingerprint");
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_contact_details); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_contact_details);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
@ -238,14 +242,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme(); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
if (this.mTheme != theme) { this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
recreate(); this.showLastSeen = preferences.getBoolean("last_activity", false);
} else {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
this.showLastSeen = preferences.getBoolean("last_activity", false);
}
binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE); binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE);
mMediaAdapter.setAttachments(Collections.emptyList()); mMediaAdapter.setAttachments(Collections.emptyList());
} }
@ -268,8 +267,6 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
if (MenuDoubleTabUtil.shouldIgnoreTap()) { if (MenuDoubleTabUtil.shouldIgnoreTap()) {
return false; return false;
} }
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(getString(R.string.cancel), null);
switch (menuItem.getItemId()) { switch (menuItem.getItemId()) {
case android.R.id.home: case android.R.id.home:
finish(); finish();
@ -281,6 +278,8 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
shareLink(false); shareLink(false);
break; break;
case R.id.action_delete_contact: case R.id.action_delete_contact:
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setTitle(getString(R.string.action_delete_contact)) builder.setTitle(getString(R.string.action_delete_contact))
.setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString())) .setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString()))
.setPositiveButton(getString(R.string.delete), .setPositiveButton(getString(R.string.delete),
@ -431,12 +430,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
} }
binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this, contact.getJid())); binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this, contact.getJid()));
String account; final String account = contact.getAccount().getJid().asBareJid().toEscapedString();
if (Config.DOMAIN_LOCK != null) {
account = contact.getAccount().getJid().getEscapedLocal();
} else {
account = contact.getAccount().getJid().asBareJid().toEscapedString();
}
binding.detailsAccount.setText(getString(R.string.using_account, account)); binding.detailsAccount.setText(getString(R.string.using_account, account));
AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size); AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size);
binding.detailsContactBadge.setOnClickListener(this::onBadgeClick); binding.detailsContactBadge.setOnClickListener(this::onBadgeClick);
@ -498,7 +492,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
TextView keyType = view.findViewById(R.id.key_type); TextView keyType = view.findViewById(R.id.key_type);
keyType.setText(R.string.openpgp_key_id); keyType.setText(R.string.openpgp_key_id);
if ("pgp".equals(messageFingerprint)) { if ("pgp".equals(messageFingerprint)) {
keyType.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); keyType.setTextColor(MaterialColors.getColor(keyType, com.google.android.material.R.attr.colorPrimaryVariant));
} }
key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId())); key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
final OnClickListener openKey = v -> launchOpenKeyChain(contact.getPgpKeyId()); final OnClickListener openKey = v -> launchOpenKeyChain(contact.getPgpKeyId());
@ -510,7 +504,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
binding.keysWrapper.setVisibility(hasKeys ? View.VISIBLE : View.GONE); binding.keysWrapper.setVisibility(hasKeys ? View.VISIBLE : View.GONE);
List<ListItem.Tag> tagList = contact.getTags(this); List<ListItem.Tag> tagList = contact.getTags(this);
if (tagList.size() == 0 || !this.showDynamicTags) { if (tagList.isEmpty() || !this.showDynamicTags) {
binding.tags.setVisibility(View.GONE); binding.tags.setVisibility(View.GONE);
} else { } else {
binding.tags.setVisibility(View.VISIBLE); binding.tags.setVisibility(View.VISIBLE);

View file

@ -16,10 +16,4 @@ public class ConversationActivity extends AppCompatActivity {
startActivity(new Intent(this, ConversationsActivity.class)); startActivity(new Intent(this, ConversationsActivity.class));
finish(); finish();
} }
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
} }

View file

@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -63,6 +64,7 @@ import androidx.core.view.inputmethod.InputConnectionCompat;
import androidx.core.view.inputmethod.InputContentInfoCompat; import androidx.core.view.inputmethod.InputContentInfoCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -1062,7 +1064,7 @@ public class ConversationFragment extends XmppFragment
}; };
if (conversation == null if (conversation == null
|| conversation.getMode() == Conversation.MODE_MULTI || conversation.getMode() == Conversation.MODE_MULTI
|| Attachment.canBeSendInband(attachments) || Attachment.canBeSendInBand(attachments)
|| (conversation.getAccount().httpUploadAvailable() || (conversation.getAccount().httpUploadAvailable()
&& FileBackend.allFilesUnderSize( && FileBackend.allFilesUnderSize(
getActivity(), attachments, getMaxHttpUploadSize(conversation)))) { getActivity(), attachments, getMaxHttpUploadSize(conversation)))) {
@ -1934,8 +1936,8 @@ public class ConversationFragment extends XmppFragment
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
protected void clearHistoryDialog(final Conversation conversation) { protected void clearHistoryDialog(final Conversation conversation) {
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(getString(R.string.clear_conversation_history)); builder.setTitle(R.string.clear_conversation_history);
final View dialogView = final View dialogView =
requireActivity().getLayoutInflater().inflate(R.layout.dialog_clear_history, null); requireActivity().getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
final CheckBox endConversationCheckBox = final CheckBox endConversationCheckBox =
@ -1958,7 +1960,7 @@ public class ConversationFragment extends XmppFragment
} }
protected void muteConversationDialog(final Conversation conversation) { protected void muteConversationDialog(final Conversation conversation) {
final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(R.string.disable_notifications); builder.setTitle(R.string.disable_notifications);
final int[] durations = getResources().getIntArray(R.array.mute_options_durations); final int[] durations = getResources().getIntArray(R.array.mute_options_durations);
final CharSequence[] labels = new CharSequence[durations.length]; final CharSequence[] labels = new CharSequence[durations.length];
@ -2132,7 +2134,7 @@ public class ConversationFragment extends XmppFragment
} }
private void showErrorMessage(final Message message) { private void showErrorMessage(final Message message) {
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(R.string.error_message); builder.setTitle(R.string.error_message);
final String errorMessage = message.getErrorMessage(); final String errorMessage = message.getErrorMessage();
final String[] errorMessageParts = final String[] errorMessageParts =
@ -2159,7 +2161,7 @@ public class ConversationFragment extends XmppFragment
} }
private void deleteFile(final Message message) { private void deleteFile(final Message message) {
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_file_dialog); builder.setTitle(R.string.delete_file_dialog);
builder.setMessage(R.string.delete_file_dialog_msg); builder.setMessage(R.string.delete_file_dialog_msg);
@ -2921,10 +2923,11 @@ public class ConversationFragment extends XmppFragment
status = Presence.Status.OFFLINE; status = Presence.Status.OFFLINE;
} }
this.binding.textSendButton.setTag(action); this.binding.textSendButton.setTag(action);
this.binding.textSendButton.setIconResource(SendButtonTool.getSendButtonImageResource(action));
this.binding.textSendButton.setIconTint(ColorStateList.valueOf(SendButtonTool.getSendButtonColor(this.binding.textSendButton, status)));
// TODO send button color
final Activity activity = getActivity(); final Activity activity = getActivity();
if (activity != null) { if (activity != null) {
this.binding.textSendButton.setImageResource(
SendButtonTool.getSendButtonImageResource(activity, action, status));
} }
} }
@ -3247,9 +3250,8 @@ public class ConversationFragment extends XmppFragment
}); });
} }
public void showNoPGPKeyDialog(boolean plural, DialogInterface.OnClickListener listener) { public void showNoPGPKeyDialog(final boolean plural, final DialogInterface.OnClickListener listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setIconAttribute(android.R.attr.alertDialogIcon);
if (plural) { if (plural) {
builder.setTitle(getString(R.string.no_pgp_keys)); builder.setTitle(getString(R.string.no_pgp_keys));
builder.setMessage(getText(R.string.contacts_have_no_pgp_keys)); builder.setMessage(getText(R.string.contacts_have_no_pgp_keys));

View file

@ -59,18 +59,18 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting; import eu.siacs.conversations.crypto.OmemoSetting;
import eu.siacs.conversations.databinding.ActivityConversationsBinding; import eu.siacs.conversations.databinding.ActivityConversationsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Conversational;
@ -80,11 +80,11 @@ import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
import eu.siacs.conversations.ui.interfaces.OnConversationRead; import eu.siacs.conversations.ui.interfaces.OnConversationRead;
import eu.siacs.conversations.ui.interfaces.OnConversationSelected; import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
import eu.siacs.conversations.ui.interfaces.OnConversationsListItemUpdated; import eu.siacs.conversations.ui.interfaces.OnConversationsListItemUpdated;
import eu.siacs.conversations.ui.util.ActionBarUtil;
import eu.siacs.conversations.ui.util.ActivityResult; import eu.siacs.conversations.ui.util.ActivityResult;
import eu.siacs.conversations.ui.util.ConversationMenuConfigurator; import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil; import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ToolbarUtils;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.SignupUtils; import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.utils.XmppUri;
@ -227,10 +227,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
} }
private boolean openBatteryOptimizationDialogIfNeeded() { private boolean openBatteryOptimizationDialogIfNeeded() {
if (isOptimizingBattery() if (isOptimizingBattery() && getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
&& android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
&& getPreferences().getBoolean(getBatteryOptimizationPreferenceKey(), true)) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.battery_optimizations_enabled); builder.setTitle(R.string.battery_optimizations_enabled);
builder.setMessage(getString(R.string.battery_optimizations_enabled_dialog, getString(R.string.app_name))); builder.setMessage(getString(R.string.battery_optimizations_enabled_dialog, getString(R.string.app_name)));
builder.setPositiveButton(R.string.next, (dialog, which) -> { builder.setPositiveButton(R.string.next, (dialog, which) -> {
@ -372,6 +370,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
ConversationMenuConfigurator.reloadFeatures(this); ConversationMenuConfigurator.reloadFeatures(this);
OmemoSetting.load(this); OmemoSetting.load(this);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_conversations);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
this.getFragmentManager().addOnBackStackChangedListener(this::invalidateActionBarTitle); this.getFragmentManager().addOnBackStackChangedListener(this::invalidateActionBarTitle);
@ -466,9 +465,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
conversationFragment.reInit(conversation, extras == null ? new Bundle() : extras); conversationFragment.reInit(conversation, extras == null ? new Bundle() : extras);
if (mainNeedsRefresh) { if (mainNeedsRefresh) {
refreshFragment(R.id.main_fragment); refreshFragment(R.id.main_fragment);
} else {
invalidateActionBarTitle();
} }
invalidateActionBarTitle();
} }
private static void executePendingTransactions(final FragmentManager fragmentManager) { private static void executePendingTransactions(final FragmentManager fragmentManager) {
@ -546,15 +544,8 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
this.mSkipBackgroundBinding = true;
recreate();
} else {
this.mSkipBackgroundBinding = false;
}
mRedirectInProcess.set(false); mRedirectInProcess.set(false);
} }
@ -630,21 +621,31 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
} }
final FragmentManager fragmentManager = getFragmentManager(); final FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment); final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
if (mainFragment instanceof ConversationFragment) { if (mainFragment instanceof ConversationFragment conversationFragment) {
final Conversation conversation = ((ConversationFragment) mainFragment).getConversation(); final Conversation conversation = conversationFragment.getConversation();
if (conversation != null) { if (conversation != null) {
actionBar.setTitle(conversation.getName()); actionBar.setTitle(conversation.getName());
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
ActionBarUtil.setActionBarOnClickListener( ToolbarUtils.setActionBarOnClickListener(
binding.toolbar, binding.toolbar,
(v) -> openConversationDetails(conversation) (v) -> openConversationDetails(conversation)
); );
return; return;
} }
} }
actionBar.setTitle(R.string.app_name); final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
if (secondaryFragment instanceof ConversationFragment conversationFragment) {
final Conversation conversation = conversationFragment.getConversation();
if (conversation != null) {
actionBar.setTitle(conversation.getName());
} else {
actionBar.setTitle(R.string.app_name);
}
} else {
actionBar.setTitle(R.string.app_name);
}
actionBar.setDisplayHomeAsUpEnabled(false); actionBar.setDisplayHomeAsUpEnabled(false);
ActionBarUtil.resetActionBarOnClickListeners(binding.toolbar); ToolbarUtils.resetActionBarOnClickListeners(binding.toolbar);
} }
private void openConversationDetails(final Conversation conversation) { private void openConversationDetails(final Conversation conversation) {

View file

@ -50,6 +50,8 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
@ -72,10 +74,8 @@ import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.ui.util.PendingActionHelper; import eu.siacs.conversations.ui.util.PendingActionHelper;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ScrollState; import eu.siacs.conversations.ui.util.ScrollState;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.EasyOnboardingInvite; import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.utils.ThemeHelper;
import static androidx.recyclerview.widget.ItemTouchHelper.LEFT; import static androidx.recyclerview.widget.ItemTouchHelper.LEFT;
import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT; import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
@ -111,7 +111,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if(actionState != ItemTouchHelper.ACTION_STATE_IDLE){ if(actionState != ItemTouchHelper.ACTION_STATE_IDLE){
Paint paint = new Paint(); Paint paint = new Paint();
paint.setColor(StyledAttributes.getColor(activity,R.attr.conversations_overview_background)); paint.setColor(MaterialColors.getColor(viewHolder.itemView, com.google.android.material.R.attr.colorSecondaryFixedDim));
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
c.drawRect(viewHolder.itemView.getLeft(),viewHolder.itemView.getTop() c.drawRect(viewHolder.itemView.getLeft(),viewHolder.itemView.getTop()
,viewHolder.itemView.getRight(),viewHolder.itemView.getBottom(), paint); ,viewHolder.itemView.getRight(),viewHolder.itemView.getBottom(), paint);
@ -196,8 +196,6 @@ public class ConversationsOverviewFragment extends XmppFragment {
activity.xmppConnectionService.archiveConversation(c); activity.xmppConnectionService.archiveConversation(c);
} }
}); });
ThemeHelper.fix(snackbar);
snackbar.show(); snackbar.show();
} }
}; };
@ -381,14 +379,14 @@ public class ConversationsOverviewFragment extends XmppFragment {
private void selectAccountToStartEasyInvite() { private void selectAccountToStartEasyInvite() {
final List<Account> accounts = EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService); final List<Account> accounts = EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService);
if (accounts.size() == 0) { if (accounts.isEmpty()) {
//This can technically happen if opening the menu item races with accounts reconnecting or something //This can technically happen if opening the menu item races with accounts reconnecting or something
Toast.makeText(getActivity(),R.string.no_active_accounts_support_this, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(),R.string.no_active_accounts_support_this, Toast.LENGTH_LONG).show();
} else if (accounts.size() == 1) { } else if (accounts.size() == 1) {
openEasyInviteScreen(accounts.get(0)); openEasyInviteScreen(accounts.get(0));
} else { } else {
final AtomicReference<Account> selectedAccount = new AtomicReference<>(accounts.get(0)); final AtomicReference<Account> selectedAccount = new AtomicReference<>(accounts.get(0));
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); final MaterialAlertDialogBuilder alertDialogBuilder = new MaterialAlertDialogBuilder(activity);
alertDialogBuilder.setTitle(R.string.choose_account); alertDialogBuilder.setTitle(R.string.choose_account);
final String[] asStrings = Collections2.transform(accounts, a -> a.getJid().asBareJid().toEscapedString()).toArray(new String[0]); final String[] asStrings = Collections2.transform(accounts, a -> a.getJid().asBareJid().toEscapedString()).toArray(new String[0]);
alertDialogBuilder.setSingleChoiceItems(asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which))); alertDialogBuilder.setSingleChoiceItems(asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which)));

View file

@ -3,18 +3,20 @@ package eu.siacs.conversations.ui;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Spinner; import android.widget.AutoCompleteTextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.CreateConferenceDialogBinding; import eu.siacs.conversations.databinding.DialogCreateConferenceBinding;
import eu.siacs.conversations.ui.util.DelayedHintHelper; import eu.siacs.conversations.ui.util.DelayedHintHelper;
public class CreatePrivateGroupChatDialog extends DialogFragment { public class CreatePrivateGroupChatDialog extends DialogFragment {
@ -39,9 +41,9 @@ public class CreatePrivateGroupChatDialog extends DialogFragment {
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(R.string.create_private_group_chat); builder.setTitle(R.string.create_private_group_chat);
CreateConferenceDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.create_conference_dialog, null, false); final DialogCreateConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_create_conference, null, false);
ArrayList<String> mActivatedAccounts = getArguments().getStringArrayList(ACCOUNTS_LIST_KEY); ArrayList<String> mActivatedAccounts = getArguments().getStringArrayList(ACCOUNTS_LIST_KEY);
StartConversationActivity.populateAccountSpinner(getActivity(), mActivatedAccounts, binding.account); StartConversationActivity.populateAccountSpinner(getActivity(), mActivatedAccounts, binding.account);
builder.setView(binding.getRoot()); builder.setView(binding.getRoot());
@ -57,7 +59,7 @@ public class CreatePrivateGroupChatDialog extends DialogFragment {
public interface CreateConferenceDialogListener { public interface CreateConferenceDialogListener {
void onCreateDialogPositiveClick(Spinner spinner, String subject); void onCreateDialogPositiveClick(AutoCompleteTextView spinner, String subject);
} }
@Override @Override

View file

@ -17,13 +17,14 @@ import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import java.security.SecureRandom; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.CreatePublicChannelDialogBinding; import eu.siacs.conversations.databinding.DialogCreatePublicChannelBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
@ -44,7 +45,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
private boolean nameEntered = false; private boolean nameEntered = false;
private boolean skipTetxWatcher = false; private boolean skipTetxWatcher = false;
public static CreatePublicChannelDialog newInstance(List<String> accounts) { public static CreatePublicChannelDialog newInstance(final List<String> accounts) {
CreatePublicChannelDialog dialog = new CreatePublicChannelDialog(); CreatePublicChannelDialog dialog = new CreatePublicChannelDialog();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts); bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts);
@ -63,9 +64,9 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
jidWasModified = savedInstanceState != null && savedInstanceState.getBoolean("jid_was_modified_false", false); jidWasModified = savedInstanceState != null && savedInstanceState.getBoolean("jid_was_modified_false", false);
nameEntered = savedInstanceState != null && savedInstanceState.getBoolean("name_entered", false); nameEntered = savedInstanceState != null && savedInstanceState.getBoolean("name_entered", false);
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(R.string.create_public_channel); builder.setTitle(R.string.create_public_channel);
final CreatePublicChannelDialogBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.create_public_channel_dialog, null, false); final DialogCreatePublicChannelBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_create_public_channel, null, false);
binding.account.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { binding.account.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
@ -107,7 +108,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
builder.setPositiveButton(nameEntered ? R.string.create : R.string.next, null); builder.setPositiveButton(nameEntered ? R.string.create : R.string.next, null);
builder.setNegativeButton(nameEntered ? R.string.back : R.string.cancel, null); builder.setNegativeButton(nameEntered ? R.string.back : R.string.cancel, null);
DelayedHintHelper.setHint(R.string.channel_bare_jid_example, binding.jid); DelayedHintHelper.setHint(R.string.channel_bare_jid_example, binding.jid);
this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item); this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
binding.jid.setAdapter(knownHostsAdapter); binding.jid.setAdapter(knownHostsAdapter);
final AlertDialog dialog = builder.create(); final AlertDialog dialog = builder.create();
binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> { binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> {
@ -121,7 +122,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
return dialog; return dialog;
} }
private void updateJidSuggestion(CreatePublicChannelDialogBinding binding) { private void updateJidSuggestion(final DialogCreatePublicChannelBinding binding) {
if (jidWasModified) { if (jidWasModified) {
return; return;
} }
@ -138,7 +139,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
private static String getJidSuggestion(CreatePublicChannelDialogBinding binding) { private static String getJidSuggestion(final DialogCreatePublicChannelBinding binding) {
final Account account = StartConversationActivity.getSelectedAccount(binding.getRoot().getContext(), binding.account); final Account account = StartConversationActivity.getSelectedAccount(binding.getRoot().getContext(), binding.account);
final XmppConnection connection = account == null ? null : account.getXmppConnection(); final XmppConnection connection = account == null ? null : account.getXmppConnection();
if (connection == null) { if (connection == null) {
@ -169,7 +170,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
return name.replaceAll("\\s+","-"); return name.replaceAll("\\s+","-");
} }
private void goBack(AlertDialog dialog, CreatePublicChannelDialogBinding binding) { private void goBack(AlertDialog dialog, DialogCreatePublicChannelBinding binding) {
if (nameEntered) { if (nameEntered) {
nameEntered = false; nameEntered = false;
updateInputs(binding, true); updateInputs(binding, true);
@ -179,7 +180,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
} }
} }
private void submit(AlertDialog dialog, CreatePublicChannelDialogBinding binding) { private void submit(AlertDialog dialog, DialogCreatePublicChannelBinding binding) {
final Context context = binding.getRoot().getContext(); final Context context = binding.getRoot().getContext();
final Editable nameText = binding.groupChatName.getText(); final Editable nameText = binding.groupChatName.getText();
final String name = nameText == null ? "" : nameText.toString().trim(); final String name = nameText == null ? "" : nameText.toString().trim();
@ -227,7 +228,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
} }
private void updateInputs(CreatePublicChannelDialogBinding binding, boolean requestFocus) { private void updateInputs(final DialogCreatePublicChannelBinding binding, final boolean requestFocus) {
binding.xmppAddressLayout.setVisibility(nameEntered ? View.VISIBLE : View.GONE); binding.xmppAddressLayout.setVisibility(nameEntered ? View.VISIBLE : View.GONE);
binding.nameLayout.setVisibility(nameEntered ? View.GONE : View.VISIBLE); binding.nameLayout.setVisibility(nameEntered ? View.GONE : View.VISIBLE);
if (!requestFocus) { if (!requestFocus) {
@ -265,7 +266,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
} }
@Override @Override
public void onAttach(Context context) { public void onAttach(@NonNull Context context) {
super.onAttach(context); super.onAttach(context);
try { try {
mListener = (CreatePublicChannelDialogListener) context; mListener = (CreatePublicChannelDialogListener) context;

View file

@ -33,9 +33,10 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import com.google.common.base.CharMatcher; import com.google.common.base.CharMatcher;
@ -98,7 +99,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private Jid jidToEdit; private Jid jidToEdit;
private boolean mInitMode = false; private boolean mInitMode = false;
private Boolean mForceRegister = null; private Boolean mForceRegister = null;
private boolean mUsernameMode = Config.DOMAIN_LOCK != null; private boolean mUsernameMode = false;
private boolean mShowOptions = false; private boolean mShowOptions = false;
private Account mAccount; private Account mAccount;
private final OnClickListener mCancelButtonClickListener = v -> { private final OnClickListener mCancelButtonClickListener = v -> {
@ -609,6 +610,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false); this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
} }
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_edit_account); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_edit_account);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
binding.accountJid.addTextChangedListener(this.mTextWatcher); binding.accountJid.addTextChangedListener(this.mTextWatcher);
binding.accountJid.setOnFocusChangeListener(this.mEditTextFocusListener); binding.accountJid.setOnFocusChangeListener(this.mEditTextFocusListener);
@ -697,13 +699,10 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
final Intent intent = getIntent(); final Intent intent = getIntent();
final int theme = findTheme(); if (intent != null) {
if (this.mTheme != theme) {
recreate();
} else if (intent != null) {
try { try {
this.jidToEdit = Jid.ofEscaped(intent.getStringExtra("jid")); this.jidToEdit = Jid.ofEscaped(intent.getStringExtra("jid"));
} catch (final IllegalArgumentException | NullPointerException ignored) { } catch (final IllegalArgumentException | NullPointerException ignored) {
@ -758,7 +757,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} }
private void displayVerificationWarningDialog(final XmppUri xmppUri) { private void displayVerificationWarningDialog(final XmppUri xmppUri) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.verify_omemo_keys); builder.setTitle(R.string.verify_omemo_keys);
View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null); View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null);
final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source); final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source);
@ -773,7 +772,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} }
}); });
builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish()); builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
AlertDialog dialog = builder.create(); final var dialog = builder.create();
dialog.setCanceledOnTouchOutside(false); dialog.setCanceledOnTouchOutside(false);
dialog.setOnCancelListener(d -> finish()); dialog.setOnCancelListener(d -> finish());
dialog.show(); dialog.show();
@ -835,7 +834,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
this.binding.accountJidLayout.setHint(getString(R.string.username_hint)); this.binding.accountJidLayout.setHint(getString(R.string.username_hint));
} else { } else {
final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this, final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
R.layout.simple_list_item, R.layout.item_autocomplete,
xmppConnectionService.getKnownHosts()); xmppConnectionService.getKnownHosts());
this.binding.accountJid.setAdapter(mKnownHostsAdapter); this.binding.accountJid.setAdapter(mKnownHostsAdapter);
} }
@ -853,7 +852,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (mAccount != null && mAccount.getJid().getDomain() != null) { if (mAccount != null && mAccount.getJid().getDomain() != null) {
return mAccount.getServer(); return mAccount.getServer();
} else { } else {
return Config.DOMAIN_LOCK; return null;
} }
} }
@ -940,7 +939,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private void changePresence() { private void changePresence() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean manualStatus = sharedPreferences.getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence)); boolean manualStatus = sharedPreferences.getBoolean(SettingsActivity.MANUALLY_CHANGE_PRESENCE, getResources().getBoolean(R.bool.manually_change_presence));
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
final DialogPresenceBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_presence, null, false); final DialogPresenceBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_presence, null, false);
String current = mAccount.getPresenceStatusMessage(); String current = mAccount.getPresenceStatusMessage();
if (current != null && !current.trim().isEmpty()) { if (current != null && !current.trim().isEmpty()) {
@ -949,7 +948,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
setAvailabilityRadioButton(mAccount.getPresenceStatus(), binding); setAvailabilityRadioButton(mAccount.getPresenceStatus(), binding);
binding.show.setVisibility(manualStatus ? View.VISIBLE : View.GONE); binding.show.setVisibility(manualStatus ? View.VISIBLE : View.GONE);
List<PresenceTemplate> templates = xmppConnectionService.getPresenceTemplates(mAccount); List<PresenceTemplate> templates = xmppConnectionService.getPresenceTemplates(mAccount);
PresenceTemplateAdapter presenceTemplateAdapter = new PresenceTemplateAdapter(this, R.layout.simple_list_item, templates); PresenceTemplateAdapter presenceTemplateAdapter = new PresenceTemplateAdapter(this, R.layout.item_autocomplete, templates);
binding.statusMessage.setAdapter(presenceTemplateAdapter); binding.statusMessage.setAdapter(presenceTemplateAdapter);
binding.statusMessage.setOnItemClickListener((parent, view, position, id) -> { binding.statusMessage.setOnItemClickListener((parent, view, position, id) -> {
PresenceTemplate template = (PresenceTemplate) parent.getItemAtPosition(position); PresenceTemplate template = (PresenceTemplate) parent.getItemAtPosition(position);
@ -1144,7 +1143,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
this.binding.pgpFingerprint.setText(OpenPgpUtils.convertKeyIdToHex(pgpKeyId)); this.binding.pgpFingerprint.setText(OpenPgpUtils.convertKeyIdToHex(pgpKeyId));
this.binding.pgpFingerprint.setOnClickListener(openPgp); this.binding.pgpFingerprint.setOnClickListener(openPgp);
if ("pgp".equals(messageFingerprint)) { if ("pgp".equals(messageFingerprint)) {
this.binding.pgpFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); this.binding.pgpFingerprintDesc.setTextColor(MaterialColors.getColor(binding.pgpFingerprintDesc, com.google.android.material.R.attr.colorPrimaryVariant));
} }
this.binding.pgpFingerprintDesc.setOnClickListener(openPgp); this.binding.pgpFingerprintDesc.setOnClickListener(openPgp);
this.binding.actionDeletePgp.setOnClickListener(delete); this.binding.actionDeletePgp.setOnClickListener(delete);
@ -1155,10 +1154,10 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (ownAxolotlFingerprint != null && Config.supportOmemo()) { if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
this.binding.axolotlFingerprintBox.setVisibility(View.VISIBLE); this.binding.axolotlFingerprintBox.setVisibility(View.VISIBLE);
if (ownAxolotlFingerprint.equals(messageFingerprint)) { if (ownAxolotlFingerprint.equals(messageFingerprint)) {
this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption_Highlight); this.binding.ownFingerprintDesc.setTextColor(MaterialColors.getColor(binding.ownFingerprintDesc, com.google.android.material.R.attr.colorPrimaryVariant));
this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint_selected_message); this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint_selected_message);
} else { } else {
this.binding.ownFingerprintDesc.setTextAppearance(this, R.style.TextAppearance_Conversations_Caption); this.binding.ownFingerprintDesc.setTextColor(MaterialColors.getColor(binding.ownFingerprintDesc, com.google.android.material.R.attr.colorOnSurface));
this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint); this.binding.ownFingerprintDesc.setText(R.string.omemo_fingerprint);
} }
this.binding.axolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2))); this.binding.axolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
@ -1222,10 +1221,10 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
private void updateDisplayName(String displayName) { private void updateDisplayName(String displayName) {
if (TextUtils.isEmpty(displayName)) { if (TextUtils.isEmpty(displayName)) {
this.binding.yourName.setText(R.string.no_name_set_instructions); this.binding.yourName.setText(R.string.no_name_set_instructions);
this.binding.yourName.setTextAppearance(this, R.style.TextAppearance_Conversations_Body1_Tertiary); this.binding.yourName.setTextColor(MaterialColors.getColor(binding.yourName, com.google.android.material.R.attr.colorOnSurfaceVariant));
} else { } else {
this.binding.yourName.setText(displayName); this.binding.yourName.setText(displayName);
this.binding.yourName.setTextAppearance(this, R.style.TextAppearance_Conversations_Body1); this.binding.yourName.setTextColor(MaterialColors.getColor(binding.yourName, com.google.android.material.R.attr.colorOnSurfaceVariant));
} }
} }
@ -1249,7 +1248,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} }
private void showDeletePgpDialog() { private void showDeletePgpDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.unpublish_pgp); builder.setTitle(R.string.unpublish_pgp);
builder.setMessage(R.string.unpublish_pgp_message); builder.setMessage(R.string.unpublish_pgp_message);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
@ -1279,7 +1278,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
Toast.makeText(EditAccountActivity.this, getString(R.string.device_does_not_support_data_saver, getString(R.string.app_name)), Toast.LENGTH_SHORT).show(); Toast.makeText(EditAccountActivity.this, getString(R.string.device_does_not_support_data_saver, getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
} }
}); });
} else if (showBatteryWarning && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { } else if (showBatteryWarning) {
this.binding.osOptimizationDisable.setText(R.string.disable); this.binding.osOptimizationDisable.setText(R.string.disable);
this.binding.osOptimizationHeadline.setText(R.string.battery_optimizations_enabled); this.binding.osOptimizationHeadline.setText(R.string.battery_optimizations_enabled);
this.binding.osOptimizationBody.setText(getString(R.string.battery_optimizations_enabled_explained, getString(R.string.app_name))); this.binding.osOptimizationBody.setText(getString(R.string.battery_optimizations_enabled_explained, getString(R.string.app_name)));
@ -1297,7 +1296,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} }
public void showWipePepDialog() { public void showWipePepDialog() {
Builder builder = new Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(getString(R.string.clear_other_devices)); builder.setTitle(getString(R.string.clear_other_devices));
builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(getString(R.string.clear_other_devices_desc)); builder.setMessage(getString(R.string.clear_other_devices_desc));
@ -1324,7 +1323,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (mCaptchaDialog != null && mCaptchaDialog.isShowing()) { if (mCaptchaDialog != null && mCaptchaDialog.isShowing()) {
mCaptchaDialog.dismiss(); mCaptchaDialog.dismiss();
} }
final Builder builder = new Builder(EditAccountActivity.this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(EditAccountActivity.this);
final View view = getLayoutInflater().inflate(R.layout.captcha, null); final View view = getLayoutInflater().inflate(R.layout.captcha, null);
final ImageView imageView = view.findViewById(R.id.captcha); final ImageView imageView = view.findViewById(R.id.captcha);
final EditText input = view.findViewById(R.id.input); final EditText input = view.findViewById(R.id.input);
@ -1372,7 +1371,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
if (mFetchingMamPrefsToast != null) { if (mFetchingMamPrefsToast != null) {
mFetchingMamPrefsToast.cancel(); mFetchingMamPrefsToast.cancel();
} }
Builder builder = new Builder(EditAccountActivity.this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(EditAccountActivity.this);
builder.setTitle(R.string.server_side_mam_prefs); builder.setTitle(R.string.server_side_mam_prefs);
String defaultAttr = prefs.getAttribute("default"); String defaultAttr = prefs.getAttribute("default");
final List<String> defaults = Arrays.asList("never", "roster", "always"); final List<String> defaults = Arrays.asList("never", "roster", "always");

View file

@ -13,15 +13,17 @@ import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.EnterJidDialogBinding; import eu.siacs.conversations.databinding.DialogEnterJidBinding;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected; import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
@ -46,28 +48,28 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
private KnownHostsAdapter knownHostsAdapter; private KnownHostsAdapter knownHostsAdapter;
private Collection<String> whitelistedDomains = Collections.emptyList(); private Collection<String> whitelistedDomains = Collections.emptyList();
private EnterJidDialogBinding binding; private DialogEnterJidBinding binding;
private AlertDialog dialog; private AlertDialog dialog;
private boolean sanityCheckJid = false; private boolean sanityCheckJid = false;
private boolean issuedWarning = false; private boolean issuedWarning = false;
public static EnterJidDialog newInstance( public static EnterJidDialog newInstance(
final List<String> activatedAccounts, final ArrayList<String> activatedAccounts,
final String title, final String title,
final String positiveButton, final String positiveButton,
final String prefilledJid, final String prefilledJid,
final String account, final String account,
boolean allowEditJid, boolean allowEditJid,
final boolean sanity_check_jid) { final boolean sanity_check_jid) {
EnterJidDialog dialog = new EnterJidDialog(); final EnterJidDialog dialog = new EnterJidDialog();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(TITLE_KEY, title); bundle.putString(TITLE_KEY, title);
bundle.putString(POSITIVE_BUTTON_KEY, positiveButton); bundle.putString(POSITIVE_BUTTON_KEY, positiveButton);
bundle.putString(PREFILLED_JID_KEY, prefilledJid); bundle.putString(PREFILLED_JID_KEY, prefilledJid);
bundle.putString(ACCOUNT_KEY, account); bundle.putString(ACCOUNT_KEY, account);
bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid); bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts); bundle.putStringArrayList(ACCOUNTS_LIST_KEY, activatedAccounts);
bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid); bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
dialog.setArguments(bundle); dialog.setArguments(bundle);
return dialog; return dialog;
@ -91,16 +93,16 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(final Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final var arguments = getArguments();
builder.setTitle(getArguments().getString(TITLE_KEY)); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(arguments.getString(TITLE_KEY));
binding = binding =
DataBindingUtil.inflate( DataBindingUtil.inflate(requireActivity().getLayoutInflater(), R.layout.dialog_enter_jid, null, false);
getActivity().getLayoutInflater(), R.layout.enter_jid_dialog, null, false); this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item);
binding.jid.setAdapter(this.knownHostsAdapter); binding.jid.setAdapter(this.knownHostsAdapter);
binding.jid.addTextChangedListener(this); binding.jid.addTextChangedListener(this);
String prefilledJid = getArguments().getString(PREFILLED_JID_KEY); final String prefilledJid = arguments.getString(PREFILLED_JID_KEY);
if (prefilledJid != null) { if (prefilledJid != null) {
binding.jid.append(prefilledJid); binding.jid.append(prefilledJid);
if (!getArguments().getBoolean(ALLOW_EDIT_JID_KEY)) { if (!getArguments().getBoolean(ALLOW_EDIT_JID_KEY)) {
@ -114,18 +116,18 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid); DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
String account = getArguments().getString(ACCOUNT_KEY); final String account = getArguments().getString(ACCOUNT_KEY);
if (account == null) { if (Strings.isNullOrEmpty(account)) {
StartConversationActivity.populateAccountSpinner( StartConversationActivity.populateAccountSpinner(
getActivity(), getActivity(),
getArguments().getStringArrayList(ACCOUNTS_LIST_KEY), arguments.getStringArrayList(ACCOUNTS_LIST_KEY),
binding.account); binding.account);
} else { } else {
ArrayAdapter<String> adapter = final ArrayAdapter<String> adapter =
new ArrayAdapter<>( new ArrayAdapter<>(requireActivity(), R.layout.item_autocomplete, new String[] {account});
getActivity(), R.layout.simple_list_item, new String[] {account}); binding.account.setText(account);
binding.account.setEnabled(false); binding.account.setEnabled(false);
adapter.setDropDownViewResource(R.layout.simple_list_item); adapter.setDropDownViewResource(R.layout.item_autocomplete);
binding.account.setAdapter(adapter); binding.account.setAdapter(adapter);
} }
@ -135,9 +137,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
this.dialog = builder.create(); this.dialog = builder.create();
View.OnClickListener dialogOnClick = View.OnClickListener dialogOnClick =
v -> { v -> handleEnter(binding, account);
handleEnter(binding, account);
};
binding.jid.setOnEditorActionListener( binding.jid.setOnEditorActionListener(
(v, actionId, event) -> { (v, actionId, event) -> {
@ -150,21 +150,13 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
return dialog; return dialog;
} }
private void handleEnter(EnterJidDialogBinding binding, String account) { private void handleEnter(DialogEnterJidBinding binding, String account) {
final Jid accountJid; final Jid accountJid;
if (!binding.account.isEnabled() && account == null) { if (!binding.account.isEnabled() && account == null) {
return; return;
} }
try { try {
if (Config.DOMAIN_LOCK != null) { accountJid = Jid.ofEscaped((String) binding.account.getEditableText().toString());
accountJid =
Jid.ofEscaped(
(String) binding.account.getSelectedItem(),
Config.DOMAIN_LOCK,
null);
} else {
accountJid = Jid.ofEscaped((String) binding.account.getSelectedItem());
}
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
return; return;
} }

View file

@ -13,6 +13,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import java.util.ArrayList; import java.util.ArrayList;
@ -50,11 +51,11 @@ public class JoinConferenceDialog extends DialogFragment implements OnBackendCon
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(R.string.join_public_channel); builder.setTitle(R.string.join_public_channel);
DialogJoinConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_join_conference, null, false); final DialogJoinConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_join_conference, null, false);
DelayedHintHelper.setHint(R.string.channel_full_jid_example, binding.jid); DelayedHintHelper.setHint(R.string.channel_full_jid_example, binding.jid);
this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.simple_list_item); this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
binding.jid.setAdapter(knownHostsAdapter); binding.jid.setAdapter(knownHostsAdapter);
String prefilledJid = getArguments().getString(PREFILLED_JID_KEY); String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
if (prefilledJid != null) { if (prefilledJid != null) {
@ -117,6 +118,6 @@ public class JoinConferenceDialog extends DialogFragment implements OnBackendCon
} }
public interface JoinConferenceDialogListener { public interface JoinConferenceDialogListener {
void onJoinDialogPositiveClick(Dialog dialog, Spinner spinner, TextInputLayout jidLayout, AutoCompleteTextView jid, boolean isBookmarkChecked); void onJoinDialogPositiveClick(Dialog dialog, AutoCompleteTextView spinner, TextInputLayout jidLayout, AutoCompleteTextView jid, boolean isBookmarkChecked);
} }
} }

View file

@ -40,7 +40,6 @@ import eu.siacs.conversations.ui.util.LocationHelper;
import eu.siacs.conversations.ui.widget.Marker; import eu.siacs.conversations.ui.widget.Marker;
import eu.siacs.conversations.ui.widget.MyLocation; import eu.siacs.conversations.ui.widget.MyLocation;
import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
public abstract class LocationActivity extends ActionBarActivity implements LocationListener { public abstract class LocationActivity extends ActionBarActivity implements LocationListener {
protected LocationManager locationManager; protected LocationManager locationManager;
@ -78,7 +77,6 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final Context ctx = getApplicationContext(); final Context ctx = getApplicationContext();
setTheme(ThemeHelper.find(this));
final PackageManager packageManager = ctx.getPackageManager(); final PackageManager packageManager = ctx.getPackageManager();
hasLocationFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION) || hasLocationFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION) ||
@ -90,7 +88,7 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
// Ask for location permissions if location services are enabled and we're // Ask for location permissions if location services are enabled and we're
// just starting the activity (we don't want to keep pestering them on every // just starting the activity (we don't want to keep pestering them on every
// screen rotation or if there's no point because it's disabled anyways). // screen rotation or if there's no point because it's disabled anyways).
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && savedInstanceState == null) { if (savedInstanceState == null) {
requestPermissions(REQUEST_CODE_CREATE); requestPermissions(REQUEST_CODE_CREATE);
} }
@ -224,7 +222,6 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
Configuration.getInstance().load(this, getPreferences()); Configuration.getInstance().load(this, getPreferences());
map.onResume(); map.onResume();
this.setMyLoc(null); this.setMyLoc(null);

View file

@ -29,6 +29,7 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this,R.layout.activity_media_browser); this.binding = DataBindingUtil.setContentView(this,R.layout.activity_media_browser);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
mMediaAdapter = new MediaAdapter(this, R.dimen.media_size); mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);

View file

@ -33,6 +33,8 @@ import android.os.Bundle;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -40,7 +42,6 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.MTMDecision; import eu.siacs.conversations.entities.MTMDecision;
import eu.siacs.conversations.services.MemorizingTrustManager; import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
public class MemorizingActivity extends AppCompatActivity implements OnClickListener, OnCancelListener { public class MemorizingActivity extends AppCompatActivity implements OnClickListener, OnCancelListener {
@ -53,10 +54,7 @@ public class MemorizingActivity extends AppCompatActivity implements OnClickList
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
LOGGER.log(Level.FINE, "onCreate"); LOGGER.log(Level.FINE, "onCreate");
setTheme(ThemeHelper.find(this));
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getLayoutInflater().inflate(R.layout.toolbar, findViewById(android.R.id.content));
setSupportActionBar(findViewById(R.id.toolbar));
} }
@Override @Override
@ -69,7 +67,7 @@ public class MemorizingActivity extends AppCompatActivity implements OnClickList
int titleId = i.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert); int titleId = i.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert);
String cert = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT); String cert = i.getStringExtra(MemorizingTrustManager.DECISION_INTENT_CERT);
LOGGER.log(Level.FINE, "onResume with " + i.getExtras() + " decId=" + decisionId + " data: " + i.getData()); LOGGER.log(Level.FINE, "onResume with " + i.getExtras() + " decId=" + decisionId + " data: " + i.getData());
dialog = new AlertDialog.Builder(this).setTitle(titleId) dialog = new MaterialAlertDialogBuilder(this).setTitle(titleId)
.setMessage(cert) .setMessage(cert)
.setPositiveButton(R.string.always, this) .setPositiveButton(R.string.always, this)
.setNeutralButton(R.string.once, this) .setNeutralButton(R.string.once, this)

View file

@ -102,8 +102,9 @@ public class MucUsersActivity extends XmppActivity implements XmppConnectionServ
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ActivityMucUsersBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_users); final ActivityMucUsersBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_muc_users);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
configureActionBar(getSupportActionBar(), true); configureActionBar(getSupportActionBar(), true);
this.userAdapter = new UserAdapter(getPreferences().getBoolean("advanced_muc_mode", false)); this.userAdapter = new UserAdapter(getPreferences().getBoolean("advanced_muc_mode", false));
binding.list.setAdapter(this.userAdapter); binding.list.setAdapter(this.userAdapter);

View file

@ -11,6 +11,9 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
@ -33,10 +36,7 @@ public abstract class OmemoActivity extends XmppActivity {
Object account = v.getTag(R.id.TAG_ACCOUNT); Object account = v.getTag(R.id.TAG_ACCOUNT);
Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT); Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS); Object fingerprintStatus = v.getTag(R.id.TAG_FINGERPRINT_STATUS);
if (account != null if (account instanceof Account
&& fingerprint != null
&& account instanceof Account
&& fingerprintStatus != null
&& fingerprint instanceof String && fingerprint instanceof String
&& fingerprintStatus instanceof FingerprintStatus) { && fingerprintStatus instanceof FingerprintStatus) {
getMenuInflater().inflate(R.menu.omemo_key_context, menu); getMenuInflater().inflate(R.menu.omemo_key_context, menu);
@ -130,8 +130,8 @@ public abstract class OmemoActivity extends XmppActivity {
binding.tglTrust.setChecked(status.isTrusted()); binding.tglTrust.setChecked(status.isTrusted());
if (status.isActive()) { if (status.isActive()) {
binding.key.setTextAppearance(this,R.style.TextAppearance_Conversations_Fingerprint); binding.key.setTextColor(MaterialColors.getColor(binding.key, com.google.android.material.R.attr.colorOnSurface));
binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption); binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorOnSurface));
if (status.isVerified()) { if (status.isVerified()) {
binding.verifiedFingerprint.setVisibility(View.VISIBLE); binding.verifiedFingerprint.setVisibility(View.VISIBLE);
binding.verifiedFingerprint.setAlpha(1.0f); binding.verifiedFingerprint.setAlpha(1.0f);
@ -157,8 +157,8 @@ public abstract class OmemoActivity extends XmppActivity {
toast = v -> hideToast(); toast = v -> hideToast();
} }
} else { } else {
binding.key.setTextAppearance(this,R.style.TextAppearance_Conversations_Fingerprint_Disabled); binding.key.setTextColor(MaterialColors.getColor(binding.key, com.google.android.material.R.attr.colorOnSurfaceVariant));
binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption_Disabled); binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorOnSurfaceVariant));
toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false); toast = v -> replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
if (status.isVerified()) { if (status.isVerified()) {
binding.tglTrust.setVisibility(View.GONE); binding.tglTrust.setVisibility(View.GONE);
@ -181,7 +181,7 @@ public abstract class OmemoActivity extends XmppActivity {
binding.keyType.setVisibility(View.GONE); binding.keyType.setVisibility(View.GONE);
} }
if (highlight) { if (highlight) {
binding.keyType.setTextAppearance(this,R.style.TextAppearance_Conversations_Caption_Highlight); binding.keyType.setTextColor(MaterialColors.getColor(binding.keyType, com.google.android.material.R.attr.colorPrimaryVariant));
binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message)); binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
} else { } else {
binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint)); binding.keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
@ -191,7 +191,7 @@ public abstract class OmemoActivity extends XmppActivity {
} }
public void showPurgeKeyDialog(final Account account, final String fingerprint) { public void showPurgeKeyDialog(final Account account, final String fingerprint) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.distrust_omemo_key); builder.setTitle(R.string.distrust_omemo_key);
builder.setMessage(R.string.distrust_omemo_key_text); builder.setMessage(R.string.distrust_omemo_key_text);
builder.setNegativeButton(getString(R.string.cancel), null); builder.setNegativeButton(getString(R.string.cancel), null);

View file

@ -91,6 +91,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_publish_profile_picture); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_publish_profile_picture);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(this.binding.toolbar); setSupportActionBar(this.binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
this.binding.cancelButton.setOnClickListener((v) -> this.finish()); this.binding.cancelButton.setOnClickListener((v) -> this.finish());
@ -114,6 +115,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
final CropImage.ActivityResult result = CropImage.getActivityResult(data); final CropImage.ActivityResult result = CropImage.getActivityResult(data);
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {

View file

@ -18,6 +18,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.databinding.DataBindingUtil;
import com.canhub.cropper.CropImage; import com.canhub.cropper.CropImage;
@ -25,6 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityPublishProfilePictureBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
@ -77,7 +79,6 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
public void onAvatarPublicationFailed(int res) { public void onAvatarPublicationFailed(int res) {
runOnUiThread(() -> { runOnUiThread(() -> {
hintOrWarning.setText(res); hintOrWarning.setText(res);
hintOrWarning.setTextAppearance(this,R.style.TextAppearance_Conversations_Body1_Warning);
hintOrWarning.setVisibility(View.VISIBLE); hintOrWarning.setVisibility(View.VISIBLE);
publishing = false; publishing = false;
togglePublishButton(true, R.string.publish); togglePublishButton(true, R.string.publish);
@ -87,8 +88,12 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publish_profile_picture);
setSupportActionBar(findViewById(R.id.toolbar)); ActivityPublishProfilePictureBinding 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.avatar = findViewById(R.id.account_image);
this.cancelButton = findViewById(R.id.cancel_button); this.cancelButton = findViewById(R.id.cancel_button);
@ -220,7 +225,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
final Intent intent = getIntent(); final Intent intent = getIntent();
this.mInitialAccountSetup = intent != null && intent.getBooleanExtra("setup", false); this.mInitialAccountSetup = intent != null && intent.getBooleanExtra("setup", false);
@ -261,7 +266,6 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
if (bm == null) { if (bm == null) {
togglePublishButton(false, R.string.publish); togglePublishButton(false, R.string.publish);
this.hintOrWarning.setVisibility(View.VISIBLE); this.hintOrWarning.setVisibility(View.VISIBLE);
this.hintOrWarning.setTextAppearance(this,R.style.TextAppearance_Conversations_Body1_Warning);
this.hintOrWarning.setText(R.string.error_publish_avatar_converting); this.hintOrWarning.setText(R.string.error_publish_avatar_converting);
return; return;
} }
@ -272,7 +276,6 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
} else { } else {
togglePublishButton(false, R.string.publish); togglePublishButton(false, R.string.publish);
this.hintOrWarning.setVisibility(View.VISIBLE); this.hintOrWarning.setVisibility(View.VISIBLE);
this.hintOrWarning.setTextAppearance(this,R.style.TextAppearance_Conversations_Body1_Warning);
if (account.getStatus() == Account.State.ONLINE) { if (account.getStatus() == Account.State.ONLINE) {
this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support); this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
} else { } else {

View file

@ -15,6 +15,7 @@ import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -33,10 +34,9 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityRecordingBinding; import eu.siacs.conversations.databinding.ActivityRecordingBinding;
import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.TimeFrameUtils;
public class RecordingActivity extends Activity implements View.OnClickListener { public class RecordingActivity extends BaseActivity implements View.OnClickListener {
private ActivityRecordingBinding binding; private ActivityRecordingBinding binding;
@ -61,7 +61,6 @@ public class RecordingActivity extends Activity implements View.OnClickListener
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
setTheme(ThemeHelper.findDialog(this));
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_recording); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_recording);
this.binding.cancelButton.setOnClickListener(this); this.binding.cancelButton.setOnClickListener(this);
@ -69,19 +68,13 @@ public class RecordingActivity extends Activity implements View.OnClickListener
this.setFinishOnTouchOutside(false); this.setFinishOnTouchOutside(false);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} }
@Override @Override
protected void onResume() { public void onStart() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override
protected void onStart() {
super.onStart(); super.onStart();
if (!startRecording()) { if (!startRecording()) {
this.binding.shareButton.setEnabled(false); this.binding.shareButton.setEnabled(false);
this.binding.timer.setTextAppearance(this, R.style.TextAppearance_Conversations_Title); this.binding.timer.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyMedium);
// TODO reset font family. make red?
this.binding.timer.setText(R.string.unable_to_start_recording); this.binding.timer.setText(R.string.unable_to_start_recording);
} }
} }

View file

@ -179,6 +179,7 @@ public class RtpSessionActivity extends XmppActivity
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
} }
@Override @Override
@ -920,34 +921,34 @@ public class RtpSessionActivity extends XmppActivity
} else if (state == RtpEndUserState.INCOMING_CALL) { } else if (state == RtpEndUserState.INCOMING_CALL) {
this.binding.rejectCall.setContentDescription(getString(R.string.dismiss_call)); this.binding.rejectCall.setContentDescription(getString(R.string.dismiss_call));
this.binding.rejectCall.setOnClickListener(this::rejectCall); this.binding.rejectCall.setOnClickListener(this::rejectCall);
this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp); this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_24dp);
this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.rejectCall.setVisibility(View.VISIBLE);
this.binding.endCall.setVisibility(View.INVISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE);
this.binding.acceptCall.setContentDescription(getString(R.string.answer_call)); this.binding.acceptCall.setContentDescription(getString(R.string.answer_call));
this.binding.acceptCall.setOnClickListener(this::acceptCall); this.binding.acceptCall.setOnClickListener(this::acceptCall);
this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp); this.binding.acceptCall.setImageResource(R.drawable.ic_call_24dp);
this.binding.acceptCall.setVisibility(View.VISIBLE); this.binding.acceptCall.setVisibility(View.VISIBLE);
} else if (state == RtpEndUserState.INCOMING_CONTENT_ADD) { } else if (state == RtpEndUserState.INCOMING_CONTENT_ADD) {
this.binding.rejectCall.setContentDescription( this.binding.rejectCall.setContentDescription(
getString(R.string.reject_switch_to_video)); getString(R.string.reject_switch_to_video));
this.binding.rejectCall.setOnClickListener(this::rejectContentAdd); this.binding.rejectCall.setOnClickListener(this::rejectContentAdd);
this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_24dp);
this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.rejectCall.setVisibility(View.VISIBLE);
this.binding.endCall.setVisibility(View.INVISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE);
this.binding.acceptCall.setContentDescription(getString(R.string.accept)); this.binding.acceptCall.setContentDescription(getString(R.string.accept));
this.binding.acceptCall.setOnClickListener((v -> acceptContentAdd(contentAddition))); this.binding.acceptCall.setOnClickListener((v -> acceptContentAdd(contentAddition)));
this.binding.acceptCall.setImageResource(R.drawable.ic_baseline_check_24); this.binding.acceptCall.setImageResource(R.drawable.ic_check_24dp);
this.binding.acceptCall.setVisibility(View.VISIBLE); this.binding.acceptCall.setVisibility(View.VISIBLE);
} else if (asList(RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONTACT_OFFLINE) } else if (asList(RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONTACT_OFFLINE)
.contains(state)) { .contains(state)) {
this.binding.rejectCall.setContentDescription(getString(R.string.exit)); this.binding.rejectCall.setContentDescription(getString(R.string.exit));
this.binding.rejectCall.setOnClickListener(this::exit); this.binding.rejectCall.setOnClickListener(this::exit);
this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_24dp);
this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.rejectCall.setVisibility(View.VISIBLE);
this.binding.endCall.setVisibility(View.INVISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE);
this.binding.acceptCall.setContentDescription(getString(R.string.record_voice_mail)); this.binding.acceptCall.setContentDescription(getString(R.string.record_voice_mail));
this.binding.acceptCall.setOnClickListener(this::recordVoiceMail); this.binding.acceptCall.setOnClickListener(this::recordVoiceMail);
this.binding.acceptCall.setImageResource(R.drawable.ic_voicemail_white_24dp); this.binding.acceptCall.setImageResource(R.drawable.ic_voicemail_24dp);
this.binding.acceptCall.setVisibility(View.VISIBLE); this.binding.acceptCall.setVisibility(View.VISIBLE);
} else if (asList( } else if (asList(
RtpEndUserState.CONNECTIVITY_ERROR, RtpEndUserState.CONNECTIVITY_ERROR,
@ -958,18 +959,18 @@ public class RtpSessionActivity extends XmppActivity
.contains(state)) { .contains(state)) {
this.binding.rejectCall.setContentDescription(getString(R.string.exit)); this.binding.rejectCall.setContentDescription(getString(R.string.exit));
this.binding.rejectCall.setOnClickListener(this::exit); this.binding.rejectCall.setOnClickListener(this::exit);
this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp); this.binding.rejectCall.setImageResource(R.drawable.ic_clear_24dp);
this.binding.rejectCall.setVisibility(View.VISIBLE); this.binding.rejectCall.setVisibility(View.VISIBLE);
this.binding.endCall.setVisibility(View.INVISIBLE); this.binding.endCall.setVisibility(View.INVISIBLE);
this.binding.acceptCall.setContentDescription(getString(R.string.try_again)); this.binding.acceptCall.setContentDescription(getString(R.string.try_again));
this.binding.acceptCall.setOnClickListener(this::retry); this.binding.acceptCall.setOnClickListener(this::retry);
this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp); this.binding.acceptCall.setImageResource(R.drawable.ic_replay_24dp);
this.binding.acceptCall.setVisibility(View.VISIBLE); this.binding.acceptCall.setVisibility(View.VISIBLE);
} else { } else {
this.binding.rejectCall.setVisibility(View.INVISIBLE); this.binding.rejectCall.setVisibility(View.INVISIBLE);
this.binding.endCall.setContentDescription(getString(R.string.hang_up)); this.binding.endCall.setContentDescription(getString(R.string.hang_up));
this.binding.endCall.setOnClickListener(this::endCall); this.binding.endCall.setOnClickListener(this::endCall);
this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp); this.binding.endCall.setImageResource(R.drawable.ic_call_end_24dp);
this.binding.endCall.setVisibility(View.VISIBLE); this.binding.endCall.setVisibility(View.VISIBLE);
this.binding.acceptCall.setVisibility(View.INVISIBLE); this.binding.acceptCall.setVisibility(View.INVISIBLE);
} }
@ -1038,7 +1039,7 @@ public class RtpSessionActivity extends XmppActivity
switch (selectedAudioDevice) { switch (selectedAudioDevice) {
case EARPIECE -> { case EARPIECE -> {
this.binding.inCallActionRight.setImageResource( this.binding.inCallActionRight.setImageResource(
R.drawable.ic_volume_off_black_24dp); R.drawable.ic_volume_off_24dp);
if (numberOfChoices >= 2) { if (numberOfChoices >= 2) {
this.binding.inCallActionRight.setOnClickListener(this::switchToSpeaker); this.binding.inCallActionRight.setOnClickListener(this::switchToSpeaker);
} else { } else {
@ -1047,12 +1048,12 @@ public class RtpSessionActivity extends XmppActivity
} }
} }
case WIRED_HEADSET -> { case WIRED_HEADSET -> {
this.binding.inCallActionRight.setImageResource(R.drawable.ic_headset_black_24dp); this.binding.inCallActionRight.setImageResource(R.drawable.ic_headset_mic_24dp);
this.binding.inCallActionRight.setOnClickListener(null); this.binding.inCallActionRight.setOnClickListener(null);
this.binding.inCallActionRight.setClickable(false); this.binding.inCallActionRight.setClickable(false);
} }
case SPEAKER_PHONE -> { case SPEAKER_PHONE -> {
this.binding.inCallActionRight.setImageResource(R.drawable.ic_volume_up_black_24dp); this.binding.inCallActionRight.setImageResource(R.drawable.ic_volume_up_24dp);
if (numberOfChoices >= 2) { if (numberOfChoices >= 2) {
this.binding.inCallActionRight.setOnClickListener(this::switchToEarpiece); this.binding.inCallActionRight.setOnClickListener(this::switchToEarpiece);
} else { } else {
@ -1062,7 +1063,7 @@ public class RtpSessionActivity extends XmppActivity
} }
case BLUETOOTH -> { case BLUETOOTH -> {
this.binding.inCallActionRight.setImageResource( this.binding.inCallActionRight.setImageResource(
R.drawable.ic_bluetooth_audio_black_24dp); R.drawable.ic_bluetooth_audio_24dp);
this.binding.inCallActionRight.setOnClickListener(null); this.binding.inCallActionRight.setOnClickListener(null);
this.binding.inCallActionRight.setClickable(false); this.binding.inCallActionRight.setClickable(false);
} }
@ -1076,17 +1077,17 @@ public class RtpSessionActivity extends XmppActivity
this.binding.inCallActionRight.setVisibility(View.VISIBLE); this.binding.inCallActionRight.setVisibility(View.VISIBLE);
if (isCameraSwitchable) { if (isCameraSwitchable) {
this.binding.inCallActionFarRight.setImageResource( this.binding.inCallActionFarRight.setImageResource(
R.drawable.ic_flip_camera_android_black_24dp); R.drawable.ic_flip_camera_android_24dp);
this.binding.inCallActionFarRight.setVisibility(View.VISIBLE); this.binding.inCallActionFarRight.setVisibility(View.VISIBLE);
this.binding.inCallActionFarRight.setOnClickListener(this::switchCamera); this.binding.inCallActionFarRight.setOnClickListener(this::switchCamera);
} else { } else {
this.binding.inCallActionFarRight.setVisibility(View.GONE); this.binding.inCallActionFarRight.setVisibility(View.GONE);
} }
if (videoEnabled) { if (videoEnabled) {
this.binding.inCallActionRight.setImageResource(R.drawable.ic_videocam_black_24dp); this.binding.inCallActionRight.setImageResource(R.drawable.ic_videocam_24dp);
this.binding.inCallActionRight.setOnClickListener(this::disableVideo); this.binding.inCallActionRight.setOnClickListener(this::disableVideo);
} else { } else {
this.binding.inCallActionRight.setImageResource(R.drawable.ic_videocam_off_black_24dp); this.binding.inCallActionRight.setImageResource(R.drawable.ic_videocam_off_24dp);
this.binding.inCallActionRight.setOnClickListener(this::enableVideo); this.binding.inCallActionRight.setOnClickListener(this::enableVideo);
} }
} }
@ -1140,10 +1141,10 @@ public class RtpSessionActivity extends XmppActivity
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
private void updateInCallButtonConfigurationMicrophone(final boolean microphoneEnabled) { private void updateInCallButtonConfigurationMicrophone(final boolean microphoneEnabled) {
if (microphoneEnabled) { if (microphoneEnabled) {
this.binding.inCallActionLeft.setImageResource(R.drawable.ic_mic_black_24dp); this.binding.inCallActionLeft.setImageResource(R.drawable.ic_mic_24dp);
this.binding.inCallActionLeft.setOnClickListener(this::disableMicrophone); this.binding.inCallActionLeft.setOnClickListener(this::disableMicrophone);
} else { } else {
this.binding.inCallActionLeft.setImageResource(R.drawable.ic_mic_off_black_24dp); this.binding.inCallActionLeft.setImageResource(R.drawable.ic_mic_off_24dp);
this.binding.inCallActionLeft.setOnClickListener(this::enableMicrophone); this.binding.inCallActionLeft.setOnClickListener(this::enableMicrophone);
} }
this.binding.inCallActionLeft.setVisibility(View.VISIBLE); this.binding.inCallActionLeft.setVisibility(View.VISIBLE);

View file

@ -182,7 +182,6 @@ public final class ScanActivity extends Activity implements SurfaceTextureListen
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
maybeOpenCamera(); maybeOpenCamera();
} }

View file

@ -42,8 +42,10 @@ import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.EditText; import android.widget.EditText;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -64,7 +66,6 @@ import eu.siacs.conversations.ui.util.DateSeparator;
import eu.siacs.conversations.ui.util.ListViewUtils; import eu.siacs.conversations.ui.util.ListViewUtils;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ShareUtil; import eu.siacs.conversations.ui.util.ShareUtil;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.FtsUtils; import eu.siacs.conversations.utils.FtsUtils;
import eu.siacs.conversations.utils.MessageUtils; import eu.siacs.conversations.utils.MessageUtils;
@ -95,6 +96,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
} }
super.onCreate(bundle); super.onCreate(bundle);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_search); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_search);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(this.binding.toolbar); setSupportActionBar(this.binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
this.messageListAdapter = new MessageAdapter(this, this.messages, uuid == null); this.messageListAdapter = new MessageAdapter(this, this.messages, uuid == null);
@ -223,12 +225,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
private void changeBackground(boolean hasSearch, boolean hasResults) { private void changeBackground(boolean hasSearch, boolean hasResults) {
if (hasSearch) { if (hasSearch) {
if (hasResults) { if (hasResults) {
binding.searchResults.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_secondary)); binding.searchResults.setBackgroundColor(MaterialColors.getColor(binding.searchResults, com.google.android.material.R.attr.colorSurface));
} else { } else {
binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_no_results)); binding.searchResults.setBackgroundResource(R.drawable.background_no_results);
} }
} else { } else {
binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_search)); binding.searchResults.setBackgroundResource(R.drawable.background_search);
} }
} }
@ -248,14 +250,14 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
if (!currentSearch.watch(term)) { if (!currentSearch.watch(term)) {
return; return;
} }
if (term.size() > 0) { if (term.isEmpty()) {
xmppConnectionService.search(term, uuid,this);
} else {
MessageSearchTask.cancelRunningTasks(); MessageSearchTask.cancelRunningTasks();
this.messages.clear(); this.messages.clear();
messageListAdapter.setHighlightedTerm(null); messageListAdapter.setHighlightedTerm(null);
messageListAdapter.notifyDataSetChanged(); messageListAdapter.notifyDataSetChanged();
changeBackground(false, false); changeBackground(false, false);
} else {
xmppConnectionService.search(term, uuid,this);
} }
} }
@ -267,7 +269,7 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc
DateSeparator.addAll(messages); DateSeparator.addAll(messages);
this.messages.addAll(messages); this.messages.addAll(messages);
messageListAdapter.notifyDataSetChanged(); messageListAdapter.notifyDataSetChanged();
changeBackground(true, messages.size() > 0); changeBackground(true, !messages.isEmpty());
ListViewUtils.scrollToBottom(this.binding.searchResults); ListViewUtils.scrollToBottom(this.binding.searchResults);
}); });
} }

View file

@ -22,7 +22,10 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.DynamicColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ -37,8 +40,10 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.Conversations;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting; import eu.siacs.conversations.crypto.OmemoSetting;
import eu.siacs.conversations.databinding.ActivitySettingsBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.ExportBackupService; import eu.siacs.conversations.services.ExportBackupService;
@ -46,10 +51,8 @@ import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.services.QuickConversationsService; import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.services.UnifiedPushDistributor; import eu.siacs.conversations.services.UnifiedPushDistributor;
import eu.siacs.conversations.ui.util.SettingsUtils; import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener { public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener {
@ -74,7 +77,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings); final ActivitySettingsBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_settings);
FragmentManager fm = getFragmentManager(); FragmentManager fm = getFragmentManager();
mSettingsFragment = (SettingsFragment) fm.findFragmentById(R.id.settings_content); mSettingsFragment = (SettingsFragment) fm.findFragmentById(R.id.settings_content);
if (mSettingsFragment == null if (mSettingsFragment == null
@ -83,13 +86,8 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit(); fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit();
} }
mSettingsFragment.setActivityIntent(getIntent()); mSettingsFragment.setActivityIntent(getIntent());
this.mTheme = findTheme(); Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setTheme(this.mTheme); setSupportActionBar(binding.toolbar);
getWindow()
.getDecorView()
.setBackgroundColor(
StyledAttributes.getColor(this, R.attr.color_background_primary));
setSupportActionBar(findViewById(R.id.toolbar));
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
} }
@ -185,6 +183,12 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
} }
} }
final PreferenceCategory uiPreferenceCategory = (PreferenceCategory) mSettingsFragment.findPreference("ui");
final Preference dynamicColorsPreference = mSettingsFragment.findPreference("dynamic_colors");
if (dynamicColorsPreference != null && !DynamicColors.isDynamicColorAvailable()) {
uiPreferenceCategory.removePreference(dynamicColorsPreference);
}
ListPreference automaticMessageDeletionList = ListPreference automaticMessageDeletionList =
(ListPreference) mSettingsFragment.findPreference(AUTOMATIC_MESSAGE_DELETION); (ListPreference) mSettingsFragment.findPreference(AUTOMATIC_MESSAGE_DELETION);
if (automaticMessageDeletionList != null) { if (automaticMessageDeletionList != null) {
@ -204,36 +208,6 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
automaticMessageDeletionList.setEntryValues(entryValues); automaticMessageDeletionList.setEntryValues(entryValues);
} }
boolean removeLocation =
new Intent("eu.siacs.conversations.location.request")
.resolveActivity(getPackageManager())
== null;
boolean removeVoice =
new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.resolveActivity(getPackageManager())
== null;
ListPreference quickAction =
(ListPreference) mSettingsFragment.findPreference("quick_action");
if (quickAction != null && (removeLocation || removeVoice)) {
ArrayList<CharSequence> entries =
new ArrayList<>(Arrays.asList(quickAction.getEntries()));
ArrayList<CharSequence> entryValues =
new ArrayList<>(Arrays.asList(quickAction.getEntryValues()));
int index = entryValues.indexOf("location");
if (index > 0 && removeLocation) {
entries.remove(index);
entryValues.remove(index);
}
index = entryValues.indexOf("voice");
if (index > 0 && removeVoice) {
entries.remove(index);
entryValues.remove(index);
}
quickAction.setEntries(entries.toArray(new CharSequence[entries.size()]));
quickAction.setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
}
final Preference removeCertsPreference = final Preference removeCertsPreference =
mSettingsFragment.findPreference("remove_trusted_certificates"); mSettingsFragment.findPreference("remove_trusted_certificates");
if (removeCertsPreference != null) { if (removeCertsPreference != null) {
@ -242,17 +216,16 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
final MemorizingTrustManager mtm = final MemorizingTrustManager mtm =
xmppConnectionService.getMemorizingTrustManager(); xmppConnectionService.getMemorizingTrustManager();
final ArrayList<String> aliases = Collections.list(mtm.getCertificates()); final ArrayList<String> aliases = Collections.list(mtm.getCertificates());
if (aliases.size() == 0) { if (aliases.isEmpty()) {
displayToast(getString(R.string.toast_no_trusted_certs)); displayToast(getString(R.string.toast_no_trusted_certs));
return true; return true;
} }
final ArrayList<Integer> selectedItems = new ArrayList<>(); final ArrayList<Integer> selectedItems = new ArrayList<>();
final AlertDialog.Builder dialogBuilder = final MaterialAlertDialogBuilder dialogBuilder = new MaterialAlertDialogBuilder(SettingsActivity.this);
new AlertDialog.Builder(SettingsActivity.this);
dialogBuilder.setTitle( dialogBuilder.setTitle(
getResources().getString(R.string.dialog_manage_certs_title)); getResources().getString(R.string.dialog_manage_certs_title));
dialogBuilder.setMultiChoiceItems( dialogBuilder.setMultiChoiceItems(
aliases.toArray(new CharSequence[aliases.size()]), aliases.toArray(new CharSequence[0]),
null, null,
(dialog, indexSelected, isChecked) -> { (dialog, indexSelected, isChecked) -> {
if (isChecked) { if (isChecked) {
@ -262,7 +235,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
} }
((AlertDialog) dialog) ((AlertDialog) dialog)
.getButton(DialogInterface.BUTTON_POSITIVE) .getButton(DialogInterface.BUTTON_POSITIVE)
.setEnabled(selectedItems.size() > 0); .setEnabled(!selectedItems.isEmpty());
}); });
dialogBuilder.setPositiveButton( dialogBuilder.setPositiveButton(
@ -273,13 +246,12 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
if (count > 0) { if (count > 0) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
try { try {
Integer item = final int item =
Integer.valueOf( Integer.parseInt(
selectedItems.get(i).toString()); selectedItems.get(i).toString());
String alias = aliases.get(item); String alias = aliases.get(item);
mtm.deleteCertificate(alias); mtm.deleteCertificate(alias);
} catch (KeyStoreException e) { } catch (final KeyStoreException e) {
e.printStackTrace();
displayToast("Error: " + e.getLocalizedMessage()); displayToast("Error: " + e.getLocalizedMessage());
} }
} }
@ -372,10 +344,8 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
private boolean isCallable(final Intent i) { private boolean isCallable(final Intent i) {
return i != null return i != null
&& getPackageManager() && !getPackageManager()
.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY) .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY).isEmpty();
.size()
> 0;
} }
private boolean cleanCache() { private boolean cleanCache() {
@ -413,7 +383,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
} }
private boolean deleteOmemoIdentities() { private boolean deleteOmemoIdentities() {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.pref_delete_omemo_identities); builder.setTitle(R.string.pref_delete_omemo_identities);
final List<CharSequence> accounts = new ArrayList<>(); final List<CharSequence> accounts = new ArrayList<>();
for (Account account : xmppConnectionService.getAccounts()) { for (Account account : xmppConnectionService.getAccounts()) {
@ -502,10 +472,12 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
} else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) { } else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) {
xmppConnectionService.expireOldMessages(true); xmppConnectionService.expireOldMessages(true);
} else if (name.equals(THEME)) { } else if (name.equals(THEME)) {
final int theme = findTheme(); final var value = preferences.getString(THEME,getString(R.string.theme));
if (this.mTheme != theme) { final int desiredNightMode = Conversations.getDesiredNightMode(value);
recreate(); setDesiredNightMode(desiredNightMode);
} } else if (name.equals("dynamic_colors")) {
final var value = preferences.getBoolean("dynamic_colors",false);
setDynamicColors(value);
} else if (name.equals(PREVENT_SCREENSHOTS)) { } else if (name.equals(PREVENT_SCREENSHOTS)) {
SettingsUtils.applyScreenshotPreventionSetting(this); SettingsUtils.applyScreenshotPreventionSetting(this);
} else if (UnifiedPushDistributor.PREFERENCES.contains(name)) { } else if (UnifiedPushDistributor.PREFERENCES.contains(name)) {
@ -572,7 +544,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
private void createBackup() { private void createBackup() {
ContextCompat.startForegroundService(this, new Intent(this, ExportBackupService.class)); ContextCompat.startForegroundService(this, new Intent(this, ExportBackupService.class));
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setMessage(R.string.backup_started_message); builder.setMessage(R.string.backup_started_message);
builder.setPositiveButton(R.string.ok, null); builder.setPositiveButton(R.string.ok, null);
builder.create().show(); builder.create().show();

View file

@ -5,7 +5,6 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.location.Location; import android.location.Location;
import android.location.LocationListener; import android.location.LocationListener;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -15,11 +14,6 @@ import androidx.databinding.DataBindingUtil;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.common.math.DoubleMath; import com.google.common.math.DoubleMath;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.util.GeoPoint;
import java.math.RoundingMode;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityShareLocationBinding; import eu.siacs.conversations.databinding.ActivityShareLocationBinding;
@ -27,7 +21,11 @@ import eu.siacs.conversations.ui.util.LocationHelper;
import eu.siacs.conversations.ui.widget.Marker; import eu.siacs.conversations.ui.widget.Marker;
import eu.siacs.conversations.ui.widget.MyLocation; import eu.siacs.conversations.ui.widget.MyLocation;
import eu.siacs.conversations.utils.LocationProvider; import eu.siacs.conversations.utils.LocationProvider;
import eu.siacs.conversations.utils.ThemeHelper;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.util.GeoPoint;
import java.math.RoundingMode;
public class ShareLocationActivity extends LocationActivity implements LocationListener { public class ShareLocationActivity extends LocationActivity implements LocationListener {
@ -58,6 +56,7 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_share_location); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_share_location);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
setupMapView(binding.map, LocationProvider.getGeoPoint(this)); setupMapView(binding.map, LocationProvider.getGeoPoint(this));
@ -71,13 +70,12 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
this.snackBar.setAction(R.string.enable, view -> { this.snackBar.setAction(R.string.enable, view -> {
if (isLocationEnabledAndAllowed()) { if (isLocationEnabledAndAllowed()) {
updateUi(); updateUi();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) { } else if (!hasLocationPermissions()) {
requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED); requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
} else if (!isLocationEnabled()) { } else if (!isLocationEnabled()) {
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
} }
}); });
ThemeHelper.fix(this.snackBar);
this.binding.shareButton.setOnClickListener(this::shareLocation); this.binding.shareButton.setOnClickListener(this::shareLocation);
@ -87,7 +85,7 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
if (!marker_fixed_to_loc) { if (!marker_fixed_to_loc) {
if (!isLocationEnabled()) { if (!isLocationEnabled()) {
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { } else {
requestPermissions(REQUEST_CODE_FAB_PRESSED); requestPermissions(REQUEST_CODE_FAB_PRESSED);
} }
} }
@ -117,16 +115,9 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
@NonNull final int[] grantResults) { @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED && permissions.length > 0 && (
grantResults[0] != PackageManager.PERMISSION_GRANTED && Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) || Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) || Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
Build.VERSION.SDK_INT >= 23 && ) && !shouldShowRequestPermissionRationale(permissions[0])) {
permissions.length > 0 &&
(
Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
) &&
!shouldShowRequestPermissionRationale(permissions[0])) {
noAskAgain = true; noAskAgain = true;
} }
@ -172,7 +163,7 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
} }
@Override @Override
public void onLocationChanged(final Location location) { public void onLocationChanged(@NonNull final Location location) {
if (this.myLoc == null) { if (this.myLoc == null) {
this.marker_fixed_to_loc = true; this.marker_fixed_to_loc = true;
} }
@ -206,7 +197,7 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
} }
private boolean isLocationEnabledAndAllowed() { private boolean isLocationEnabledAndAllowed() {
return this.hasLocationFeature && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || this.hasLocationPermissions()) && this.isLocationEnabled(); return this.hasLocationFeature && this.hasLocationPermissions() && this.isLocationEnabled();
} }
private void toggleFixedLocation() { private void toggleFixedLocation() {
@ -229,8 +220,8 @@ public class ShareLocationActivity extends LocationActivity implements LocationL
if (isLocationEnabledAndAllowed()) { if (isLocationEnabledAndAllowed()) {
this.binding.fab.setVisibility(View.VISIBLE); this.binding.fab.setVisibility(View.VISIBLE);
runOnUiThread(() -> { runOnUiThread(() -> {
this.binding.fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp : this.binding.fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_24dp :
R.drawable.ic_gps_not_fixed_white_24dp); R.drawable.ic_gps_not_fixed_24dp);
this.binding.fab.setContentDescription(getResources().getString( this.binding.fab.setContentDescription(getResources().getString(
marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
)); ));

View file

@ -9,21 +9,24 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityShareWithBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate { import java.util.ArrayList;
import java.util.List;
public class ShareWithActivity extends XmppActivity
implements XmppConnectionService.OnConversationUpdate {
private static final int REQUEST_STORAGE_PERMISSION = 0x733f32; private static final int REQUEST_STORAGE_PERMISSION = 0x733f32;
private Conversation mPendingConversation = null; private Conversation mPendingConversation = null;
@ -48,11 +51,10 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
private ConversationAdapter mAdapter; private ConversationAdapter mAdapter;
private final List<Conversation> mConversations = new ArrayList<>(); private final List<Conversation> mConversations = new ArrayList<>();
protected void onActivityResult(
protected void onActivityResult(int requestCode, int resultCode, final Intent data) { final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_START_NEW_CONVERSATION if (requestCode == REQUEST_START_NEW_CONVERSATION && resultCode == RESULT_OK) {
&& resultCode == RESULT_OK) {
share.contact = data.getStringExtra("contact"); share.contact = data.getStringExtra("contact");
share.account = data.getStringExtra(EXTRA_ACCOUNT); share.account = data.getStringExtra(EXTRA_ACCOUNT);
} }
@ -65,7 +67,10 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(
final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0) if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@ -77,27 +82,35 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} }
} }
} else { } else {
Toast.makeText(this, getString(R.string.no_storage_permission, getString(R.string.app_name)), Toast.LENGTH_SHORT).show(); Toast.makeText(
this,
getString(
R.string.no_storage_permission,
getString(R.string.app_name)),
Toast.LENGTH_SHORT)
.show();
} }
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_with);
setSupportActionBar(findViewById(R.id.toolbar)); final ActivityShareWithBinding binding =
if (getSupportActionBar() != null) { DataBindingUtil.setContentView(this, R.layout.activity_share_with);
getSupportActionBar().setDisplayHomeAsUpEnabled(false); setSupportActionBar(binding.toolbar);
getSupportActionBar().setHomeButtonEnabled(false); final var actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
} }
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setTitle(R.string.title_activity_share_with);
setTitle(getString(R.string.title_activity_sharewith));
RecyclerView mListView = findViewById(R.id.choose_conversation_list);
mAdapter = new ConversationAdapter(this, this.mConversations); mAdapter = new ConversationAdapter(this, this.mConversations);
mListView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); binding.chooseConversationList.setLayoutManager(
mListView.setAdapter(mAdapter); new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
binding.chooseConversationList.setAdapter(mAdapter);
mAdapter.setConversationClickListener((view, conversation) -> share(conversation)); mAdapter.setConversationClickListener((view, conversation) -> share(conversation));
this.share = new Share(); this.share = new Share();
} }
@ -112,8 +125,9 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_add: case R.id.action_add:
final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class); final Intent intent =
intent.putExtra("direct_search",true); new Intent(getApplicationContext(), ChooseContactActivity.class);
intent.putExtra("direct_search", true);
startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION); startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
return true; return true;
} }
@ -133,7 +147,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
if (Intent.ACTION_SEND.equals(action)) { if (Intent.ACTION_SEND.equals(action)) {
final String text = intent.getStringExtra(Intent.EXTRA_TEXT); final String text = intent.getStringExtra(Intent.EXTRA_TEXT);
final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
final boolean asQuote = intent.getBooleanExtra(ConversationsActivity.EXTRA_AS_QUOTE, false); final boolean asQuote =
intent.getBooleanExtra(ConversationsActivity.EXTRA_AS_QUOTE, false);
if (data != null && "geo".equals(data.getScheme())) { if (data != null && "geo".equals(data.getScheme())) {
this.share.uris.clear(); this.share.uris.clear();
@ -151,14 +166,16 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
this.share.uris = uris == null ? new ArrayList<>() : uris; this.share.uris = uris == null ? new ArrayList<>() : uris;
} }
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0, false); xmppConnectionService.populateWithOrderedConversations(
mConversations, this.share.uris.isEmpty(), false);
} }
} }
@Override @Override
void onBackendConnected() { void onBackendConnected() {
if (xmppConnectionServiceBound && share != null && ((share.contact != null && share.account != null))) { if (xmppConnectionServiceBound
&& share != null
&& ((share.contact != null && share.account != null))) {
share(); share();
return; return;
} }
@ -167,32 +184,34 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
private void share() { private void share() {
final Conversation conversation; final Conversation conversation;
Account account; Account account;
try { try {
account = xmppConnectionService.findAccountByJid(Jid.ofEscaped(share.account)); account = xmppConnectionService.findAccountByJid(Jid.ofEscaped(share.account));
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
account = null; account = null;
} }
if (account == null) { if (account == null) {
return; return;
} }
try { try {
conversation = xmppConnectionService.findOrCreateConversation(account, Jid.of(share.contact), false, true); conversation =
} catch (final IllegalArgumentException e) { xmppConnectionService.findOrCreateConversation(
return; account, Jid.of(share.contact), false, true);
} } catch (final IllegalArgumentException e) {
return;
}
share(conversation); share(conversation);
} }
private void share(final Conversation conversation) { private void share(final Conversation conversation) {
if (share.uris.size() != 0 && !hasStoragePermission(REQUEST_STORAGE_PERMISSION)) { if (!share.uris.isEmpty() && !hasStoragePermission(REQUEST_STORAGE_PERMISSION)) {
mPendingConversation = conversation; mPendingConversation = conversation;
return; return;
} }
Intent intent = new Intent(this, ConversationsActivity.class); final Intent intent = new Intent(this, ConversationsActivity.class);
intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid()); intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid());
if (share.uris.size() > 0) { if (!share.uris.isEmpty()) {
intent.setAction(Intent.ACTION_SEND_MULTIPLE); intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris); intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@ -207,15 +226,20 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
try { try {
startActivity(intent); startActivity(intent);
} catch (SecurityException e) { } catch (SecurityException e) {
Toast.makeText(this, R.string.sharing_application_not_grant_permission, Toast.LENGTH_SHORT).show(); Toast.makeText(
this,
R.string.sharing_application_not_grant_permission,
Toast.LENGTH_SHORT)
.show();
return; return;
} }
finish(); finish();
} }
public void refreshUiReal() { public void refreshUiReal() {
//TODO inject desired order to not resort on refresh // TODO inject desired order to not resort on refresh
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share != null && this.share.uris.size() == 0, false); xmppConnectionService.populateWithOrderedConversations(
mConversations, this.share != null && this.share.uris.isEmpty(), false);
mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged();
} }
} }

View file

@ -45,7 +45,7 @@ public class ShortcutActivity extends AbstractSearchableListItemActivity {
} }
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
ActionBar bar = getSupportActionBar(); ActionBar bar = getSupportActionBar();
if(bar != null){ if(bar != null){

View file

@ -49,6 +49,8 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_show_location); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_show_location);
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
setupMapView(this.binding.map, this.loc); setupMapView(this.binding.map, this.loc);

View file

@ -6,7 +6,6 @@ import android.app.Dialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -55,7 +54,10 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import com.google.common.collect.Iterables;
import com.leinardi.android.speeddial.SpeedDialActionItem; import com.leinardi.android.speeddial.SpeedDialActionItem;
import com.leinardi.android.speeddial.SpeedDialView; import com.leinardi.android.speeddial.SpeedDialView;
@ -109,7 +111,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
private ListItemAdapter mContactsAdapter; private ListItemAdapter mContactsAdapter;
private final List<ListItem> conferences = new ArrayList<>(); private final List<ListItem> conferences = new ArrayList<>();
private ListItemAdapter mConferenceAdapter; private ListItemAdapter mConferenceAdapter;
private final List<String> mActivatedAccounts = new ArrayList<>(); private final ArrayList<String> mActivatedAccounts = new ArrayList<>();
private EditText mSearchEditText; private EditText mSearchEditText;
private final AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false); private final AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false);
private final AtomicBoolean mOpenedFab = new AtomicBoolean(false); private final AtomicBoolean mOpenedFab = new AtomicBoolean(false);
@ -220,19 +222,20 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
}; };
public static void populateAccountSpinner(Context context, List<String> accounts, Spinner spinner) { public static void populateAccountSpinner(final Context context, final List<String> accounts, final AutoCompleteTextView spinner) {
if (accounts.size() > 0) { if (accounts.isEmpty()) {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.simple_list_item, accounts);
adapter.setDropDownViewResource(R.layout.simple_list_item);
spinner.setAdapter(adapter);
spinner.setEnabled(true);
} else {
ArrayAdapter<String> adapter = new ArrayAdapter<>(context, ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
R.layout.simple_list_item, R.layout.item_autocomplete,
Collections.singletonList(context.getString(R.string.no_accounts))); Collections.singletonList(context.getString(R.string.no_accounts)));
adapter.setDropDownViewResource(R.layout.simple_list_item); adapter.setDropDownViewResource(R.layout.item_autocomplete);
spinner.setAdapter(adapter); spinner.setAdapter(adapter);
spinner.setEnabled(false); spinner.setEnabled(false);
} else {
final ArrayAdapter<String> adapter = new ArrayAdapter<>(context, R.layout.item_autocomplete, accounts);
adapter.setDropDownViewResource(R.layout.item_autocomplete);
spinner.setAdapter(adapter);
spinner.setEnabled(true);
spinner.setText(Iterables.getFirst(accounts,null),false);
} }
} }
@ -273,6 +276,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_start_conversation); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_start_conversation);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar()); configureActionBar(getSupportActionBar());
@ -363,7 +367,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
final SpeedDialActionItem actionItem = new SpeedDialActionItem.Builder(menuItem.getItemId(), menuItem.getIcon()) final SpeedDialActionItem actionItem = new SpeedDialActionItem.Builder(menuItem.getItemId(), menuItem.getIcon())
.setLabel(menuItem.getTitle() != null ? menuItem.getTitle().toString() : null) .setLabel(menuItem.getTitle() != null ? menuItem.getTitle().toString() : null)
.setFabImageTintColor(ContextCompat.getColor(this, R.color.white)) .setFabImageTintColor(MaterialColors.getColor(speedDialView, com.google.android.material.R.attr.colorOnSurface))
.setFabBackgroundColor(MaterialColors.getColor(speedDialView, com.google.android.material.R.attr.colorSurfaceContainerHighest))
.create(); .create();
speedDialView.addActionItem(actionItem); speedDialView.addActionItem(actionItem);
} }
@ -394,13 +399,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
final int theme = findTheme(); if (pendingViewIntent.peek() == null) {
if (this.mTheme != theme) { askForContactsPermissions();
recreate();
} else {
if (pendingViewIntent.peek() == null) {
askForContactsPermissions();
}
} }
mConferenceAdapter.refreshSettings(); mConferenceAdapter.refreshSettings();
mContactsAdapter.refreshSettings(); mContactsAdapter.refreshSettings();
@ -490,7 +490,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
protected void deleteContact() { protected void deleteContact() {
final int position = contact_context_id; final int position = contact_context_id;
final Contact contact = (Contact) contacts.get(position); final Contact contact = (Contact) contacts.get(position);
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact); builder.setTitle(R.string.action_delete_contact);
builder.setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString())); builder.setMessage(JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toEscapedString()));
@ -506,7 +506,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
final Bookmark bookmark = (Bookmark) conferences.get(position); final Bookmark bookmark = (Bookmark) conferences.get(position);
final var conversation = bookmark.getConversation(); final var conversation = bookmark.getConversation();
final boolean hasConversation = conversation != null; final boolean hasConversation = conversation != null;
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_bookmark); builder.setTitle(R.string.delete_bookmark);
if (hasConversation) { if (hasConversation) {
@ -611,18 +611,14 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
dialog.show(ft, FRAGMENT_TAG_DIALOG); dialog.show(ft, FRAGMENT_TAG_DIALOG);
} }
public static Account getSelectedAccount(Context context, Spinner spinner) { public static Account getSelectedAccount(final Context context, final AutoCompleteTextView spinner) {
if (spinner == null || !spinner.isEnabled()) { if (spinner == null || !spinner.isEnabled()) {
return null; return null;
} }
if (context instanceof XmppActivity) { if (context instanceof XmppActivity) {
Jid jid; final Jid jid;
try { try {
if (Config.DOMAIN_LOCK != null) { jid = Jid.ofEscaped(spinner.getText().toString());
jid = Jid.ofEscaped((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null);
} else {
jid = Jid.ofEscaped((String) spinner.getSelectedItem());
}
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
return null; return null;
} }
@ -792,7 +788,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
if (requiresConsent if (requiresConsent
|| shouldShowRequestPermissionRationale( || shouldShowRequestPermissionRationale(
Manifest.permission.READ_CONTACTS)) { Manifest.permission.READ_CONTACTS)) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
final AtomicBoolean requestPermission = new AtomicBoolean(false); final AtomicBoolean requestPermission = new AtomicBoolean(false);
if (QuickConversationsService.isQuicksy()) { if (QuickConversationsService.isQuicksy()) {
builder.setTitle(R.string.quicksy_wants_your_consent); builder.setTitle(R.string.quicksy_wants_your_consent);
@ -1007,7 +1003,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
private void displayVerificationWarningDialog(final Contact contact, final Invite invite) { private void displayVerificationWarningDialog(final Contact contact, final Invite invite) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.verify_omemo_keys); builder.setTitle(R.string.verify_omemo_keys);
View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null); View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null);
final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source); final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source);
@ -1104,7 +1100,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
@Override @Override
public void onCreateDialogPositiveClick(Spinner spinner, String name) { public void onCreateDialogPositiveClick(AutoCompleteTextView spinner, String name) {
if (!xmppConnectionServiceBound) { if (!xmppConnectionServiceBound) {
return; return;
} }
@ -1122,7 +1118,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
} }
@Override @Override
public void onJoinDialogPositiveClick(Dialog dialog, Spinner spinner, TextInputLayout layout, AutoCompleteTextView jid, boolean isBookmarkChecked) { public void onJoinDialogPositiveClick(Dialog dialog, AutoCompleteTextView spinner, TextInputLayout layout, AutoCompleteTextView jid, boolean isBookmarkChecked) {
if (!xmppConnectionServiceBound) { if (!xmppConnectionServiceBound) {
return; return;
} }

View file

@ -14,14 +14,7 @@ import android.widget.Toast;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import org.whispersystems.libsignal.IdentityKey; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
@ -40,6 +33,14 @@ import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import org.whispersystems.libsignal.IdentityKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated { public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
private final Map<String, Boolean> ownKeysToTrust = new HashMap<>(); private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
@ -70,12 +71,14 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_trust_keys); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_trust_keys);
Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
this.contactJids = new ArrayList<>(); this.contactJids = new ArrayList<>();
for (String jid : getIntent().getStringArrayExtra("contacts")) { final var intent = getIntent();
final String[] contacts = intent == null ? null : intent.getStringArrayExtra("contacts");
for (final String jid : (contacts == null ? new String[0] : contacts)) {
try { try {
this.contactJids.add(Jid.of(jid)); this.contactJids.add(Jid.of(jid));
} catch (IllegalArgumentException e) { } catch (final IllegalArgumentException ignored) {
e.printStackTrace();
} }
} }
@ -100,7 +103,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.trust_keys, menu); getMenuInflater().inflate(R.menu.trust_keys, menu);
MenuItem scanQrCode = menu.findItem(R.id.action_scan_qr_code); MenuItem scanQrCode = menu.findItem(R.id.action_scan_qr_code);
scanQrCode.setVisible((ownKeysToTrust.size() > 0 || foreignActuallyHasKeys()) && isCameraFeatureAvailable()); scanQrCode.setVisible((!ownKeysToTrust.isEmpty() || foreignActuallyHasKeys()) && isCameraFeatureAvailable());
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
} }
@ -191,7 +194,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
} }
); );
} }
if (fingerprints.size() == 0) { if (fingerprints.isEmpty()) {
keysCardBinding.noKeysToAccept.setVisibility(View.VISIBLE); keysCardBinding.noKeysToAccept.setVisibility(View.VISIBLE);
if (hasNoOtherTrustedKeys(jid)) { if (hasNoOtherTrustedKeys(jid)) {
if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) { if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) {
@ -254,8 +257,8 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
} }
} }
private void disableEncryptionDialog(View view) { private void disableEncryptionDialog(final View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.disable_encryption); builder.setTitle(R.string.disable_encryption);
builder.setMessage(R.string.disable_encryption_message); builder.setMessage(R.string.disable_encryption_message);
builder.setPositiveButton(R.string.disable_now, (dialog, which) -> { builder.setPositiveButton(R.string.disable_now, (dialog, which) -> {
@ -279,7 +282,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
private boolean foreignActuallyHasKeys() { private boolean foreignActuallyHasKeys() {
synchronized (this.foreignKeysToTrust) { synchronized (this.foreignKeysToTrust) {
for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) { for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
if (entry.getValue().size() > 0) { if (!entry.getValue().isEmpty()) {
return true; return true;
} }
} }
@ -305,7 +308,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
foreignKeysToTrust.clear(); foreignKeysToTrust.clear();
for (Jid jid : contactJids) { for (Jid jid : contactJids) {
Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid); Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
if (hasNoOtherTrustedKeys(jid) && ownKeysSet.size() == 0) { if (hasNoOtherTrustedKeys(jid) && ownKeysSet.isEmpty()) {
foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid)); foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
} }
Map<String, Boolean> foreignFingerprints = new HashMap<>(); Map<String, Boolean> foreignFingerprints = new HashMap<>();
@ -315,7 +318,7 @@ public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdat
foreignFingerprints.put(fingerprint, false); foreignFingerprints.put(fingerprint, false);
} }
} }
if (foreignFingerprints.size() > 0 || !acceptedTargets.contains(jid)) { if (!foreignFingerprints.isEmpty() || !acceptedTargets.contains(jid)) {
foreignKeysToTrust.put(jid, foreignFingerprints); foreignKeysToTrust.put(jid, foreignFingerprints);
} }
} }

View file

@ -42,7 +42,7 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class UriHandlerActivity extends AppCompatActivity { public class UriHandlerActivity extends BaseActivity {
public static final String ACTION_SCAN_QR_CODE = "scan_qr_code"; public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
private static final String EXTRA_ALLOW_PROVISIONING = "extra_allow_provisioning"; private static final String EXTRA_ALLOW_PROVISIONING = "extra_allow_provisioning";

View file

@ -2,7 +2,6 @@ package eu.siacs.conversations.ui;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ClipData; import android.content.ClipData;
@ -17,10 +16,10 @@ import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -50,10 +49,11 @@ import androidx.annotation.BoolRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import eu.siacs.conversations.BuildConfig; import eu.siacs.conversations.BuildConfig;
@ -80,7 +80,6 @@ import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.SignupUtils; import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated; import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
@ -106,7 +105,6 @@ public abstract class XmppActivity extends ActionBarActivity {
private boolean isCameraFeatureAvailable = false; private boolean isCameraFeatureAvailable = false;
protected int mTheme;
protected boolean mUsingEnterKey = false; protected boolean mUsingEnterKey = false;
protected boolean mUseTor = false; protected boolean mUseTor = false;
protected Toast mToast; protected Toast mToast;
@ -154,7 +152,6 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
}; };
public boolean mSkipBackgroundBinding = false;
public static boolean cancelPotentialWork(Message message, ImageView imageView) { public static boolean cancelPotentialWork(Message message, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
@ -212,14 +209,10 @@ public abstract class XmppActivity extends ActionBarActivity {
abstract protected void refreshUiReal(); abstract protected void refreshUiReal();
@Override @Override
protected void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (!xmppConnectionServiceBound) { if (!xmppConnectionServiceBound) {
if (this.mSkipBackgroundBinding) { connectToBackend();
Log.d(Config.LOGTAG, "skipping background binding");
} else {
connectToBackend();
}
} else { } else {
this.registerListeners(); this.registerListeners();
this.onBackendConnected(); this.onBackendConnected();
@ -255,7 +248,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
public void showInstallPgpDialog() { public void showInstallPgpDialog() {
Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(getString(R.string.openkeychain_required)); builder.setTitle(getString(R.string.openkeychain_required));
builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setMessage(Html.fromHtml(getString(R.string.openkeychain_required_long, getString(R.string.app_name)))); builder.setMessage(Html.fromHtml(getString(R.string.openkeychain_required_long, getString(R.string.app_name))));
@ -298,7 +291,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
protected void deleteAccount(final Account account, final Runnable postDelete) { protected void deleteAccount(final Account account, final Runnable postDelete) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
final View dialogView = getLayoutInflater().inflate(R.layout.dialog_delete_account, null); final View dialogView = getLayoutInflater().inflate(R.layout.dialog_delete_account, null);
final CheckBox deleteFromServer = final CheckBox deleteFromServer =
dialogView.findViewById(R.id.delete_from_server); dialogView.findViewById(R.id.delete_from_server);
@ -495,28 +488,12 @@ public abstract class XmppActivity extends ActionBarActivity {
ExceptionHelper.init(getApplicationContext()); ExceptionHelper.init(getApplicationContext());
EmojiInitializationService.execute(this); EmojiInitializationService.execute(this);
this.isCameraFeatureAvailable = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); this.isCameraFeatureAvailable = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
this.mTheme = findTheme();
setTheme(this.mTheme);
} }
protected boolean isCameraFeatureAvailable() { protected boolean isCameraFeatureAvailable() {
return this.isCameraFeatureAvailable; return this.isCameraFeatureAvailable;
} }
public boolean isDarkTheme() {
return ThemeHelper.isDark(mTheme);
}
public int getThemeResource(int r_attr_name, int r_drawable_def) {
int[] attrs = {r_attr_name};
TypedArray ta = this.getTheme().obtainStyledAttributes(attrs);
int res = ta.getResourceId(0, r_drawable_def);
ta.recycle();
return res;
}
protected boolean isOptimizingBattery() { protected boolean isOptimizingBattery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); final PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
@ -698,21 +675,10 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
} }
@SuppressWarnings("deprecation") protected void choosePgpSignId(final Account account) {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<>() {
protected void setListItemBackgroundOnView(View view) {
int sdk = android.os.Build.VERSION.SDK_INT;
if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
} else {
view.setBackground(getResources().getDrawable(R.drawable.greybackground));
}
}
protected void choosePgpSignId(Account account) {
xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
@Override @Override
public void success(Account account1) { public void success(final Account a) {
} }
@Override @Override
@ -733,8 +699,7 @@ public abstract class XmppActivity extends ActionBarActivity {
protected void displayErrorDialog(final int errorCode) { protected void displayErrorDialog(final int errorCode) {
runOnUiThread(() -> { runOnUiThread(() -> {
Builder builder = new Builder(XmppActivity.this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(XmppActivity.this);
builder.setIconAttribute(android.R.attr.alertDialogIcon);
builder.setTitle(getString(R.string.error)); builder.setTitle(getString(R.string.error));
builder.setMessage(errorCode); builder.setMessage(errorCode);
builder.setNeutralButton(R.string.accept, null); builder.setNeutralButton(R.string.accept, null);
@ -744,7 +709,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
protected void showAddToRosterDialog(final Contact contact) { protected void showAddToRosterDialog(final Contact contact) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(contact.getJid().toString()); builder.setTitle(contact.getJid().toString());
builder.setMessage(getString(R.string.not_in_roster)); builder.setMessage(getString(R.string.not_in_roster));
builder.setNegativeButton(getString(R.string.cancel), null); builder.setNegativeButton(getString(R.string.cancel), null);
@ -753,7 +718,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
private void showAskForPresenceDialog(final Contact contact) { private void showAskForPresenceDialog(final Contact contact) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(contact.getJid().toString()); builder.setTitle(contact.getJid().toString());
builder.setMessage(R.string.request_presence_updates); builder.setMessage(R.string.request_presence_updates);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
@ -787,8 +752,8 @@ public abstract class XmppActivity extends ActionBarActivity {
final @StringRes int hint, final @StringRes int hint,
boolean password, boolean password,
boolean permitEmpty) { boolean permitEmpty) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
DialogQuickeditBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_quickedit, null, false); final DialogQuickeditBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.dialog_quickedit, null, false);
if (password) { if (password) {
binding.inputEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); binding.inputEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
} }
@ -829,7 +794,7 @@ public abstract class XmppActivity extends ActionBarActivity {
} }
protected boolean hasStoragePermission(int requestCode) { protected boolean hasStoragePermission(int requestCode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode); requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
return false; return false;
@ -911,10 +876,6 @@ public abstract class XmppActivity extends ActionBarActivity {
SettingsUtils.applyScreenshotPreventionSetting(this); SettingsUtils.applyScreenshotPreventionSetting(this);
} }
protected int findTheme() {
return ThemeHelper.find(this);
}
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
@ -936,14 +897,26 @@ public abstract class XmppActivity extends ActionBarActivity {
if (uri == null || uri.isEmpty()) { if (uri == null || uri.isEmpty()) {
return; return;
} }
Point size = new Point(); final Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size); getWindowManager().getDefaultDisplay().getSize(size);
final int width = (size.x < size.y ? size.x : size.y); final int width = Math.min(size.x, size.y);
Bitmap bitmap = BarcodeProvider.create2dBarcodeBitmap(uri, width); final boolean nightMode = (this.getResources().getConfiguration().uiMode
ImageView view = new ImageView(this); & Configuration.UI_MODE_NIGHT_MASK)
view.setBackgroundColor(Color.WHITE); == Configuration.UI_MODE_NIGHT_YES;
final int black;
final int white;
if (nightMode) {
black = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceContainerHighest,"No surface color configured");
white = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceInverse,"No inverse surface color configured");
} else {
black = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceInverse,"No inverse surface color configured");
white = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceContainerHighest,"No surface color configured");
}
final var bitmap = BarcodeProvider.create2dBarcodeBitmap(uri, width, black, white);
final ImageView view = new ImageView(this);
view.setBackgroundColor(white);
view.setImageBitmap(bitmap); view.setImageBitmap(bitmap);
AlertDialog.Builder builder = new AlertDialog.Builder(this); MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setView(view); builder.setView(view);
builder.create().show(); builder.create().show();
} }

View file

@ -8,15 +8,16 @@ import android.widget.ArrayAdapter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import java.util.List; import com.google.android.material.color.MaterialColors;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.AccountRowBinding; import eu.siacs.conversations.databinding.ItemAccountBinding;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
import java.util.List;
public class AccountAdapter extends ArrayAdapter<Account> { public class AccountAdapter extends ArrayAdapter<Account> {
@ -35,36 +36,33 @@ public class AccountAdapter extends ArrayAdapter<Account> {
this.showStateButton = true; this.showStateButton = true;
} }
@NonNull
@Override @Override
public View getView(int position, View view, @NonNull ViewGroup parent) { public View getView(int position, View view, @NonNull ViewGroup parent) {
final Account account = getItem(position); final Account account = getItem(position);
final ViewHolder viewHolder; final ViewHolder viewHolder;
if (view == null) { if (view == null) {
AccountRowBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.account_row, parent, false); ItemAccountBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_account, parent, false);
view = binding.getRoot(); view = binding.getRoot();
viewHolder = new ViewHolder(binding); viewHolder = new ViewHolder(binding);
view.setTag(viewHolder); view.setTag(viewHolder);
} else { } else {
viewHolder = (ViewHolder) view.getTag(); viewHolder = (ViewHolder) view.getTag();
} }
if (Config.DOMAIN_LOCK != null) { viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toEscapedString());
viewHolder.binding.accountJid.setText(account.getJid().getLocal());
} else {
viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toEscapedString());
}
AvatarWorkerTask.loadAvatar(account, viewHolder.binding.accountImage, R.dimen.avatar); AvatarWorkerTask.loadAvatar(account, viewHolder.binding.accountImage, R.dimen.avatar);
viewHolder.binding.accountStatus.setText(getContext().getString(account.getStatus().getReadableId())); viewHolder.binding.accountStatus.setText(getContext().getString(account.getStatus().getReadableId()));
switch (account.getStatus()) { switch (account.getStatus()) {
case ONLINE: case ONLINE:
viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline)); viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorPrimary));
break; break;
case DISABLED: case DISABLED:
case LOGGED_OUT: case LOGGED_OUT:
case CONNECTING: case CONNECTING:
viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary)); viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorOnSurfaceVariant));
break; break;
default: default:
viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError)); viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorError));
break; break;
} }
final boolean isDisabled = (account.getStatus() == Account.State.DISABLED); final boolean isDisabled = (account.getStatus() == Account.State.DISABLED);
@ -84,9 +82,9 @@ public class AccountAdapter extends ArrayAdapter<Account> {
} }
private static class ViewHolder { private static class ViewHolder {
private final AccountRowBinding binding; private final ItemAccountBinding binding;
private ViewHolder(AccountRowBinding binding) { private ViewHolder(ItemAccountBinding binding) {
this.binding = binding; this.binding = binding;
} }
} }

View file

@ -13,18 +13,18 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.Locale;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.SearchResultItemBinding; import eu.siacs.conversations.databinding.ItemChannelDiscoveryBinding;
import eu.siacs.conversations.entities.Room; import eu.siacs.conversations.entities.Room;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import java.util.Locale;
public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchResultAdapter.ViewHolder> implements View.OnCreateContextMenuListener { public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchResultAdapter.ViewHolder> implements View.OnCreateContextMenuListener {
private static final DiffUtil.ItemCallback<Room> DIFF = new DiffUtil.ItemCallback<Room>() { private static final DiffUtil.ItemCallback<Room> DIFF = new DiffUtil.ItemCallback<>() {
@Override @Override
public boolean areItemsTheSame(@NonNull Room a, @NonNull Room b) { public boolean areItemsTheSame(@NonNull Room a, @NonNull Room b) {
return a.address != null && a.address.equals(b.address); return a.address != null && a.address.equals(b.address);
@ -45,7 +45,7 @@ public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchR
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.search_result_item, viewGroup, false)); return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_channel_discovery, viewGroup, false));
} }
@Override @Override
@ -99,9 +99,9 @@ public class ChannelSearchResultAdapter extends ListAdapter<Room, ChannelSearchR
public static class ViewHolder extends RecyclerView.ViewHolder { public static class ViewHolder extends RecyclerView.ViewHolder {
private final SearchResultItemBinding binding; private final ItemChannelDiscoveryBinding binding;
private ViewHolder(SearchResultItemBinding binding) { private ViewHolder(final ItemChannelDiscoveryBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }

View file

@ -6,30 +6,30 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.color.MaterialColors;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Strings;
import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ConversationListRowBinding; import eu.siacs.conversations.databinding.ItemConversationBinding;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationFragment; import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.IrregularUnicodeDetector; import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession; import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import java.util.List;
public class ConversationAdapter public class ConversationAdapter
extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> { extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
@ -48,7 +48,7 @@ public class ConversationAdapter
return new ConversationViewHolder( return new ConversationViewHolder(
DataBindingUtil.inflate( DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), LayoutInflater.from(parent.getContext()),
R.layout.conversation_list_row, R.layout.item_conversation,
parent, parent,
false)); false));
} }
@ -68,14 +68,13 @@ public class ConversationAdapter
} }
if (conversation == ConversationFragment.getConversation(activity)) { if (conversation == ConversationFragment.getConversation(activity)) {
viewHolder.binding.frame.setBackgroundColor( viewHolder.binding.frame.setBackgroundResource(R.drawable.background_selected_item_conversation);
StyledAttributes.getColor(activity, R.attr.color_background_tertiary)); //viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurfaceDim));
} else { } else {
viewHolder.binding.frame.setBackgroundColor( viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurface));
StyledAttributes.getColor(activity, R.attr.color_background_primary));
} }
Message message = conversation.getLatestMessage(); final Message message = conversation.getLatestMessage();
final int unreadCount = conversation.unreadCount(); final int unreadCount = conversation.unreadCount();
final boolean isRead = conversation.isRead(); final boolean isRead = conversation.isRead();
final Conversation.Draft draft = isRead ? conversation.getDraft() : null; final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
@ -106,68 +105,9 @@ public class ConversationAdapter
&& (message.isFileOrImage() && (message.isFileOrImage()
|| message.treatAsDownloadable() || message.treatAsDownloadable()
|| message.isGeoUri())) { || message.isGeoUri())) {
final int imageResource; final var attachment = Attachment.of(message);
if (message.isGeoUri()) { final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
imageResource = showPreviewText = false;
activity.getThemeResource(
R.attr.ic_attach_location, R.drawable.ic_attach_location);
showPreviewText = false;
} else {
// TODO move this into static MediaPreview method and use same icons as in
// MediaAdapter
final String mime = message.getMimeType();
if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
final Message.FileParams fileParams = message.getFileParams();
if (fileParams.width > 0 && fileParams.height > 0) {
imageResource =
activity.getThemeResource(
R.attr.ic_attach_videocam,
R.drawable.ic_attach_videocam);
showPreviewText = false;
} else if (fileParams.runtime > 0) {
imageResource =
activity.getThemeResource(
R.attr.ic_attach_record, R.drawable.ic_attach_record);
showPreviewText = false;
} else {
imageResource =
activity.getThemeResource(
R.attr.ic_attach_document,
R.drawable.ic_attach_document);
showPreviewText = true;
}
} else {
switch (Strings.nullToEmpty(mime).split("/")[0]) {
case "image":
imageResource =
activity.getThemeResource(
R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
showPreviewText = false;
break;
case "video":
imageResource =
activity.getThemeResource(
R.attr.ic_attach_videocam,
R.drawable.ic_attach_videocam);
showPreviewText = false;
break;
case "audio":
imageResource =
activity.getThemeResource(
R.attr.ic_attach_record,
R.drawable.ic_attach_record);
showPreviewText = false;
break;
default:
imageResource =
activity.getThemeResource(
R.attr.ic_attach_document,
R.drawable.ic_attach_document);
showPreviewText = true;
break;
}
}
}
viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource); viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE); viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
} else { } else {
@ -231,36 +171,21 @@ public class ConversationAdapter
if (ongoingCall.isPresent()) { if (ongoingCall.isPresent()) {
viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
final int ic_ongoing_call = viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_phone_in_talk_24dp);
activity.getThemeResource(
R.attr.ic_ongoing_call_hint, R.drawable.ic_phone_in_talk_black_18dp);
viewHolder.binding.notificationStatus.setImageResource(ic_ongoing_call);
} else { } else {
final long muted_till = final long muted_till =
conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0); conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
if (muted_till == Long.MAX_VALUE) { if (muted_till == Long.MAX_VALUE) {
viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
int ic_notifications_off = viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_off_24dp);
activity.getThemeResource(
R.attr.icon_notifications_off,
R.drawable.ic_notifications_off_black_24dp);
viewHolder.binding.notificationStatus.setImageResource(ic_notifications_off);
} else if (muted_till >= System.currentTimeMillis()) { } else if (muted_till >= System.currentTimeMillis()) {
viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
int ic_notifications_paused = viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_paused_24dp);
activity.getThemeResource(
R.attr.icon_notifications_paused,
R.drawable.ic_notifications_paused_black_24dp);
viewHolder.binding.notificationStatus.setImageResource(ic_notifications_paused);
} else if (conversation.alwaysNotify()) { } else if (conversation.alwaysNotify()) {
viewHolder.binding.notificationStatus.setVisibility(View.GONE); viewHolder.binding.notificationStatus.setVisibility(View.GONE);
} else { } else {
viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
int ic_notifications_none = viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_none_24dp);
activity.getThemeResource(
R.attr.icon_notifications_none,
R.drawable.ic_notifications_none_black_24dp);
viewHolder.binding.notificationStatus.setImageResource(ic_notifications_none);
} }
} }
@ -307,9 +232,9 @@ public class ConversationAdapter
} }
static class ConversationViewHolder extends RecyclerView.ViewHolder { static class ConversationViewHolder extends RecyclerView.ViewHolder {
private final ConversationListRowBinding binding; private final ItemConversationBinding binding;
private ConversationViewHolder(ConversationListRowBinding binding) { private ConversationViewHolder(final ItemConversationBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }

View file

@ -6,32 +6,35 @@ import android.widget.Filter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.util.ArrayList; import com.google.common.collect.ImmutableList;
import java.util.Collection; import com.google.common.collect.Ordering;
import java.util.Collections;
import java.util.Locale;
import java.util.regex.Pattern;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
public class KnownHostsAdapter extends ArrayAdapter<String> { public class KnownHostsAdapter extends ArrayAdapter<String> {
private static final Pattern E164_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); private static final Pattern E164_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$");
private ArrayList<String> domains; private List<String> domains;
private final Filter domainFilter = new Filter() { private final Filter domainFilter = new Filter() {
@Override @Override
protected FilterResults performFiltering(CharSequence constraint) { protected FilterResults performFiltering(final CharSequence constraint) {
final ArrayList<String> suggestions = new ArrayList<>(); final ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
final String[] split = constraint == null ? new String[0] : constraint.toString().split("@"); final String[] split = constraint == null ? new String[0] : constraint.toString().split("@");
if (split.length == 1) { if (split.length == 1) {
final String local = split[0].toLowerCase(Locale.ENGLISH); final String local = split[0].toLowerCase(Locale.ENGLISH);
if (Config.QUICKSY_DOMAIN != null && E164_PATTERN.matcher(local).matches()) { if (Config.QUICKSY_DOMAIN != null && E164_PATTERN.matcher(local).matches()) {
suggestions.add(local + '@' + Config.QUICKSY_DOMAIN.toEscapedString()); builder.add(local + '@' + Config.QUICKSY_DOMAIN.toEscapedString());
} else { } else {
for (String domain : domains) { for (String domain : domains) {
suggestions.add(local + '@' + domain); builder.add(local + '@' + domain);
} }
} }
} else if (split.length == 2) { } else if (split.length == 2) {
@ -40,45 +43,49 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
if (domains.contains(domainPart)) { if (domains.contains(domainPart)) {
return new FilterResults(); return new FilterResults();
} }
for (String domain : domains) { for (final String domain : domains) {
if (domain.contains(domainPart)) { if (domain.contains(domainPart)) {
suggestions.add(localPart + "@" + domain); builder.add(localPart + "@" + domain);
} }
} }
} else { } else {
return new FilterResults(); return new FilterResults();
} }
FilterResults filterResults = new FilterResults(); final var suggestions = builder.build();
final FilterResults filterResults = new FilterResults();
filterResults.values = suggestions; filterResults.values = suggestions;
filterResults.count = suggestions.size(); filterResults.count = suggestions.size();
return filterResults; return filterResults;
} }
@Override @Override
protected void publishResults(CharSequence constraint, FilterResults results) { protected void publishResults(final CharSequence constraint, final FilterResults results) {
ArrayList filteredList = (ArrayList) results.values; final ImmutableList.Builder<String> suggestions = new ImmutableList.Builder<>();
if (results.count > 0) { if (results.values instanceof Collection<?> collection) {
clear(); for(final Object item : collection) {
addAll(filteredList); if (item instanceof String string) {
notifyDataSetChanged(); suggestions.add(string);
}
}
} }
clear();
addAll(suggestions.build());
notifyDataSetChanged();
} }
}; };
public KnownHostsAdapter(Context context, int viewResourceId, Collection<String> mKnownHosts) { public KnownHostsAdapter(final Context context, final int viewResourceId, final Collection<String> knownHosts) {
super(context, viewResourceId, new ArrayList<>()); super(context, viewResourceId, new ArrayList<>());
domains = new ArrayList<>(mKnownHosts); domains = Ordering.natural().sortedCopy(knownHosts);
Collections.sort(domains);
} }
public KnownHostsAdapter(Context context, int viewResourceId) { public KnownHostsAdapter(final Context context, final int viewResourceId) {
super(context, viewResourceId, new ArrayList<>()); super(context, viewResourceId, new ArrayList<>());
domains = new ArrayList<>(); domains = ImmutableList.of();
} }
public void refresh(Collection<String> knownHosts) { public void refresh(final Collection<String> knownHosts) {
domains = new ArrayList<>(knownHosts); this.domains = Ordering.natural().sortedCopy(knownHosts);
Collections.sort(domains);
notifyDataSetChanged(); notifyDataSetChanged();
} }

View file

@ -2,6 +2,7 @@ package eu.siacs.conversations.ui.adapter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -9,30 +10,30 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import com.wefika.flowlayout.FlowLayout; import com.wefika.flowlayout.FlowLayout;
import java.util.List; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ContactBinding; import eu.siacs.conversations.databinding.ItemContactBinding;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.IrregularUnicodeDetector; import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import java.util.List;
public class ListItemAdapter extends ArrayAdapter<ListItem> { public class ListItemAdapter extends ArrayAdapter<ListItem> {
protected XmppActivity activity; protected XmppActivity activity;
private boolean showDynamicTags = false; private boolean showDynamicTags = false;
private OnTagClickedListener mOnTagClickedListener = null; private OnTagClickedListener mOnTagClickedListener = null;
private final View.OnClickListener onTagTvClick = view -> { private final View.OnClickListener onTagTvClick = view -> {
if (view instanceof TextView && mOnTagClickedListener != null) { if (view instanceof TextView tv && mOnTagClickedListener != null) {
TextView tv = (TextView) view;
final String tag = tv.getText().toString(); final String tag = tv.getText().toString();
mOnTagClickedListener.onTagClicked(tag); mOnTagClickedListener.onTagClicked(tag);
} }
@ -49,22 +50,25 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false); this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
} }
@NonNull
@Override @Override
public View getView(int position, View view, ViewGroup parent) { public View getView(int position, View view, @NonNull ViewGroup parent) {
LayoutInflater inflater = activity.getLayoutInflater(); LayoutInflater inflater = activity.getLayoutInflater();
ListItem item = getItem(position); ListItem item = getItem(position);
ViewHolder viewHolder; ViewHolder viewHolder;
if (view == null) { if (view == null) {
ContactBinding binding = DataBindingUtil.inflate(inflater,R.layout.contact,parent,false); final ItemContactBinding binding = DataBindingUtil.inflate(inflater,R.layout.item_contact,parent,false);
viewHolder = ViewHolder.get(binding); viewHolder = ViewHolder.get(binding);
view = binding.getRoot(); view = binding.getRoot();
} else { } else {
viewHolder = (ViewHolder) view.getTag(); viewHolder = (ViewHolder) view.getTag();
} }
view.setBackground(StyledAttributes.getDrawable(view.getContext(),R.attr.list_item_background)); if (view.isActivated()) {
Log.d(Config.LOGTAG,"item "+item.getDisplayName()+" is activated");
List<ListItem.Tag> tags = item.getTags(activity); }
if (tags.size() == 0 || !this.showDynamicTags) { //view.setBackground(StyledAttributes.getDrawable(view.getContext(),R.attr.list_item_background));
final List<ListItem.Tag> tags = item.getTags(activity);
if (tags.isEmpty() || !this.showDynamicTags) {
viewHolder.tags.setVisibility(View.GONE); viewHolder.tags.setVisibility(View.GONE);
} else { } else {
viewHolder.tags.setVisibility(View.VISIBLE); viewHolder.tags.setVisibility(View.VISIBLE);
@ -108,7 +112,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
} }
public static ViewHolder get(ContactBinding binding) { public static ViewHolder get(final ItemContactBinding binding) {
ViewHolder viewHolder = new ViewHolder(); ViewHolder viewHolder = new ViewHolder();
viewHolder.name = binding.contactDisplayName; viewHolder.name = binding.contactDisplayName;
viewHolder.jid = binding.contactJid; viewHolder.jid = binding.contactJid;

View file

@ -1,47 +1,50 @@
package eu.siacs.conversations.ui.adapter; package eu.siacs.conversations.ui.adapter;
import android.content.Context; import android.content.res.ColorStateList;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.AttrRes;
import androidx.annotation.DimenRes; import androidx.annotation.DimenRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.widget.ImageViewCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.color.MaterialColors;
import com.google.common.base.Strings;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ItemMediaBinding;
import eu.siacs.conversations.services.ExportBackupService;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.ViewUtil;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.MediaBinding;
import eu.siacs.conversations.services.ExportBackupService;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.ui.util.ViewUtil;
public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHolder> { public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHolder> {
public static final List<String> DOCUMENT_MIMES = Arrays.asList( public static final List<String> DOCUMENT_MIMES =
"application/pdf", Arrays.asList(
"application/vnd.oasis.opendocument.text", "application/pdf",
"application/msword", "application/vnd.oasis.opendocument.text",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/msword",
"text/x-tex", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/plain" "text/x-tex",
); "text/plain");
public static final List<String> CODE_MIMES = Arrays.asList("text/html", "text/xml");
private final ArrayList<Attachment> attachments = new ArrayList<>(); private final ArrayList<Attachment> attachments = new ArrayList<>();
@ -55,58 +58,77 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public static void setMediaSize(RecyclerView recyclerView, int mediaSize) { public static void setMediaSize(final RecyclerView recyclerView, int mediaSize) {
final RecyclerView.Adapter adapter = recyclerView.getAdapter(); final RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter instanceof MediaAdapter) { if (adapter instanceof MediaAdapter mediaAdapter) {
((MediaAdapter) adapter).setMediaSize(mediaSize); mediaAdapter.setMediaSize(mediaSize);
} }
} }
private static @AttrRes public static @DrawableRes int getImageDrawable(final Attachment attachment) {
int getImageAttr(Attachment attachment) {
final @AttrRes int attr;
if (attachment.getType() == Attachment.Type.LOCATION) { if (attachment.getType() == Attachment.Type.LOCATION) {
attr = R.attr.media_preview_location; return R.drawable.ic_location_pin_48dp;
} else if (attachment.getType() == Attachment.Type.RECORDING) { } else if (attachment.getType() == Attachment.Type.RECORDING) {
attr = R.attr.media_preview_recording; return R.drawable.ic_mic_48dp;
} else { } else {
final String mime = attachment.getMime(); return getImageDrawable(attachment.getMime());
Log.d(Config.LOGTAG, "mime=" + mime);
if (mime == null) {
attr = R.attr.media_preview_unknown;
} else if (mime.equals("audio/x-m4b")) {
attr = R.attr.media_preview_audiobook;
} else if (mime.startsWith("audio/")) {
attr = R.attr.media_preview_audio;
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
attr = R.attr.media_preview_calendar;
} else if (mime.equals("text/x-vcard")) {
attr = R.attr.media_preview_contact;
} else if (mime.equals("application/vnd.android.package-archive")) {
attr = R.attr.media_preview_app;
} else if (mime.equals("application/zip") || mime.equals("application/rar")) {
attr = R.attr.media_preview_archive;
} else if (mime.equals("application/epub+zip") || mime.equals("application/vnd.amazon.mobi8-ebook")) {
attr = R.attr.media_preview_ebook;
} else if (mime.equals(ExportBackupService.MIME_TYPE)) {
attr = R.attr.media_preview_backup;
} else if (DOCUMENT_MIMES.contains(mime)) {
attr = R.attr.media_preview_document;
} else if (mime.equals("application/gpx+xml")) {
attr = R.attr.media_preview_tour;
} else if (mime.startsWith("image/")) {
attr = R.attr.media_preview_image;
} else {
attr = R.attr.media_preview_unknown;
}
} }
return attr;
} }
static void renderPreview(Context context, Attachment attachment, ImageView imageView) { private static @DrawableRes int getImageDrawable(final String mime) {
imageView.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
imageView.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255)); // TODO ideas for more mime types: XML, HTML documents, GPG/PGP files, eml files,
imageView.setImageDrawable(StyledAttributes.getDrawable(context, getImageAttr(attachment))); // spreadsheets (table symbol)
// add bz2 and tar.gz to archive detection
if (Strings.isNullOrEmpty(mime)) {
return R.drawable.ic_help_center_48dp;
} else if (mime.equals("audio/x-m4b")) {
return R.drawable.ic_play_lesson_48dp;
} else if (mime.startsWith("audio/")) {
return R.drawable.ic_headphones_48dp;
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
return R.drawable.ic_event_48dp;
} else if (mime.equals("text/x-vcard")) {
return R.drawable.ic_person_48dp;
} else if (mime.equals("application/vnd.android.package-archive")) {
return R.drawable.ic_adb_48dp;
} else if (mime.equals("application/zip") || mime.equals("application/rar")) {
return R.drawable.ic_archive_48dp;
} else if (mime.equals("application/epub+zip")
|| mime.equals("application/vnd.amazon.mobi8-ebook")) {
return R.drawable.ic_book_48dp;
} else if (mime.equals(ExportBackupService.MIME_TYPE)) {
return R.drawable.ic_backup_48dp;
} else if (DOCUMENT_MIMES.contains(mime)) {
return R.drawable.ic_description_48dp;
} else if (mime.equals("application/gpx+xml")) {
return R.drawable.ic_tour_48dp;
} else if (mime.startsWith("image/")) {
return R.drawable.ic_image_48dp;
} else if (mime.startsWith("video/")) {
return R.drawable.ic_movie_48dp;
} else if (CODE_MIMES.contains(mime)) {
return R.drawable.ic_code_48dp;
} else if (mime.equals("message/rfc822")) {
return R.drawable.ic_email_48dp;
} else {
return R.drawable.ic_help_center_48dp;
}
}
static void renderPreview(final Attachment attachment, final ImageView imageView) {
ImageViewCompat.setImageTintList(
imageView,
ColorStateList.valueOf(
MaterialColors.getColor(
imageView, com.google.android.material.R.attr.colorOnSurface)));
imageView.setImageResource(getImageDrawable(attachment));
imageView.setBackgroundColor(
MaterialColors.getColor(
imageView,
com.google.android.material.R.attr.colorSurfaceContainerHighest));
} }
private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) { private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) {
@ -126,8 +148,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) { if (imageView != null) {
final Drawable drawable = imageView.getDrawable(); final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) { if (drawable instanceof AsyncDrawable asyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask(); return asyncDrawable.getBitmapWorkerTask();
} }
} }
@ -137,8 +158,9 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
@NonNull @NonNull
@Override @Override
public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
MediaBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media, parent, false); ItemMediaBinding binding =
DataBindingUtil.inflate(layoutInflater, R.layout.item_media, parent, false);
return new MediaViewHolder(binding); return new MediaViewHolder(binding);
} }
@ -146,16 +168,15 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) { public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
final Attachment attachment = attachments.get(position); final Attachment attachment = attachments.get(position);
if (attachment.renderThumbnail()) { if (attachment.renderThumbnail()) {
holder.binding.media.setImageAlpha(255);
loadPreview(attachment, holder.binding.media); loadPreview(attachment, holder.binding.media);
} else { } else {
cancelPotentialWork(attachment, holder.binding.media); cancelPotentialWork(attachment, holder.binding.media);
renderPreview(activity, attachment, holder.binding.media); renderPreview(attachment, holder.binding.media);
} }
holder.binding.getRoot().setOnClickListener(v -> ViewUtil.view(activity, attachment)); holder.binding.getRoot().setOnClickListener(v -> ViewUtil.view(activity, attachment));
} }
public void setAttachments(List<Attachment> attachments) { public void setAttachments(final List<Attachment> attachments) {
this.attachments.clear(); this.attachments.clear();
this.attachments.addAll(attachments); this.attachments.addAll(attachments);
notifyDataSetChanged(); notifyDataSetChanged();
@ -167,16 +188,21 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
private void loadPreview(Attachment attachment, ImageView imageView) { private void loadPreview(Attachment attachment, ImageView imageView) {
if (cancelPotentialWork(attachment, imageView)) { if (cancelPotentialWork(attachment, imageView)) {
final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment, mediaSize, true); final Bitmap bm =
activity.xmppConnectionService
.getFileBackend()
.getPreviewForUri(attachment, mediaSize, true);
if (bm != null) { if (bm != null) {
cancelPotentialWork(attachment, imageView); cancelPotentialWork(attachment, imageView);
imageView.setImageBitmap(bm); imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000); imageView.setBackgroundColor(Color.TRANSPARENT);
} else { } else {
// TODO consider if this is still a good, general purpose loading color
imageView.setBackgroundColor(0xff333333); imageView.setBackgroundColor(0xff333333);
imageView.setImageDrawable(null); imageView.setImageDrawable(null);
final BitmapWorkerTask task = new BitmapWorkerTask(mediaSize, imageView); final BitmapWorkerTask task = new BitmapWorkerTask(mediaSize, imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task); final AsyncDrawable asyncDrawable =
new AsyncDrawable(activity.getResources(), null, task);
imageView.setImageDrawable(asyncDrawable); imageView.setImageDrawable(asyncDrawable);
try { try {
task.execute(attachment); task.execute(attachment);
@ -204,11 +230,11 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
} }
} }
class MediaViewHolder extends RecyclerView.ViewHolder { static class MediaViewHolder extends RecyclerView.ViewHolder {
private final MediaBinding binding; private final ItemMediaBinding binding;
MediaViewHolder(MediaBinding binding) { MediaViewHolder(ItemMediaBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
@ -225,13 +251,15 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
} }
@Override @Override
protected Bitmap doInBackground(Attachment... params) { protected Bitmap doInBackground(final Attachment... params) {
this.attachment = params[0]; this.attachment = params[0];
final XmppActivity activity = XmppActivity.find(imageViewReference); final XmppActivity activity = XmppActivity.find(imageViewReference);
if (activity == null) { if (activity == null) {
return null; return null;
} }
return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, mediaSize, false); return activity.xmppConnectionService
.getFileBackend()
.getPreviewForUri(this.attachment, mediaSize, false);
} }
@Override @Override

View file

@ -15,22 +15,25 @@ import android.widget.ImageView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.widget.ImageViewCompat;
import androidx.databinding.DataBindingUtil; import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ItemMediaPreviewBinding;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import eu.siacs.conversations.R; public class MediaPreviewAdapter
import eu.siacs.conversations.databinding.MediaPreviewBinding; extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.Attachment;
public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
private final ArrayList<Attachment> mediaPreviews = new ArrayList<>(); private final ArrayList<Attachment> mediaPreviews = new ArrayList<>();
@ -43,8 +46,9 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
@NonNull @NonNull
@Override @Override
public MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
MediaPreviewBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media_preview, parent, false); ItemMediaPreviewBinding binding =
DataBindingUtil.inflate(layoutInflater, R.layout.item_media_preview, parent, false);
return new MediaPreviewViewHolder(binding); return new MediaPreviewViewHolder(binding);
} }
@ -53,18 +57,19 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
final Context context = conversationFragment.getActivity(); final Context context = conversationFragment.getActivity();
final Attachment attachment = mediaPreviews.get(position); final Attachment attachment = mediaPreviews.get(position);
if (attachment.renderThumbnail()) { if (attachment.renderThumbnail()) {
holder.binding.mediaPreview.setImageAlpha(255); ImageViewCompat.setImageTintList(holder.binding.mediaPreview, null);
loadPreview(attachment, holder.binding.mediaPreview); loadPreview(attachment, holder.binding.mediaPreview);
} else { } else {
cancelPotentialWork(attachment, holder.binding.mediaPreview); cancelPotentialWork(attachment, holder.binding.mediaPreview);
MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview); MediaAdapter.renderPreview(attachment, holder.binding.mediaPreview);
} }
holder.binding.deleteButton.setOnClickListener(v -> { holder.binding.deleteButton.setOnClickListener(
final int pos = mediaPreviews.indexOf(attachment); v -> {
mediaPreviews.remove(pos); final int pos = mediaPreviews.indexOf(attachment);
notifyItemRemoved(pos); mediaPreviews.remove(pos);
conversationFragment.toggleInputMethod(); notifyItemRemoved(pos);
}); conversationFragment.toggleInputMethod();
});
holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment)); holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment));
} }
@ -76,9 +81,14 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
try { try {
context.startActivity(view); context.startActivity(view);
} catch (final ActivityNotFoundException e) { } catch (final ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT)
.show();
} catch (final SecurityException e) { } catch (final SecurityException e) {
Toast.makeText(context, R.string.sharing_application_not_grant_permission, Toast.LENGTH_SHORT).show(); Toast.makeText(
context,
R.string.sharing_application_not_grant_permission,
Toast.LENGTH_SHORT)
.show();
} }
} }
@ -90,16 +100,27 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
private void loadPreview(Attachment attachment, ImageView imageView) { private void loadPreview(Attachment attachment, ImageView imageView) {
if (cancelPotentialWork(attachment, imageView)) { if (cancelPotentialWork(attachment, imageView)) {
XmppActivity activity = (XmppActivity) conversationFragment.getActivity(); XmppActivity activity = (XmppActivity) conversationFragment.getActivity();
final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment,Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)),true); final Bitmap bm =
activity.xmppConnectionService
.getFileBackend()
.getPreviewForUri(
attachment,
Math.round(
activity.getResources()
.getDimension(R.dimen.media_preview_size)),
true);
if (bm != null) { if (bm != null) {
cancelPotentialWork(attachment, imageView); cancelPotentialWork(attachment, imageView);
imageView.setImageBitmap(bm); imageView.setImageBitmap(bm);
imageView.setBackgroundColor(0x00000000); imageView.setBackgroundColor(0x00000000);
} else { } else {
imageView.setBackgroundColor(0xff333333); imageView.setBackgroundColor(
ContextCompat.getColor(imageView.getContext(), R.color.gray_800));
imageView.setImageDrawable(null); imageView.setImageDrawable(null);
final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(conversationFragment.getActivity().getResources(), null, task); final AsyncDrawable asyncDrawable =
new AsyncDrawable(
conversationFragment.getActivity().getResources(), null, task);
imageView.setImageDrawable(asyncDrawable); imageView.setImageDrawable(asyncDrawable);
try { try {
task.execute(attachment); task.execute(attachment);
@ -126,8 +147,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) { if (imageView != null) {
final Drawable drawable = imageView.getDrawable(); final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) { if (drawable instanceof AsyncDrawable asyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask(); return asyncDrawable.getBitmapWorkerTask();
} }
} }
@ -140,7 +160,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
} }
public boolean hasAttachments() { public boolean hasAttachments() {
return mediaPreviews.size() > 0; return !mediaPreviews.isEmpty();
} }
public ArrayList<Attachment> getAttachments() { public ArrayList<Attachment> getAttachments() {
@ -153,9 +173,9 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
static class MediaPreviewViewHolder extends RecyclerView.ViewHolder { static class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
private final MediaPreviewBinding binding; private final ItemMediaPreviewBinding binding;
MediaPreviewViewHolder(MediaPreviewBinding binding) { MediaPreviewViewHolder(ItemMediaPreviewBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }
@ -189,7 +209,14 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
if (activity == null) { if (activity == null) {
return null; return null;
} }
return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), false); return activity.xmppConnectionService
.getFileBackend()
.getPreviewForUri(
this.attachment,
Math.round(
activity.getResources()
.getDimension(R.dimen.media_preview_size)),
false);
} }
@Override @Override

View file

@ -5,6 +5,7 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
@ -20,16 +21,22 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.widget.ImageViewCompat;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.color.MaterialColors;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.net.URI; import java.net.URI;
@ -58,6 +65,7 @@ import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.service.AudioPlayer; import eu.siacs.conversations.ui.service.AudioPlayer;
import eu.siacs.conversations.ui.text.DividerSpan; import eu.siacs.conversations.ui.text.DividerSpan;
import eu.siacs.conversations.ui.text.QuoteSpan; import eu.siacs.conversations.ui.text.QuoteSpan;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MyLinkify; import eu.siacs.conversations.ui.util.MyLinkify;
import eu.siacs.conversations.ui.util.QuoteHelper; import eu.siacs.conversations.ui.util.QuoteHelper;
@ -160,15 +168,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return this.getItemViewType(getItem(position)); return this.getItemViewType(getItem(position));
} }
private int getMessageTextColor(boolean onDark, boolean primary) { private void displayStatus(ViewHolder viewHolder, Message message, int type, final BubbleColor bubbleColor) {
if (onDark) {
return ContextCompat.getColor(activity, primary ? R.color.white : R.color.white70);
} else {
return ContextCompat.getColor(activity, primary ? R.color.black87 : R.color.black54);
}
}
private void displayStatus(ViewHolder viewHolder, Message message, int type, boolean darkBackground) {
String filesize = null; String filesize = null;
String info = null; String info = null;
boolean error = false; boolean error = false;
@ -179,8 +179,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (viewHolder.edit_indicator != null) { if (viewHolder.edit_indicator != null) {
if (message.edited()) { if (message.edited()) {
viewHolder.edit_indicator.setVisibility(View.VISIBLE); viewHolder.edit_indicator.setVisibility(View.VISIBLE);
viewHolder.edit_indicator.setImageResource(darkBackground ? R.drawable.ic_mode_edit_white_18dp : R.drawable.ic_mode_edit_black_18dp); setImageTint(viewHolder.edit_indicator, bubbleColor);
viewHolder.edit_indicator.setAlpha(darkBackground ? 0.7f : 0.57f);
} else { } else {
viewHolder.edit_indicator.setVisibility(View.GONE); viewHolder.edit_indicator.setVisibility(View.GONE);
} }
@ -211,8 +210,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break; break;
case Message.STATUS_SEND_RECEIVED: case Message.STATUS_SEND_RECEIVED:
case Message.STATUS_SEND_DISPLAYED: case Message.STATUS_SEND_DISPLAYED:
viewHolder.indicatorReceived.setImageResource(darkBackground ? R.drawable.ic_done_white_18dp : R.drawable.ic_done_black_18dp); setImageTint(viewHolder.indicatorReceived, bubbleColor);
viewHolder.indicatorReceived.setAlpha(darkBackground ? 0.7f : 0.57f);
viewHolder.indicatorReceived.setVisibility(View.VISIBLE); viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
break; break;
case Message.STATUS_SEND_FAILED: case Message.STATUS_SEND_FAILED:
@ -245,18 +243,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
break; break;
} }
if (error && type == SENT) { if (error && type == SENT) {
if (darkBackground) { viewHolder.time.setTextColor(MaterialColors.getColor(viewHolder.time, com.google.android.material.R.attr.colorError));
viewHolder.time.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Caption_Warning_OnDark);
} else {
viewHolder.time.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Caption_Warning);
}
} else { } else {
if (darkBackground) { setTextColor(viewHolder.time,bubbleColor);
viewHolder.time.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Caption_OnDark);
} else {
viewHolder.time.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Caption);
}
viewHolder.time.setTextColor(this.getMessageTextColor(darkBackground, false));
} }
if (message.getEncryption() == Message.ENCRYPTION_NONE) { if (message.getEncryption() == Message.ENCRYPTION_NONE) {
viewHolder.indicator.setVisibility(View.GONE); viewHolder.indicator.setVisibility(View.GONE);
@ -271,15 +260,11 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
if (verified) { if (verified) {
viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_verified_user_white_18dp : R.drawable.ic_verified_user_black_18dp); viewHolder.indicator.setImageResource(R.drawable.ic_verified_user_24dp);
} else { } else {
viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_lock_white_18dp : R.drawable.ic_lock_black_18dp); viewHolder.indicator.setImageResource(R.drawable.ic_lock_24dp);
}
if (darkBackground) {
viewHolder.indicator.setAlpha(0.7f);
} else {
viewHolder.indicator.setAlpha(0.57f);
} }
setImageTint(viewHolder.indicator, bubbleColor);
viewHolder.indicator.setVisibility(View.VISIBLE); viewHolder.indicator.setVisibility(View.VISIBLE);
} }
@ -313,37 +298,29 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
private void displayInfoMessage(ViewHolder viewHolder, CharSequence text, boolean darkBackground) { private void displayInfoMessage(ViewHolder viewHolder, CharSequence text, final BubbleColor bubbleColor) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
viewHolder.messageBody.setText(text); viewHolder.messageBody.setText(text);
if (darkBackground) { viewHolder.messageBody.setTextColor(bubbleToOnSurfaceVariant(viewHolder.messageBody,bubbleColor));
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1_Secondary_OnDark);
} else {
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1_Secondary);
}
viewHolder.messageBody.setTextIsSelectable(false); viewHolder.messageBody.setTextIsSelectable(false);
} }
private void displayEmojiMessage(final ViewHolder viewHolder, final String body, final boolean darkBackground) { private void displayEmojiMessage(final ViewHolder viewHolder, final String body, final BubbleColor bubbleColor) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
if (darkBackground) { setTextColor(viewHolder.messageBody, bubbleColor);
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1_Emoji_OnDark); final Spannable span = new SpannableString(body);
} else {
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1_Emoji);
}
Spannable span = new SpannableString(body);
float size = Emoticons.isEmoji(body) ? 3.0f : 2.0f; float size = Emoticons.isEmoji(body) ? 3.0f : 2.0f;
span.setSpan(new RelativeSizeSpan(size), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); span.setSpan(new RelativeSizeSpan(size), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
viewHolder.messageBody.setText(span); viewHolder.messageBody.setText(span);
} }
private void applyQuoteSpan(SpannableStringBuilder body, int start, int end, boolean darkBackground) { private void applyQuoteSpan(final TextView textView, SpannableStringBuilder body, int start, int end, final BubbleColor bubbleColor) {
if (start > 1 && !"\n\n".equals(body.subSequence(start - 2, start).toString())) { if (start > 1 && !"\n\n".equals(body.subSequence(start - 2, start).toString())) {
body.insert(start++, "\n"); body.insert(start++, "\n");
body.setSpan(new DividerSpan(false), start - 2, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new DividerSpan(false), start - 2, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
@ -353,17 +330,15 @@ public class MessageAdapter extends ArrayAdapter<Message> {
body.insert(end, "\n"); body.insert(end, "\n");
body.setSpan(new DividerSpan(false), end, end + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new DividerSpan(false), end, end + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
int color = darkBackground ? this.getMessageTextColor(darkBackground, false) final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
: ContextCompat.getColor(activity, R.color.green700_desaturated); body.setSpan(new QuoteSpan(bubbleToOnSurfaceVariant(textView, bubbleColor), metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
body.setSpan(new QuoteSpan(color, metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
/** /**
* Applies QuoteSpan to group of lines which starts with > or » characters. * Applies QuoteSpan to group of lines which starts with > or » characters.
* Appends likebreaks and applies DividerSpan to them to show a padding between quote and text. * Appends likebreaks and applies DividerSpan to them to show a padding between quote and text.
*/ */
private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) { private boolean handleTextQuotes(final TextView textView, final SpannableStringBuilder body, final BubbleColor bubbleColor) {
boolean startsWithQuote = false; boolean startsWithQuote = false;
int quoteDepth = 1; int quoteDepth = 1;
while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) { while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) {
@ -382,7 +357,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (i == 0) startsWithQuote = true; if (i == 0) startsWithQuote = true;
} else if (quoteStart >= 0) { } else if (quoteStart >= 0) {
// Line start without quote, apply spans there // Line start without quote, apply spans there
applyQuoteSpan(body, quoteStart, i - 1, darkBackground); applyQuoteSpan(textView, body, quoteStart, i - 1, bubbleColor);
quoteStart = -1; quoteStart = -1;
} }
} }
@ -407,26 +382,19 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
if (quoteStart >= 0) { if (quoteStart >= 0) {
// Apply spans to finishing open quote // Apply spans to finishing open quote
applyQuoteSpan(body, quoteStart, body.length(), darkBackground); applyQuoteSpan(textView, body, quoteStart, body.length(), bubbleColor);
} }
quoteDepth++; quoteDepth++;
} }
return startsWithQuote; return startsWithQuote;
} }
private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground, int type) { private void displayTextMessage(final ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor, int type) {
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
setTextColor(viewHolder.messageBody, bubbleColor);
if (darkBackground) {
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1_OnDark);
} else {
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1);
}
viewHolder.messageBody.setHighlightColor(ContextCompat.getColor(activity, darkBackground
? (type == SENT || !mUseGreenBackground ? R.color.black26 : R.color.grey800) : R.color.grey500));
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
if (message.getBody() != null) { if (message.getBody() != null) {
@ -446,7 +414,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
int end = body.getSpanEnd(mergeSeparator); int end = body.getSpanEnd(mergeSeparator);
body.setSpan(new DividerSpan(true), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new DividerSpan(true), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
boolean startsWithQuote = handleTextQuotes(body, darkBackground); boolean startsWithQuote = handleTextQuotes(viewHolder.messageBody, body, bubbleColor);
if (!message.isPrivateMessage()) { if (!message.isPrivateMessage()) {
if (hasMeCommand) { if (hasMeCommand) {
body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(),
@ -469,7 +437,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else { } else {
body.insert(privateMarkerIndex, " "); body.insert(privateMarkerIndex, " ");
} }
body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new ForegroundColorSpan(bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (hasMeCommand) { if (hasMeCommand) {
body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1,
@ -477,8 +445,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) { if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) {
if (message.getConversation() instanceof Conversation) { if (message.getConversation() instanceof Conversation conversation) {
final Conversation conversation = (Conversation) message.getConversation();
Pattern pattern = NotificationService.generateNickHighlightPattern(conversation.getMucOptions().getActualNick()); Pattern pattern = NotificationService.generateNickHighlightPattern(conversation.getMucOptions().getActualNick());
Matcher matcher = pattern.matcher(body); Matcher matcher = pattern.matcher(body);
while (matcher.find()) { while (matcher.find()) {
@ -495,7 +462,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
StylingHelper.format(body, viewHolder.messageBody.getCurrentTextColor()); StylingHelper.format(body, viewHolder.messageBody.getCurrentTextColor());
if (highlightedTerm != null) { if (highlightedTerm != null) {
StylingHelper.highlight(activity, body, highlightedTerm, StylingHelper.isDarkText(viewHolder.messageBody)); StylingHelper.highlight(viewHolder.messageBody, body, highlightedTerm);
} }
MyLinkify.addLinks(body, true); MyLinkify.addLinks(body, true);
viewHolder.messageBody.setAutoLinkMask(0); viewHolder.messageBody.setAutoLinkMask(0);
@ -507,45 +474,54 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text, final boolean darkBackground) { private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text, final BubbleColor bubbleColor) {
toggleWhisperInfo(viewHolder, message, darkBackground); toggleWhisperInfo(viewHolder, message, bubbleColor);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE); viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(text); viewHolder.download_button.setText(text);
final var attachment = Attachment.of(message);
final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
viewHolder.download_button.setIconResource(imageResource);
viewHolder.download_button.setOnClickListener(v -> ConversationFragment.downloadFile(activity, message)); viewHolder.download_button.setOnClickListener(v -> ConversationFragment.downloadFile(activity, message));
} }
private void displayOpenableMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) { private void displayOpenableMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) {
toggleWhisperInfo(viewHolder, message, darkBackground); toggleWhisperInfo(viewHolder, message, bubbleColor);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE); viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message))); viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
final var attachment = Attachment.of(message);
final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
viewHolder.download_button.setIconResource(imageResource);
viewHolder.download_button.setOnClickListener(v -> openDownloadable(message)); viewHolder.download_button.setOnClickListener(v -> openDownloadable(message));
} }
private void displayLocationMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) { private void displayLocationMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) {
toggleWhisperInfo(viewHolder, message, darkBackground); toggleWhisperInfo(viewHolder, message, bubbleColor);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE); viewHolder.download_button.setVisibility(View.VISIBLE);
viewHolder.download_button.setText(R.string.show_location); viewHolder.download_button.setText(R.string.show_location);
final var attachment = Attachment.of(message);
final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
viewHolder.download_button.setIconResource(imageResource);
viewHolder.download_button.setOnClickListener(v -> showLocation(message)); viewHolder.download_button.setOnClickListener(v -> showLocation(message));
} }
private void displayAudioMessage(ViewHolder viewHolder, Message message, boolean darkBackground) { private void displayAudioMessage(ViewHolder viewHolder, Message message, final BubbleColor bubbleColor) {
toggleWhisperInfo(viewHolder, message, darkBackground); toggleWhisperInfo(viewHolder, message, bubbleColor);
viewHolder.image.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
final RelativeLayout audioPlayer = viewHolder.audioPlayer; final RelativeLayout audioPlayer = viewHolder.audioPlayer;
audioPlayer.setVisibility(View.VISIBLE); audioPlayer.setVisibility(View.VISIBLE);
AudioPlayer.ViewHolder.get(audioPlayer).setDarkBackground(darkBackground); AudioPlayer.ViewHolder.get(audioPlayer).setBubbleColor(bubbleColor);
this.audioPlayer.init(audioPlayer, message); this.audioPlayer.init(audioPlayer, message);
} }
private void displayMediaPreviewMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) { private void displayMediaPreviewMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) {
toggleWhisperInfo(viewHolder, message, darkBackground); toggleWhisperInfo(viewHolder, message, bubbleColor);
viewHolder.download_button.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE); viewHolder.image.setVisibility(View.VISIBLE);
@ -573,7 +549,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.image.setOnClickListener(v -> openDownloadable(message)); viewHolder.image.setOnClickListener(v -> openDownloadable(message));
} }
private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final boolean darkBackground) { private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) {
if (message.isPrivateMessage()) { if (message.isPrivateMessage()) {
final String privateMarker; final String privateMarker;
if (message.getStatus() <= Message.STATUS_RECEIVED) { if (message.getStatus() <= Message.STATUS_RECEIVED) {
@ -583,7 +559,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource())); privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource()));
} }
final SpannableString body = new SpannableString(privateMarker); final SpannableString body = new SpannableString(privateMarker);
body.setSpan(new ForegroundColorSpan(getMessageTextColor(darkBackground, false)), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); body.setSpan(new ForegroundColorSpan(bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
body.setSpan(new StyleSpan(Typeface.BOLD), 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.setText(body);
viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setVisibility(View.VISIBLE);
@ -611,7 +587,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
@Override @Override
public View getView(int position, View view, ViewGroup parent) { public View getView(final int position, View view, final @NonNull ViewGroup parent) {
final Message message = getItem(position); final Message message = getItem(position);
final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL; final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL;
final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted()); final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted());
@ -623,19 +599,18 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder = new ViewHolder(); viewHolder = new ViewHolder();
switch (type) { switch (type) {
case DATE_SEPARATOR: case DATE_SEPARATOR:
view = activity.getLayoutInflater().inflate(R.layout.message_date_bubble, parent, false); view = activity.getLayoutInflater().inflate(R.layout.item_message_date_bubble, parent, false);
viewHolder.status_message = view.findViewById(R.id.message_body); viewHolder.status_message = view.findViewById(R.id.message_body);
viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.message_box = view.findViewById(R.id.message_box);
viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
break; break;
case RTP_SESSION: case RTP_SESSION:
view = activity.getLayoutInflater().inflate(R.layout.message_rtp_session, parent, false); view = activity.getLayoutInflater().inflate(R.layout.item_message_rtp_session, parent, false);
viewHolder.status_message = view.findViewById(R.id.message_body); viewHolder.status_message = view.findViewById(R.id.message_body);
viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.message_box = view.findViewById(R.id.message_box);
viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
break; break;
case SENT: case SENT:
view = activity.getLayoutInflater().inflate(R.layout.message_sent, parent, false); view = activity.getLayoutInflater().inflate(R.layout.item_message_sent, parent, false);
viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.message_box = view.findViewById(R.id.message_box);
viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.contact_picture = view.findViewById(R.id.message_photo);
viewHolder.download_button = view.findViewById(R.id.download_button); viewHolder.download_button = view.findViewById(R.id.download_button);
@ -648,7 +623,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.audioPlayer = view.findViewById(R.id.audio_player); viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
break; break;
case RECEIVED: case RECEIVED:
view = activity.getLayoutInflater().inflate(R.layout.message_received, parent, false); view = activity.getLayoutInflater().inflate(R.layout.item_message_received, parent, false);
viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.message_box = view.findViewById(R.id.message_box);
viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.contact_picture = view.findViewById(R.id.message_photo);
viewHolder.download_button = view.findViewById(R.id.download_button); viewHolder.download_button = view.findViewById(R.id.download_button);
@ -662,7 +637,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.audioPlayer = view.findViewById(R.id.audio_player); viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
break; break;
case STATUS: case STATUS:
view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false); view = activity.getLayoutInflater().inflate(R.layout.item_message_status, parent, false);
viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.contact_picture = view.findViewById(R.id.message_photo);
viewHolder.status_message = view.findViewById(R.id.status_message); viewHolder.status_message = view.findViewById(R.id.status_message);
viewHolder.load_more_messages = view.findViewById(R.id.load_more_messages); viewHolder.load_more_messages = view.findViewById(R.id.load_more_messages);
@ -678,7 +653,17 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
boolean darkBackground = type == RECEIVED && (!isInValidSession || mUseGreenBackground) || activity.isDarkTheme(); final boolean colorfulBackground = mUseGreenBackground;
final BubbleColor bubbleColor;
if (type == RECEIVED) {
if (isInValidSession) {
bubbleColor = colorfulBackground ? BubbleColor.TERTIARY : BubbleColor.SURFACE;
} else {
bubbleColor = BubbleColor.WARNING;
}
} else {
bubbleColor = colorfulBackground ? BubbleColor.SECONDARY : BubbleColor.SURFACE;
}
if (type == DATE_SEPARATOR) { if (type == DATE_SEPARATOR) {
if (UIHelper.today(message.getTimeSent())) { if (UIHelper.today(message.getTimeSent())) {
@ -688,10 +673,15 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else { } else {
viewHolder.status_message.setText(DateUtils.formatDateTime(activity, message.getTimeSent(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR)); viewHolder.status_message.setText(DateUtils.formatDateTime(activity, message.getTimeSent(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR));
} }
viewHolder.message_box.setBackgroundResource(activity.isDarkTheme() ? R.drawable.date_bubble_grey : R.drawable.date_bubble_white); if (colorfulBackground) {
setBackgroundTint(viewHolder.message_box,BubbleColor.PRIMARY);
setTextColor(viewHolder.status_message, BubbleColor.PRIMARY);
} else {
setBackgroundTint(viewHolder.message_box,BubbleColor.SURFACE);
setTextColor(viewHolder.status_message, BubbleColor.SURFACE);
}
return view; return view;
} else if (type == RTP_SESSION) { } else if (type == RTP_SESSION) {
final boolean isDarkTheme = activity.isDarkTheme();
final boolean received = message.getStatus() <= Message.STATUS_RECEIVED; final boolean received = message.getStatus() <= Message.STATUS_RECEIVED;
final RtpSessionStatus rtpSessionStatus = RtpSessionStatus.of(message.getBody()); final RtpSessionStatus rtpSessionStatus = RtpSessionStatus.of(message.getBody());
final long duration = rtpSessionStatus.duration; final long duration = rtpSessionStatus.duration;
@ -710,9 +700,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.status_message.setText(activity.getString(R.string.outgoing_call_timestamp, UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent()))); viewHolder.status_message.setText(activity.getString(R.string.outgoing_call_timestamp, UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent())));
} }
} }
viewHolder.indicatorReceived.setImageResource(RtpSessionStatus.getDrawable(received, rtpSessionStatus.successful, isDarkTheme)); if (colorfulBackground) {
viewHolder.indicatorReceived.setAlpha(isDarkTheme ? 0.7f : 0.57f); setBackgroundTint(viewHolder.message_box,BubbleColor.TERTIARY);
viewHolder.message_box.setBackgroundResource(isDarkTheme ? R.drawable.date_bubble_grey : R.drawable.date_bubble_white); setTextColor(viewHolder.status_message, BubbleColor.TERTIARY);
setImageTint(viewHolder.indicatorReceived, BubbleColor.TERTIARY);
} else {
setBackgroundTint(viewHolder.message_box,BubbleColor.SURFACE);
setTextColor(viewHolder.status_message, BubbleColor.SURFACE);
setImageTint(viewHolder.indicatorReceived, BubbleColor.SURFACE);
}
viewHolder.indicatorReceived.setImageResource(RtpSessionStatus.getDrawable(received, rtpSessionStatus.successful));
return view; return view;
} else if (type == STATUS) { } else if (type == STATUS) {
if ("LOAD_MORE".equals(message.getBody())) { if ("LOAD_MORE".equals(message.getBody())) {
@ -769,43 +766,43 @@ public class MessageAdapter extends ArrayAdapter<Message> {
final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message); final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message);
if (unInitiatedButKnownSize || message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { if (unInitiatedButKnownSize || message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) {
if (unInitiatedButKnownSize || transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { if (unInitiatedButKnownSize || transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) {
displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), darkBackground); displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), bubbleColor);
} else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { } 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)), darkBackground); displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), bubbleColor);
} else { } else {
displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground); displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, bubbleColor);
} }
} else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { } else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
if (message.getFileParams().width > 0 && message.getFileParams().height > 0) { if (message.getFileParams().width > 0 && message.getFileParams().height > 0) {
displayMediaPreviewMessage(viewHolder, message, darkBackground); displayMediaPreviewMessage(viewHolder, message, bubbleColor);
} else if (message.getFileParams().runtime > 0) { } else if (message.getFileParams().runtime > 0) {
displayAudioMessage(viewHolder, message, darkBackground); displayAudioMessage(viewHolder, message, bubbleColor);
} else { } else {
displayOpenableMessage(viewHolder, message, darkBackground); displayOpenableMessage(viewHolder, message, bubbleColor);
} }
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) { } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
if (account.isPgpDecryptionServiceConnected()) { if (account.isPgpDecryptionServiceConnected()) {
if (conversation instanceof Conversation && !account.hasPendingPgpIntent((Conversation) conversation)) { if (conversation instanceof Conversation && !account.hasPendingPgpIntent((Conversation) conversation)) {
displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), bubbleColor);
} else { } else {
displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), bubbleColor);
} }
} else { } else {
displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), bubbleColor);
viewHolder.message_box.setOnClickListener(this::promptOpenKeychainInstall); viewHolder.message_box.setOnClickListener(this::promptOpenKeychainInstall);
viewHolder.messageBody.setOnClickListener(this::promptOpenKeychainInstall); viewHolder.messageBody.setOnClickListener(this::promptOpenKeychainInstall);
} }
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
displayInfoMessage(viewHolder, activity.getString(R.string.decryption_failed), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.decryption_failed), bubbleColor);
} else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) { } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) {
displayInfoMessage(viewHolder, activity.getString(R.string.not_encrypted_for_this_device), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.not_encrypted_for_this_device), bubbleColor);
} else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) { } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) {
displayInfoMessage(viewHolder, activity.getString(R.string.omemo_decryption_failed), darkBackground); displayInfoMessage(viewHolder, activity.getString(R.string.omemo_decryption_failed), bubbleColor);
} else { } else {
if (message.isGeoUri()) { if (message.isGeoUri()) {
displayLocationMessage(viewHolder, message, darkBackground); displayLocationMessage(viewHolder, message, bubbleColor);
} else if (message.bodyIsOnlyEmojis() && message.getType() != Message.TYPE_PRIVATE) { } else if (message.bodyIsOnlyEmojis() && message.getType() != Message.TYPE_PRIVATE) {
displayEmojiMessage(viewHolder, message.getBody().trim(), darkBackground); displayEmojiMessage(viewHolder, message.getBody().trim(), bubbleColor);
} else if (message.treatAsDownloadable()) { } else if (message.treatAsDownloadable()) {
try { try {
final URI uri = new URI(message.getBody()); final URI uri = new URI(message.getBody());
@ -814,31 +811,27 @@ public class MessageAdapter extends ArrayAdapter<Message> {
activity.getString(R.string.check_x_filesize_on_host, activity.getString(R.string.check_x_filesize_on_host,
UIHelper.getFileDescriptionString(activity, message), UIHelper.getFileDescriptionString(activity, message),
uri.getHost()), uri.getHost()),
darkBackground); bubbleColor);
} catch (Exception e) { } catch (Exception e) {
displayDownloadableMessage(viewHolder, displayDownloadableMessage(viewHolder,
message, message,
activity.getString(R.string.check_x_filesize, activity.getString(R.string.check_x_filesize,
UIHelper.getFileDescriptionString(activity, message)), UIHelper.getFileDescriptionString(activity, message)),
darkBackground); bubbleColor);
} }
} else { } else {
displayTextMessage(viewHolder, message, darkBackground, type); displayTextMessage(viewHolder, message, bubbleColor, type);
} }
} }
setBackgroundTint(viewHolder.message_box, bubbleColor);
setTextColor(viewHolder.messageBody, bubbleColor);
if (type == RECEIVED) { if (type == RECEIVED) {
setTextColor(viewHolder.encryption, bubbleColor);
if (isInValidSession) { if (isInValidSession) {
int bubble;
if (!mUseGreenBackground) {
bubble = activity.getThemeResource(R.attr.message_bubble_received_monochrome, R.drawable.message_bubble_received_white);
} else {
bubble = activity.getThemeResource(R.attr.message_bubble_received_green, R.drawable.message_bubble_received);
}
viewHolder.message_box.setBackgroundResource(bubble);
viewHolder.encryption.setVisibility(View.GONE); viewHolder.encryption.setVisibility(View.GONE);
} else { } else {
viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
viewHolder.encryption.setVisibility(View.VISIBLE); viewHolder.encryption.setVisibility(View.VISIBLE);
if (omemoEncryption && !message.isTrusted()) { if (omemoEncryption && !message.isTrusted()) {
viewHolder.encryption.setText(R.string.not_trusted); viewHolder.encryption.setText(R.string.not_trusted);
@ -848,7 +841,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} }
} }
displayStatus(viewHolder, message, type, darkBackground); displayStatus(viewHolder, message, type, bubbleColor);
return view; return view;
} }
@ -911,13 +904,68 @@ public class MessageAdapter extends ArrayAdapter<Message> {
void onContactPictureLongClicked(View v, Message message); void onContactPictureLongClicked(View v, Message message);
} }
private static void setBackgroundTint(final View view, final BubbleColor bubbleColor) {
view.setBackgroundTintList(bubbleToColorStateList(view, bubbleColor));
}
private static ColorStateList bubbleToColorStateList(final View view, final BubbleColor bubbleColor) {
final @AttrRes int colorAttributeResId = switch (bubbleColor) {
case SURFACE -> com.google.android.material.R.attr.colorSurfaceContainerHigh;
case PRIMARY -> com.google.android.material.R.attr.colorPrimaryContainer;
case SECONDARY -> com.google.android.material.R.attr.colorSecondaryContainer;
case TERTIARY -> com.google.android.material.R.attr.colorTertiaryContainer;
case WARNING -> com.google.android.material.R.attr.colorErrorContainer;
};
return ColorStateList.valueOf(MaterialColors.getColor(view,colorAttributeResId));
}
public static void setImageTint(final ImageView imageView, final BubbleColor bubbleColor) {
ImageViewCompat.setImageTintList(imageView,bubbleToOnSurfaceColorStateList(imageView, bubbleColor));
}
public static void setTextColor(final TextView textView, final BubbleColor bubbleColor) {
textView.setTextColor(bubbleToOnSurfaceColor(textView, bubbleColor));
}
private static @ColorInt int bubbleToOnSurfaceVariant(final View view, final BubbleColor bubbleColor) {
final @AttrRes int colorAttributeResId;
if (bubbleColor == BubbleColor.SURFACE) {
colorAttributeResId = com.google.android.material.R.attr.colorOnSurfaceVariant;
} else {
colorAttributeResId = bubbleToOnSurface(bubbleColor);
}
return MaterialColors.getColor(view,colorAttributeResId);
}
private static @ColorInt int bubbleToOnSurfaceColor(final View view, final BubbleColor bubbleColor) {
return MaterialColors.getColor(view, bubbleToOnSurface(bubbleColor));
}
public static ColorStateList bubbleToOnSurfaceColorStateList(final View view, final BubbleColor bubbleColor) {
return ColorStateList.valueOf(bubbleToOnSurfaceColor(view, bubbleColor));
}
private static @AttrRes int bubbleToOnSurface(final BubbleColor bubbleColor) {
return switch (bubbleColor) {
case SURFACE -> com.google.android.material.R.attr.colorOnSurface;
case PRIMARY -> com.google.android.material.R.attr.colorOnPrimaryContainer;
case SECONDARY -> com.google.android.material.R.attr.colorOnSecondaryContainer;
case TERTIARY -> com.google.android.material.R.attr.colorOnTertiaryContainer;
case WARNING -> com.google.android.material.R.attr.colorOnErrorContainer;
};
}
public enum BubbleColor {
SURFACE, PRIMARY, SECONDARY, TERTIARY, WARNING
}
private static class ViewHolder { private static class ViewHolder {
public Button load_more_messages; public MaterialButton load_more_messages;
public ImageView edit_indicator; public ImageView edit_indicator;
public RelativeLayout audioPlayer; public RelativeLayout audioPlayer;
protected LinearLayout message_box; protected LinearLayout message_box;
protected Button download_button; protected MaterialButton download_button;
protected ImageView image; protected ImageView image;
protected ImageView indicator; protected ImageView indicator;
protected ImageView indicatorReceived; protected ImageView indicatorReceived;

View file

@ -17,7 +17,7 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.databinding.ContactBinding; import eu.siacs.conversations.databinding.ItemContactBinding;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
@ -62,7 +62,7 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.contact, viewGroup, false)); return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_contact, viewGroup, false));
} }
@Override @Override
@ -129,11 +129,11 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
MucDetailsContextMenuHelper.onCreateContextMenu(menu,v); MucDetailsContextMenuHelper.onCreateContextMenu(menu,v);
} }
class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
private final ContactBinding binding; private final ItemContactBinding binding;
private ViewHolder(ContactBinding binding) { private ViewHolder(ItemContactBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }

View file

@ -11,13 +11,14 @@ import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.UserPreviewBinding; import eu.siacs.conversations.databinding.ItemUserPreviewBinding;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper; import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreviewAdapter.ViewHolder> implements View.OnCreateContextMenuListener { public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreviewAdapter.ViewHolder>
implements View.OnCreateContextMenuListener {
private MucOptions.User selectedUser = null; private MucOptions.User selectedUser = null;
@ -28,29 +29,43 @@ public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreview
@NonNull @NonNull
@Override @Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) { public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.user_preview, viewGroup, false)); return new ViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(viewGroup.getContext()),
R.layout.item_user_preview,
viewGroup,
false));
} }
@Override @Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final MucOptions.User user = getItem(position); final MucOptions.User user = getItem(position);
AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size); AvatarWorkerTask.loadAvatar(user, viewHolder.binding.avatar, R.dimen.media_size);
viewHolder.binding.getRoot().setOnClickListener(v -> { viewHolder
final XmppActivity activity = XmppActivity.find(v); .binding
if (activity != null) { .getRoot()
activity.highlightInMuc(user.getConversation(), user.getName()); .setOnClickListener(
} v -> {
}); final XmppActivity activity = XmppActivity.find(v);
if (activity != null) {
activity.highlightInMuc(user.getConversation(), user.getName());
}
});
viewHolder.binding.getRoot().setOnCreateContextMenuListener(this); viewHolder.binding.getRoot().setOnCreateContextMenuListener(this);
viewHolder.binding.getRoot().setTag(user); viewHolder.binding.getRoot().setTag(user);
viewHolder.binding.getRoot().setOnLongClickListener(v -> { viewHolder
selectedUser = user; .binding
return false; .getRoot()
}); .setOnLongClickListener(
v -> {
selectedUser = user;
return false;
});
} }
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { public void onCreateContextMenu(
ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MucDetailsContextMenuHelper.onCreateContextMenu(menu, v); MucDetailsContextMenuHelper.onCreateContextMenu(menu, v);
} }
@ -60,9 +75,9 @@ public class UserPreviewAdapter extends ListAdapter<MucOptions.User, UserPreview
class ViewHolder extends RecyclerView.ViewHolder { class ViewHolder extends RecyclerView.ViewHolder {
private final UserPreviewBinding binding; private final ItemUserPreviewBinding binding;
private ViewHolder(UserPreviewBinding binding) { private ViewHolder(final ItemUserPreviewBinding binding) {
super(binding.getRoot()); super(binding.getRoot());
this.binding = binding; this.binding = binding;
} }

View file

@ -1,80 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Field;
public class FormBooleanFieldWrapper extends FormFieldWrapper {
protected CheckBox checkBox;
protected FormBooleanFieldWrapper(Context context, Field field) {
super(context, field);
checkBox = view.findViewById(R.id.field);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
checkBox.setError(null);
invokeOnFormFieldValuesEdited();
}
});
}
@Override
protected void setLabel(String label, boolean required) {
CheckBox checkBox = view.findViewById(R.id.field);
checkBox.setText(createSpannableLabelString(label, required));
}
@Override
public List<String> getValues() {
List<String> values = new ArrayList<>();
values.add(Boolean.toString(checkBox.isChecked()));
return values;
}
@Override
protected void setValues(List<String> values) {
if (values.size() == 0) {
checkBox.setChecked(false);
} else {
checkBox.setChecked(Boolean.parseBoolean(values.get(0)));
}
}
@Override
public boolean validates() {
if (checkBox.isChecked() || !field.isRequired()) {
return true;
} else {
checkBox.setError(context.getString(R.string.this_field_is_required));
checkBox.requestFocus();
return false;
}
}
@Override
public boolean edited() {
if (field.getValues().size() == 0) {
return checkBox.isChecked();
} else {
return super.edited();
}
}
@Override
protected int getLayoutResource() {
return R.layout.form_boolean;
}
@Override
void setReadOnly(boolean readOnly) {
checkBox.setEnabled(!readOnly);
}
}

View file

@ -1,30 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import java.util.Hashtable;
import eu.siacs.conversations.xmpp.forms.Field;
public class FormFieldFactory {
private static final Hashtable<String, Class> typeTable = new Hashtable<>();
static {
typeTable.put("text-single", FormTextFieldWrapper.class);
typeTable.put("text-multi", FormTextFieldWrapper.class);
typeTable.put("text-private", FormTextFieldWrapper.class);
typeTable.put("jid-single", FormJidSingleFieldWrapper.class);
typeTable.put("boolean", FormBooleanFieldWrapper.class);
}
protected static FormFieldWrapper createFromField(Context context, Field field) {
Class clazz = typeTable.get(field.getType());
if (clazz == null) {
clazz = FormTextFieldWrapper.class;
}
return FormFieldWrapper.createFromField(clazz, context, field);
}
}

View file

@ -1,93 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.LayoutInflater;
import android.view.View;
import java.util.List;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.xmpp.forms.Field;
public abstract class FormFieldWrapper {
protected final Context context;
protected final Field field;
protected final View view;
OnFormFieldValuesEdited onFormFieldValuesEditedListener;
FormFieldWrapper(Context context, Field field) {
this.context = context;
this.field = field;
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.view = inflater.inflate(getLayoutResource(), null);
String label = field.getLabel();
if (label == null) {
label = field.getFieldName();
}
setLabel(label, field.isRequired());
}
public final void submit() {
this.field.setValues(getValues());
}
public final View getView() {
return view;
}
protected abstract void setLabel(String label, boolean required);
abstract List<String> getValues();
protected abstract void setValues(List<String> values);
abstract boolean validates();
abstract protected int getLayoutResource();
abstract void setReadOnly(boolean readOnly);
protected SpannableString createSpannableLabelString(String label, boolean required) {
SpannableString spannableString = new SpannableString(label + (required ? " *" : ""));
if (required) {
int start = label.length();
int end = label.length() + 2;
spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0);
spannableString.setSpan(new ForegroundColorSpan(StyledAttributes.getColor(context, androidx.appcompat.R.attr.colorAccent)), start, end, 0);
}
return spannableString;
}
protected void invokeOnFormFieldValuesEdited() {
if (this.onFormFieldValuesEditedListener != null) {
this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
}
}
public boolean edited() {
return !field.getValues().equals(getValues());
}
public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
this.onFormFieldValuesEditedListener = listener;
}
protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
try {
F fieldWrapper = c.getDeclaredConstructor(Context.class, Field.class).newInstance(context,field);
fieldWrapper.setValues(field.getValues());
return fieldWrapper;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public interface OnFormFieldValuesEdited {
void onFormFieldValuesEdited();
}
}

View file

@ -1,43 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import android.text.InputType;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.forms.Field;
public class FormJidSingleFieldWrapper extends FormTextFieldWrapper {
protected FormJidSingleFieldWrapper(Context context, Field field) {
super(context, field);
editText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
editText.setHint(R.string.account_settings_example_jabber_id);
}
@Override
public boolean validates() {
String value = getValue();
if (!value.isEmpty()) {
try {
Jid.of(value);
} catch (IllegalArgumentException e) {
editText.setError(context.getString(R.string.invalid_jid));
editText.requestFocus();
return false;
}
}
return super.validates();
}
@Override
protected void setValues(List<String> values) {
StringBuilder builder = new StringBuilder();
for(String value : values) {
builder.append(value);
}
editText.setText(builder.toString());
}
}

View file

@ -1,97 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.xmpp.forms.Field;
public class FormTextFieldWrapper extends FormFieldWrapper {
protected EditText editText;
protected FormTextFieldWrapper(Context context, Field field) {
super(context, field);
editText = view.findViewById(R.id.field);
editText.setSingleLine(!"text-multi".equals(field.getType()));
if ("text-private".equals(field.getType())) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
editText.setError(null);
invokeOnFormFieldValuesEdited();
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
@Override
protected void setLabel(String label, boolean required) {
TextView textView = view.findViewById(R.id.label);
textView.setText(createSpannableLabelString(label, required));
}
protected String getValue() {
return editText.getText().toString();
}
@Override
public List<String> getValues() {
List<String> values = new ArrayList<>();
for (String line : getValue().split("\\n")) {
if (line.length() > 0) {
values.add(line);
}
}
return values;
}
@Override
protected void setValues(List<String> values) {
StringBuilder builder = new StringBuilder();
for(int i = 0; i < values.size(); ++i) {
builder.append(values.get(i));
if (i < values.size() - 1 && "text-multi".equals(field.getType())) {
builder.append("\n");
}
}
editText.setText(builder.toString());
}
@Override
public boolean validates() {
if (getValue().trim().length() > 0 || !field.isRequired()) {
return true;
} else {
editText.setError(context.getString(R.string.this_field_is_required));
editText.requestFocus();
return false;
}
}
@Override
protected int getLayoutResource() {
return R.layout.form_text;
}
@Override
void setReadOnly(boolean readOnly) {
editText.setEnabled(!readOnly);
}
}

View file

@ -1,72 +0,0 @@
package eu.siacs.conversations.ui.forms;
import android.content.Context;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
public class FormWrapper {
private final LinearLayout layout;
private final Data form;
private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
this.form = form;
this.layout = linearLayout;
this.layout.removeAllViews();
for(Field field : form.getFields()) {
FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context,field);
if (fieldWrapper != null) {
layout.addView(fieldWrapper.getView());
fieldWrappers.add(fieldWrapper);
}
}
}
public Data submit() {
for(FormFieldWrapper fieldWrapper : fieldWrappers) {
fieldWrapper.submit();
}
this.form.submit();
return this.form;
}
public boolean validates() {
boolean validates = true;
for(FormFieldWrapper fieldWrapper : fieldWrappers) {
validates &= fieldWrapper.validates();
}
return validates;
}
public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
for(FormFieldWrapper fieldWrapper : fieldWrappers) {
fieldWrapper.setOnFormFieldValuesEditedListener(listener);
}
}
public void setReadOnly(boolean b) {
for(FormFieldWrapper fieldWrapper : fieldWrappers) {
fieldWrapper.setReadOnly(b);
}
}
public boolean edited() {
boolean edited = false;
for(FormFieldWrapper fieldWrapper : fieldWrappers) {
edited |= fieldWrapper.edited();
}
return edited;
}
public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
return new FormWrapper(context, layout, form);
}
}

View file

@ -22,11 +22,6 @@ import android.widget.TextView;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
@ -36,7 +31,17 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.utils.WeakReferenceSet; import eu.siacs.conversations.utils.WeakReferenceSet;
public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompletionListener, SeekBar.OnSeekBarChangeListener, Runnable, SensorEventListener { import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AudioPlayer
implements View.OnClickListener,
MediaPlayer.OnCompletionListener,
SeekBar.OnSeekBarChangeListener,
Runnable,
SensorEventListener {
private static final int REFRESH_INTERVAL = 250; private static final int REFRESH_INTERVAL = 250;
private static final Object LOCK = new Object(); private static final Object LOCK = new Object();
@ -57,33 +62,43 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
final Context context = adapter.getContext(); final Context context = adapter.getContext();
this.messageAdapter = adapter; this.messageAdapter = adapter;
this.sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); this.sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
this.proximitySensor = this.sensorManager == null ? null : this.sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); this.proximitySensor =
this.sensorManager == null
? null
: this.sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
initializeProximityWakeLock(context); initializeProximityWakeLock(context);
synchronized (AudioPlayer.LOCK) { synchronized (AudioPlayer.LOCK) {
if (AudioPlayer.player != null) { if (AudioPlayer.player != null) {
AudioPlayer.player.setOnCompletionListener(this); AudioPlayer.player.setOnCompletionListener(this);
if (AudioPlayer.player.isPlaying() && sensorManager != null) { if (AudioPlayer.player.isPlaying() && sensorManager != null) {
sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); sensorManager.registerListener(
this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
} }
} }
} }
} }
private static String formatTime(int ms) { private static String formatTime(int ms) {
return String.format(Locale.ENGLISH, "%d:%02d", ms / 60000, Math.min(Math.round((ms % 60000) / 1000f), 59)); return String.format(
Locale.ENGLISH,
"%d:%02d",
ms / 60000,
Math.min(Math.round((ms % 60000) / 1000f), 59));
} }
private void initializeProximityWakeLock(Context context) { private void initializeProximityWakeLock(Context context) {
if (Build.VERSION.SDK_INT >= 21) { synchronized (AudioPlayer.LOCK) {
synchronized (AudioPlayer.LOCK) { if (AudioPlayer.wakeLock == null) {
if (AudioPlayer.wakeLock == null) { final PowerManager powerManager =
final PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); (PowerManager) context.getSystemService(Context.POWER_SERVICE);
AudioPlayer.wakeLock = powerManager == null ? null : powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, AudioPlayer.class.getSimpleName()); AudioPlayer.wakeLock =
AudioPlayer.wakeLock.setReferenceCounted(false); powerManager == null
} ? null
: powerManager.newWakeLock(
PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
AudioPlayer.class.getSimpleName());
AudioPlayer.wakeLock.setReferenceCounted(false);
} }
} else {
AudioPlayer.wakeLock = null;
} }
} }
@ -92,41 +107,39 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
audioPlayer.setTag(message); audioPlayer.setTag(message);
if (init(ViewHolder.get(audioPlayer), message)) { if (init(ViewHolder.get(audioPlayer), message)) {
this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer); this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer);
executor.execute(()-> this.stopRefresher(true)); executor.execute(() -> this.stopRefresher(true));
} else { } else {
this.audioPlayerLayouts.removeWeakReferenceTo(audioPlayer); this.audioPlayerLayouts.removeWeakReferenceTo(audioPlayer);
} }
} }
} }
private boolean init(ViewHolder viewHolder, Message message) { private boolean init(final ViewHolder viewHolder, final Message message) {
if (viewHolder.darkBackground) { MessageAdapter.setTextColor(viewHolder.runtime, viewHolder.bubbleColor);
viewHolder.runtime.setTextAppearance(this.messageAdapter.getContext(), R.style.TextAppearance_Conversations_Caption_OnDark);
} else {
viewHolder.runtime.setTextAppearance(this.messageAdapter.getContext(), R.style.TextAppearance_Conversations_Caption);
}
viewHolder.progress.setOnSeekBarChangeListener(this); viewHolder.progress.setOnSeekBarChangeListener(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { final ColorStateList color =
ColorStateList color = ContextCompat.getColorStateList(messageAdapter.getContext(), viewHolder.darkBackground ? R.color.white70 : R.color.green700_desaturated); MessageAdapter.bubbleToOnSurfaceColorStateList(
viewHolder.progress.setThumbTintList(color); viewHolder.progress, viewHolder.bubbleColor);
viewHolder.progress.setProgressTintList(color); viewHolder.progress.setThumbTintList(color);
} viewHolder.progress.setProgressTintList(color);
viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f);
viewHolder.playPause.setOnClickListener(this); viewHolder.playPause.setOnClickListener(this);
final Context context = viewHolder.playPause.getContext(); final Context context = viewHolder.playPause.getContext();
if (message == currentlyPlayingMessage) { if (message == currentlyPlayingMessage) {
if (AudioPlayer.player != null && AudioPlayer.player.isPlaying()) { if (AudioPlayer.player != null && AudioPlayer.player.isPlaying()) {
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_pause_24dp);
MessageAdapter.setImageTint(viewHolder.playPause, viewHolder.bubbleColor);
viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio)); viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio));
viewHolder.progress.setEnabled(true); viewHolder.progress.setEnabled(true);
} else { } else {
viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio));
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_play_arrow_24dp);
MessageAdapter.setImageTint(viewHolder.playPause, viewHolder.bubbleColor);
viewHolder.progress.setEnabled(false); viewHolder.progress.setEnabled(false);
} }
return true; return true;
} else { } else {
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_play_arrow_24dp);
MessageAdapter.setImageTint(viewHolder.playPause, viewHolder.bubbleColor);
viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio));
viewHolder.runtime.setText(formatTime(message.getFileParams().runtime)); viewHolder.runtime.setText(formatTime(message.getFileParams().runtime));
viewHolder.progress.setProgress(0); viewHolder.progress.setProgress(0);
@ -145,9 +158,16 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
} }
private void startStop(ImageButton playPause) { private void startStop(ImageButton playPause) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(messageAdapter.getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
&& ContextCompat.checkSelfPermission(
messageAdapter.getActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
pendingOnClickView.push(new WeakReference<>(playPause)); pendingOnClickView.push(new WeakReference<>(playPause));
ActivityCompat.requestPermissions(messageAdapter.getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, ConversationsActivity.REQUEST_PLAY_PAUSE); ActivityCompat.requestPermissions(
messageAdapter.getActivity(),
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
ConversationsActivity.REQUEST_PLAY_PAUSE);
return; return;
} }
initializeProximityWakeLock(playPause.getContext()); initializeProximityWakeLock(playPause.getContext());
@ -163,13 +183,13 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
private boolean playPauseCurrent(final ViewHolder viewHolder) { private boolean playPauseCurrent(final ViewHolder viewHolder) {
final Context context = viewHolder.playPause.getContext(); final Context context = viewHolder.playPause.getContext();
viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f);
if (player.isPlaying()) { if (player.isPlaying()) {
viewHolder.progress.setEnabled(false); viewHolder.progress.setEnabled(false);
player.pause(); player.pause();
messageAdapter.flagScreenOff(); messageAdapter.flagScreenOff();
releaseProximityWakeLock(); releaseProximityWakeLock();
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_play_arrow_24dp);
MessageAdapter.setImageTint(viewHolder.playPause,viewHolder.bubbleColor);
viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio)); viewHolder.playPause.setContentDescription(context.getString(R.string.play_audio));
} else { } else {
viewHolder.progress.setEnabled(true); viewHolder.progress.setEnabled(true);
@ -177,7 +197,8 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
messageAdapter.flagScreenOn(); messageAdapter.flagScreenOn();
acquireProximityWakeLock(); acquireProximityWakeLock();
this.stopRefresher(true); this.stopRefresher(true);
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_pause_24dp);
MessageAdapter.setImageTint(viewHolder.playPause,viewHolder.bubbleColor);
viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio)); viewHolder.playPause.setContentDescription(context.getString(R.string.pause_audio));
} }
return false; return false;
@ -193,19 +214,24 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
AudioPlayer.player = new MediaPlayer(); AudioPlayer.player = new MediaPlayer();
try { try {
AudioPlayer.currentlyPlayingMessage = message; AudioPlayer.currentlyPlayingMessage = message;
AudioPlayer.player.setAudioStreamType(earpiece ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); AudioPlayer.player.setAudioStreamType(
AudioPlayer.player.setDataSource(messageAdapter.getFileBackend().getFile(message).getAbsolutePath()); earpiece ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC);
AudioPlayer.player.setDataSource(
messageAdapter.getFileBackend().getFile(message).getAbsolutePath());
AudioPlayer.player.setOnCompletionListener(this); AudioPlayer.player.setOnCompletionListener(this);
AudioPlayer.player.prepare(); AudioPlayer.player.prepare();
AudioPlayer.player.start(); AudioPlayer.player.start();
messageAdapter.flagScreenOn(); messageAdapter.flagScreenOn();
acquireProximityWakeLock(); acquireProximityWakeLock();
viewHolder.progress.setEnabled(true); viewHolder.progress.setEnabled(true);
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp); viewHolder.playPause.setImageResource(R.drawable.ic_pause_24dp);
viewHolder.playPause.setContentDescription(viewHolder.playPause.getContext().getString(R.string.pause_audio)); MessageAdapter.setImageTint(viewHolder.playPause,viewHolder.bubbleColor);
sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); viewHolder.playPause.setContentDescription(
viewHolder.playPause.getContext().getString(R.string.pause_audio));
sensorManager.registerListener(
this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
return true; return true;
} catch (Exception e) { } catch (final Exception e) {
messageAdapter.flagScreenOff(); messageAdapter.flagScreenOff();
releaseProximityWakeLock(); releaseProximityWakeLock();
AudioPlayer.currentlyPlayingMessage = null; AudioPlayer.currentlyPlayingMessage = null;
@ -251,14 +277,16 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
} }
} }
private void resetPlayerUi(RelativeLayout audioPlayer) { private void resetPlayerUi(final RelativeLayout audioPlayer) {
if (audioPlayer == null) { if (audioPlayer == null) {
return; return;
} }
final ViewHolder viewHolder = ViewHolder.get(audioPlayer); final ViewHolder viewHolder = ViewHolder.get(audioPlayer);
final Message message = (Message) audioPlayer.getTag(); final Message message = (Message) audioPlayer.getTag();
viewHolder.playPause.setContentDescription(viewHolder.playPause.getContext().getString(R.string.play_audio)); viewHolder.playPause.setContentDescription(
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp); viewHolder.playPause.getContext().getString(R.string.play_audio));
viewHolder.playPause.setImageResource(R.drawable.ic_play_arrow_24dp);
MessageAdapter.setImageTint(viewHolder.playPause,viewHolder.bubbleColor);
if (message != null) { if (message != null) {
viewHolder.runtime.setText(formatTime(message.getFileParams().runtime)); viewHolder.runtime.setText(formatTime(message.getFileParams().runtime));
} }
@ -297,14 +325,10 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
} }
@Override @Override
public void onStartTrackingTouch(SeekBar seekBar) { public void onStartTrackingTouch(SeekBar seekBar) {}
}
@Override @Override
public void onStopTrackingTouch(SeekBar seekBar) { public void onStopTrackingTouch(SeekBar seekBar) {}
}
public void stop() { public void stop() {
synchronized (AudioPlayer.LOCK) { synchronized (AudioPlayer.LOCK) {
@ -361,7 +385,8 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
} else { } else {
viewHolder.progress.setProgress(current * 100 / duration); viewHolder.progress.setProgress(current * 100 / duration);
} }
viewHolder.runtime.setText(String.format("%s / %s", formatTime(current), formatTime(duration))); viewHolder.runtime.setText(
String.format("%s / %s", formatTime(current), formatTime(duration)));
return true; return true;
} }
@ -391,7 +416,11 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
try { try {
ViewHolder currentViewHolder = getCurrentViewHolder(); ViewHolder currentViewHolder = getCurrentViewHolder();
if (currentViewHolder != null) { if (currentViewHolder != null) {
play(currentViewHolder, currentlyPlayingMessage, streamType == AudioManager.STREAM_VOICE_CALL, progress); play(
currentViewHolder,
currentlyPlayingMessage,
streamType == AudioManager.STREAM_VOICE_CALL,
progress);
} }
} catch (Exception e) { } catch (Exception e) {
Log.w(Config.LOGTAG, e); Log.w(Config.LOGTAG, e);
@ -401,8 +430,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
} }
@Override @Override
public void onAccuracyChanged(Sensor sensor, int i) { public void onAccuracyChanged(Sensor sensor, int i) {}
}
private void acquireProximityWakeLock() { private void acquireProximityWakeLock() {
synchronized (AudioPlayer.LOCK) { synchronized (AudioPlayer.LOCK) {
@ -435,22 +463,24 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
private TextView runtime; private TextView runtime;
private SeekBar progress; private SeekBar progress;
private ImageButton playPause; private ImageButton playPause;
private boolean darkBackground = false; private MessageAdapter.BubbleColor bubbleColor = MessageAdapter.BubbleColor.SURFACE;
public static ViewHolder get(RelativeLayout audioPlayer) { public static ViewHolder get(final RelativeLayout audioPlayer) {
ViewHolder viewHolder = (ViewHolder) audioPlayer.getTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER); final var existingViewHolder =
if (viewHolder == null) { (ViewHolder) audioPlayer.getTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER);
viewHolder = new ViewHolder(); if (existingViewHolder != null) {
viewHolder.runtime = audioPlayer.findViewById(R.id.runtime); return existingViewHolder;
viewHolder.progress = audioPlayer.findViewById(R.id.progress);
viewHolder.playPause = audioPlayer.findViewById(R.id.play_pause);
audioPlayer.setTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER, viewHolder);
} }
final ViewHolder viewHolder = new ViewHolder();
viewHolder.runtime = audioPlayer.findViewById(R.id.runtime);
viewHolder.progress = audioPlayer.findViewById(R.id.progress);
viewHolder.playPause = audioPlayer.findViewById(R.id.play_pause);
audioPlayer.setTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER, viewHolder);
return viewHolder; return viewHolder;
} }
public void setDarkBackground(boolean darkBackground) { public void setBubbleColor(final MessageAdapter.BubbleColor bubbleColor) {
this.darkBackground = darkBackground; this.bubbleColor = bubbleColor;
} }
} }
} }

View file

@ -1,88 +0,0 @@
package eu.siacs.conversations.ui.util;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
public class ActionBarUtil {
public static void resetActionBarOnClickListeners(@NonNull View view) {
final View title = findActionBarTitle(view);
final View subtitle = findActionBarSubTitle(view);
if (title != null) {
title.setOnClickListener(null);
}
if (subtitle != null) {
subtitle.setOnClickListener(null);
}
}
public static void setActionBarOnClickListener(@NonNull View view,
@NonNull final View.OnClickListener onClickListener) {
final View title = findActionBarTitle(view);
final View subtitle = findActionBarSubTitle(view);
if (title != null) {
title.setOnClickListener(onClickListener);
}
if (subtitle != null) {
subtitle.setOnClickListener(onClickListener);
}
}
private static @Nullable View findActionBarTitle(@NonNull View root) {
return findActionBarItem(root, "action_bar_title", "mTitleTextView");
}
private static @Nullable
View findActionBarSubTitle(@NonNull View root) {
return findActionBarItem(root, "action_bar_subtitle", "mSubtitleTextView");
}
private static @Nullable View findActionBarItem(@NonNull View root,
@NonNull String resourceName,
@NonNull String toolbarFieldName) {
View result = findViewSupportOrAndroid(root, resourceName);
if (result == null) {
View actionBar = findViewSupportOrAndroid(root, "action_bar");
if (actionBar != null) {
result = reflectiveRead(actionBar, toolbarFieldName);
}
}
if (result == null && root.getClass().getName().endsWith("widget.Toolbar")) {
result = reflectiveRead(root, toolbarFieldName);
}
return result;
}
@SuppressWarnings("ConstantConditions")
private static @Nullable View findViewSupportOrAndroid(@NonNull View root,
@NonNull String resourceName) {
Context context = root.getContext();
View result = null;
if (result == null) {
int supportID = context.getResources().getIdentifier(resourceName, "id", context.getPackageName());
result = root.findViewById(supportID);
}
if (result == null) {
int androidID = context.getResources().getIdentifier(resourceName, "id", "android");
result = root.findViewById(androidID);
}
return result;
}
@SuppressWarnings("unchecked")
private static <T> T reflectiveRead(@NonNull Object object, @NonNull String fieldName) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(object);
} catch (final Exception ex) {
return null;
}
}
}

View file

@ -38,6 +38,9 @@ import android.os.Parcelable;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.utils.MimeUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
@ -46,9 +49,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.MimeUtils;
public class Attachment implements Parcelable { public class Attachment implements Parcelable {
Attachment(Parcel in) { Attachment(Parcel in) {
@ -71,17 +71,18 @@ public class Attachment implements Parcelable {
return 0; return 0;
} }
public static final Creator<Attachment> CREATOR = new Creator<Attachment>() { public static final Creator<Attachment> CREATOR =
@Override new Creator<Attachment>() {
public Attachment createFromParcel(Parcel in) { @Override
return new Attachment(in); public Attachment createFromParcel(Parcel in) {
} return new Attachment(in);
}
@Override @Override
public Attachment[] newArray(int size) { public Attachment[] newArray(int size) {
return new Attachment[size]; return new Attachment[size];
} }
}; };
public String getMime() { public String getMime() {
return mime; return mime;
@ -103,7 +104,10 @@ public class Attachment implements Parcelable {
} }
public enum Type { public enum Type {
FILE, IMAGE, LOCATION, RECORDING FILE,
IMAGE,
LOCATION,
RECORDING
} }
private final Uri uri; private final Uri uri;
@ -125,8 +129,8 @@ public class Attachment implements Parcelable {
this.uuid = UUID.randomUUID(); this.uuid = UUID.randomUUID();
} }
public static boolean canBeSendInband(final List<Attachment> attachments) { public static boolean canBeSendInBand(final List<Attachment> attachments) {
for (Attachment attachment : attachments) { for (final Attachment attachment : attachments) {
if (attachment.type != Type.LOCATION) { if (attachment.type != Type.LOCATION) {
return false; return false;
} }
@ -135,10 +139,30 @@ public class Attachment implements Parcelable {
} }
public static List<Attachment> of(final Context context, Uri uri, Type type) { public static List<Attachment> of(final Context context, Uri uri, Type type) {
final String mime = type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri); final String mime =
type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri);
return Collections.singletonList(new Attachment(uri, type, mime)); return Collections.singletonList(new Attachment(uri, type, mime));
} }
public static Attachment of(final Message message) {
final UUID uuid = UUID.fromString(message.getUuid());
if (message.isGeoUri()) {
return new Attachment(uuid, Uri.EMPTY, Type.LOCATION, null);
}
final String mime = message.getMimeType();
if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
final Message.FileParams fileParams = message.getFileParams();
if (fileParams.width > 0 && fileParams.height > 0) {
return new Attachment(uuid, Uri.EMPTY, Type.FILE, "video/*");
} else if (fileParams.runtime > 0) {
return new Attachment(uuid, Uri.EMPTY, Type.FILE, "audio/*");
} else {
return new Attachment(uuid, Uri.EMPTY, Type.FILE, "application/octet-stream");
}
}
return new Attachment(uuid, Uri.EMPTY, Type.FILE, mime);
}
public static List<Attachment> of(final Context context, List<Uri> uris, final String type) { public static List<Attachment> of(final Context context, List<Uri> uris, final String type) {
final List<Attachment> attachments = new ArrayList<>(); final List<Attachment> attachments = new ArrayList<>();
for (final Uri uri : uris) { for (final Uri uri : uris) {
@ -146,16 +170,25 @@ public class Attachment implements Parcelable {
continue; continue;
} }
final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, type); final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, type);
attachments.add(new Attachment(uri, mime != null && isImage(mime) ? Type.IMAGE : Type.FILE, mime)); attachments.add(
new Attachment(
uri, mime != null && isImage(mime) ? Type.IMAGE : Type.FILE, mime));
} }
return attachments; return attachments;
} }
public static Attachment of(UUID uuid, final File file, String mime) { public static Attachment of(UUID uuid, final File file, String mime) {
return new Attachment(uuid, Uri.fromFile(file), mime != null && (isImage(mime) || mime.startsWith("video/")) ? Type.IMAGE : Type.FILE, mime); return new Attachment(
uuid,
Uri.fromFile(file),
mime != null && (isImage(mime) || mime.startsWith("video/"))
? Type.IMAGE
: Type.FILE,
mime);
} }
public static List<Attachment> extractAttachments(final Context context, final Intent intent, Type type) { public static List<Attachment> extractAttachments(
final Context context, final Intent intent, Type type) {
List<Attachment> uris = new ArrayList<>(); List<Attachment> uris = new ArrayList<>();
if (intent == null) { if (intent == null) {
return uris; return uris;
@ -167,7 +200,8 @@ public class Attachment implements Parcelable {
if (clipData != null) { if (clipData != null) {
for (int i = 0; i < clipData.getItemCount(); ++i) { for (int i = 0; i < clipData.getItemCount(); ++i) {
final Uri uri = clipData.getItemAt(i).getUri(); final Uri uri = clipData.getItemAt(i).getUri();
final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, contentType); final String mime =
MimeUtils.guessMimeTypeFromUriAndMime(context, uri, contentType);
uris.add(new Attachment(uri, type, mime)); uris.add(new Attachment(uri, type, mime));
} }
} }
@ -179,13 +213,12 @@ public class Attachment implements Parcelable {
} }
public boolean renderThumbnail() { public boolean renderThumbnail() {
return type == Type.IMAGE || (type == Type.FILE && mime != null && renderFileThumbnail(mime)); return type == Type.IMAGE
|| (type == Type.FILE && mime != null && renderFileThumbnail(mime));
} }
private static boolean renderFileThumbnail(final String mime) { private static boolean renderFileThumbnail(final String mime) {
return mime.startsWith("video/") return mime.startsWith("video/") || isImage(mime) || "application/pdf".equals(mime);
|| isImage(mime)
|| "application/pdf".equals(mime);
} }
public Uri getUri() { public Uri getUri() {

View file

@ -102,14 +102,16 @@ public class ConversationMenuConfigurator {
return; return;
} }
if (conversation.getNextEncryption() != Message.ENCRYPTION_NONE) { if (next == Message.ENCRYPTION_NONE) {
menuSecure.setIcon(R.drawable.ic_lock_white_24dp); menuSecure.setIcon(R.drawable.ic_lock_open_outline_24dp);
} else {
menuSecure.setIcon(R.drawable.ic_lock_24dp);
} }
pgp.setVisible(Config.supportOpenPgp()); pgp.setVisible(Config.supportOpenPgp());
none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI); none.setVisible(Config.supportUnencrypted() || conversation.getMode() == Conversation.MODE_MULTI);
axolotl.setVisible(Config.supportOmemo()); axolotl.setVisible(Config.supportOmemo());
switch (conversation.getNextEncryption()) { switch (next) {
case Message.ENCRYPTION_PGP: case Message.ENCRYPTION_PGP:
menuSecure.setTitle(R.string.encrypted_with_openpgp); menuSecure.setTitle(R.string.encrypted_with_openpgp);
pgp.setChecked(true); pgp.setChecked(true);

View file

@ -12,6 +12,8 @@ import android.view.View;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -200,7 +202,7 @@ public final class MucDetailsContextMenuHelper {
activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE); activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE);
} }
} else { } else {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
builder.setTitle(R.string.ban_from_conference); builder.setTitle(R.string.ban_from_conference);
String jid = user.getRealJid().asBareJid().toString(); String jid = user.getRealJid().asBareJid().toString();
SpannableString message = new SpannableString(activity.getString(R.string.removing_from_public_conference, jid)); SpannableString message = new SpannableString(activity.getString(R.string.removing_from_public_conference, jid));

View file

@ -36,6 +36,8 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -72,7 +74,7 @@ public class PresenceSelector {
private static void showPresenceSelectionDialog(final Activity activity, final Contact contact, final String[] resourceArray, final OnFullJidSelected onFullJidSelected) { private static void showPresenceSelectionDialog(final Activity activity, final Contact contact, final String[] resourceArray, final OnFullJidSelected onFullJidSelected) {
final Presences presences = contact.getPresences(); final Presences presences = contact.getPresences();
AlertDialog.Builder builder = new AlertDialog.Builder(activity); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
builder.setTitle(activity.getString(R.string.choose_presence)); builder.setTitle(activity.getString(R.string.choose_presence));
Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap(); Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
final Map<String, String> resourceTypeMap = typeAndName.first; final Map<String, String> resourceTypeMap = typeAndName.first;
@ -128,8 +130,8 @@ public class PresenceSelector {
} }
} }
public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { public static void warnMutualPresenceSubscription(final Activity activity, final Conversation conversation, final OnPresenceSelected listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
builder.setTitle(conversation.getContact().getJid().toString()); builder.setTitle(conversation.getContact().getJid().toString());
builder.setMessage(R.string.without_mutual_presence_updates); builder.setMessage(R.string.without_mutual_presence_updates);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);

View file

@ -31,8 +31,15 @@ package eu.siacs.conversations.ui.util;
import android.app.Activity; import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.TypedArray; import android.content.res.Configuration;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
import com.google.android.material.color.MaterialColors;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -42,149 +49,90 @@ import eu.siacs.conversations.utils.UIHelper;
public class SendButtonTool { public class SendButtonTool {
public static SendButtonAction getAction(final Activity activity, final Conversation c, final String text) { public static SendButtonAction getAction(
if (activity == null) { final Activity activity, final Conversation c, final String text) {
return SendButtonAction.TEXT; if (activity == null) {
} return SendButtonAction.TEXT;
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); }
final boolean empty = text.length() == 0; final SharedPreferences preferences =
final boolean conference = c.getMode() == Conversation.MODE_MULTI; PreferenceManager.getDefaultSharedPreferences(activity);
if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) { final boolean empty = text.isEmpty();
return SendButtonAction.CANCEL; final boolean conference = c.getMode() == Conversation.MODE_MULTI;
} else if (conference && !c.getAccount().httpUploadAvailable()) { if (c.getCorrectingMessage() != null
if (empty && c.getNextCounterpart() != null) { && (empty || text.equals(c.getCorrectingMessage().getBody()))) {
return SendButtonAction.CANCEL; return SendButtonAction.CANCEL;
} else { } else if (conference && !c.getAccount().httpUploadAvailable()) {
return SendButtonAction.TEXT; if (empty && c.getNextCounterpart() != null) {
} return SendButtonAction.CANCEL;
} else { } else {
if (empty) { return SendButtonAction.TEXT;
if (conference && c.getNextCounterpart() != null) { }
return SendButtonAction.CANCEL; } else {
} else { if (empty) {
String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action)); if (conference && c.getNextCounterpart() != null) {
if (!"none".equals(setting) && UIHelper.receivedLocationQuestion(c.getLatestMessage())) { return SendButtonAction.CANCEL;
return SendButtonAction.SEND_LOCATION; } else {
} else { String setting =
if ("recent".equals(setting)) { preferences.getString(
setting = preferences.getString(ConversationFragment.RECENTLY_USED_QUICK_ACTION, SendButtonAction.TEXT.toString()); "quick_action",
return SendButtonAction.valueOfOrDefault(setting); activity.getResources().getString(R.string.quick_action));
} else { if (!"none".equals(setting)
return SendButtonAction.valueOfOrDefault(setting); && UIHelper.receivedLocationQuestion(c.getLatestMessage())) {
} return SendButtonAction.SEND_LOCATION;
} } else {
} if ("recent".equals(setting)) {
} else { setting =
return SendButtonAction.TEXT; preferences.getString(
} ConversationFragment.RECENTLY_USED_QUICK_ACTION,
} SendButtonAction.TEXT.toString());
} return SendButtonAction.valueOfOrDefault(setting);
} else {
return SendButtonAction.valueOfOrDefault(setting);
}
}
}
} else {
return SendButtonAction.TEXT;
}
}
}
public static int getSendButtonImageResource(Activity activity, SendButtonAction action, Presence.Status status) { public @DrawableRes static int getSendButtonImageResource(final SendButtonAction action) {
switch (action) { return switch (action) {
case TEXT: case TEXT -> R.drawable.ic_send_24dp;
switch (status) { case TAKE_PHOTO -> R.drawable.ic_camera_alt_24dp;
case CHAT: case SEND_LOCATION -> R.drawable.ic_location_pin_24dp;
case ONLINE: case CHOOSE_PICTURE -> R.drawable.ic_image_24dp;
return R.drawable.ic_send_text_online; case RECORD_VIDEO -> R.drawable.ic_videocam_24dp;
case AWAY: case RECORD_VOICE -> R.drawable.ic_mic_24dp;
return R.drawable.ic_send_text_away; case CANCEL -> R.drawable.ic_cancel_24dp;
case XA: };
case DND: }
return R.drawable.ic_send_text_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_text_offline, R.drawable.ic_send_text_offline);
}
case RECORD_VIDEO:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_videocam_online;
case AWAY:
return R.drawable.ic_send_videocam_away;
case XA:
case DND:
return R.drawable.ic_send_videocam_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_videocam_offline, R.drawable.ic_send_videocam_offline);
}
case TAKE_PHOTO:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_photo_online;
case AWAY:
return R.drawable.ic_send_photo_away;
case XA:
case DND:
return R.drawable.ic_send_photo_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_photo_offline, R.drawable.ic_send_photo_offline);
}
case RECORD_VOICE:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_voice_online;
case AWAY:
return R.drawable.ic_send_voice_away;
case XA:
case DND:
return R.drawable.ic_send_voice_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_voice_offline, R.drawable.ic_send_voice_offline);
}
case SEND_LOCATION:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_location_online;
case AWAY:
return R.drawable.ic_send_location_away;
case XA:
case DND:
return R.drawable.ic_send_location_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_location_offline, R.drawable.ic_send_location_offline);
}
case CANCEL:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_cancel_online;
case AWAY:
return R.drawable.ic_send_cancel_away;
case XA:
case DND:
return R.drawable.ic_send_cancel_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_cancel_offline, R.drawable.ic_send_cancel_offline);
}
case CHOOSE_PICTURE:
switch (status) {
case CHAT:
case ONLINE:
return R.drawable.ic_send_picture_online;
case AWAY:
return R.drawable.ic_send_picture_away;
case XA:
case DND:
return R.drawable.ic_send_picture_dnd;
default:
return getThemeResource(activity, R.attr.ic_send_picture_offline, R.drawable.ic_send_picture_offline);
}
}
return getThemeResource(activity, R.attr.ic_send_text_offline, R.drawable.ic_send_text_offline);
}
private static int getThemeResource(Activity activity, int r_attr_name, int r_drawable_def) {
int[] attrs = {r_attr_name};
TypedArray ta = activity.getTheme().obtainStyledAttributes(attrs);
int res = ta.getResourceId(0, r_drawable_def);
ta.recycle();
return res;
}
public @ColorInt static int getSendButtonColor(final View view, final Presence.Status status) {
final boolean nightMode =
(view.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
== Configuration.UI_MODE_NIGHT_YES;
return switch (status) {
case OFFLINE -> MaterialColors.getColor(
view, com.google.android.material.R.attr.colorOnSurface);
case ONLINE, CHAT -> MaterialColors.harmonizeWithPrimary(
view.getContext(),
ContextCompat.getColor(
view.getContext(), nightMode ? R.color.green_200 : R.color.green_800));
case AWAY -> MaterialColors.harmonizeWithPrimary(
view.getContext(),
ContextCompat.getColor(
view.getContext(), nightMode ? R.color.amber_200 : R.color.amber_800));
case XA -> MaterialColors.harmonizeWithPrimary(
view.getContext(),
ContextCompat.getColor(
view.getContext(),
nightMode ? R.color.orange_200 : R.color.orange_800));
case DND -> MaterialColors.harmonizeWithPrimary(
view.getContext(),
ContextCompat.getColor(
view.getContext(), nightMode ? R.color.red_200 : R.color.red_800));
};
}
} }

View file

@ -1,59 +0,0 @@
/*
* Copyright (c) 2018, Daniel Gultsch All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package eu.siacs.conversations.ui.util;
import android.content.Context;
import android.content.res.TypedArray;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
public class StyledAttributes {
public static android.graphics.drawable.Drawable getDrawable(Context context, @AttrRes int id) {
TypedArray typedArray = context.obtainStyledAttributes(new int[]{id});
android.graphics.drawable.Drawable drawable = typedArray.getDrawable(0);
typedArray.recycle();
return drawable;
}
public static float getFloat(Context context, @AttrRes int id) {
TypedArray typedArray = context.obtainStyledAttributes(new int[]{id});
float value = typedArray.getFloat(0,0f);
typedArray.recycle();
return value;
}
public static @ColorInt int getColor(Context context, @AttrRes int attr) {
TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr});
int color = typedArray.getColor(0,0);
typedArray.recycle();
return color;
}
}

View file

@ -0,0 +1,166 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.siacs.conversations.ui.util;
import static java.util.Collections.max;
import static java.util.Collections.min;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.ActionMenuView;
import androidx.appcompat.widget.Toolbar;
import com.google.android.material.appbar.MaterialToolbar;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class ToolbarUtils {
private static final Comparator<View> VIEW_TOP_COMPARATOR =
new Comparator<View>() {
@Override
public int compare(View view1, View view2) {
return view1.getTop() - view2.getTop();
}
};
private ToolbarUtils() {
// Private constructor to prevent unwanted construction.
}
public static void resetActionBarOnClickListeners(@NonNull MaterialToolbar view) {
final TextView title = getTitleTextView(view);
final TextView subtitle = getSubtitleTextView(view);
if (title != null) {
title.setOnClickListener(null);
}
if (subtitle != null) {
subtitle.setOnClickListener(null);
}
}
public static void setActionBarOnClickListener(
@NonNull MaterialToolbar view, @NonNull final View.OnClickListener onClickListener) {
final TextView title = getTitleTextView(view);
final TextView subtitle = getSubtitleTextView(view);
if (title != null) {
title.setOnClickListener(onClickListener);
}
if (subtitle != null) {
subtitle.setOnClickListener(onClickListener);
}
}
@Nullable
public static TextView getTitleTextView(@NonNull Toolbar toolbar) {
List<TextView> textViews = getTextViewsWithText(toolbar, toolbar.getTitle());
return textViews.isEmpty() ? null : min(textViews, VIEW_TOP_COMPARATOR);
}
@Nullable
public static TextView getSubtitleTextView(@NonNull Toolbar toolbar) {
List<TextView> textViews = getTextViewsWithText(toolbar, toolbar.getSubtitle());
return textViews.isEmpty() ? null : max(textViews, VIEW_TOP_COMPARATOR);
}
private static List<TextView> getTextViewsWithText(
@NonNull Toolbar toolbar, CharSequence text) {
List<TextView> textViews = new ArrayList<>();
for (int i = 0; i < toolbar.getChildCount(); i++) {
View child = toolbar.getChildAt(i);
if (child instanceof TextView textView) {
if (TextUtils.equals(textView.getText(), text)) {
textViews.add(textView);
}
}
}
return textViews;
}
@Nullable
public static ImageView getLogoImageView(@NonNull Toolbar toolbar) {
return getImageView(toolbar, toolbar.getLogo());
}
@Nullable
private static ImageView getImageView(@NonNull Toolbar toolbar, @Nullable Drawable content) {
if (content == null) {
return null;
}
for (int i = 0; i < toolbar.getChildCount(); i++) {
View child = toolbar.getChildAt(i);
if (child instanceof ImageView imageView) {
Drawable drawable = imageView.getDrawable();
if (drawable != null
&& drawable.getConstantState() != null
&& drawable.getConstantState().equals(content.getConstantState())) {
return imageView;
}
}
}
return null;
}
@Nullable
public static View getSecondaryActionMenuItemView(@NonNull Toolbar toolbar) {
ActionMenuView actionMenuView = getActionMenuView(toolbar);
if (actionMenuView != null) {
// Only return the first child of the ActionMenuView if there is more than one child
if (actionMenuView.getChildCount() > 1) {
return actionMenuView.getChildAt(0);
}
}
return null;
}
@Nullable
public static ActionMenuView getActionMenuView(@NonNull Toolbar toolbar) {
for (int i = 0; i < toolbar.getChildCount(); i++) {
View child = toolbar.getChildAt(i);
if (child instanceof ActionMenuView) {
return (ActionMenuView) child;
}
}
return null;
}
@Nullable
public static ImageButton getNavigationIconButton(@NonNull Toolbar toolbar) {
Drawable navigationIcon = toolbar.getNavigationIcon();
if (navigationIcon == null) {
return null;
}
for (int i = 0; i < toolbar.getChildCount(); i++) {
View child = toolbar.getChildAt(i);
if (child instanceof ImageButton imageButton) {
if (imageButton.getDrawable() == navigationIcon) {
return imageButton;
}
}
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show more