Update look of contact selection activities.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-07-14 14:31:03 -07:00
parent 704f2b91e2
commit 2ef0054840
30 changed files with 418 additions and 269 deletions

View File

@@ -0,0 +1,198 @@
/**
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import java.io.IOException;
/**
* Base activity container for selecting a list of contacts.
*
* @author Moxie Marlinspike
*
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
implements SwipeRefreshLayout.OnRefreshListener,
ContactSelectionListFragment.OnContactSelectedListener
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
public final static String PUSH_ONLY_EXTRA = "push_only";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
protected ContactSelectionListFragment contactsFragment;
private Toolbar toolbar;
private EditText searchText;
private AnimatingToggle toggle;
protected ImageView action;
private ImageView keyboardToggle;
private ImageView dialpadToggle;
private ImageView clearToggle;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
setContentView(R.layout.contact_selection_activity);
initializeToolbar();
initializeResources();
initializeSearch();
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
private void initializeToolbar() {
this.toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
private void initializeResources() {
this.action = (ImageView) findViewById(R.id.action_icon);
this.searchText = (EditText) findViewById(R.id.search_view);
this.toggle = (AnimatingToggle) findViewById(R.id.button_toggle);
this.keyboardToggle = (ImageView) findViewById(R.id.search_keyboard);
this.dialpadToggle = (ImageView) findViewById(R.id.search_dialpad);
this.clearToggle = (ImageView) findViewById(R.id.search_clear);
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this);
contactsFragment.setOnRefreshListener(this);
this.keyboardToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
toggle.display(dialpadToggle);
}
});
this.dialpadToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchText.setInputType(InputType.TYPE_CLASS_PHONE);
toggle.display(keyboardToggle);
}
});
this.clearToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchText.setText("");
if (SearchUtil.isTextInput(searchText)) toggle.display(dialpadToggle);
else toggle.display(keyboardToggle);
}
});
}
private void initializeSearch() {
this.searchText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (!SearchUtil.isEmpty(searchText)) toggle.display(clearToggle);
else if (SearchUtil.isTextInput(searchText)) toggle.display(dialpadToggle);
else if (SearchUtil.isPhoneInput(searchText)) toggle.display(keyboardToggle);
contactsFragment.setQueryFilter(searchText.getText().toString());
}
});
}
@Override
public void onRefresh() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
DirectoryHelper.refreshDirectory(ContactSelectionActivity.this);
} catch (IOException e) {
Log.w(TAG, e);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
searchText.setText("");
contactsFragment.resetQueryFilter();
}
}.execute();
}
@Override
public void onContactSelected(String number) {}
private static class SearchUtil {
public static boolean isTextInput(EditText editText) {
return (editText.getInputType() & 0x0000000F) == InputType.TYPE_CLASS_TEXT;
}
public static boolean isPhoneInput(EditText editText) {
return (editText.getInputType() & 0x0000000F) == InputType.TYPE_CLASS_PHONE;
}
public static boolean isEmpty(EditText editText) {
return editText.getText().length() <= 0;
}
}
}

View File

@@ -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
@@ -23,13 +23,11 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.text.Editable;
import android.text.TextWatcher;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.TextView;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
@@ -49,17 +47,17 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
* @author Moxie Marlinspike
*
*/
public class PushContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
private static final String TAG = "ContactSelectFragment";
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
private TextView emptyText;
private Map<Long, String> selectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private StickyListHeadersListView listView;
private SwipeRefreshLayout swipeRefresh;
private String cursorFilter;
private boolean multi = false;
@@ -67,7 +65,6 @@ public class PushContactSelectionListFragment extends Fragment
@Override
public void onActivityCreated(Bundle icicle) {
super.onCreate(icicle);
initializeResources();
initializeCursor();
}
@@ -83,7 +80,17 @@ public class PushContactSelectionListFragment extends Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.push_contact_selection_list_activity, container, false);
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
this.emptyText = (TextView) view.findViewById(android.R.id.empty);
swipeRefresh = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
listView = (StickyListHeadersListView) view.findViewById(android.R.id.list);
listView.setFocusable(true);
listView.setFastScrollEnabled(true);
listView.setDrawingListUnderStickyHeader(false);
listView.setOnItemClickListener(new ListClickListener());
return view;
}
public List<String> getSelectedContacts() {
@@ -106,42 +113,19 @@ public class PushContactSelectionListFragment extends Fragment
this.getLoaderManager().initLoader(0, null, this);
}
private void initializeResources() {
emptyText = (TextView) getView().findViewById(android.R.id.empty);
listView = (StickyListHeadersListView) getView().findViewById(android.R.id.list);
listView.setFocusable(true);
listView.setFastScrollEnabled(true);
listView.setDrawingListUnderStickyHeader(false);
listView.setOnItemClickListener(new ListClickListener());
EditText filterEditText = (EditText) getView().findViewById(R.id.filter);
filterEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
cursorFilter = charSequence.toString();
update();
}
@Override
public void afterTextChanged(Editable editable) {
}
});
cursorFilter = null;
public void setQueryFilter(String filter) {
this.cursorFilter = filter;
this.getLoaderManager().restartLoader(0, null, this);
}
public void update() {
this.getLoaderManager().restartLoader(0, null, this);
public void resetQueryFilter() {
setQueryFilter(null);
swipeRefresh.setRefreshing(false);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
boolean pushOnly = getActivity().getIntent().getBooleanExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, false);
boolean pushOnly = getActivity().getIntent().getBooleanExtra(ContactSelectionActivity.PUSH_ONLY_EXTRA, false);
boolean supportsSms = TextSecurePreferences.isSmsEnabled(getActivity());
return new ContactsCursorLoader(getActivity(), !pushOnly && supportsSms, cursorFilter);
@@ -178,6 +162,10 @@ public class PushContactSelectionListFragment extends Fragment
this.onContactSelectedListener = onContactSelectedListener;
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
public interface OnContactSelectedListener {
public void onContactSelected(String number);
}

View File

@@ -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
@@ -17,99 +17,26 @@
package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
/**
* Activity container for selecting a list of contacts.
* Activity container for starting a new conversation.
*
* @author Moxie Marlinspike
*
*/
public class NewConversationActivity extends PassphraseRequiredActionBarActivity {
public class NewConversationActivity extends ContactSelectionActivity {
private static final String TAG = NewConversationActivity.class.getSimpleName();
private final DynamicTheme dynamicTheme = new DynamicTheme ();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private PushContactSelectionListFragment contactsFragment;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
public void onContactSelected(String number) {
Recipients recipients = RecipientFactory.getRecipientsFromString(this, number, true);
@Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.new_conversation_activity);
initializeResources();
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
getSupportActionBar().setTitle(R.string.AndroidManifest__select_contacts);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
menu.clear();
if (TextSecurePreferences.isPushRegistered(this)) inflater.inflate(R.menu.push_directory, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true;
case android.R.id.home: finish(); return true;
}
return false;
}
private void initializeResources() {
contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() {
@Override
public void onContactSelected(String number) {
Log.i(TAG, "Choosing contact from list.");
Recipients recipients = RecipientFactory.getRecipientsFromString(NewConversationActivity.this, number, true);
openNewConversation(recipients);
}
});
}
private void handleDirectoryRefresh() {
DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() {
@Override
public void onUpdateFinished() {
contactsFragment.update();
}
});
}
private void openNewConversation(Recipients recipients) {
if (recipients != null) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
@@ -117,11 +44,14 @@ public class NewConversationActivity extends PassphraseRequiredActionBarActivity
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_AUDIO_EXTRA));
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_VIDEO_EXTRA));
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_IMAGE_EXTRA));
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
startActivity(intent);
finish();
}
}
}

