/* Copyright (C) 2015, Silent Circle, LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Any redistribution, use, or modification is done solely for personal benefit and not for any commercial purpose or for monetary gain * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Silent Circle nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SILENT CIRCLE, LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.silentcircle.silenttext.fragment; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.annotation.SuppressLint; import android.app.Fragment; import android.content.ActivityNotFoundException; import android.content.ContentUris; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PorterDuff; import android.net.Uri; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; import com.silentcircle.silenttext.R; import com.silentcircle.silenttext.activity.ConversationListActivity; import com.silentcircle.silenttext.application.SilentTextApplication; import com.silentcircle.silenttext.dialog.SCDialogFragment; import com.silentcircle.silenttext.headerlistview.HeaderListView; import com.silentcircle.silenttext.headerlistview.SectionAdapter; import com.silentcircle.silenttext.loader.ContactUser; import com.silentcircle.silenttext.loader.ScContactsLoader; import com.silentcircle.silenttext.loader.ScDirectoryLoader; import com.silentcircle.silenttext.loader.ScDirectoryLoader.UserData; import com.silentcircle.silenttext.log.Log; import com.silentcircle.silenttext.service.ScContactSaveService; import com.silentcircle.silenttext.util.Constants; import com.silentcircle.silenttext.util.SilentPhone; import com.silentcircle.silenttext.view.CircularImageView; public class DirectorySearchListFragment extends Fragment { public static final String TAG = "DirectorySearchListFragment"; public static final String ACTION_SET_STARRED = "setStarred"; public static final String EXTRA_CONTACT_URI = "contactUri"; public static final String EXTRA_STARRED_FLAG = "starred"; ConversationListActivity mActivity; LinearLayout mLayout, mClickedLayout; public SectionAdapter mAdapter; List<UserData> mDirectorySearchList = new ArrayList<UserData>(); List<ContactUser> mScContactsList = new ArrayList<ContactUser>(); HeaderListView mListView; String mSearchQuery; int mClickedSection = -1, mClickedRow = -1; View mClickedItemView; private RequestQueue mVolleyQueue; ImageLoader mImageLoader; public DirectorySearchListFragment() { } public DirectorySearchListFragment( ConversationListActivity activity, List<UserData> directorySearchList, List<ContactUser> scContactsList ) { mActivity = activity; mDirectorySearchList = directorySearchList; mScContactsList = scContactsList; createList(); mVolleyQueue = Volley.newRequestQueue( mActivity ); mImageLoader = new ImageLoader( mVolleyQueue, new BitmapLruCache( BitmapLruCache.getDefaultLruCacheSize() ) ); } private void addContact( boolean isFavorite ) { UserData user = mDirectorySearchList.get( mClickedRow ); String display_name = user.getDisplayName(); String jid = user.getJid(); String phone_number = user.getNumbers(); launchSPA( display_name, jid, phone_number, isFavorite ); } void colorSearchString( TextView textView ) { String text = textView.getText().toString(); String [] nameAry = text.split( " " ); boolean isAddedFullname = false; String name = ""; int start = 0; for( int l = 0; l < nameAry.length; l++ ) { if( nameAry[l].toUpperCase().startsWith( mSearchQuery.toUpperCase() ) ) { isAddedFullname = true; name = nameAry[l]; break; } start += nameAry[l].length() + 1; } if( isAddedFullname ) { SpannableStringBuilder sb = new SpannableStringBuilder( text ); Pattern p = Pattern.compile( mSearchQuery, Pattern.CASE_INSENSITIVE ); // Matcher m = p.matcher( text ); Matcher m = p.matcher( name ); if( m.find() ) { sb.setSpan( new ForegroundColorSpan( mActivity.getResources().getColor( R.color.directory_search_yellow ) ), start + m.start(), start + m.end(), Spanned.SPAN_INCLUSIVE_INCLUSIVE ); textView.setText( sb ); } } } private void createList() { mListView = new HeaderListView( mActivity ); mAdapter = new SectionAdapter( this, mDirectorySearchList, mScContactsList ) { @Override public Object getRowItem( int section, int row ) { return null; } @Override public View getRowView( int section, int row, View convertView1, ViewGroup parent ) { View convertView = convertView1; if( convertView == null ) { convertView = mActivity.getLayoutInflater().inflate( R.layout.directory_search_item, null ); } TextView displayName = (TextView) convertView.findViewById( R.id.search_display_name_tv_id ); TextView jid = (TextView) convertView.findViewById( R.id.search_jid_tv_id ); ImageView infoImg = (ImageView) convertView.findViewById( R.id.icon_info_id ); infoImg.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); ImageView addImg = (ImageView) convertView.findViewById( R.id.icon_add_contact_id ); addImg.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); ImageView favouriteIcon = (ImageView) convertView.findViewById( R.id.icon_favourite_id ); favouriteIcon.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); if( section == Constants.CONTACTS_SECTION ) { if( mScContactsList.get( row ).isStarred() ) { favouriteIcon.setColorFilter( Color.CYAN, PorterDuff.Mode.SRC_ATOP ); } } ImageView phoneIcon = (ImageView) convertView.findViewById( R.id.icon_phone_id ); phoneIcon.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); ImageView messageIcon = (ImageView) convertView.findViewById( R.id.icon_message_id ); messageIcon.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); CircularImageView avarta = (CircularImageView) convertView.findViewById( R.id.avatar ); if( section == 0 ) { String avartUrl = mScContactsList.get( row ).getAvatarUrl(); if( !TextUtils.isEmpty( avartUrl ) ) { try { Bitmap bitmap = BitmapFactory.decodeStream( getActivity().getContentResolver().openInputStream( Uri.parse( mScContactsList.get( row ).getAvatarUrl() ) ) ); avarta.setImageBitmap( bitmap ); } catch( FileNotFoundException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } displayName.setText( mScContactsList.get( row ).getDisplayName() ); // jid.setText( mScContactsList.get( row ).getNumbers() + ":" + // mScContactsList.get( row ).getJid() ); jid.setText( mScContactsList.get( row ).getNumbers() ); infoImg.setVisibility( View.VISIBLE ); addImg.setVisibility( View.GONE ); } else if( section == 1 ) { String avartaUrl = SilentTextApplication.ACCOUNT_CREATION_CLIENT_BASE_URL + mDirectorySearchList.get( row ).getAvatarUrl(); mImageLoader.get( avartaUrl, ImageLoader.getImageListener( avarta, R.drawable.ic_avatar_placeholder, R.drawable.ic_action_picture ) ); displayName.setText( mDirectorySearchList.get( row ).getDisplayName() ); // jid.setText( mDirectorySearchList.get( row ).getNumbers() + ":" + // mDirectorySearchList.get( row ).getJid() ); jid.setText( mDirectorySearchList.get( row ).getNumbers() ); addImg.setVisibility( View.VISIBLE ); infoImg.setVisibility( View.GONE ); } colorSearchString( displayName ); colorSearchString( jid ); mLayout = (LinearLayout) convertView.findViewById( R.id.contact_list_icons_layout_id ); if( mClickedSection == section && mClickedRow == row ) { // programmatically click on the view to avoid problem. onRowItemClick( (AdapterView<?>) parent, convertView, section, row, getItemId( row ) ); mLayout.setVisibility( View.VISIBLE ); mClickedLayout = mLayout; } else { mLayout.setVisibility( View.GONE ); } return convertView; } @Override public int getSectionHeaderItemViewType( int section ) { return section % 2; } @Override public View getSectionHeaderView( int section, View convertView1, ViewGroup parent ) { View convertView = convertView1; if( convertView == null ) { convertView = mActivity.getLayoutInflater().inflate( R.layout.directory_hearder, null ); } TextView orgName = (TextView) convertView.findViewById( R.id.org_name_tv_id ); orgName.setText( Constants.mOrgName ); return convertView; } @Override public int getSectionHeaderViewTypeCount() { return 2; } @Override public boolean hasSectionHeaderView( int section ) { if( section == Constants.CONTACTS_SECTION ) { return false; } if( section == Constants.DIRECTORY_SECTION && !TextUtils.isEmpty( mSearchQuery ) ) { return true; } return false; } @Override public int numberOfRows( int section ) { // if( section == Constants.CONTACTS_SECTION ) { // return mScContactsList.size(); // } // return mDirectorySearchList.size(); if( section == Constants.DIRECTORY_SECTION && !TextUtils.isEmpty( Constants.mOrgName ) ) { return mDirectorySearchList.size(); } if( section == Constants.CONTACTS_SECTION ) { return mScContactsList.size(); } return 0; } @Override public int numberOfSections() { if( TextUtils.isEmpty( Constants.mOrgName ) ) { return 1; } return 2; } @Override public void onRowItemClick( AdapterView<?> parent, View view, int section, int row, long id ) { super.onRowItemClick( parent, view, section, row, id ); if( mClickedLayout != null ) { mClickedLayout.setVisibility( View.GONE ); } mClickedSection = section; mClickedRow = row; mClickedItemView = view; ImageView favouriteIcon = (ImageView) view.findViewById( R.id.icon_favourite_id ); if( section == Constants.CONTACTS_SECTION && mScContactsList.get( row ).isStarred() ) { favouriteIcon.setColorFilter( Color.CYAN, PorterDuff.Mode.SRC_ATOP ); } else { favouriteIcon.setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); } favouriteIcon.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { onClickFavourite( v ); } } ); ImageView phoneIcon = (ImageView) view.findViewById( R.id.icon_phone_id ); phoneIcon.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { onClickPhone(); } } ); ImageView addContactIcon = (ImageView) view.findViewById( R.id.icon_add_contact_id ); addContactIcon.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { onClickAddContact(); } } ); ImageView messageIcon = (ImageView) view.findViewById( R.id.icon_message_id ); messageIcon.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { onClickMessage(); } } ); ImageView contactInfoIcon = (ImageView) view.findViewById( R.id.icon_info_id ); contactInfoIcon.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { onClickContactInfoIcon(); } } ); mLayout = (LinearLayout) view.findViewById( R.id.contact_list_icons_layout_id ); mLayout.setVisibility( View.VISIBLE ); } }; mListView.setAdapter( mAdapter ); mAdapter.notifyDataSetChanged(); } public SectionAdapter getAdapter() { return mAdapter; } @SuppressLint( "UseValueOf" ) private void launchSPA( String displayName, String jid, String phoneNumber, boolean isFavorite ) { // launch SPA to add contact, or make call Constants.mIsLaunchSPA = true; Constants.mCurrentRow = mClickedRow; Constants.mCurrentSection = mClickedSection; Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage( Constants.SPA_PACKAGE_NAME ); if( intent != null ) { if( !SilentPhone.supports( mActivity ) ) { Toast.makeText( getActivity(), R.string.spa_locked, Toast.LENGTH_LONG ).show(); return; } if( !TextUtils.isEmpty( phoneNumber ) && TextUtils.isEmpty( displayName ) && TextUtils.isEmpty( jid ) ) { // launch SPA dialer try { startActivity( SilentPhone.getCallIntent( phoneNumber ) ); } catch( ActivityNotFoundException exception ) { Toast.makeText( getActivity(), R.string.spa_locked, Toast.LENGTH_LONG ).show(); } } else if( !TextUtils.isEmpty( phoneNumber ) && !TextUtils.isEmpty( displayName ) && !TextUtils.isEmpty( jid ) ) { // launch SPA to add contact and add contact as favorite intent = new Intent( Intent.ACTION_INSERT, com.silentcircle.silentcontacts2.ScContactsContract.RawContacts.CONTENT_URI ); intent.setType( com.silentcircle.silentcontacts2.ScContactsContract.RawContacts.CONTENT_TYPE ); intent.putExtra( Constants.STARRED, isFavorite ); intent.putExtra( com.silentcircle.silentcontacts2.ScContactsContract.Intents.Insert.NAME, displayName ); intent.putExtra( com.silentcircle.silentcontacts2.ScContactsContract.Intents.Insert.PHONE, phoneNumber ); intent.putExtra( com.silentcircle.silentcontacts2.ScContactsContract.Intents.Insert.IM_HANDLE, jid ); intent.setFlags( Intent.FLAG_ACTIVITY_FORWARD_RESULT ); startActivity( intent ); } } else { String title = getResources().getString( R.string.directory_search_dialog_information_title ); String msg = getResources().getString( R.string.directory_search_dialog_no_spa_msg ); SCDialogFragment dialog = SCDialogFragment.newInstance( title, msg, android.R.string.ok, android.R.string.cancel, Constants.DIRECTORY_SEARCH_DIALOG_NO_SPA ); dialog.setCancelable( false ); dialog.show( getFragmentManager(), TAG ); } } @Override public void onActivityCreated( Bundle savedInstanceState ) { super.onActivityCreated( savedInstanceState ); } public void onClickAddContact() { addContact( false ); } public void onClickContactInfoIcon() { // save variables for recovery later. Constants.mIsContactInfoClicked = true; Constants.mCurrentRow = mClickedRow; Constants.mCurrentSection = mClickedSection; long rowContactId = mScContactsList.get( mClickedRow ).getRaw_id(); Intent intent = new Intent( Intent.ACTION_VIEW, ContentUris.withAppendedId( com.silentcircle.silentcontacts2.ScContactsContract.RawContacts.CONTENT_URI, rowContactId ) ); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP ); getActivity().startActivity( intent ); } public void onClickFavourite( View v ) { if( mClickedSection == Constants.DIRECTORY_SECTION ) { addContact( true ); } else { boolean isStarred = mScContactsList.get( mClickedRow ).isStarred(); setFavorite( mScContactsList.get( mClickedRow ).getRaw_id(), !isStarred ); if( isStarred ) { ( (ImageView) v ).setColorFilter( getResources().getColor( R.color.directory_search_yellow ), PorterDuff.Mode.SRC_ATOP ); } else { ( (ImageView) v ).setColorFilter( Color.CYAN, PorterDuff.Mode.SRC_ATOP ); } mScContactsList.get( mClickedRow ).setStarred( !isStarred ); } } public void onClickMessage() { String jid; if( mClickedSection == Constants.CONTACTS_SECTION ) { jid = mScContactsList.get( mClickedRow ).getJid(); } else { jid = mDirectorySearchList.get( mClickedRow ).getJid(); } if( TextUtils.isEmpty( jid ) ) { showToast( R.string.null_jid ); return; } Constants.mIsMessageClicked = true; ( (ConversationListActivity) getActivity() ).launchConversationActivity( jid ); } public void onClickPhone() { String phoneNumber; if( mClickedSection == Constants.CONTACTS_SECTION ) { phoneNumber = mScContactsList.get( mClickedRow ).getNumbers(); } else { phoneNumber = mDirectorySearchList.get( mClickedRow ).getNumbers(); } if( TextUtils.isEmpty( phoneNumber ) ) { showToast( R.string.null_phone_number ); return; } launchSPA( null, null, phoneNumber, false ); } @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { // reset variables. if( Constants.mCurrentRow != -1 ) { mClickedRow = Constants.mCurrentRow; Constants.mCurrentRow = -1; mClickedSection = Constants.mCurrentSection; Constants.mCurrentSection = -1; } return mListView; } @SuppressLint( "NewApi" ) @Override public void onDetach() { super.onDetach(); if( Constants.mIsHomeClicked || Constants.mIsLaunchSPA || Constants.mIsContactInfoClicked ) { Constants.mIsLaunchSPA = false; Constants.mIsContactInfoClicked = false; Log.i( TAG, "Must keep empty block and do nothing here." ); } else { mActivity.setCurrentQuery( "" ); } // // int count = getFragmentManager().getBackStackEntryCount(); // if( count > 0 && getFragmentManager().getBackStackEntryAt( count - 1 ).getName().equals( // DirectorySearchListFragment.TAG ) ) { // if( Build.VERSION.SDK_INT < 17 ) { // if( mActivity.isFinishing() ) { // return; // } // } else { // if( mActivity.isFinishing() || mActivity.isDestroyed() ) { // return; // } // } // getFragmentManager().popBackStack(); // } } @Override public void onResume() { super.onResume(); } public void setContacts( List<ContactUser> list ) { mScContactsList.clear(); int size = list.size(); if( list.size() == 0 ) { return; } for( int i = 0; i < size; i++ ) { mScContactsList.add( list.get( i ) ); } mAdapter.notifyDataSetChanged(); } public void setContactsSearchList( ScContactsLoader loader ) { mScContactsList.clear(); if( loader != null && loader.getUserList() != null ) { for( int i = 0; i < loader.getUserList().size(); i++ ) { mScContactsList.add( loader.getUserList().get( i ) ); } } mAdapter.notifyDataSetChanged(); } public void setDirectorySearchList( Cursor cursor, int section ) { mDirectorySearchList.clear(); if( cursor != null && section == R.id.directory_search ) { if( ScDirectoryLoader.getUserList() != null ) { for( int i = 0; i < ScDirectoryLoader.getUserList().size(); i++ ) { mDirectorySearchList.add( ScDirectoryLoader.getUserList().get( i ) ); } } } mAdapter.notifyDataSetChanged(); } // contactLookupUri = content://com.silentcircle.contacts2/raw_contacts/24 private void setFavorite( long rawContactId, boolean isStarred ) { Intent serviceIntent = new Intent( getActivity(), ScContactSaveService.class ); serviceIntent.setAction( ACTION_SET_STARRED ); Uri lookupUri = ContentUris.withAppendedId( com.silentcircle.silentcontacts2.ScContactsContract.RawContacts.CONTENT_URI, rawContactId ); serviceIntent.putExtra( EXTRA_CONTACT_URI, lookupUri ); serviceIntent.putExtra( EXTRA_STARRED_FLAG, isStarred ); getActivity().startService( serviceIntent ); } public void setSearchString( String query ) { mSearchQuery = query; } private void showToast( int resId ) { Toast mytoast = Toast.makeText( mActivity, resId, Toast.LENGTH_LONG ); mytoast.setGravity( Gravity.CENTER_VERTICAL, 0, 0 ); mytoast.show(); } }