Support for per-recipient muting, blocking, and ringtones.
Fixes #757 Fixes #354 Fixes #222 Closes #1815 Closes #3378 // FREEBIEpull/1/head
After Width: | Height: | Size: 502 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 470 B |
After Width: | Height: | Size: 461 B |
After Width: | Height: | Size: 359 B |
After Width: | Height: | Size: 355 B |
After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 342 B |
After Width: | Height: | Size: 622 B |
After Width: | Height: | Size: 606 B |
After Width: | Height: | Size: 543 B |
After Width: | Height: | Size: 532 B |
After Width: | Height: | Size: 855 B |
After Width: | Height: | Size: 848 B |
After Width: | Height: | Size: 754 B |
After Width: | Height: | Size: 745 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 921 B |
After Width: | Height: | Size: 911 B |
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<org.thoughtcrime.securesms.preferences.BlockedContactListItem
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingRight="25dip">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/contact_photo_image"
|
||||
android:layout_width="@dimen/contact_selection_photo_size"
|
||||
android:layout_height="@dimen/contact_selection_photo_size"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:cropToPadding="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:gravity="center_vertical|left"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</org.thoughtcrime.securesms.preferences.BlockedContactListItem>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip">
|
||||
|
||||
<ListView android:id="@id/android:list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawSelectorOnTop="false"/>
|
||||
|
||||
<TextView android:id="@id/android:empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center|center_vertical"
|
||||
android:gravity="center|center_vertical"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/blocked_contacts_fragment__no_blocked_contacts"/>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.ConversationTitleView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:drawablePadding="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="@style/TextSecure.TitleTextStyle"/>
|
||||
|
||||
<TextView android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
style="@style/TextSecure.SubtitleTextStyle"/>
|
||||
|
||||
</org.thoughtcrime.securesms.ConversationTitleView>
|
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_height="130dp"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:theme="@style/TextSecure.LightActionBar">
|
||||
|
||||
<RelativeLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.AvatarImageView
|
||||
android:id="@+id/avatar"
|
||||
android:foreground="@drawable/contact_photo_background"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:cropToPadding="true"
|
||||
android:layout_marginLeft="0dp"
|
||||
android:layout_alignParentLeft="true"/>
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
style="@style/TextSecure.TitleTextStyle"
|
||||
android:layout_toRightOf="@id/avatar"
|
||||
android:layout_marginLeft="10dip"/>
|
||||
|
||||
<TextView android:id="@+id/blocked_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/avatar"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_alignLeft="@id/name"
|
||||
android:text="@string/recipient_preference_activity__blocked"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?recipient_preference_blocked"
|
||||
android:textAllCaps="true" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<FrameLayout android:id="@+id/preference_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:title="@string/conversation_muted__unmute"
|
||||
android:id="@+id/menu_unmute_notifications" />
|
||||
</menu>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/conversation_unmuted__mute_notifications"
|
||||
android:id="@+id/menu_mute_notifications" />
|
||||
|
||||
</menu>
|
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
|
||||
android:key="pref_key_recipient_mute"
|
||||
android:title="@string/recipient_preferences__mute_conversation"
|
||||
android:summary="@string/recipient_preferences__disable_notifications_for_this_conversation"
|
||||
android:defaultValue="false"
|
||||
android:disableDependentsState="true"
|
||||
android:persistent="false" />
|
||||
|
||||
<RingtonePreference android:dependency="pref_key_recipient_mute"
|
||||
android:key="pref_key_recipient_ringtone"
|
||||
android:title="@string/recipient_preferences__ringtone"
|
||||
android:ringtoneType="notification"
|
||||
android:showSilent="false"
|
||||
android:showDefault="true"
|
||||
android:persistent="false"/>
|
||||
|
||||
<ListPreference android:dependency="pref_key_recipient_mute"
|
||||
android:key="pref_key_recipient_vibrate"
|
||||
android:title="@string/recipient_preferences__vibrate"
|
||||
android:entries="@array/recipient_vibrate_entries"
|
||||
android:entryValues="@array/recipient_vibrate_values"
|
||||
android:defaultValue="0"
|
||||
android:persistent="false"/>
|
||||
|
||||
<Preference android:key="pref_key_recipient_block"
|
||||
android:title="@string/recipient_preferences__block" />
|
||||
|
||||
|
||||
</PreferenceScreen>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|