mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-25 12:48:33 +00:00
Support for per-recipient muting, blocking, and ringtones.
Fixes #757 Fixes #354 Fixes #222 Closes #1815 Closes #3378 // FREEBIE
This commit is contained in:
@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
@@ -10,7 +10,7 @@ import android.view.ViewConfiguration;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
|
||||
public abstract class BaseActionBarActivity extends ActionBarActivity {
|
||||
public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
@@ -46,7 +46,9 @@ public abstract class BaseActionBarActivity extends ActionBarActivity {
|
||||
menuKeyField.setAccessible(true);
|
||||
menuKeyField.setBoolean(config, false);
|
||||
}
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.w(TAG, "Failed to force overflow menu.");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Log.w(TAG, "Failed to force overflow menu.");
|
||||
}
|
||||
}
|
||||
|
||||
136
src/org/thoughtcrime/securesms/BlockedContactsActivity.java
Normal file
136
src/org/thoughtcrime/securesms/BlockedContactsActivity.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
|
||||
import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
@Override
|
||||
public void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.BlockedContactsActivity_blocked_contacts);
|
||||
initFragment(android.R.id.content, new BlockedContactsFragment(), masterSecret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
dynamicLanguage.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class BlockedContactsFragment
|
||||
extends ListFragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>, ListView.OnItemClickListener
|
||||
{
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
return inflater.inflate(R.layout.blocked_contacts_fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setListAdapter(new BlockedContactAdapter(getActivity(), null));
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle bundle) {
|
||||
super.onActivityCreated(bundle);
|
||||
getListView().setOnItemClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new BlockedContactsLoader(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
if (getListAdapter() != null) {
|
||||
((CursorAdapter) getListAdapter()).changeCursor(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
if (getListAdapter() != null) {
|
||||
((CursorAdapter) getListAdapter()).changeCursor(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Recipients recipients = ((BlockedContactListItem)view).getRecipients();
|
||||
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
|
||||
intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private static class BlockedContactAdapter extends CursorAdapter {
|
||||
|
||||
public BlockedContactAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return LayoutInflater.from(context)
|
||||
.inflate(R.layout.blocked_contact_list_item, parent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
String recipientIds = cursor.getString(1);
|
||||
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, true);
|
||||
|
||||
((BlockedContactListItem) view).set(recipients);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,13 +27,13 @@ import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.support.v7.app.ActionBar.LayoutParams;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
@@ -44,6 +44,7 @@ import android.view.View.OnKeyListener;
|
||||
import android.view.ViewStub;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -54,9 +55,9 @@ import com.google.protobuf.ByteString;
|
||||
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
import org.thoughtcrime.securesms.components.ComposeText;
|
||||
import org.thoughtcrime.securesms.components.SendButton;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||
import org.thoughtcrime.securesms.components.SendButton;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
@@ -83,6 +84,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients.RecipientsModifiedListener;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
|
||||
@@ -107,7 +109,6 @@ import java.util.List;
|
||||
|
||||
import static org.thoughtcrime.securesms.TransportOption.Type;
|
||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import static org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
|
||||
import static org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||
|
||||
/**
|
||||
@@ -120,7 +121,7 @@ import static org.whispersystems.textsecure.internal.push.PushMessageProtos.Push
|
||||
public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
implements ConversationFragment.ConversationFragmentListener,
|
||||
AttachmentManager.AttachmentListener,
|
||||
RecipientModifiedListener
|
||||
RecipientsModifiedListener
|
||||
{
|
||||
private static final String TAG = ConversationActivity.class.getSimpleName();
|
||||
|
||||
@@ -139,13 +140,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private static final int GROUP_EDIT = 5;
|
||||
private static final int CAPTURE_PHOTO = 6;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private ComposeText composeText;
|
||||
private AnimatingToggle buttonToggle;
|
||||
private SendButton sendButton;
|
||||
private ImageButton attachButton;
|
||||
private TextView charactersLeft;
|
||||
private ConversationFragment fragment;
|
||||
private MasterSecret masterSecret;
|
||||
private ComposeText composeText;
|
||||
private AnimatingToggle buttonToggle;
|
||||
private SendButton sendButton;
|
||||
private ImageButton attachButton;
|
||||
private ConversationTitleView titleView;
|
||||
private TextView charactersLeft;
|
||||
private ConversationFragment fragment;
|
||||
private Button unblockButton;
|
||||
private View composePanel;
|
||||
|
||||
private AttachmentTypeSelectorAdapter attachmentAdapter;
|
||||
private AttachmentManager attachmentManager;
|
||||
@@ -175,10 +179,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
this.masterSecret = masterSecret;
|
||||
|
||||
setContentView(R.layout.conversation_activity);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
fragment = initFragment(R.id.fragment_content, new ConversationFragment(), masterSecret, dynamicLanguage.getCurrentLocale());
|
||||
|
||||
fragment = initFragment(R.id.fragment_content, new ConversationFragment(),
|
||||
masterSecret, dynamicLanguage.getCurrentLocale());
|
||||
|
||||
initializeReceivers();
|
||||
initializeActionBar();
|
||||
initializeViews();
|
||||
initializeResources();
|
||||
initializeDraft();
|
||||
@@ -210,10 +216,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
dynamicLanguage.onResume(this);
|
||||
|
||||
initializeSecurity();
|
||||
initializeTitleBar();
|
||||
initializeEnabledCheck();
|
||||
initializeMmsEnabledCheck();
|
||||
initializeIme();
|
||||
|
||||
titleView.setTitle(recipients);
|
||||
setBlockedUserState(recipients);
|
||||
calculateCharactersRemaining();
|
||||
|
||||
MessageNotifier.setVisibleThread(threadId);
|
||||
@@ -263,7 +271,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
break;
|
||||
case GROUP_EDIT:
|
||||
this.recipients = RecipientFactory.getRecipientsForIds(this, data.getLongArrayExtra(GroupCreateActivity.GROUP_RECIPIENT_EXTRA), true);
|
||||
initializeTitleBar();
|
||||
titleView.setTitle(recipients);
|
||||
setBlockedUserState(recipients);
|
||||
supportInvalidateOptionsMenu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -299,6 +309,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
inflater.inflate(R.menu.conversation, menu);
|
||||
|
||||
if (recipients != null && recipients.isMuted()) inflater.inflate(R.menu.conversation_muted, menu);
|
||||
else inflater.inflate(R.menu.conversation_unmuted, menu);
|
||||
|
||||
if (isSingleConversation() && getRecipients().getPrimaryRecipient().getContactUri() == null) {
|
||||
inflater.inflate(R.menu.conversation_add_to_contacts, menu);
|
||||
}
|
||||
@@ -324,6 +337,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
case R.id.menu_edit_group: handleEditPushGroup(); return true;
|
||||
case R.id.menu_leave: handleLeavePushGroup(); return true;
|
||||
case R.id.menu_invite: handleInviteLink(); return true;
|
||||
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
|
||||
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
}
|
||||
|
||||
@@ -349,6 +364,61 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleMuteNotifications() {
|
||||
MuteDialog.show(this, new MuteDialog.MuteSelectionListener() {
|
||||
@Override
|
||||
public void onMuted(final long until) {
|
||||
recipients.setMuted(until);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
|
||||
.setMuted(recipients, until);
|
||||
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleUnmuteNotifications() {
|
||||
recipients.setMuted(0);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
|
||||
.setMuted(recipients, 0);
|
||||
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleUnblock() {
|
||||
new AlertDialogWrapper.Builder(this)
|
||||
.setTitle(R.string.ConversationActivity_unblock_question)
|
||||
.setMessage(R.string.ConversationActivity_are_you_sure_you_want_to_unblock_this_contact)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ConversationActivity_unblock, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
recipients.setBlocked(false);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
|
||||
.setBlocked(recipients, false);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleInviteLink() {
|
||||
try {
|
||||
boolean a = SecureRandom.getInstance("SHA1PRNG").nextBoolean();
|
||||
@@ -551,45 +621,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
///// Initializers
|
||||
|
||||
private void initializeTitleBar() {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final Recipient recipient = getRecipients().getPrimaryRecipient();
|
||||
|
||||
if (isSingleConversation()) {
|
||||
if (TextUtils.isEmpty(recipient.getName())) {
|
||||
title = recipient.getNumber();
|
||||
subtitle = null;
|
||||
} else {
|
||||
title = recipient.getName();
|
||||
subtitle = PhoneNumberUtils.formatNumber(recipient.getNumber());
|
||||
}
|
||||
} else if (isGroupConversation()) {
|
||||
if (isPushGroupConversation()) {
|
||||
final String groupName = recipient.getName();
|
||||
|
||||
title = (!TextUtils.isEmpty(groupName)) ? groupName : getString(R.string.ConversationActivity_unnamed_group);
|
||||
subtitle = null;
|
||||
} else {
|
||||
final int size = getRecipients().getRecipientsList().size();
|
||||
|
||||
title = getString(R.string.ConversationActivity_group_conversation);
|
||||
subtitle = (size == 1) ? getString(R.string.ConversationActivity_d_recipients_in_group_singular)
|
||||
: String.format(getString(R.string.ConversationActivity_d_recipients_in_group), size);
|
||||
}
|
||||
} else {
|
||||
title = getString(R.string.ConversationActivity_compose_message);
|
||||
subtitle = null;
|
||||
}
|
||||
|
||||
getSupportActionBar().setTitle(title);
|
||||
getSupportActionBar().setSubtitle(subtitle);
|
||||
|
||||
getWindow().getDecorView().setContentDescription(getString(R.string.conversation_activity__window_description, title));
|
||||
|
||||
this.supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void initializeDraft() {
|
||||
String draftText = getIntent().getStringExtra(DRAFT_TEXT_EXTRA);
|
||||
Uri draftImage = getIntent().getParcelableExtra(DRAFT_IMAGE_EXTRA);
|
||||
@@ -659,6 +690,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
else sendButton.setDefaultTransport(Type.SMS);
|
||||
|
||||
calculateCharactersRemaining();
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private void initializeMmsEnabledCheck() {
|
||||
@@ -692,6 +724,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
|
||||
charactersLeft = (TextView) findViewById(R.id.space_left);
|
||||
emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
|
||||
titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
|
||||
unblockButton = (Button) findViewById(R.id.unblock_button);
|
||||
composePanel = findViewById(R.id.bottom_panel);
|
||||
|
||||
attachmentAdapter = new AttachmentTypeSelectorAdapter(this);
|
||||
attachmentManager = new AttachmentManager(this, this);
|
||||
@@ -710,6 +745,22 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
});
|
||||
|
||||
titleView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(ConversationActivity.this, RecipientPreferenceActivity.class);
|
||||
intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
unblockButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleUnblock();
|
||||
}
|
||||
});
|
||||
|
||||
composeText.setOnKeyListener(composeKeyPressedListener);
|
||||
composeText.addTextChangedListener(composeKeyPressedListener);
|
||||
composeText.setOnEditorActionListener(sendButtonListener);
|
||||
@@ -718,6 +769,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
emojiToggle.setOnClickListener(new EmojiToggleListener());
|
||||
}
|
||||
|
||||
private void initializeActionBar() {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setCustomView(R.layout.conversation_title_view);
|
||||
getSupportActionBar().setDisplayShowCustomEnabled(true);
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
}
|
||||
|
||||
private EmojiDrawer getEmojiDrawer() {
|
||||
if (emojiDrawer.isPresent()) return emojiDrawer.get();
|
||||
EmojiDrawer emojiDrawer = (EmojiDrawer)((ViewStub)findViewById(R.id.emoji_drawer_stub)).inflate();
|
||||
@@ -739,8 +797,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
initializeTitleBar();
|
||||
public void onModified(Recipients recipients) {
|
||||
titleView.setTitle(recipients);
|
||||
setBlockedUserState(recipients);
|
||||
}
|
||||
|
||||
private void initializeReceivers() {
|
||||
@@ -751,7 +810,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
if (eventThreadId == threadId || eventThreadId == -2) {
|
||||
initializeSecurity();
|
||||
initializeTitleBar();
|
||||
calculateCharactersRemaining();
|
||||
}
|
||||
}
|
||||
@@ -765,7 +823,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
long[] ids = recipients.getIds();
|
||||
Log.w("ConversationActivity", "Looking up new recipients...");
|
||||
recipients = RecipientFactory.getRecipientsForIds(context, ids, false);
|
||||
initializeTitleBar();
|
||||
titleView.setTitle(recipients);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -917,6 +975,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}.execute(thisThreadId);
|
||||
}
|
||||
|
||||
private void setBlockedUserState(Recipients recipients) {
|
||||
if (recipients.isBlocked()) {
|
||||
unblockButton.setVisibility(View.VISIBLE);
|
||||
composePanel.setVisibility(View.GONE);
|
||||
} else {
|
||||
composePanel.setVisibility(View.VISIBLE);
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateCharactersRemaining() {
|
||||
int charactersSpent = composeText.getText().toString().length();
|
||||
TransportOption transportOption = sendButton.getSelectedTransport();
|
||||
@@ -994,7 +1062,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (refreshFragment) {
|
||||
fragment.reload(recipients, threadId);
|
||||
|
||||
initializeTitleBar();
|
||||
initializeSecurity();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import android.support.v4.app.ListFragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.text.ClipboardManager;
|
||||
import android.util.Log;
|
||||
@@ -335,7 +335,7 @@ public class ConversationFragment extends ListFragment
|
||||
((ConversationAdapter) getListAdapter()).toggleBatchSelected(messageRecord);
|
||||
((ConversationAdapter) getListAdapter()).notifyDataSetChanged();
|
||||
|
||||
actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||
actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -248,7 +248,7 @@ public class ConversationListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public void onItemLongClick(ConversationListItem item) {
|
||||
actionMode = ((ActionBarActivity)getActivity()).startSupportActionMode(ConversationListFragment.this);
|
||||
actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(ConversationListFragment.this);
|
||||
|
||||
getListAdapter().initializeBatchMode(true);
|
||||
getListAdapter().toggleThreadInBatchSet(item.getThreadId());
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.widget.TextView;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
|
||||
@@ -44,7 +43,7 @@ import static org.thoughtcrime.securesms.util.SpanUtil.color;
|
||||
*/
|
||||
|
||||
public class ConversationListItem extends RelativeLayout
|
||||
implements Recipient.RecipientModifiedListener
|
||||
implements Recipients.RecipientsModifiedListener
|
||||
{
|
||||
private final static String TAG = ConversationListItem.class.getSimpleName();
|
||||
|
||||
@@ -76,12 +75,11 @@ public class ConversationListItem extends RelativeLayout
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.subjectView = (TextView) findViewById(R.id.subject);
|
||||
this.fromView = (FromTextView) findViewById(R.id.from);
|
||||
this.dateView = (TextView) findViewById(R.id.date);
|
||||
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
|
||||
|
||||
initializeContactWidgetVisibility();
|
||||
}
|
||||
|
||||
public void set(ThreadRecord thread, Locale locale, Set<Long> selectedThreads, boolean batchMode) {
|
||||
@@ -112,10 +110,6 @@ public class ConversationListItem extends RelativeLayout
|
||||
this.recipients.removeListener(this);
|
||||
}
|
||||
|
||||
private void initializeContactWidgetVisibility() {
|
||||
contactPhotoImage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setBatchState(boolean batch) {
|
||||
setSelected(batch && selectedThreads.contains(threadId));
|
||||
}
|
||||
@@ -133,7 +127,7 @@ public class ConversationListItem extends RelativeLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
public void onModified(final Recipients recipients) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
94
src/org/thoughtcrime/securesms/ConversationTitleView.java
Normal file
94
src/org/thoughtcrime/securesms/ConversationTitleView.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
public class ConversationTitleView extends LinearLayout {
|
||||
|
||||
private static final String TAG = ConversationTitleView.class.getSimpleName();
|
||||
|
||||
private TextView title;
|
||||
private TextView subtitle;
|
||||
|
||||
public ConversationTitleView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ConversationTitleView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
this.title = (TextView) findViewById(R.id.title);
|
||||
this.subtitle = (TextView) findViewById(R.id.subtitle);
|
||||
}
|
||||
|
||||
|
||||
public void setTitle(@Nullable Recipients recipients) {
|
||||
if (recipients == null) setComposeTitle();
|
||||
else if (recipients.isSingleRecipient()) setRecipientTitle(recipients.getPrimaryRecipient());
|
||||
else setRecipientsTitle(recipients);
|
||||
|
||||
if (recipients != null && recipients.isBlocked()) {
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_white_18dp, 0, 0, 0);
|
||||
} else if (recipients != null && recipients.isMuted()) {
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_white_18dp, 0, 0, 0);
|
||||
} else {
|
||||
title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void setComposeTitle() {
|
||||
this.title.setText(R.string.ConversationActivity_compose_message);
|
||||
this.subtitle.setText(null);
|
||||
this.subtitle.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void setRecipientTitle(Recipient recipient) {
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
if (TextUtils.isEmpty(recipient.getName())) {
|
||||
this.title.setText(recipient.getNumber());
|
||||
this.subtitle.setText(null);
|
||||
this.subtitle.setVisibility(View.GONE);
|
||||
} else {
|
||||
this.title.setText(recipient.getName());
|
||||
this.subtitle.setText(recipient.getNumber());
|
||||
this.subtitle.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
String groupName = (!TextUtils.isEmpty(recipient.getName())) ?
|
||||
recipient.getName() :
|
||||
getContext().getString(R.string.ConversationActivity_unnamed_group);
|
||||
|
||||
this.title.setText(groupName);
|
||||
this.subtitle.setText(null);
|
||||
this.subtitle.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRecipientsTitle(Recipients recipients) {
|
||||
int size = recipients.getRecipientsList().size();
|
||||
|
||||
title.setText(getContext().getString(R.string.ConversationActivity_group_conversation));
|
||||
subtitle.setText((size == 1) ? getContext().getString(R.string.ConversationActivity_d_recipients_in_group_singular) :
|
||||
String.format(getContext().getString(R.string.ConversationActivity_d_recipients_in_group), size));
|
||||
subtitle.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -465,7 +465,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
||||
}
|
||||
|
||||
private long handleCreateMmsGroup(Set<Recipient> members) {
|
||||
Recipients recipients = new Recipients(new LinkedList<Recipient>(members));
|
||||
Recipients recipients = RecipientFactory.getRecipientsFor(this, new LinkedList<>(members), false);
|
||||
return DatabaseFactory.getThreadDatabase(this)
|
||||
.getThreadIdFor(recipients,
|
||||
ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||
@@ -532,7 +532,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
|
||||
ArrayList<Recipient> selectedContactsList = setToArrayList(selectedContacts);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, new Recipients(selectedContactsList).getIds());
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, RecipientFactory.getRecipientsFor(GroupCreateActivity.this, selectedContactsList, true).getIds());
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} else {
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.afollestad.materialdialogs.AlertDialogWrapper;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -45,7 +46,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, Recipients> {
|
||||
.getGroupMembers(GroupUtil.getDecodedId(groupId), true);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return new Recipients(new LinkedList<Recipient>());
|
||||
return RecipientFactory.getRecipientsFor(context, new LinkedList<Recipient>(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
@@ -276,7 +277,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
.getGroupMembers(GroupUtil.getDecodedId(groupId), false);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
recipients = new Recipients(new LinkedList<Recipient>());
|
||||
recipients = RecipientFactory.getRecipientsFor(MessageDetailsActivity.this, new LinkedList<Recipient>(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
src/org/thoughtcrime/securesms/MuteDialog.java
Normal file
43
src/org/thoughtcrime/securesms/MuteDialog.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.afollestad.materialdialogs.AlertDialogWrapper;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MuteDialog extends AlertDialogWrapper {
|
||||
|
||||
private MuteDialog() {}
|
||||
|
||||
public static void show(final Context context, final @NonNull MuteSelectionListener listener) {
|
||||
AlertDialogWrapper.Builder builder = new AlertDialogWrapper.Builder(context);
|
||||
builder.setTitle(R.string.MuteDialog_mute_notifications);
|
||||
builder.setItems(R.array.mute_durations, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, final int which) {
|
||||
final long muteUntil;
|
||||
|
||||
switch (which) {
|
||||
case 0: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break;
|
||||
case 1: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(2); break;
|
||||
case 2: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1); break;
|
||||
case 3: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7); break;
|
||||
default: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break;
|
||||
}
|
||||
|
||||
listener.onMuted(muteUntil);
|
||||
}
|
||||
});
|
||||
|
||||
builder.show();
|
||||
|
||||
}
|
||||
|
||||
public interface MuteSelectionListener {
|
||||
public void onMuted(long until);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,14 +80,35 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
else finish();
|
||||
}
|
||||
|
||||
protected <T extends Fragment> T initFragment(@IdRes int target,
|
||||
@NonNull T fragment,
|
||||
@NonNull MasterSecret masterSecret)
|
||||
{
|
||||
return initFragment(target, fragment, masterSecret, null);
|
||||
}
|
||||
|
||||
protected <T extends Fragment> T initFragment(@IdRes int target,
|
||||
@NonNull T fragment,
|
||||
@NonNull MasterSecret masterSecret,
|
||||
@Nullable Locale locale) {
|
||||
@Nullable Locale locale)
|
||||
{
|
||||
return initFragment(target, fragment, masterSecret, locale, null);
|
||||
}
|
||||
|
||||
protected <T extends Fragment> T initFragment(@IdRes int target,
|
||||
@NonNull T fragment,
|
||||
@NonNull MasterSecret masterSecret,
|
||||
@Nullable Locale locale,
|
||||
@Nullable Bundle extras)
|
||||
{
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable("master_secret", masterSecret);
|
||||
args.putSerializable(LOCALE_EXTRA, locale);
|
||||
|
||||
if (extras != null) {
|
||||
args.putAll(extras);
|
||||
}
|
||||
|
||||
fragment.setArguments(args);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(target, fragment)
|
||||
@@ -95,12 +116,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
return fragment;
|
||||
}
|
||||
|
||||
protected <T extends Fragment> T initFragment(@IdRes int target,
|
||||
@NonNull T fragment,
|
||||
@NonNull MasterSecret masterSecret) {
|
||||
return initFragment(target, fragment, masterSecret, null);
|
||||
}
|
||||
|
||||
private void routeApplicationState(MasterSecret masterSecret) {
|
||||
Intent intent = getIntentForState(masterSecret, getApplicationState(masterSecret));
|
||||
if (intent != null) {
|
||||
|
||||
348
src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java
Normal file
348
src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java
Normal file
@@ -0,0 +1,348 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.Preference;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.preference.PreferenceFragment;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.AlertDialogWrapper;
|
||||
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements Recipients.RecipientsModifiedListener
|
||||
{
|
||||
private static final String TAG = RecipientPreferenceActivity.class.getSimpleName();
|
||||
|
||||
public static final String RECIPIENTS_EXTRA = "recipient_ids";
|
||||
|
||||
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
|
||||
private static final String PREFERENCE_TONE = "pref_key_recipient_ringtone";
|
||||
private static final String PREFERENCE_VIBRATE = "pref_key_recipient_vibrate";
|
||||
private static final String PREFERENCE_BLOCK = "pref_key_recipient_block";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private AvatarImageView avatar;
|
||||
private TextView title;
|
||||
private TextView blockedIndicator;
|
||||
|
||||
@Override
|
||||
public void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle instanceState, @NonNull MasterSecret masterSecret) {
|
||||
setContentView(R.layout.recipient_preference_activity);
|
||||
|
||||
long[] recipientIds = getIntent().getLongArrayExtra(RECIPIENTS_EXTRA);
|
||||
Recipients recipients = RecipientFactory.getRecipientsForIds(this, recipientIds, true);
|
||||
|
||||
initializeToolbar();
|
||||
setHeader(recipients);
|
||||
recipients.addListener(this);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLongArray(RECIPIENTS_EXTRA, recipientIds);
|
||||
initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), masterSecret, null, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
dynamicLanguage.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.preference_fragment);
|
||||
fragment.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initializeToolbar() {
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
|
||||
this.avatar = (AvatarImageView) toolbar.findViewById(R.id.avatar);
|
||||
this.title = (TextView) toolbar.findViewById(R.id.name);
|
||||
this.blockedIndicator = (TextView) toolbar.findViewById(R.id.blocked_indicator);
|
||||
}
|
||||
|
||||
private void setHeader(Recipients recipients) {
|
||||
this.avatar.setAvatar(recipients.getPrimaryRecipient(), true);
|
||||
this.title.setText(recipients.toShortString());
|
||||
|
||||
if (recipients.isBlocked()) this.blockedIndicator.setVisibility(View.VISIBLE);
|
||||
else this.blockedIndicator.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipients recipients) {
|
||||
title.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setHeader(recipients);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class RecipientPreferenceFragment
|
||||
extends PreferenceFragment
|
||||
implements Recipients.RecipientsModifiedListener
|
||||
{
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
private Recipients recipients;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
addPreferencesFromResource(R.xml.recipient_preferences);
|
||||
|
||||
this.recipients = RecipientFactory.getRecipientsForIds(getActivity(),
|
||||
getArguments().getLongArray(RECIPIENTS_EXTRA),
|
||||
true);
|
||||
|
||||
this.recipients.addListener(this);
|
||||
this.findPreference(PREFERENCE_TONE)
|
||||
.setOnPreferenceChangeListener(new RingtoneChangeListener());
|
||||
this.findPreference(PREFERENCE_VIBRATE)
|
||||
.setOnPreferenceChangeListener(new VibrateChangeListener());
|
||||
this.findPreference(PREFERENCE_MUTED)
|
||||
.setOnPreferenceClickListener(new MuteClickedListener());
|
||||
this.findPreference(PREFERENCE_BLOCK)
|
||||
.setOnPreferenceClickListener(new BlockClickedListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setSummaries(recipients);
|
||||
}
|
||||
|
||||
private void setSummaries(Recipients recipients) {
|
||||
CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED);
|
||||
Preference ringtonePreference = this.findPreference(PREFERENCE_TONE);
|
||||
Preference vibratePreference = this.findPreference(PREFERENCE_VIBRATE);
|
||||
Preference blockPreference = this.findPreference(PREFERENCE_BLOCK);
|
||||
|
||||
mutePreference.setChecked(recipients.isMuted());
|
||||
|
||||
if (recipients.getRingtone() != null) {
|
||||
Ringtone tone = RingtoneManager.getRingtone(getActivity(), recipients.getRingtone());
|
||||
|
||||
if (tone != null) {
|
||||
ringtonePreference.setSummary(tone.getTitle(getActivity()));
|
||||
}
|
||||
} else {
|
||||
ringtonePreference.setSummary(R.string.preferences__default);
|
||||
}
|
||||
|
||||
if (recipients.getVibrate() == VibrateState.DEFAULT) {
|
||||
vibratePreference.setSummary(R.string.preferences__default);
|
||||
} else if (recipients.getVibrate() == VibrateState.ENABLED) {
|
||||
vibratePreference.setSummary("Enabled");
|
||||
} else {
|
||||
vibratePreference.setSummary("Disabled");
|
||||
}
|
||||
|
||||
if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
|
||||
blockPreference.setEnabled(false);
|
||||
} else {
|
||||
blockPreference.setEnabled(true);
|
||||
if (recipients.isBlocked()) blockPreference.setTitle("Unblock");
|
||||
else blockPreference.setTitle("Block");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipients recipients) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setSummaries(recipients);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class RingtoneChangeListener implements Preference.OnPreferenceChangeListener {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String value = (String)newValue;
|
||||
|
||||
final Uri uri;
|
||||
|
||||
if (TextUtils.isEmpty(value) || Settings.System.DEFAULT_NOTIFICATION_URI.toString().equals(value)) {
|
||||
uri = null;
|
||||
} else {
|
||||
uri = Uri.parse(value);
|
||||
}
|
||||
|
||||
recipients.setRingtone(uri);
|
||||
|
||||
new AsyncTask<Uri, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Uri... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
|
||||
.setRingtone(recipients, params[0]);
|
||||
return null;
|
||||
}
|
||||
}.execute(uri);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class VibrateChangeListener implements Preference.OnPreferenceChangeListener {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int value = Integer.parseInt((String) newValue);
|
||||
final VibrateState vibrateState = VibrateState.fromId(value);
|
||||
|
||||
recipients.setVibrate(vibrateState);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
|
||||
.setVibrate(recipients, vibrateState);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class MuteClickedListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (recipients.isMuted()) handleUnmute();
|
||||
else handleMute();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleMute() {
|
||||
MuteDialog.show(getActivity(), new MuteDialog.MuteSelectionListener() {
|
||||
@Override
|
||||
public void onMuted(long until) {
|
||||
setMuted(recipients, until);
|
||||
}
|
||||
});
|
||||
|
||||
setSummaries(recipients);
|
||||
}
|
||||
|
||||
private void handleUnmute() {
|
||||
setMuted(recipients, 0);
|
||||
}
|
||||
|
||||
private void setMuted(final Recipients recipients, final long until) {
|
||||
recipients.setMuted(until);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
|
||||
.setMuted(recipients, until);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockClickedListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (recipients.isBlocked()) handleUnblock();
|
||||
else handleBlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleBlock() {
|
||||
new AlertDialogWrapper.Builder(getActivity())
|
||||
.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question)
|
||||
.setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_see_messages_from_this_user)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_block, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setBlocked(recipients, true);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleUnblock() {
|
||||
new AlertDialogWrapper.Builder(getActivity())
|
||||
.setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question)
|
||||
.setMessage(R.string.RecipientPreferenceActivity_are_you_sure_you_want_to_unblock_this_contact)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setBlocked(recipients, false);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void setBlocked(final Recipients recipients, final boolean blocked) {
|
||||
recipients.setBlocked(blocked);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
|
||||
.setBlocked(recipients, blocked);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import android.widget.RelativeLayout;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ShareListItem extends RelativeLayout
|
||||
implements Recipient.RecipientModifiedListener
|
||||
implements Recipients.RecipientsModifiedListener
|
||||
{
|
||||
private final static String TAG = ShareListItem.class.getSimpleName();
|
||||
|
||||
@@ -102,7 +101,7 @@ public class ShareListItem extends RelativeLayout
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
public void onModified(final Recipients recipients) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -3,19 +3,24 @@ package org.thoughtcrime.securesms.components;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
public class FromTextView extends EmojiTextView {
|
||||
|
||||
private static final String TAG = FromTextView.class.getSimpleName();
|
||||
|
||||
public FromTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -25,7 +30,7 @@ public class FromTextView extends EmojiTextView {
|
||||
}
|
||||
|
||||
public void setText(Recipient recipient) {
|
||||
setText(new Recipients(recipient));
|
||||
setText(RecipientFactory.getRecipientsFor(getContext(), recipient, true));
|
||||
}
|
||||
|
||||
public void setText(Recipients recipients) {
|
||||
@@ -63,6 +68,10 @@ public class FromTextView extends EmojiTextView {
|
||||
colors.recycle();
|
||||
|
||||
setText(builder);
|
||||
|
||||
if (recipients.isBlocked()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0);
|
||||
else if (recipients.isMuted()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_grey600_18dp, 0, 0, 0);
|
||||
else setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public class PushRecipientsPanel extends RelativeLayout {
|
||||
try {
|
||||
recipients = getRecipients();
|
||||
} catch (RecipientFormattingException e) {
|
||||
recipients = new Recipients( new LinkedList<Recipient>() );
|
||||
recipients = RecipientFactory.getRecipientsFor(getContext(), new LinkedList<Recipient>(), true);
|
||||
}
|
||||
|
||||
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
|
||||
|
||||
@@ -130,7 +130,7 @@ public class SingleRecipientPanel extends RelativeLayout {
|
||||
try {
|
||||
recipients = getRecipients();
|
||||
} catch (RecipientFormattingException e) {
|
||||
recipients = new Recipients( new LinkedList<Recipient>() );
|
||||
recipients = RecipientFactory.getRecipientsFor(getContext(), new LinkedList<Recipient>(), true);
|
||||
}
|
||||
|
||||
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
|
||||
|
||||
@@ -96,11 +96,6 @@ public class ContactPhotoFactory {
|
||||
localUserContactPhotoCache.clear();
|
||||
}
|
||||
|
||||
public static void clearCache(Recipient recipient) {
|
||||
if (localUserContactPhotoCache.containsKey(recipient.getContactUri()))
|
||||
localUserContactPhotoCache.remove(recipient.getContactUri());
|
||||
}
|
||||
|
||||
public static Drawable getContactPhoto(Context context, Uri uri, String name) {
|
||||
final InputStream inputStream = getContactPhotoStream(context, uri);
|
||||
final int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
|
||||
|
||||
@@ -61,7 +61,8 @@ public class DatabaseFactory {
|
||||
private static final int INTRODUCED_THUMBNAILS_VERSION = 15;
|
||||
private static final int INTRODUCED_IDENTITY_COLUMN_VERSION = 16;
|
||||
private static final int INTRODUCED_UNIQUE_PART_IDS_VERSION = 17;
|
||||
private static final int DATABASE_VERSION = 17;
|
||||
private static final int INTRODUCED_RECIPIENT_PREFS_DB = 18;
|
||||
private static final int DATABASE_VERSION = 18;
|
||||
|
||||
private static final String DATABASE_NAME = "messages.db";
|
||||
private static final Object lock = new Object();
|
||||
@@ -82,6 +83,7 @@ public class DatabaseFactory {
|
||||
private final DraftDatabase draftDatabase;
|
||||
private final PushDatabase pushDatabase;
|
||||
private final GroupDatabase groupDatabase;
|
||||
private final RecipientPreferenceDatabase recipientPreferenceDatabase;
|
||||
|
||||
public static DatabaseFactory getInstance(Context context) {
|
||||
synchronized (lock) {
|
||||
@@ -140,20 +142,25 @@ public class DatabaseFactory {
|
||||
return getInstance(context).groupDatabase;
|
||||
}
|
||||
|
||||
public static RecipientPreferenceDatabase getRecipientPreferenceDatabase(Context context) {
|
||||
return getInstance(context).recipientPreferenceDatabase;
|
||||
}
|
||||
|
||||
private DatabaseFactory(Context context) {
|
||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.sms = new SmsDatabase(context, databaseHelper);
|
||||
this.encryptingSms = new EncryptingSmsDatabase(context, databaseHelper);
|
||||
this.mms = new MmsDatabase(context, databaseHelper);
|
||||
this.part = new PartDatabase(context, databaseHelper);
|
||||
this.thread = new ThreadDatabase(context, databaseHelper);
|
||||
this.address = CanonicalAddressDatabase.getInstance(context);
|
||||
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
|
||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
this.sms = new SmsDatabase(context, databaseHelper);
|
||||
this.encryptingSms = new EncryptingSmsDatabase(context, databaseHelper);
|
||||
this.mms = new MmsDatabase(context, databaseHelper);
|
||||
this.part = new PartDatabase(context, databaseHelper);
|
||||
this.thread = new ThreadDatabase(context, databaseHelper);
|
||||
this.address = CanonicalAddressDatabase.getInstance(context);
|
||||
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
|
||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||
this.recipientPreferenceDatabase = new RecipientPreferenceDatabase(context, databaseHelper);
|
||||
}
|
||||
|
||||
public void reset(Context context) {
|
||||
@@ -171,6 +178,7 @@ public class DatabaseFactory {
|
||||
this.draftDatabase.reset(databaseHelper);
|
||||
this.pushDatabase.reset(databaseHelper);
|
||||
this.groupDatabase.reset(databaseHelper);
|
||||
this.recipientPreferenceDatabase.reset(databaseHelper);
|
||||
old.close();
|
||||
|
||||
this.address.reset(context);
|
||||
@@ -480,6 +488,7 @@ public class DatabaseFactory {
|
||||
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||
db.execSQL(PushDatabase.CREATE_TABLE);
|
||||
db.execSQL(GroupDatabase.CREATE_TABLE);
|
||||
db.execSQL(RecipientPreferenceDatabase.CREATE_TABLE);
|
||||
|
||||
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
||||
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
||||
@@ -722,6 +731,12 @@ public class DatabaseFactory {
|
||||
db.execSQL("ALTER TABLE part ADD COLUMN unique_id INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
|
||||
if (oldVersion < INTRODUCED_RECIPIENT_PREFS_DB) {
|
||||
db.execSQL("CREATE TABLE recipient_preferences " +
|
||||
"(_id INTEGER PRIMARY KEY, recipient_ids TEXT UNIQUE, block INTEGER DEFAULT 0, " +
|
||||
"notification TEXT DEFAULT NULL, vibrate INTEGER DEFAULT 0, mute_until INTEGER DEFAULT 0)");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class GroupDatabase extends Database {
|
||||
public Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
|
||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
List<String> members = getCurrentMembers(groupId);
|
||||
List<Recipient> recipients = new LinkedList<Recipient>();
|
||||
List<Recipient> recipients = new LinkedList<>();
|
||||
|
||||
for (String member : members) {
|
||||
if (!includeSelf && member.equals(localNumber))
|
||||
@@ -100,7 +100,7 @@ public class GroupDatabase extends Database {
|
||||
.getRecipientsList());
|
||||
}
|
||||
|
||||
return new Recipients(recipients);
|
||||
return RecipientFactory.getRecipientsFor(context, recipients, false);
|
||||
}
|
||||
|
||||
public void create(byte[] groupId, String title, List<String> members,
|
||||
|
||||
@@ -145,7 +145,7 @@ public class MmsAddressDatabase extends Database {
|
||||
}
|
||||
}
|
||||
|
||||
return new Recipients(results);
|
||||
return RecipientFactory.getRecipientsFor(context, results, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
? Util.toIsoString(notification.getFrom().getTextString())
|
||||
: "";
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString, false);
|
||||
if (recipients.isEmpty()) recipients = new Recipients(Recipient.getUnknownRecipient(context));
|
||||
if (recipients.isEmpty()) recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
}
|
||||
|
||||
@@ -1054,13 +1054,13 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
|
||||
private Recipients getRecipientsFor(String address) {
|
||||
if (TextUtils.isEmpty(address) || address.equals("insert-address-token")) {
|
||||
return new Recipients(Recipient.getUnknownRecipient(context));
|
||||
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
}
|
||||
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false);
|
||||
|
||||
if (recipients == null || recipients.isEmpty()) {
|
||||
return new Recipients(Recipient.getUnknownRecipient(context));
|
||||
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
}
|
||||
|
||||
return recipients;
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class RecipientPreferenceDatabase extends Database {
|
||||
|
||||
private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName();
|
||||
|
||||
private static final String TABLE_NAME = "recipient_preferences";
|
||||
private static final String ID = "_id";
|
||||
private static final String RECIPIENT_IDS = "recipient_ids";
|
||||
private static final String BLOCK = "block";
|
||||
private static final String NOTIFICATION = "notification";
|
||||
private static final String VIBRATE = "vibrate";
|
||||
private static final String MUTE_UNTIL = "mute_until";
|
||||
|
||||
public enum VibrateState {
|
||||
DEFAULT(0), ENABLED(1), DISABLED(2);
|
||||
|
||||
private final int id;
|
||||
|
||||
VibrateState(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static VibrateState fromId(int id) {
|
||||
return values()[id];
|
||||
}
|
||||
}
|
||||
|
||||
public static final String CREATE_TABLE =
|
||||
"CREATE TABLE " + TABLE_NAME +
|
||||
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||
RECIPIENT_IDS + " TEXT UNIQUE, " +
|
||||
BLOCK + " INTEGER DEFAULT 0," +
|
||||
NOTIFICATION + " TEXT DEFAULT NULL, " +
|
||||
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
|
||||
MUTE_UNTIL + " INTEGER DEFAULT 0);";
|
||||
|
||||
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
}
|
||||
|
||||
public Cursor getBlocked() {
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
|
||||
return database.query(TABLE_NAME, new String[] {ID, RECIPIENT_IDS}, BLOCK + " = 1",
|
||||
null, null, null, null, null);
|
||||
}
|
||||
|
||||
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull long[] recipients) {
|
||||
Arrays.sort(recipients);
|
||||
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = database.query(TABLE_NAME, null, RECIPIENT_IDS + " = ?",
|
||||
new String[] {Util.join(recipients, " ")},
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
|
||||
String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
|
||||
int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
|
||||
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
|
||||
Uri notificationUri = notification == null ? null : Uri.parse(notification);
|
||||
|
||||
Log.w(TAG, "Muted until: " + muteUntil);
|
||||
|
||||
return Optional.of(new RecipientsPreferences(blocked, muteUntil,
|
||||
VibrateState.fromId(vibrateState),
|
||||
notificationUri));
|
||||
}
|
||||
|
||||
return Optional.absent();
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void setBlocked(Recipients recipients, boolean blocked) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(BLOCK, blocked ? 1 : 0);
|
||||
updateOrInsert(recipients, values);
|
||||
}
|
||||
|
||||
public void setRingtone(Recipients recipients, @Nullable Uri notification) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NOTIFICATION, notification == null ? null : notification.toString());
|
||||
updateOrInsert(recipients, values);
|
||||
}
|
||||
|
||||
public void setVibrate(Recipients recipients, @NonNull VibrateState enabled) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(VIBRATE, enabled.getId());
|
||||
updateOrInsert(recipients, values);
|
||||
}
|
||||
|
||||
public void setMuted(Recipients recipients, long until) {
|
||||
Log.w(TAG, "Setting muted until: " + until);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MUTE_UNTIL, until);
|
||||
updateOrInsert(recipients, values);
|
||||
}
|
||||
|
||||
private void updateOrInsert(Recipients recipients, ContentValues contentValues) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
database.beginTransaction();
|
||||
|
||||
int updated = database.update(TABLE_NAME, contentValues, RECIPIENT_IDS + " = ?",
|
||||
new String[] {String.valueOf(recipients.getSortedIdsString())});
|
||||
|
||||
if (updated < 1) {
|
||||
contentValues.put(RECIPIENT_IDS, recipients.getSortedIdsString());
|
||||
database.insert(TABLE_NAME, null, contentValues);
|
||||
}
|
||||
|
||||
database.setTransactionSuccessful();
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
public static class RecipientsPreferences {
|
||||
private final boolean blocked;
|
||||
private final long muteUntil;
|
||||
private final VibrateState vibrateState;
|
||||
private final Uri notification;
|
||||
|
||||
public RecipientsPreferences(boolean blocked, long muteUntil, VibrateState vibrateState, Uri notification) {
|
||||
this.blocked = blocked;
|
||||
this.muteUntil = muteUntil;
|
||||
this.vibrateState = vibrateState;
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
public boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public long getMuteUntil() {
|
||||
return muteUntil;
|
||||
}
|
||||
|
||||
public @NonNull VibrateState getVibrateState() {
|
||||
return vibrateState;
|
||||
}
|
||||
|
||||
public @Nullable Uri getRingtone() {
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,7 +368,7 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), true);
|
||||
} else {
|
||||
Log.w(TAG, "Sender is null, returning unknown recipient");
|
||||
recipients = new Recipients(Recipient.getUnknownRecipient(context));
|
||||
recipients = RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
}
|
||||
|
||||
Recipients groupRecipients;
|
||||
@@ -615,13 +615,13 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, address, false);
|
||||
|
||||
if (recipients == null || recipients.isEmpty()) {
|
||||
return new Recipients(Recipient.getUnknownRecipient(context));
|
||||
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
}
|
||||
|
||||
return recipients;
|
||||
} else {
|
||||
Log.w(TAG, "getRecipientsFor() address is null");
|
||||
return new Recipients(Recipient.getUnknownRecipient(context));
|
||||
return RecipientFactory.getRecipientsFor(context, Recipient.getUnknownRecipient(context), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.thoughtcrime.securesms.database.loaders;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||
|
||||
public class BlockedContactsLoader extends AbstractCursorLoader {
|
||||
|
||||
public BlockedContactsLoader(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
return DatabaseFactory.getRecipientPreferenceDatabase(getContext())
|
||||
.getBlocked();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
@@ -11,19 +10,15 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.push.TextSecurePushTrustStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.libaxolotl.InvalidMessageException;
|
||||
import org.whispersystems.textsecure.api.crypto.AttachmentCipherInputStream;
|
||||
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException;
|
||||
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.textsecure.internal.util.StaticCredentialsProvider;
|
||||
|
||||
import java.io.File;
|
||||
@@ -72,10 +67,6 @@ public class AvatarDownloadJob extends MasterSecretJob {
|
||||
Bitmap avatar = BitmapUtil.createScaledBitmap(measureInputStream, scaleInputStream, 500, 500);
|
||||
|
||||
database.updateAvatar(groupId, avatar);
|
||||
|
||||
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(groupId), true)
|
||||
.getPrimaryRecipient();
|
||||
groupRecipient.setContactPhoto(new BitmapDrawable(avatar));
|
||||
}
|
||||
} catch (InvalidMessageException | BitmapDecodingException | NonSuccessfulResponseCodeException e) {
|
||||
Log.w(TAG, e);
|
||||
|
||||
@@ -7,6 +7,9 @@ import android.util.Pair;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
|
||||
import ws.com.google.android.mms.pdu.GenericPdu;
|
||||
@@ -49,7 +52,7 @@ public class MmsReceiveJob extends ContextJob {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
if (pdu != null && pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) {
|
||||
if (isNotification(pdu) && !isBlocked(pdu)) {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu);
|
||||
|
||||
@@ -61,6 +64,8 @@ public class MmsReceiveJob extends ContextJob {
|
||||
messageAndThreadId.first,
|
||||
messageAndThreadId.second,
|
||||
true));
|
||||
} else if (isNotification(pdu)) {
|
||||
Log.w(TAG, "*** Received blocked MMS, ignoring...");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,4 +78,17 @@ public class MmsReceiveJob extends ContextJob {
|
||||
public boolean onShouldRetry(Exception exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlocked(GenericPdu pdu) {
|
||||
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, Util.toIsoString(pdu.getFrom().getTextString()), false);
|
||||
return recipients.isBlocked();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isNotification(GenericPdu pdu) {
|
||||
return pdu != null && pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.whispersystems.jobqueue.JobManager;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureEnvelope;
|
||||
@@ -34,16 +36,21 @@ public abstract class PushReceivedJob extends ContextJob {
|
||||
}
|
||||
|
||||
private void handleMessage(TextSecureEnvelope envelope, boolean sendExplicitReceipt) {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
|
||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||
long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope);
|
||||
|
||||
if (!recipients.isBlocked()) {
|
||||
long messageId = DatabaseFactory.getPushDatabase(context).insert(envelope);
|
||||
jobManager.add(new PushDecryptJob(context, messageId, envelope.getSource()));
|
||||
} else {
|
||||
Log.w(TAG, "*** Received blocked push message, ignoring...");
|
||||
}
|
||||
|
||||
if (sendExplicitReceipt) {
|
||||
jobManager.add(new DeliveryReceiptJob(context, envelope.getSource(),
|
||||
envelope.getTimestamp(),
|
||||
envelope.getRelay()));
|
||||
}
|
||||
|
||||
jobManager.add(new PushDecryptJob(context, messageId, envelope.getSource()));
|
||||
}
|
||||
|
||||
private void handleReceipt(TextSecureEnvelope envelope) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
@@ -11,6 +12,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
@@ -42,9 +45,11 @@ public class SmsReceiveJob extends ContextJob {
|
||||
public void onRun() {
|
||||
Optional<IncomingTextMessage> message = assembleMessageFragments(pdus);
|
||||
|
||||
if (message.isPresent()) {
|
||||
if (message.isPresent() && !isBlocked(message.get())) {
|
||||
Pair<Long, Long> messageAndThreadId = storeMessage(message.get());
|
||||
MessageNotifier.updateNotification(context, KeyCachingService.getMasterSecret(context), messageAndThreadId.second);
|
||||
} else if (message.isPresent()) {
|
||||
Log.w(TAG, "*** Received blocked SMS, ignoring...");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +63,15 @@ public class SmsReceiveJob extends ContextJob {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlocked(IncomingTextMessage message) {
|
||||
if (message.getSender() != null) {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
|
||||
return recipients.isBlocked();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Pair<Long, Long> storeMessage(IncomingTextMessage message) {
|
||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationCompat.Action;
|
||||
import android.support.v4.app.NotificationCompat.BigTextStyle;
|
||||
@@ -49,7 +50,9 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
@@ -88,7 +91,7 @@ public class MessageNotifier {
|
||||
|
||||
public static void notifyMessageDeliveryFailed(Context context, Recipients recipients, long threadId) {
|
||||
if (visibleThread == threadId) {
|
||||
sendInThreadNotification(context);
|
||||
sendInThreadNotification(context, recipients);
|
||||
} else {
|
||||
Intent intent = new Intent(context, ConversationActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
@@ -105,7 +108,7 @@ public class MessageNotifier {
|
||||
builder.setTicker(context.getString(R.string.MessageNotifier_error_delivering_message));
|
||||
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
|
||||
builder.setAutoCancel(true);
|
||||
setNotificationAlarms(context, builder, true);
|
||||
setNotificationAlarms(context, builder, true, null, VibrateState.DEFAULT);
|
||||
|
||||
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
|
||||
.notify((int)threadId, builder.build());
|
||||
@@ -126,8 +129,9 @@ public class MessageNotifier {
|
||||
}
|
||||
|
||||
if (visibleThread == threadId) {
|
||||
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
|
||||
sendInThreadNotification(context);
|
||||
ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
|
||||
threads.setRead(threadId);
|
||||
sendInThreadNotification(context, threads.getRecipientsForThreadId(threadId));
|
||||
} else {
|
||||
updateNotification(context, masterSecret, true, 0);
|
||||
}
|
||||
@@ -225,7 +229,9 @@ public class MessageNotifier {
|
||||
|
||||
builder.setStyle(new BigTextStyle().bigText(content));
|
||||
|
||||
setNotificationAlarms(context, builder, signal);
|
||||
setNotificationAlarms(context, builder, signal,
|
||||
notificationState.getRingtone(),
|
||||
notificationState.getVibrate());
|
||||
|
||||
if (signal) {
|
||||
builder.setTicker(notifications.get(0).getTickerText());
|
||||
@@ -283,7 +289,9 @@ public class MessageNotifier {
|
||||
|
||||
builder.setStyle(style);
|
||||
|
||||
setNotificationAlarms(context, builder, signal);
|
||||
setNotificationAlarms(context, builder, signal,
|
||||
notificationState.getRingtone(),
|
||||
notificationState.getVibrate());
|
||||
|
||||
if (signal) {
|
||||
builder.setTicker(notifications.get(0).getTickerText());
|
||||
@@ -293,23 +301,27 @@ public class MessageNotifier {
|
||||
.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
private static void sendInThreadNotification(Context context) {
|
||||
private static void sendInThreadNotification(Context context, Recipients recipients) {
|
||||
try {
|
||||
if (!TextSecurePreferences.isInThreadNotifications(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String ringtone = TextSecurePreferences.getNotificationRingtone(context);
|
||||
|
||||
if (ringtone == null) {
|
||||
Log.w(TAG, "ringtone preference was null.");
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = Uri.parse(ringtone);
|
||||
Uri uri = recipients.getRingtone();
|
||||
|
||||
if (uri == null) {
|
||||
Log.w(TAG, "couldn't parse ringtone uri " + ringtone);
|
||||
String ringtone = TextSecurePreferences.getNotificationRingtone(context);
|
||||
|
||||
if (ringtone == null) {
|
||||
Log.w(TAG, "ringtone preference was null.");
|
||||
return;
|
||||
} else {
|
||||
uri = Uri.parse(ringtone);
|
||||
}
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
Log.w(TAG, "couldn't parse ringtone uri " + TextSecurePreferences.getNotificationRingtone(context));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -358,7 +370,9 @@ public class MessageNotifier {
|
||||
SpannableString body = new SpannableString(context.getString(R.string.MessageNotifier_encrypted_message));
|
||||
body.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null, 0));
|
||||
if (!recipients.isMuted()) {
|
||||
notificationState.addNotification(new NotificationItem(recipient, recipients, null, threadId, body, null, 0));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (reader != null)
|
||||
@@ -403,7 +417,9 @@ public class MessageNotifier {
|
||||
body = SpanUtil.italic(message, italicLength);
|
||||
}
|
||||
|
||||
notificationState.addNotification(new NotificationItem(recipient, recipients, threadRecipients, threadId, body, image, timestamp));
|
||||
if (threadRecipients == null || !threadRecipients.isMuted()) {
|
||||
notificationState.addNotification(new NotificationItem(recipient, recipients, threadRecipients, threadId, body, image, timestamp));
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
@@ -412,18 +428,23 @@ public class MessageNotifier {
|
||||
|
||||
private static void setNotificationAlarms(Context context,
|
||||
NotificationCompat.Builder builder,
|
||||
boolean signal)
|
||||
boolean signal,
|
||||
@Nullable Uri ringtone,
|
||||
VibrateState vibrate)
|
||||
|
||||
{
|
||||
String ringtone = TextSecurePreferences.getNotificationRingtone(context);
|
||||
boolean vibrate = TextSecurePreferences.isNotificationVibrateEnabled(context);
|
||||
String defaultRingtoneName = TextSecurePreferences.getNotificationRingtone(context);
|
||||
boolean defaultVibrate = TextSecurePreferences.isNotificationVibrateEnabled(context);
|
||||
String ledColor = TextSecurePreferences.getNotificationLedColor(context);
|
||||
String ledBlinkPattern = TextSecurePreferences.getNotificationLedPattern(context);
|
||||
String ledBlinkPatternCustom = TextSecurePreferences.getNotificationLedPatternCustom(context);
|
||||
String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom);
|
||||
|
||||
builder.setSound(TextUtils.isEmpty(ringtone) || !signal ? null : Uri.parse(ringtone));
|
||||
if (signal && ringtone != null) builder.setSound(ringtone);
|
||||
else if (signal && !TextUtils.isEmpty(defaultRingtoneName)) builder.setSound(Uri.parse(defaultRingtoneName));
|
||||
else builder.setSound(null);
|
||||
|
||||
if (signal && vibrate) {
|
||||
if (signal && (vibrate == VibrateState.ENABLED || (vibrate == VibrateState.DEFAULT && defaultVibrate))) {
|
||||
builder.setDefaults(Notification.DEFAULT_VIBRATE);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationActivity;
|
||||
@@ -34,6 +35,10 @@ public class NotificationItem {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public @Nullable Recipients getRecipients() {
|
||||
return threadRecipients;
|
||||
}
|
||||
|
||||
public Recipient getIndividualRecipient() {
|
||||
return individualRecipient;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,14 @@ package org.thoughtcrime.securesms.notifications;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@@ -14,8 +19,8 @@ import java.util.Set;
|
||||
|
||||
public class NotificationState {
|
||||
|
||||
private final LinkedList<NotificationItem> notifications = new LinkedList<NotificationItem>();
|
||||
private final Set<Long> threads = new HashSet<Long>();
|
||||
private final LinkedList<NotificationItem> notifications = new LinkedList<>();
|
||||
private final Set<Long> threads = new HashSet<>();
|
||||
|
||||
private int notificationCount = 0;
|
||||
|
||||
@@ -25,6 +30,30 @@ public class NotificationState {
|
||||
notificationCount++;
|
||||
}
|
||||
|
||||
public @Nullable Uri getRingtone() {
|
||||
if (!notifications.isEmpty()) {
|
||||
Recipients recipients = notifications.getFirst().getRecipients();
|
||||
|
||||
if (recipients != null) {
|
||||
return recipients.getRingtone();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public VibrateState getVibrate() {
|
||||
if (!notifications.isEmpty()) {
|
||||
Recipients recipients = notifications.getFirst().getRecipients();
|
||||
|
||||
if (recipients != null) {
|
||||
return recipients.getVibrate();
|
||||
}
|
||||
}
|
||||
|
||||
return VibrateState.DEFAULT;
|
||||
}
|
||||
|
||||
public boolean hasMultipleThreads() {
|
||||
return threads.size() > 1;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.doomonafireball.betterpickers.hmspicker.HmsPickerBuilder;
|
||||
import com.doomonafireball.betterpickers.hmspicker.HmsPickerDialogFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.BlockedContactsActivity;
|
||||
import org.thoughtcrime.securesms.PassphraseChangeActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
@@ -27,6 +28,9 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AppProtectionPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
private static final String PREFERENCE_CATEGORY_BLOCKED = "preference_category_blocked";
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private CheckBoxPreference disablePassphrase;
|
||||
|
||||
@@ -42,6 +46,8 @@ public class AppProtectionPreferenceFragment extends PreferenceFragment {
|
||||
.setOnPreferenceClickListener(new ChangePassphraseClickListener());
|
||||
this.findPreference(TextSecurePreferences.PASSPHRASE_TIMEOUT_INTERVAL_PREF)
|
||||
.setOnPreferenceClickListener(new PassphraseIntervalClickListener());
|
||||
this.findPreference(PREFERENCE_CATEGORY_BLOCKED)
|
||||
.setOnPreferenceClickListener(new BlockedContactsClickListener());
|
||||
disablePassphrase
|
||||
.setOnPreferenceChangeListener(new DisablePassphraseClickListener());
|
||||
}
|
||||
@@ -73,6 +79,15 @@ public class AppProtectionPreferenceFragment extends PreferenceFragment {
|
||||
.setSummary(getString(R.string.AppProtectionPreferenceFragment_minutes, timeoutMinutes));
|
||||
}
|
||||
|
||||
private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent(getActivity(), BlockedContactsActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.thoughtcrime.securesms.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
public class BlockedContactListItem extends RelativeLayout implements Recipients.RecipientsModifiedListener {
|
||||
|
||||
private AvatarImageView contactPhotoImage;
|
||||
private TextView nameView;
|
||||
private Recipients recipients;
|
||||
|
||||
public BlockedContactListItem(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public BlockedContactListItem(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BlockedContactListItem(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.contactPhotoImage = (AvatarImageView)findViewById(R.id.contact_photo_image);
|
||||
this.nameView = (TextView) findViewById(R.id.name);
|
||||
}
|
||||
|
||||
public void set(Recipients recipients) {
|
||||
this.recipients = recipients;
|
||||
|
||||
onModified(recipients);
|
||||
recipients.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipients recipients) {
|
||||
this.contactPhotoImage.setAvatar(recipients.getPrimaryRecipient(), false);
|
||||
this.nameView.setText(recipients.toShortString());
|
||||
}
|
||||
|
||||
public Recipients getRecipients() {
|
||||
return recipients;
|
||||
}
|
||||
}
|
||||
@@ -91,16 +91,6 @@ public class Recipient {
|
||||
return this.contactUri;
|
||||
}
|
||||
|
||||
public synchronized void setContactPhoto(Drawable bitmap) {
|
||||
this.contactPhoto = bitmap;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized void setName(String name) {
|
||||
this.name = name;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ package org.thoughtcrime.securesms.recipients;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -34,59 +35,73 @@ public class RecipientFactory {
|
||||
|
||||
public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) {
|
||||
if (TextUtils.isEmpty(recipientIds))
|
||||
return new Recipients(new LinkedList<Recipient>());
|
||||
return new Recipients();
|
||||
|
||||
List<Recipient> results = new LinkedList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(recipientIds.trim(), " ");
|
||||
return getRecipientsForIds(context, Util.split(recipientIds, " "), asynchronous);
|
||||
}
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String recipientId = tokenizer.nextToken();
|
||||
Recipient recipient = getRecipientFromProviderId(context, Long.parseLong(recipientId), asynchronous);
|
||||
public static Recipients getRecipientsFor(Context context, List<Recipient> recipients, boolean asynchronous) {
|
||||
long[] ids = new long[recipients.size()];
|
||||
int i = 0;
|
||||
|
||||
results.add(recipient);
|
||||
for (Recipient recipient : recipients) {
|
||||
ids[i++] = recipient.getRecipientId();
|
||||
}
|
||||
|
||||
return new Recipients(results);
|
||||
return provider.getRecipients(context, ids, asynchronous);
|
||||
}
|
||||
|
||||
public static Recipients getRecipientsFor(Context context, Recipient recipient, boolean asynchronous) {
|
||||
long[] ids = new long[1];
|
||||
ids[0] = recipient.getRecipientId();
|
||||
|
||||
return provider.getRecipients(context, ids, asynchronous);
|
||||
}
|
||||
|
||||
public static Recipient getRecipientForId(Context context, long recipientId, boolean asynchronous) {
|
||||
return getRecipientFromProviderId(context, recipientId, asynchronous);
|
||||
}
|
||||
|
||||
public static Recipients getRecipientsForIds(Context context, long[] recipientIds, boolean asynchronous) {
|
||||
List<Recipient> results = new LinkedList<>();
|
||||
if (recipientIds == null) return new Recipients(results);
|
||||
for (long recipientId : recipientIds) {
|
||||
results.add(getRecipientFromProviderId(context, recipientId, asynchronous));
|
||||
}
|
||||
return new Recipients(results);
|
||||
}
|
||||
|
||||
private static Recipient getRecipientForNumber(Context context, String number, boolean asynchronous) {
|
||||
long recipientId = CanonicalAddressDatabase.getInstance(context).getCanonicalAddressId(number);
|
||||
return provider.getRecipient(context, recipientId, asynchronous);
|
||||
}
|
||||
|
||||
public static Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
|
||||
List<Recipient> results = new LinkedList<>();
|
||||
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
Recipient recipient = parseRecipient(context, tokenizer.nextToken(), asynchronous);
|
||||
if( recipient != null )
|
||||
results.add(recipient);
|
||||
}
|
||||
|
||||
return new Recipients(results);
|
||||
public static Recipients getRecipientsForIds(Context context, long[] recipientIds, boolean asynchronous) {
|
||||
return provider.getRecipients(context, recipientIds, asynchronous);
|
||||
}
|
||||
|
||||
private static Recipient getRecipientFromProviderId(Context context, long recipientId, boolean asynchronous) {
|
||||
try {
|
||||
return provider.getRecipient(context, recipientId, asynchronous);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w("RecipientFactory", e);
|
||||
return Recipient.getUnknownRecipient(context);
|
||||
public static Recipients getRecipientsFromString(Context context, @NonNull String rawText, boolean asynchronous) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
|
||||
List<String> ids = new LinkedList<>();
|
||||
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
Optional<Long> id = getRecipientIdFromNumber(context, tokenizer.nextToken());
|
||||
|
||||
if (id.isPresent()) {
|
||||
ids.add(String.valueOf(id.get()));
|
||||
}
|
||||
}
|
||||
|
||||
return getRecipientsForIds(context, ids, asynchronous);
|
||||
}
|
||||
|
||||
private static Recipients getRecipientsForIds(Context context, List<String> idStrings, boolean asynchronous) {
|
||||
long[] ids = new long[idStrings.size()];
|
||||
int i = 0;
|
||||
|
||||
for (String id : idStrings) {
|
||||
ids[i++] = Long.parseLong(id);
|
||||
}
|
||||
|
||||
return provider.getRecipients(context, ids, asynchronous);
|
||||
}
|
||||
|
||||
private static Optional<Long> getRecipientIdFromNumber(Context context, String number) {
|
||||
number = number.trim();
|
||||
|
||||
if (number.isEmpty()) return Optional.absent();
|
||||
|
||||
if (hasBracketedNumber(number)) {
|
||||
number = parseBracketedNumber(number);
|
||||
}
|
||||
|
||||
return Optional.of(CanonicalAddressDatabase.getInstance(context).getCanonicalAddressId(number));
|
||||
}
|
||||
|
||||
private static boolean hasBracketedNumber(String recipient) {
|
||||
@@ -104,26 +119,9 @@ public class RecipientFactory {
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Recipient parseRecipient(Context context, String recipient, boolean asynchronous) {
|
||||
recipient = recipient.trim();
|
||||
|
||||
if( recipient.length() == 0 )
|
||||
return null;
|
||||
|
||||
if (hasBracketedNumber(recipient))
|
||||
return getRecipientForNumber(context, parseBracketedNumber(recipient), asynchronous);
|
||||
|
||||
return getRecipientForNumber(context, recipient, asynchronous);
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
ContactPhotoFactory.clearCache();
|
||||
provider.clearCache();
|
||||
}
|
||||
|
||||
public static void clearCache(Recipient recipient) {
|
||||
ContactPhotoFactory.clearCache(recipient);
|
||||
provider.clearCache(recipient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,27 +22,34 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
public class RecipientProvider {
|
||||
|
||||
private static final Map<Long,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<Long,Recipient>(1000));
|
||||
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
|
||||
private static final Map<Long,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<Long,Recipient>(1000));
|
||||
private static final Map<RecipientIds,Recipients> recipientsCache = Collections.synchronizedMap(new LRUCache<RecipientIds, Recipients>(1000));
|
||||
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
|
||||
|
||||
private static final String[] CALLER_ID_PROJECTION = new String[] {
|
||||
PhoneLookup.DISPLAY_NAME,
|
||||
@@ -51,12 +58,29 @@ public class RecipientProvider {
|
||||
PhoneLookup.NUMBER
|
||||
};
|
||||
|
||||
public Recipient getRecipient(Context context, long recipientId, boolean asynchronous) {
|
||||
Recipient getRecipient(Context context, long recipientId, boolean asynchronous) {
|
||||
Recipient cachedRecipient = recipientCache.get(recipientId);
|
||||
|
||||
if (cachedRecipient != null) return cachedRecipient;
|
||||
else if (asynchronous) return getAsynchronousRecipient(context, recipientId);
|
||||
else return getSynchronousRecipient(context, recipientId);
|
||||
if (cachedRecipient != null) return cachedRecipient;
|
||||
else if (asynchronous) return getAsynchronousRecipient(context, recipientId);
|
||||
else return getSynchronousRecipient(context, recipientId);
|
||||
}
|
||||
|
||||
Recipients getRecipients(Context context, long[] recipientIds, boolean asynchronous) {
|
||||
Recipients cachedRecipients = recipientsCache.get(new RecipientIds(recipientIds));
|
||||
if (cachedRecipients != null) return cachedRecipients;
|
||||
|
||||
List<Recipient> recipientList = new LinkedList<>();
|
||||
|
||||
for (long recipientId : recipientIds) {
|
||||
recipientList.add(getRecipient(context, recipientId, false));
|
||||
}
|
||||
|
||||
if (asynchronous) cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesAsync(context, recipientIds));
|
||||
else cachedRecipients = new Recipients(recipientList, getRecipientsPreferencesSync(context, recipientIds));
|
||||
|
||||
recipientsCache.put(new RecipientIds(recipientIds), cachedRecipients);
|
||||
return cachedRecipients;
|
||||
}
|
||||
|
||||
private Recipient getSynchronousRecipient(final Context context, final long recipientId) {
|
||||
@@ -116,13 +140,9 @@ public class RecipientProvider {
|
||||
return recipient;
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
void clearCache() {
|
||||
recipientCache.clear();
|
||||
}
|
||||
|
||||
public void clearCache(Recipient recipient) {
|
||||
if (recipientCache.containsKey(recipient.getRecipientId()))
|
||||
recipientCache.remove(recipient.getRecipientId());
|
||||
recipientsCache.clear();
|
||||
}
|
||||
|
||||
private RecipientDetails getRecipientDetails(Context context, String number) {
|
||||
@@ -164,6 +184,25 @@ public class RecipientProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable RecipientsPreferences getRecipientsPreferencesSync(Context context, long[] recipientIds) {
|
||||
return DatabaseFactory.getRecipientPreferenceDatabase(context)
|
||||
.getRecipientsPreferences(recipientIds)
|
||||
.orNull();
|
||||
}
|
||||
|
||||
private ListenableFutureTask<RecipientsPreferences> getRecipientsPreferencesAsync(final Context context, final long[] recipientIds) {
|
||||
ListenableFutureTask<RecipientsPreferences> task = new ListenableFutureTask<>(new Callable<RecipientsPreferences>() {
|
||||
@Override
|
||||
public RecipientsPreferences call() throws Exception {
|
||||
return getRecipientsPreferencesSync(context, recipientIds);
|
||||
}
|
||||
});
|
||||
|
||||
asyncRecipientResolver.execute(task);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public static class RecipientDetails {
|
||||
public final String name;
|
||||
public final String number;
|
||||
@@ -178,5 +217,23 @@ public class RecipientProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static class RecipientIds {
|
||||
private final long[] ids;
|
||||
|
||||
private RecipientIds(long[] ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (other == null || !(other instanceof RecipientIds)) return false;
|
||||
return Arrays.equals(this.ids, ((RecipientIds) other).ids);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(ids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,52 +16,155 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.util.Patterns;
|
||||
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class Recipients implements Iterable<Recipient> {
|
||||
public class Recipients implements Iterable<Recipient>, RecipientModifiedListener {
|
||||
|
||||
private List<Recipient> recipients;
|
||||
private static final String TAG = Recipients.class.getSimpleName();
|
||||
|
||||
public Recipients(List<Recipient> recipients) {
|
||||
private final Set<RecipientsModifiedListener> listeners = Collections.newSetFromMap(new WeakHashMap<RecipientsModifiedListener, Boolean>());
|
||||
private final List<Recipient> recipients;
|
||||
|
||||
private Uri ringtone = null;
|
||||
private long mutedUntil = 0;
|
||||
private boolean blocked = false;
|
||||
private VibrateState vibrate = VibrateState.DEFAULT;
|
||||
|
||||
Recipients() {
|
||||
this(new LinkedList<Recipient>(), (RecipientsPreferences)null);
|
||||
}
|
||||
|
||||
Recipients(List<Recipient> recipients, @Nullable RecipientsPreferences preferences) {
|
||||
this.recipients = recipients;
|
||||
}
|
||||
|
||||
public Recipients(final Recipient recipient) {
|
||||
this.recipients = new LinkedList<Recipient>() {{
|
||||
add(recipient);
|
||||
}};
|
||||
}
|
||||
|
||||
public void append(Recipients recipients) {
|
||||
this.recipients.addAll(recipients.getRecipientsList());
|
||||
}
|
||||
|
||||
// public Recipients truncateToSingleRecipient() {
|
||||
// assert(!this.recipients.isEmpty());
|
||||
// this.recipients = this.recipients.subList(0, 1);
|
||||
// return this;
|
||||
// }
|
||||
|
||||
public void addListener(RecipientModifiedListener listener) {
|
||||
for (Recipient recipient : recipients) {
|
||||
recipient.addListener(listener);
|
||||
if (preferences != null) {
|
||||
ringtone = preferences.getRingtone();
|
||||
mutedUntil = preferences.getMuteUntil();
|
||||
vibrate = preferences.getVibrateState();
|
||||
blocked = preferences.isBlocked();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(RecipientModifiedListener listener) {
|
||||
for (Recipient recipient : recipients) {
|
||||
recipient.removeListener(listener);
|
||||
Recipients(List<Recipient> recipients, ListenableFutureTask<RecipientsPreferences> preferences) {
|
||||
this.recipients = recipients;
|
||||
|
||||
preferences.addListener(new FutureTaskListener<RecipientsPreferences>() {
|
||||
@Override
|
||||
public void onSuccess(RecipientsPreferences result) {
|
||||
if (result != null) {
|
||||
|
||||
Set<RecipientsModifiedListener> localListeners;
|
||||
|
||||
synchronized (Recipients.this) {
|
||||
ringtone = result.getRingtone();
|
||||
mutedUntil = result.getMuteUntil();
|
||||
vibrate = result.getVibrateState();
|
||||
blocked = result.isBlocked();
|
||||
|
||||
localListeners = new HashSet<>(listeners);
|
||||
}
|
||||
|
||||
for (RecipientsModifiedListener listener : localListeners) {
|
||||
listener.onModified(Recipients.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
Log.w(TAG, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized @Nullable Uri getRingtone() {
|
||||
return ringtone;
|
||||
}
|
||||
|
||||
public void setRingtone(Uri ringtone) {
|
||||
synchronized (this) {
|
||||
this.ringtone = ringtone;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized boolean isMuted() {
|
||||
return System.currentTimeMillis() <= mutedUntil;
|
||||
}
|
||||
|
||||
public void setMuted(long mutedUntil) {
|
||||
synchronized (this) {
|
||||
this.mutedUntil = mutedUntil;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized boolean isBlocked() {
|
||||
return blocked;
|
||||
}
|
||||
|
||||
public void setBlocked(boolean blocked) {
|
||||
synchronized (this) {
|
||||
this.blocked = blocked;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized VibrateState getVibrate() {
|
||||
return vibrate;
|
||||
}
|
||||
|
||||
public void setVibrate(VibrateState vibrate) {
|
||||
synchronized (this) {
|
||||
this.vibrate = vibrate;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
public synchronized void addListener(RecipientsModifiedListener listener) {
|
||||
if (listeners.isEmpty()) {
|
||||
for (Recipient recipient : recipients) {
|
||||
recipient.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeListener(RecipientsModifiedListener listener) {
|
||||
listeners.remove(listener);
|
||||
|
||||
if (listeners.isEmpty()) {
|
||||
for (Recipient recipient : recipients) {
|
||||
recipient.removeListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,30 +181,6 @@ public class Recipients implements Iterable<Recipient> {
|
||||
return isSingleRecipient() && GroupUtil.isEncodedGroup(recipients.get(0).getNumber());
|
||||
}
|
||||
|
||||
// public Recipients getSecureSessionRecipients(Context context) {
|
||||
// List<Recipient> secureRecipients = new LinkedList<Recipient>();
|
||||
//
|
||||
// for (Recipient recipient : recipients) {
|
||||
// if (KeyUtil.isSessionFor(context, recipient)) {
|
||||
// secureRecipients.add(recipient);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return new Recipients(secureRecipients);
|
||||
// }
|
||||
//
|
||||
// public Recipients getInsecureSessionRecipients(Context context) {
|
||||
// List<Recipient> insecureRecipients = new LinkedList<Recipient>();
|
||||
//
|
||||
// for (Recipient recipient : recipients) {
|
||||
// if (!KeyUtil.isSessionFor(context, recipient)) {
|
||||
// insecureRecipients.add(recipient);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return new Recipients(insecureRecipients);
|
||||
// }
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.recipients.isEmpty();
|
||||
}
|
||||
@@ -129,6 +208,25 @@ public class Recipients implements Iterable<Recipient> {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public String getSortedIdsString() {
|
||||
Set<Long> recipientSet = new HashSet<>();
|
||||
|
||||
for (Recipient recipient : this.recipients) {
|
||||
recipientSet.add(recipient.getRecipientId());
|
||||
}
|
||||
|
||||
long[] recipientArray = new long[recipientSet.size()];
|
||||
int i = 0;
|
||||
|
||||
for (Long recipientId : recipientSet) {
|
||||
recipientArray[i++] = recipientId;
|
||||
}
|
||||
|
||||
Arrays.sort(recipientArray);
|
||||
|
||||
return Util.join(recipientArray, " ");
|
||||
}
|
||||
|
||||
public String[] toNumberStringArray(boolean scrub) {
|
||||
String[] recipientsArray = new String[recipients.size()];
|
||||
Iterator<Recipient> iterator = recipients.iterator();
|
||||
@@ -163,12 +261,31 @@ public class Recipients implements Iterable<Recipient> {
|
||||
return fromString;
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Recipient> iterator() {
|
||||
return recipients.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private void notifyListeners() {
|
||||
Set<RecipientsModifiedListener> localListeners;
|
||||
|
||||
synchronized (this) {
|
||||
localListeners = new HashSet<>(listeners);
|
||||
}
|
||||
|
||||
for (RecipientsModifiedListener listener : localListeners) {
|
||||
listener.onModified(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface RecipientsModifiedListener {
|
||||
public void onModified(Recipients recipient);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@ public class OutgoingEncryptedMessage extends OutgoingTextMessage {
|
||||
super(recipients, body);
|
||||
}
|
||||
|
||||
public OutgoingEncryptedMessage(Recipient recipient, String body) {
|
||||
super(recipient, body);
|
||||
}
|
||||
|
||||
private OutgoingEncryptedMessage(OutgoingEncryptedMessage base, String body) {
|
||||
super(base, body);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.thoughtcrime.securesms.sms;
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
public class OutgoingKeyExchangeMessage extends OutgoingTextMessage {
|
||||
|
||||
public OutgoingKeyExchangeMessage(Recipient recipient, String message) {
|
||||
super(recipient, message);
|
||||
public OutgoingKeyExchangeMessage(Recipients recipients, String message) {
|
||||
super(recipients, message);
|
||||
}
|
||||
|
||||
private OutgoingKeyExchangeMessage(OutgoingKeyExchangeMessage base, String body) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.sms;
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
|
||||
public class OutgoingTextMessage {
|
||||
@@ -9,10 +8,6 @@ public class OutgoingTextMessage {
|
||||
private final Recipients recipients;
|
||||
private final String message;
|
||||
|
||||
public OutgoingTextMessage(Recipient recipient, String message) {
|
||||
this(new Recipients(recipient), message);
|
||||
}
|
||||
|
||||
public OutgoingTextMessage(Recipients recipients, String message) {
|
||||
this.recipients = recipients;
|
||||
this.message = message;
|
||||
@@ -49,13 +44,13 @@ public class OutgoingTextMessage {
|
||||
|
||||
public static OutgoingTextMessage from(SmsMessageRecord record) {
|
||||
if (record.isSecure()) {
|
||||
return new OutgoingEncryptedMessage(record.getIndividualRecipient(), record.getBody().getBody());
|
||||
return new OutgoingEncryptedMessage(record.getRecipients(), record.getBody().getBody());
|
||||
} else if (record.isKeyExchange()) {
|
||||
return new OutgoingKeyExchangeMessage(record.getIndividualRecipient(), record.getBody().getBody());
|
||||
return new OutgoingKeyExchangeMessage(record.getRecipients(), record.getBody().getBody());
|
||||
} else if (record.isEndSession()) {
|
||||
return new OutgoingEndSessionMessage(new OutgoingTextMessage(record.getIndividualRecipient(), record.getBody().getBody()));
|
||||
return new OutgoingEndSessionMessage(new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody()));
|
||||
} else {
|
||||
return new OutgoingTextMessage(record.getIndividualRecipient(), record.getBody().getBody());
|
||||
return new OutgoingTextMessage(record.getRecipients(), record.getBody().getBody());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class DynamicNoActionBarTheme extends DynamicTheme {
|
||||
@Override
|
||||
protected int getSelectedTheme(Activity activity) {
|
||||
String theme = TextSecurePreferences.getTheme(activity);
|
||||
|
||||
if (theme.equals("dark")) return R.style.TextSecure_DarkNoActionBar;
|
||||
|
||||
return R.style.TextSecure_LightNoActionBar;
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,17 @@ public class Util {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String join(long[] list, String delimeter) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int j=0;j<list.length;j++) {
|
||||
if (j != 0) sb.append(delimeter);
|
||||
sb.append(list[j]);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static ExecutorService newSingleThreadedLifoExecutor() {
|
||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingLifoQueue<Runnable>());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user