View File

@@ -22,10 +22,12 @@ import android.support.annotation.NonNull;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -38,81 +40,28 @@ import java.util.List;
* @author Moxie Marlinspike
*
*/
public class PushContactSelectionActivity extends PassphraseRequiredActionBarActivity {
private final static String TAG = "ContactSelectActivity";
public final static String PUSH_ONLY_EXTRA = "push_only";
public class PushContactSelectionActivity extends ContactSelectionActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme ();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private PushContactSelectionListFragment contactsFragment;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
private final static String TAG = PushContactSelectionActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.push_contact_selection_activity);
initializeResources();
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
getSupportActionBar().setTitle(R.string.AndroidManifest__select_contacts);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
menu.clear();
if (TextSecurePreferences.isPushRegistered(this)) inflater.inflate(R.menu.push_directory, menu);
inflater.inflate(R.menu.contact_selection, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true;
case R.id.menu_selection_finished: handleSelectionFinished(); return true;
case android.R.id.home: finish(); return true;
}
return false;
}
private void initializeResources() {
contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
super.onCreate(icicle, masterSecret);
contactsFragment.setMultiSelect(true);
}
private void handleSelectionFinished() {
Intent resultIntent = getIntent();
List<String> selectedContacts = contactsFragment.getSelectedContacts();
if (selectedContacts != null) {
resultIntent.putStringArrayListExtra("contacts", new ArrayList<>(selectedContacts));
}
setResult(RESULT_OK, resultIntent);
finish();
}
private void handleDirectoryRefresh() {
DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() {
action.setImageDrawable(getResources().getDrawable(R.drawable.ic_check_white_24dp));
action.setOnClickListener(new View.OnClickListener() {
@Override
public void onUpdateFinished() {
contactsFragment.update();
public void onClick(View v) {
Intent resultIntent = getIntent();
List<String> selectedContacts = contactsFragment.getSelectedContacts();
if (selectedContacts != null) {
resultIntent.putStringArrayListExtra("contacts", new ArrayList<>(selectedContacts));
}
setResult(RESULT_OK, resultIntent);
finish();
}
});
}

View File

@@ -65,7 +65,7 @@ public class ContactSelectionListAdapter extends CursorAdapter
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return li.inflate(R.layout.push_contact_selection_list_item, parent, false);
return li.inflate(R.layout.contact_selection_list_item, parent, false);
}
@Override
@@ -96,7 +96,7 @@ public class ContactSelectionListAdapter extends CursorAdapter
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = li.inflate(R.layout.push_contact_selection_list_header, viewGroup, false);
convertView = li.inflate(R.layout.contact_selection_list_header, viewGroup, false);
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {

View File

@@ -149,7 +149,7 @@ public class ContactsDatabase {
.withSelection(BaseColumns._ID + " = ?", new String[] {String.valueOf(rowId)})
.build());
}
public @NonNull Cursor querySystemContacts(String filter) {
Uri uri;