2014-10-29 21:58:58 +01:00
//
// MessageComposeTableViewController . m
//
//
// Created by Dylan Bourgeois on 02 / 11 / 14.
//
//
2014-11-21 14:38:37 +01:00
# import "Environment.h"
# import "Contact.h"
2015-01-14 22:30:01 +01:00
# import "PhoneNumberUtil.h"
2014-10-29 21:58:58 +01:00
# import "MessageComposeTableViewController.h"
# import "MessagesViewController.h"
# import "SignalsViewController.h"
2015-01-14 22:30:01 +01:00
# import "NotificationManifest.h"
# import "PhoneNumberDirectoryFilterManager.h"
# import < MessageUI / MessageUI . h >
# import < MessageUI / MFMessageComposeViewController . h >
2014-10-29 21:58:58 +01:00
# import "ContactTableViewCell.h"
2015-01-14 22:30:01 +01:00
# import "UIColor+OWS.h"
2014-10-29 21:58:58 +01:00
2015-01-14 22:30:01 +01:00
@ interface MessageComposeTableViewController ( ) < UISearchBarDelegate , UISearchResultsUpdating , MFMessageComposeViewControllerDelegate >
2014-10-29 21:58:58 +01:00
{
2015-01-14 22:30:01 +01:00
UIButton * sendTextButton ;
NSString * currentSearchTerm ;
2014-10-29 21:58:58 +01:00
NSArray * contacts ;
NSArray * searchResults ;
}
@ property ( nonatomic , strong ) UISearchController * searchController ;
@ end
@ implementation MessageComposeTableViewController
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
2015-01-14 22:30:01 +01:00
[ self . navigationController . navigationBar setTranslucent : NO ] ;
2014-10-29 21:58:58 +01:00
[ self initializeSearch ] ;
2014-11-21 14:38:37 +01:00
contacts = [ [ Environment getCurrent ] contactsManager ] . textSecureContacts ;
2014-10-29 21:58:58 +01:00
searchResults = contacts ;
self . tableView . tableFooterView = [ [ UIView alloc ] initWithFrame : CGRectZero ] ;
}
- ( void ) didReceiveMemoryWarning {
[ super didReceiveMemoryWarning ] ;
}
# pragma mark - Initializers
- ( void ) initializeSearch
{
self . searchController = [ [ UISearchController alloc ] initWithSearchResultsController : nil ] ;
self . searchController . searchResultsUpdater = self ;
2015-01-14 22:30:01 +01:00
self . searchController . dimsBackgroundDuringPresentation = NO ;
self . searchController . hidesNavigationBarDuringPresentation = NO ;
2014-10-29 21:58:58 +01:00
self . searchController . searchBar . frame = CGRectMake ( self . searchController . searchBar . frame . origin . x , self . searchController . searchBar . frame . origin . y , self . searchController . searchBar . frame . size . width , 44.0 ) ;
self . tableView . tableHeaderView = self . searchController . searchBar ;
self . searchController . searchBar . searchBarStyle = UISearchBarStyleMinimal ;
2015-01-14 22:30:01 +01:00
self . searchController . searchBar . delegate = self ;
self . searchController . searchBar . placeholder = @ "Search by name or number" ;
sendTextButton = [ UIButton buttonWithType : UIButtonTypeRoundedRect ] ;
[ sendTextButton setBackgroundColor : [ UIColor ows_materialBlueColor ] ] ;
[ sendTextButton setTitleColor : [ UIColor whiteColor ] forState : UIControlStateNormal ] ;
sendTextButton . frame = CGRectMake ( self . searchController . searchBar . frame . origin . x , self . searchController . searchBar . frame . origin . y + 44.0 , self . searchController . searchBar . frame . size . width , 44.0 ) ;
[ self . view addSubview : sendTextButton ] ;
sendTextButton . hidden = YES ;
[ sendTextButton addTarget : self action : @ selector ( sendText ) forControlEvents : UIControlEventTouchUpInside ] ;
[ self initializeObservers ] ;
[ self initializeRefreshControl ] ;
}
- ( void ) initializeObservers
{
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( contactsDidRefresh ) name : NOTIFICATION_DIRECTORY _WAS _UPDATED object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( contactRefreshFailed ) name : NOTIFICATION_DIRECTORY _FAILED object : nil ] ;
}
- ( void ) initializeRefreshControl {
UIRefreshControl * refreshControl = [ [ UIRefreshControl alloc ] init ] ;
[ refreshControl addTarget : self action : @ selector ( refreshContacts ) forControlEvents : UIControlEventValueChanged ] ;
self . refreshControl = refreshControl ;
[ self . tableView addSubview : self . refreshControl ] ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - UISearchResultsUpdating
- ( void ) updateSearchResultsForSearchController : ( UISearchController * ) searchController {
NSString * searchString = [ self . searchController . searchBar text ] ;
[ self filterContentForSearchText : searchString scope : nil ] ;
[ self . tableView reloadData ] ;
}
# pragma mark - UISearchBarDelegate
- ( void ) searchBar : ( UISearchBar * ) searchBar selectedScopeButtonIndexDidChange : ( NSInteger ) selectedScope {
[ self updateSearchResultsForSearchController : self . searchController ] ;
2015-01-14 22:30:01 +01:00
}
- ( void ) searchBarCancelButtonClicked : ( UISearchBar * ) searchBar {
sendTextButton . hidden = YES ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Filter
- ( void ) filterContentForSearchText : ( NSString * ) searchText scope : ( NSString * ) scope
{
2015-01-14 22:30:01 +01:00
// search by contact name or number
NSString * normalizedNumber = [ PhoneNumberUtil normalizePhoneNumber : searchText ] ;
NSPredicate * resultPredicate = [ NSPredicate predicateWithFormat : @ "(fullName contains[c] %@) OR (allPhoneNumbers contains[c] %@)" , searchText , normalizedNumber ] ;
2014-10-29 21:58:58 +01:00
searchResults = [ contacts filteredArrayUsingPredicate : resultPredicate ] ;
if ( ! searchResults . count && _searchController . searchBar . text . length = = 0 ) searchResults = contacts ;
2015-01-14 22:30:01 +01:00
// formats the user input into a pretty number to display
NSString * formattedNumber = [ PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber : normalizedNumber ] ;
// text to a non - signal number if we have no results and a valid phone #
if ( searchResults . count = = 0 && normalizedNumber . length > 8 ) {
NSString * sendTextTo = @ "Send SMS to: " ;
sendTextTo = [ sendTextTo stringByAppendingString : formattedNumber ] ;
[ sendTextButton setTitle : sendTextTo forState : UIControlStateNormal ] ;
sendTextButton . hidden = NO ;
currentSearchTerm = formattedNumber ;
} else {
sendTextButton . hidden = YES ;
}
2014-10-29 21:58:58 +01:00
}
2015-01-14 22:30:01 +01:00
# pragma mark - Send Normal Text to Unknown Contact
- ( void ) sendText {
NSString * confirmMessage = @ "Would you like to invite the following number to Signal: " ;
confirmMessage = [ confirmMessage stringByAppendingString : currentSearchTerm ] ;
confirmMessage = [ confirmMessage stringByAppendingString : @ "?" ] ;
UIAlertController * alertController = [ UIAlertController
alertControllerWithTitle : @ "Confirm"
message : confirmMessage
preferredStyle : UIAlertControllerStyleAlert ] ;
UIAlertAction * cancelAction = [ UIAlertAction
actionWithTitle : NSLocalizedString ( @ "Cancel" , @ "Cancel action" )
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action )
{
NSLog ( @ "Cancel action" ) ;
} ] ;
UIAlertAction * okAction = [ UIAlertAction
actionWithTitle : NSLocalizedString ( @ "OK" , @ "OK action" )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
[ self . searchController setActive : NO ] ;
UIDevice * device = [ UIDevice currentDevice ] ;
if ( [ [ device model ] isEqualToString : @ "iPhone" ] ) {
MFMessageComposeViewController * picker = [ [ MFMessageComposeViewController alloc ] init ] ;
picker . messageComposeDelegate = self ;
picker . recipients = [ NSArray arrayWithObject : currentSearchTerm ] ;
picker . body = @ "I'm inviting you to install Signal! Here is the link: https://itunes.apple.com/us/app/signal-private-messenger/id874139669?mt=8" ;
[ self presentViewController : picker animated : YES completion : ^ {
[ [ UIApplication sharedApplication ] setStatusBarStyle : UIStatusBarStyleLightContent ] ;
} ] ;
} else {
// TODO : better backup for iPods ( just don ' t support on )
UIAlertView * notPermitted = [ [ UIAlertView alloc ] initWithTitle : @ "Alert" message : @ "Your device doesn't support this feature." delegate : nil cancelButtonTitle : @ "OK" otherButtonTitles : nil ] ;
[ notPermitted show ] ;
}
} ] ;
[ alertController addAction : cancelAction ] ;
[ alertController addAction : okAction ] ;
sendTextButton . hidden = YES ;
self . searchController . searchBar . text = @ "" ;
[ self . navigationController dismissViewControllerAnimated : YES completion : nil ] ;
[ self presentViewController : alertController animated : YES completion : nil ] ;
}
# pragma mark - SMS Composer Delegate
// called on completion of message screen
- ( void ) messageComposeViewController : ( MFMessageComposeViewController * ) controller didFinishWithResult : ( MessageComposeResult ) result
{
switch ( result ) {
case MessageComposeResultCancelled :
break ;
case MessageComposeResultFailed : {
UIAlertView * warningAlert = [ [ UIAlertView alloc ] initWithTitle : @ "Error" message : @ "Failed to send SMS!" delegate : nil cancelButtonTitle : @ "OK" otherButtonTitles : nil ] ;
[ warningAlert show ] ;
break ;
}
case MessageComposeResultSent : {
[ self dismissViewControllerAnimated : NO completion : ^ {
NSLog ( @ "view controller dismissed" ) ;
} ] ;
UIAlertView * successAlert = [ [ UIAlertView alloc ] initWithTitle : @ "Success" message : @ "You've invited your friend to use Signal!" delegate : nil cancelButtonTitle : @ "OK" otherButtonTitles : nil ] ;
[ successAlert show ] ;
break ;
}
default :
break ;
}
[ self dismissViewControllerAnimated : YES completion : nil ] ;
}
# pragma mark - Table View Data Source
2014-10-29 21:58:58 +01:00
- ( NSInteger ) numberOfSectionsInTableView : ( UITableView * ) tableView {
return 1 ;
}
- ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
if ( self . searchController . active ) {
return ( NSInteger ) [ searchResults count ] ;
} else {
return ( NSInteger ) [ contacts count ] ;
}
}
- ( ContactTableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath {
ContactTableViewCell * cell = ( ContactTableViewCell * ) [ tableView dequeueReusableCellWithIdentifier : @ "ContactTableViewCell" ] ;
if ( cell = = nil ) {
cell = [ [ ContactTableViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier : @ "ContactTableViewCell" ] ;
}
2014-11-21 14:38:37 +01:00
cell . shouldShowContactButtons = NO ;
2014-10-29 21:58:58 +01:00
[ cell configureWithContact : [ self contactForIndexPath : indexPath ] ] ;
tableView . tableFooterView = [ [ UIView alloc ] initWithFrame : CGRectZero ] ;
return cell ;
}
- ( CGFloat ) tableView : ( UITableView * ) tableView heightForRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2015-01-26 23:22:00 +01:00
return 52.0 f ;
2014-10-29 21:58:58 +01:00
}
# pragma mark - Table View delegate
2015-01-26 23:22:00 +01:00
- ( NSIndexPath * ) tableView : ( UITableView * ) tableView willSelectRowAtIndexPath : ( NSIndexPath * ) indexPath {
Contact * person = [ self contactForIndexPath : indexPath ] ;
return person . isTextSecureContact ? indexPath : nil ;
}
2014-10-29 21:58:58 +01:00
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2014-11-26 20:29:45 +01:00
NSString * identifier = [ [ [ self contactForIndexPath : indexPath ] textSecureIdentifiers ] firstObject ] ;
2014-10-29 21:58:58 +01:00
[ self dismissViewControllerAnimated : YES completion : ^ ( ) {
2014-11-26 20:29:45 +01:00
[ Environment messageIdentifier : identifier ] ;
2014-10-29 21:58:58 +01:00
} ] ;
}
- ( void ) tableView : ( UITableView * ) tableView didDeselectRowAtIndexPath : ( NSIndexPath * ) indexPath
{
ContactTableViewCell * cell = ( ContactTableViewCell * ) [ tableView cellForRowAtIndexPath : indexPath ] ;
cell . accessoryType = UITableViewCellAccessoryNone ;
}
- ( Contact * ) contactForIndexPath : ( NSIndexPath * ) indexPath
{
Contact * contact = nil ;
if ( self . searchController . active ) {
contact = [ searchResults objectAtIndex : ( NSUInteger ) indexPath . row ] ;
} else {
contact = [ contacts objectAtIndex : ( NSUInteger ) indexPath . row ] ;
}
return contact ;
}
2015-01-14 22:30:01 +01:00
# pragma mark Refresh controls
- ( void ) contactRefreshFailed {
UIAlertView * alert = [ [ UIAlertView alloc ] initWithTitle : TIMEOUT message : TIMEOUT_CONTACTS _DETAIL delegate : nil cancelButtonTitle : NSLocalizedString ( @ "OK" , @ "" ) otherButtonTitles : nil ] ;
[ alert show ] ;
[ self . refreshControl endRefreshing ] ;
}
- ( void ) contactsDidRefresh {
[ self updateSearchResultsForSearchController : self . searchController ] ;
[ self . refreshControl endRefreshing ] ;
}
- ( void ) refreshContacts {
Environment * env = [ Environment getCurrent ] ;
PhoneNumberDirectoryFilterManager * manager = [ env phoneDirectoryManager ] ;
[ manager forceUpdate ] ;
}
2014-10-29 21:58:58 +01:00
# pragma mark - Navigation
// In a storyboard - based application , you will often want to do a little preparation before navigation
- ( void ) prepareForSegue : ( UIStoryboardSegue * ) segue sender : ( id ) sender {
2015-01-25 06:19:09 +01:00
self . searchController . active = NO ;
2014-10-29 21:58:58 +01:00
}
- ( IBAction ) closeAction : ( id ) sender
{
[ self dismissViewControllerAnimated : YES completion : nil ] ;
}
2014-11-21 14:38:37 +01:00
2014-10-29 21:58:58 +01:00
@ end