Update avatar colors; add shaded conversation color constants, modify color picker to be color-name-based, not color-based, use shaded conversation colors, remove JSQ.

This commit is contained in:
Matthew Chen 2018-09-24 14:37:23 -04:00
parent c89033591d
commit ae84528dc3
24 changed files with 296 additions and 910 deletions

View File

@ -42,16 +42,6 @@
340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CC20518C76007AEB0F /* OWSBackupJob.m */; };
340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FC8CE205BF2FA007AEB0F /* OWSBackupIO.m */; };
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */ = {isa = PBXBuildFile; fileRef = 341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */; };
3421980F21061A0700C57195 /* UIColor+JSQMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 3421980521061A0600C57195 /* UIColor+JSQMessages.m */; };
3421981021061A0700C57195 /* JSQMessagesAvatarImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3421980621061A0600C57195 /* JSQMessagesAvatarImage.m */; };
3421981121061A0700C57195 /* JSQMessagesAvatarImageFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 3421980721061A0600C57195 /* JSQMessagesAvatarImageFactory.h */; };
3421981221061A0700C57195 /* JSQMVC-LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 3421980821061A0600C57195 /* JSQMVC-LICENSE */; };
3421981321061A0700C57195 /* JSQMessagesAvatarImageFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 3421980921061A0700C57195 /* JSQMessagesAvatarImageFactory.m */; };
3421981421061A0700C57195 /* JSQMessagesAvatarImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3421980A21061A0700C57195 /* JSQMessagesAvatarImage.h */; };
3421981521061A0700C57195 /* UIColor+JSQMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 3421980B21061A0700C57195 /* UIColor+JSQMessages.h */; };
3421981621061A0700C57195 /* JSQMVC-README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3421980C21061A0700C57195 /* JSQMVC-README.md */; };
3421981721061A0700C57195 /* JSQMVC-SIGNAL.md in Resources */ = {isa = PBXBuildFile; fileRef = 3421980D21061A0700C57195 /* JSQMVC-SIGNAL.md */; };
3421981821061A0700C57195 /* JSQMessageAvatarImageDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3421980E21061A0700C57195 /* JSQMessageAvatarImageDataSource.h */; };
3421981C21061D2E00C57195 /* ByteParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3421981B21061D2E00C57195 /* ByteParserTest.swift */; };
34277A5E20751BDC006049F2 /* OWSQuotedMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */; };
3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3427C64220F500DF00EEC730 /* OWSMessageTimerView.m */; };
@ -662,16 +652,6 @@
341458471FBE11C4005ABCF9 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = "<group>"; };
341F2C0D1F2B8AE700D07D6B /* DebugUIMisc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMisc.h; sourceTree = "<group>"; };
341F2C0E1F2B8AE700D07D6B /* DebugUIMisc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIMisc.m; sourceTree = "<group>"; };
3421980521061A0600C57195 /* UIColor+JSQMessages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+JSQMessages.m"; sourceTree = "<group>"; };
3421980621061A0600C57195 /* JSQMessagesAvatarImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesAvatarImage.m; sourceTree = "<group>"; };
3421980721061A0600C57195 /* JSQMessagesAvatarImageFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesAvatarImageFactory.h; sourceTree = "<group>"; };
3421980821061A0600C57195 /* JSQMVC-LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "JSQMVC-LICENSE"; sourceTree = "<group>"; };
3421980921061A0700C57195 /* JSQMessagesAvatarImageFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSQMessagesAvatarImageFactory.m; sourceTree = "<group>"; };
3421980A21061A0700C57195 /* JSQMessagesAvatarImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesAvatarImage.h; sourceTree = "<group>"; };
3421980B21061A0700C57195 /* UIColor+JSQMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+JSQMessages.h"; sourceTree = "<group>"; };
3421980C21061A0700C57195 /* JSQMVC-README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "JSQMVC-README.md"; sourceTree = "<group>"; };
3421980D21061A0700C57195 /* JSQMVC-SIGNAL.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "JSQMVC-SIGNAL.md"; sourceTree = "<group>"; };
3421980E21061A0700C57195 /* JSQMessageAvatarImageDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessageAvatarImageDataSource.h; sourceTree = "<group>"; };
3421981B21061D2E00C57195 /* ByteParserTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ByteParserTest.swift; sourceTree = "<group>"; };
34277A5C20751BDC006049F2 /* OWSQuotedMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQuotedMessageView.m; sourceTree = "<group>"; };
34277A5D20751BDC006049F2 /* OWSQuotedMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQuotedMessageView.h; sourceTree = "<group>"; };
@ -1470,28 +1450,10 @@
34219803210619D300C57195 /* Libraries */ = {
isa = PBXGroup;
children = (
3421980421061A0600C57195 /* JSQMessagesViewController */,
);
path = Libraries;
sourceTree = "<group>";
};
3421980421061A0600C57195 /* JSQMessagesViewController */ = {
isa = PBXGroup;
children = (
3421980521061A0600C57195 /* UIColor+JSQMessages.m */,
3421980621061A0600C57195 /* JSQMessagesAvatarImage.m */,
3421980721061A0600C57195 /* JSQMessagesAvatarImageFactory.h */,
3421980821061A0600C57195 /* JSQMVC-LICENSE */,
3421980921061A0700C57195 /* JSQMessagesAvatarImageFactory.m */,
3421980A21061A0700C57195 /* JSQMessagesAvatarImage.h */,
3421980B21061A0700C57195 /* UIColor+JSQMessages.h */,
3421980C21061A0700C57195 /* JSQMVC-README.md */,
3421980D21061A0700C57195 /* JSQMVC-SIGNAL.md */,
3421980E21061A0700C57195 /* JSQMessageAvatarImageDataSource.h */,
);
path = JSQMessagesViewController;
sourceTree = "<group>";
};
34330A581E7875FB00DF2FB9 /* Fonts */ = {
isa = PBXGroup;
children = (
@ -2527,7 +2489,6 @@
346129711FD1D74C00532771 /* SignalKeyingStorage.h in Headers */,
34AC0A20211B39EA00997B47 /* ThreadViewHelper.h in Headers */,
34AC09DE211B39B100997B47 /* OWSNavigationController.h in Headers */,
3421981121061A0700C57195 /* JSQMessagesAvatarImageFactory.h in Headers */,
34C82E5120F8E1F300E9688D /* Theme.h in Headers */,
34612A011FD5F31400532771 /* OWS104CreateRecipientIdentities.h in Headers */,
450998691FD8C10200D89EB3 /* AttachmentSharing.h in Headers */,
@ -2535,7 +2496,6 @@
34AC09EC211B39B100997B47 /* OWSTableViewController.h in Headers */,
451F8A3C1FD71392005CB9DA /* UIUtil.h in Headers */,
346129D61FD20ADC00532771 /* UIViewController+OWS.h in Headers */,
3421981421061A0700C57195 /* JSQMessagesAvatarImage.h in Headers */,
34612A061FD7238600532771 /* OWSContactsSyncing.h in Headers */,
34480B571FD0A7A400BC14EF /* OWSScrubbingLogFormatter.h in Headers */,
346129FC1FD5F31400532771 /* OWS101ExistingUsersBlockOnIdentityChange.h in Headers */,
@ -2543,11 +2503,9 @@
451F8A491FD715CF005CB9DA /* OWSAvatarBuilder.h in Headers */,
346129951FD1E30000532771 /* OWSDatabaseMigration.h in Headers */,
34B6D27420F664C900765BE2 /* OWSUnreadIndicator.h in Headers */,
3421981821061A0700C57195 /* JSQMessageAvatarImageDataSource.h in Headers */,
34AC09E4211B39B100997B47 /* ScreenLockViewController.h in Headers */,
34AC09F1211B39B100997B47 /* LockInteractionController.h in Headers */,
346129B41FD1F7E800532771 /* OWSProfileManager.h in Headers */,
3421981521061A0700C57195 /* UIColor+JSQMessages.h in Headers */,
342950892124CB0A0000B063 /* OWSSearchBar.h in Headers */,
346129FA1FD5F31400532771 /* OWS100RemoveTSRecipientsMigration.h in Headers */,
346129E21FD5C0BE00532771 /* VersionMigrations.h in Headers */,
@ -2837,9 +2795,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3421981621061A0700C57195 /* JSQMVC-README.md in Resources */,
3421981721061A0700C57195 /* JSQMVC-SIGNAL.md in Resources */,
3421981221061A0700C57195 /* JSQMVC-LICENSE in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -3181,7 +3136,6 @@
34AC09EE211B39B100997B47 /* EditContactShareNameViewController.swift in Sources */,
346129F71FD5F31400532771 /* OWS105AttachmentFilePaths.m in Sources */,
45194F931FD7215C00333B2C /* OWSContactOffersInteraction.m in Sources */,
3421980F21061A0700C57195 /* UIColor+JSQMessages.m in Sources */,
4523D016206EDC2B00A2AB51 /* LRUCache.swift in Sources */,
450998681FD8C0FF00D89EB3 /* AttachmentSharing.m in Sources */,
347850711FDAEB17007B8332 /* OWSUserProfile.m in Sources */,
@ -3190,7 +3144,6 @@
34074F61203D0CBE004596AE /* OWSSounds.m in Sources */,
346129B51FD1F7E800532771 /* OWSProfileManager.m in Sources */,
342950832124C9750000B063 /* OWSTextView.m in Sources */,
3421981321061A0700C57195 /* JSQMessagesAvatarImageFactory.m in Sources */,
452EC6E1205FF5DC000E787C /* Bench.swift in Sources */,
342950882124CB0A0000B063 /* OWSSearchBar.m in Sources */,
342950822124C9750000B063 /* OWSTextField.m in Sources */,
@ -3259,7 +3212,6 @@
34AC0A18211B39EA00997B47 /* TappableStackView.swift in Sources */,
34B6D27520F664C900765BE2 /* OWSUnreadIndicator.m in Sources */,
346129A61FD1F09100532771 /* OWSContactsManager.m in Sources */,
3421981021061A0700C57195 /* JSQMessagesAvatarImage.m in Sources */,
4541B71D209D3B7A0008608F /* ContactShareViewModel.swift in Sources */,
4598198F204E2F28009414F2 /* OWS108CallLoggingPreference.m in Sources */,
34AC09F3211B39B100997B47 /* NewNonContactConversationViewController.m in Sources */,

View File

@ -46,7 +46,7 @@ class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPicke
private let pickerView: UIPickerView
private let thread: TSThread
private let colors: [UIColor]
private let colorNames: [String]
@objc public weak var delegate: ColorPickerDelegate?
@ -54,7 +54,7 @@ class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPicke
required init(thread: TSThread) {
self.thread = thread
self.pickerView = UIPickerView()
self.colors = UIColor.ows_conversationColors
self.colorNames = UIColor.ows_conversationColorNames
super.init(nibName: nil, bundle: nil)
@ -83,8 +83,7 @@ class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPicke
super.viewDidLoad()
if let colorName = thread.conversationColorName,
let currentColor = UIColor.ows_conversationColor(colorName: colorName),
let index = colors.index(of: currentColor) {
let index = colorNames.index(of: colorName) {
pickerView.selectRow(index, inComponent: 0, animated: false)
}
}
@ -96,7 +95,7 @@ class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPicke
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.colors.count
return self.colorNames.count
}
// MARK: UIPickerViewDelegate
@ -107,34 +106,32 @@ class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPicke
}
public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
guard let color = colors[safe: row] else {
guard let colorName = colorNames[safe: row] else {
owsFailDebug("color was unexpectedly nil")
return ColorView(color: .white)
}
guard let color = UIColor.ows_conversationColor(colorName: colorName,
isShaded: Theme.isDarkThemeEnabled) else {
owsFailDebug("unknown color name")
return ColorView(color: .white)
}
return ColorView(color: color)
}
// MARK: Actions
var currentColor: UIColor {
var currentColorName: String {
let index = pickerView.selectedRow(inComponent: 0)
guard let color = self.colors[safe: index] else {
guard let colorName = colorNames[safe: index] else {
owsFailDebug("index was unexpectedly nil")
return UIColor.white
return UIColor.ows_defaultConversationColorName()
}
return color
return colorName
}
@objc
public func didTapSave() {
guard let colorName = UIColor.ows_conversationColorName(color: self.currentColor) else {
owsFailDebug("colorName was unexpectedly nil")
self.delegate?.colorPickerDidCancel(self)
return
}
let colorName = self.currentColorName
self.delegate?.colorPicker(self, didPickColorName: colorName)
}

View File

@ -269,11 +269,10 @@ NS_ASSUME_NONNULL_BEGIN
}
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.viewItem.interaction;
OWSAvatarBuilder *avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:incomingMessage.authorId
color:self.conversationStyle.primaryColor
diameter:self.avatarSize
contactsManager:contactsManager];
self.avatarView.image = [avatarBuilder build];
TSThread *authorThread = [TSContactThread getOrCreateThreadWithContactId:incomingMessage.authorId];
UIImage *_Nullable authorAvatarImage =
[OWSAvatarBuilder buildImageForThread:authorThread diameter:self.avatarSize contactsManager:contactsManager];
self.avatarView.image = authorAvatarImage;
[self.contentView addSubview:self.avatarView];
[[NSNotificationCenter defaultCenter] addObserver:self

View File

@ -289,7 +289,9 @@ const CGFloat kIconViewLength = 24;
[mainSection addItem:[OWSTableItem
itemWithCustomCellBlock:^{
NSString *colorName = self.thread.conversationColorName;
UIColor *currentColor = [UIColor ows_conversationColorForColorName:colorName];
UIColor *currentColor =
[UIColor ows_conversationColorForColorName:colorName
isShaded:Theme.isDarkThemeEnabled];
NSString *title = NSLocalizedString(@"CONVERSATION_SETTINGS_CONVERSATION_COLOR",
@"Label for table cell which leads to picking a new conversation color");
return [weakSelf disclosureCellWithName:title iconColor:currentColor];

View File

@ -1,20 +0,0 @@
MIT License
Copyright (c) 2013-present Jesse Squires
http://www.jessesquires.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,104 +0,0 @@
![JSQMessagesViewController banner](https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Assets/jsq_messages_banner.png)
[![Build Status](https://secure.travis-ci.org/jessesquires/JSQMessagesViewController.svg)](https://travis-ci.org/jessesquires/JSQMessagesViewController) [![Version Status](https://img.shields.io/cocoapods/v/JSQMessagesViewController.svg)][podLink] [![license MIT](https://img.shields.io/cocoapods/l/JSQMessagesViewController.svg)][mitLink] [![codecov](https://codecov.io/gh/jessesquires/JSQMessagesViewController/branch/develop/graph/badge.svg)](https://codecov.io/gh/jessesquires/JSQMessagesViewController) [![Platform](https://img.shields.io/cocoapods/p/JSQMessagesViewController.svg)][docsLink]
![Screenshot0][img0] &nbsp;&nbsp; ![Screenshot1][img1] &nbsp;&nbsp;
![Screenshot2][img2] &nbsp;&nbsp; ![Screenshot3][img3]
> More screenshots available at [CocoaControls](https://www.cocoacontrols.com/controls/jsqmessagesviewcontroller)
## Features
See the [website](http://www.jessesquires.com/JSQMessagesViewController/) for the list of features.
## Design Goals
- Closely mimic the [iOS Messages](http://www.apple.com/ios/messages/) style and behavior
- [SOLID](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) design
- Easy customization and extension for clients
## Dependencies
* [JSQSystemSoundPlayer][playerLink]
## Requirements
* iOS 7.0+
* ARC
## Installation
### [CocoaPods](https://cocoapods.org/) (recommended)
````ruby
# For latest release in cocoapods
pod 'JSQMessagesViewController'
# Latest on develop
pod 'JSQMessagesViewController', :git => 'https://github.com/jessesquires/JSQMessagesViewController.git', :branch => 'develop'
# For version 5.3.2
pod 'JSQMessagesViewController', :git => 'https://github.com/jessesquires/JSQMessagesViewController', :branch => 'version_5.3.2_patch'
````
## Getting Started
See the [Getting Started](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/getting_started.md) guide!
## Questions & Help
* Review the [FAQ](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/faq.md).
* Search issues for previous and current [questions](https://github.com/jessesquires/JSQMessagesViewController/issues?utf8=✓&q=label%3A%22questions+%26+help%22+). *Do not open duplicates.*
* [StackOverflow](http://stackoverflow.com/questions/tagged/jsqmessagesviewcontroller) is often the most appropriate place for questions and help. We have our own tag, `jsqmessagesviewcontroller`.
* See the [Migration Guide](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/migration.md) for migrating between major versions of the library.
* **Only ask questions that are _specific_ to this library.**
* **Please avoid emailing questions.** I prefer to keep questions and their answers open-source.
## Documentation
Read the docs, [available here][docsLink] via [@CocoaDocs](https://twitter.com/CocoaDocs).
## Core team
- Jesse Squires ([**@jesse_squires**](https://twitter.com/jesse_squires))
- Harlan Haskans ([**@harlanhaskins**](https://github.com/harlanhaskins))
- Eli Burke ([**@eliburke**](https://github.com/eliburke))
## Contributing
Please follow these sweet [contribution guidelines](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/.github/CONTRIBUTING.md).
> **Interested in becoming a core contributor with push access? See our [onboarding guide](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/contributor_onboarding.md) for details.**
## Donate
Support the development of this **free** library! **[Donate](https://cash.me/$jsq)** via [Square Cash](https://cash.me/).
## Credits
* Created and maintained by [**@jesse_squires**](https://twitter.com/jesse_squires).
* Many thanks to [**the contributors**](https://github.com/jessesquires/JSQMessagesViewController/graphs/contributors) of this project.
* iOS assets extracted using [**@0xced**](https://github.com/0xced) / [iOS-Artwork-Extractor](https://github.com/0xced/iOS-Artwork-Extractor).
## Apps using this library
According to [CocoaPods stats](https://cocoapods.org/pods/JSQMessagesViewController), over **9,000 apps** are using `JSQMessagesViewController`. [Here are the ones](https://github.com/jessesquires/JSQMessagesViewController/blob/develop/Documentation/apps_using_this_library.md) that we know about. Please submit a [pull request](https://github.com/jessesquires/JSQMessagesViewController/compare) to add your app! :smile:
## License
`JSQMessagesViewController` is released under an [MIT License][mitLink]. See `LICENSE` for details.
>**Copyright &copy; 2013-present Jesse Squires.**
*Please provide attribution, it is greatly appreciated.*
[docsLink]:http://cocoadocs.org/docsets/JSQMessagesViewController/
[podLink]:https://cocoapods.org/pods/JSQMessagesViewController
[mitLink]:http://opensource.org/licenses/MIT
[playerLink]:https://github.com/jessesquires/JSQSystemSoundPlayer
[img0]:https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot0.png
[img1]:https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot1.png
[img2]:https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot2.png
[img3]:https://raw.githubusercontent.com/jessesquires/JSQMessagesViewController/develop/Screenshots/screenshot3.png

View File

@ -1,7 +0,0 @@
The files in this directory are drawn from JSQMessagesViewController, created by Jesse Squires. These files have been pulled into the Signal-iOS repository with his permission.
https://github.com/jessesquires/JSQMessagesViewController
These files are available under the MIT License. See JSQMVC-LICENSE.

View File

@ -1,63 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
* The `JSQMessageAvatarImageDataSource` protocol defines the common interface through which
* a `JSQMessagesViewController` and `JSQMessagesCollectionView` interact with avatar image model objects.
*
* It declares the required and optional methods that a class must implement so that instances
* of that class can be display properly within a `JSQMessagesCollectionViewCell`.
*
* A concrete class that conforms to this protocol is provided in the library. See `JSQMessagesAvatarImage`.
*
* @see JSQMessagesAvatarImage.
*/
@protocol JSQMessageAvatarImageDataSource <NSObject>
@required
/**
* @return The avatar image for a regular display state.
*
* @discussion You may return `nil` from this method while the image is being downloaded.
*/
- (UIImage *)avatarImage;
/**
* @return The avatar image for a highlighted display state.
*
* @discussion You may return `nil` from this method if this does not apply.
*/
- (UIImage *)avatarHighlightedImage;
/**
* @return A placeholder avatar image to be displayed if avatarImage is not yet available, or `nil`.
* For example, if avatarImage needs to be downloaded, this placeholder image
* will be used until avatarImage is not `nil`.
*
* @discussion If you do not need support for a placeholder image, that is, your images
* are stored locally on the device, then you may simply return the same value as avatarImage here.
*
* @warning You must not return `nil` from this method.
*/
- (UIImage *)avatarPlaceholderImage;
@end

View File

@ -1,86 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "JSQMessageAvatarImageDataSource.h"
/**
* A `JSQMessagesAvatarImage` model object represents an avatar image.
* This is a concrete class that implements the `JSQMessageAvatarImageDataSource` protocol.
* It contains a regular avatar image, a highlighted avatar image, and a placeholder avatar image.
*
* @see JSQMessagesAvatarImageFactory.
*/
@interface JSQMessagesAvatarImage : NSObject <JSQMessageAvatarImageDataSource, NSCopying>
/**
* The avatar image for a regular display state.
*/
@property (nonatomic, strong) UIImage *avatarImage;
/**
* The avatar image for a highlighted display state.
*/
@property (nonatomic, strong) UIImage *avatarHighlightedImage;
/**
* Returns the placeholder image for an avatar to display if avatarImage is `nil`.
*/
@property (nonatomic, strong, readonly) UIImage *avatarPlaceholderImage;
/**
* Initializes and returns an avatar image object having the specified image.
*
* @param image The image for this avatar image. This image will be used for the all of the following
* properties: avatarImage, avatarHighlightedImage, avatarPlaceholderImage;
* This value must not be `nil`.
*
* @return An initialized `JSQMessagesAvatarImage` object if successful, `nil` otherwise.
*/
+ (instancetype)avatarWithImage:(UIImage *)image;
/**
* Initializes and returns an avatar image object having the specified placeholder image.
*
* @param placeholderImage The placeholder image for this avatar image. This value must not be `nil`.
*
* @return An initialized `JSQMessagesAvatarImage` object if successful, `nil` otherwise.
*/
+ (instancetype)avatarImageWithPlaceholder:(UIImage *)placeholderImage;
/**
* Initializes and returns an avatar image object having the specified regular, highlighed, and placeholder images.
*
* @param avatarImage The avatar image for a regular display state.
* @param highlightedImage The avatar image for a highlighted display state.
* @param placeholderImage The placeholder image for this avatar image. This value must not be `nil`.
*
* @return An initialized `JSQMessagesAvatarImage` object if successful, `nil` otherwise.
*/
- (instancetype)initWithAvatarImage:(UIImage *)avatarImage
highlightedImage:(UIImage *)highlightedImage
placeholderImage:(UIImage *)placeholderImage NS_DESIGNATED_INITIALIZER;
/**
* Not a valid initializer.
*/
- (id)init NS_UNAVAILABLE;
@end

View File

@ -1,78 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "JSQMessagesAvatarImage.h"
@implementation JSQMessagesAvatarImage
#pragma mark - Initialization
+ (instancetype)avatarWithImage:(UIImage *)image
{
NSParameterAssert(image != nil);
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:image
highlightedImage:image
placeholderImage:image];
}
+ (instancetype)avatarImageWithPlaceholder:(UIImage *)placeholderImage
{
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:nil
highlightedImage:nil
placeholderImage:placeholderImage];
}
- (instancetype)initWithAvatarImage:(UIImage *)avatarImage
highlightedImage:(UIImage *)highlightedImage
placeholderImage:(UIImage *)placeholderImage
{
NSParameterAssert(placeholderImage != nil);
self = [super init];
if (self) {
_avatarImage = avatarImage;
_avatarHighlightedImage = highlightedImage;
_avatarPlaceholderImage = placeholderImage;
}
return self;
}
#pragma mark - NSObject
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: avatarImage=%@, avatarHighlightedImage=%@, avatarPlaceholderImage=%@>",
[self class], self.avatarImage, self.avatarHighlightedImage, self.avatarPlaceholderImage];
}
- (id)debugQuickLookObject
{
return [[UIImageView alloc] initWithImage:self.avatarImage ?: self.avatarPlaceholderImage];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithAvatarImage:[UIImage imageWithCGImage:self.avatarImage.CGImage]
highlightedImage:[UIImage imageWithCGImage:self.avatarHighlightedImage.CGImage]
placeholderImage:[UIImage imageWithCGImage:self.avatarPlaceholderImage.CGImage]];
}
@end

View File

@ -1,99 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "JSQMessagesAvatarImage.h"
/**
* `JSQMessagesAvatarImageFactory` is a factory that provides a means for creating and styling
* `JSQMessagesAvatarImage` objects to be displayed in a `JSQMessagesCollectionViewCell` of a `JSQMessagesCollectionView`.
*/
@interface JSQMessagesAvatarImageFactory : NSObject
/**
* Creates and returns a `JSQMessagesAvatarImage` object with the specified placeholderImage that is
* cropped to a circle of the given diameter.
*
* @param placeholderImage An image object that represents a placeholder avatar image. This value must not be `nil`.
* @param diameter An integer value specifying the diameter size of the avatar in points. This value must be greater than `0`.
*
* @return An initialized `JSQMessagesAvatarImage` object if created successfully, `nil` otherwise.
*/
+ (JSQMessagesAvatarImage *)avatarImageWithPlaceholder:(UIImage *)placeholderImage diameter:(NSUInteger)diameter;
/**
* Creates and returns a `JSQMessagesAvatarImage` object with the specified image that is
* cropped to a circle of the given diameter and used for the `avatarImage` and `avatarPlaceholderImage` properties
* of the returned `JSQMessagesAvatarImage` object. This image is then copied and has a transparent black mask applied to it,
* which is used for the `avatarHighlightedImage` property of the returned `JSQMessagesAvatarImage` object.
*
* @param image An image object that represents an avatar image. This value must not be `nil`.
* @param diameter An integer value specifying the diameter size of the avatar in points. This value must be greater than `0`.
*
* @return An initialized `JSQMessagesAvatarImage` object if created successfully, `nil` otherwise.
*/
+ (JSQMessagesAvatarImage *)avatarImageWithImage:(UIImage *)image diameter:(NSUInteger)diameter;
/**
* Returns a copy of the specified image that is cropped to a circle with the given diameter.
*
* @param image The image to crop. This value must not be `nil`.
* @param diameter An integer value specifying the diameter size of the image in points. This value must be greater than `0`.
*
* @return A new image object if successful, `nil` otherwise.
*/
+ (UIImage *)circularAvatarImage:(UIImage *)image withDiameter:(NSUInteger)diameter;
/**
* Returns a copy of the specified image that is cropped to a circle with the given diameter.
* Additionally, a transparent overlay is applied to the image to represent a pressed or highlighted state.
*
* @param image The image to crop. This value must not be `nil`.
* @param diameter An integer value specifying the diameter size of the image in points. This value must be greater than `0`.
*
* @return A new image object if successful, `nil` otherwise.
*/
+ (UIImage *)circularAvatarHighlightedImage:(UIImage *)image withDiameter:(NSUInteger)diameter;
/**
* Creates and returns a `JSQMessagesAvatarImage` object with a circular shape that displays the specified userInitials
* with the given backgroundColor, textColor, font, and diameter.
*
* @param userInitials The user initials to display in the avatar image. This value must not be `nil`.
* @param backgroundColor The background color of the avatar. This value must not be `nil`.
* @param textColor The color of the text of the userInitials. This value must not be `nil`.
* @param font The font applied to userInitials. This value must not be `nil`.
* @param diameter The diameter of the avatar image. This value must be greater than `0`.
*
* @return An initialized `JSQMessagesAvatarImage` object if created successfully, `nil` otherwise.
*
* @discussion This method does not attempt to detect or correct incompatible parameters.
* That is to say, you are responsible for providing a font size and diameter that make sense.
* For example, a font size of `14.0f` and a diameter of `34.0f` will result in an avatar similar to Messages in iOS 7.
* However, a font size `30.0f` and diameter of `10.0f` will not produce a desirable image.
* Further, this method does not check the length of userInitials. It is recommended that you pass a string of length `2` or `3`.
*/
+ (JSQMessagesAvatarImage *)avatarImageWithUserInitials:(NSString *)userInitials
backgroundColor:(UIColor *)backgroundColor
textColor:(UIColor *)textColor
font:(UIFont *)font
diameter:(NSUInteger)diameter;
@end

View File

@ -1,159 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "JSQMessagesAvatarImageFactory.h"
#import "UIColor+JSQMessages.h"
@implementation JSQMessagesAvatarImageFactory
#pragma mark - Public
+ (JSQMessagesAvatarImage *)avatarImageWithPlaceholder:(UIImage *)placeholderImage diameter:(NSUInteger)diameter
{
UIImage *circlePlaceholderImage = [JSQMessagesAvatarImageFactory jsq_circularImage:placeholderImage
withDiameter:diameter
highlightedColor:nil];
return [JSQMessagesAvatarImage avatarImageWithPlaceholder:circlePlaceholderImage];
}
+ (JSQMessagesAvatarImage *)avatarImageWithImage:(UIImage *)image diameter:(NSUInteger)diameter
{
UIImage *avatar = [JSQMessagesAvatarImageFactory circularAvatarImage:image withDiameter:diameter];
UIImage *highlightedAvatar = [JSQMessagesAvatarImageFactory circularAvatarHighlightedImage:image withDiameter:diameter];
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:avatar
highlightedImage:highlightedAvatar
placeholderImage:avatar];
}
+ (UIImage *)circularAvatarImage:(UIImage *)image withDiameter:(NSUInteger)diameter
{
return [JSQMessagesAvatarImageFactory jsq_circularImage:image
withDiameter:diameter
highlightedColor:nil];
}
+ (UIImage *)circularAvatarHighlightedImage:(UIImage *)image withDiameter:(NSUInteger)diameter
{
return [JSQMessagesAvatarImageFactory jsq_circularImage:image
withDiameter:diameter
highlightedColor:[UIColor colorWithWhite:0.1f alpha:0.3f]];
}
+ (JSQMessagesAvatarImage *)avatarImageWithUserInitials:(NSString *)userInitials
backgroundColor:(UIColor *)backgroundColor
textColor:(UIColor *)textColor
font:(UIFont *)font
diameter:(NSUInteger)diameter
{
UIImage *avatarImage = [JSQMessagesAvatarImageFactory jsq_imageWitInitials:userInitials
backgroundColor:backgroundColor
textColor:textColor
font:font
diameter:diameter];
UIImage *avatarHighlightedImage = [JSQMessagesAvatarImageFactory jsq_circularImage:avatarImage
withDiameter:diameter
highlightedColor:[UIColor colorWithWhite:0.1f alpha:0.3f]];
return [[JSQMessagesAvatarImage alloc] initWithAvatarImage:avatarImage
highlightedImage:avatarHighlightedImage
placeholderImage:avatarImage];
}
#pragma mark - Private
+ (UIImage *)jsq_imageWitInitials:(NSString *)initials
backgroundColor:(UIColor *)backgroundColor
textColor:(UIColor *)textColor
font:(UIFont *)font
diameter:(NSUInteger)diameter
{
NSParameterAssert(initials != nil);
NSParameterAssert(backgroundColor != nil);
NSParameterAssert(textColor != nil);
NSParameterAssert(font != nil);
NSParameterAssert(diameter > 0);
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
NSDictionary *attributes = @{ NSFontAttributeName : font,
NSForegroundColorAttributeName : textColor };
CGRect textFrame = [initials boundingRectWithSize:frame.size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
CGPoint frameMidPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
CGPoint textFrameMidPoint = CGPointMake(CGRectGetMidX(textFrame), CGRectGetMidY(textFrame));
CGFloat dx = frameMidPoint.x - textFrameMidPoint.x;
CGFloat dy = frameMidPoint.y - textFrameMidPoint.y;
CGPoint drawPoint = CGPointMake(dx, dy);
UIImage *image = nil;
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, frame);
[initials drawAtPoint:drawPoint withAttributes:attributes];
image = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
return [JSQMessagesAvatarImageFactory jsq_circularImage:image withDiameter:diameter highlightedColor:nil];
}
+ (UIImage *)jsq_circularImage:(UIImage *)image withDiameter:(NSUInteger)diameter highlightedColor:(UIColor *)highlightedColor
{
NSParameterAssert(image != nil);
NSParameterAssert(diameter > 0);
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
UIImage *newImage = nil;
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
{
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *imgPath = [UIBezierPath bezierPathWithOvalInRect:frame];
[imgPath addClip];
[image drawInRect:frame];
if (highlightedColor != nil) {
CGContextSetFillColorWithColor(context, highlightedColor.CGColor);
CGContextFillEllipseInRect(context, frame);
}
newImage = UIGraphicsGetImageFromCurrentImageContext();
}
UIGraphicsEndImageContext();
return newImage;
}
@end

View File

@ -1,56 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import <UIKit/UIKit.h>
@interface UIColor (JSQMessages)
#pragma mark - Message bubble colors
/**
* @return A color object containing HSB values similar to the iOS 7 messages app green bubble color.
*/
+ (UIColor *)jsq_messageBubbleGreenColor;
/**
* @return A color object containing HSB values similar to the iOS 7 messages app blue bubble color.
*/
+ (UIColor *)jsq_messageBubbleBlueColor;
/**
* @return A color object containing HSB values similar to the iOS 7 red color.
*/
+ (UIColor *)jsq_messageBubbleRedColor;
/**
* @return A color object containing HSB values similar to the iOS 7 messages app light gray bubble color.
*/
+ (UIColor *)jsq_messageBubbleLightGrayColor;
#pragma mark - Utilities
/**
* Creates and returns a new color object whose brightness component is decreased by the given value, using the initial color values of the receiver.
*
* @param value A floating point value describing the amount by which to decrease the brightness of the receiver.
*
* @return A new color object whose brightness is decreased by the given values. The other color values remain the same as the receiver.
*/
- (UIColor *)jsq_colorByDarkeningColorWithValue:(CGFloat)value;
@end

View File

@ -1,90 +0,0 @@
//
// Created by Jesse Squires
// http://www.jessesquires.com
//
//
// Documentation
// http://cocoadocs.org/docsets/JSQMessagesViewController
//
//
// GitHub
// https://github.com/jessesquires/JSQMessagesViewController
//
//
// License
// Copyright (c) 2014 Jesse Squires
// Released under an MIT license: http://opensource.org/licenses/MIT
//
#import "UIColor+JSQMessages.h"
@implementation UIColor (JSQMessages)
#pragma mark - Message bubble colors
+ (UIColor *)jsq_messageBubbleGreenColor
{
return [UIColor colorWithHue:130.0f / 360.0f
saturation:0.68f
brightness:0.84f
alpha:1.0f];
}
+ (UIColor *)jsq_messageBubbleBlueColor
{
return [UIColor colorWithHue:210.0f / 360.0f
saturation:0.94f
brightness:1.0f
alpha:1.0f];
}
+ (UIColor *)jsq_messageBubbleRedColor
{
return [UIColor colorWithHue:0.0f / 360.0f
saturation:0.79f
brightness:1.0f
alpha:1.0f];
}
+ (UIColor *)jsq_messageBubbleLightGrayColor
{
return [UIColor colorWithHue:240.0f / 360.0f
saturation:0.02f
brightness:0.92f
alpha:1.0f];
}
#pragma mark - Utilities
- (UIColor *)jsq_colorByDarkeningColorWithValue:(CGFloat)value
{
NSUInteger totalComponents = CGColorGetNumberOfComponents(self.CGColor);
BOOL isGreyscale = (totalComponents == 2) ? YES : NO;
CGFloat *oldComponents = (CGFloat *)CGColorGetComponents(self.CGColor);
CGFloat newComponents[4];
if (isGreyscale) {
newComponents[0] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value;
newComponents[1] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value;
newComponents[2] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value;
newComponents[3] = oldComponents[1];
}
else {
newComponents[0] = oldComponents[0] - value < 0.0f ? 0.0f : oldComponents[0] - value;
newComponents[1] = oldComponents[1] - value < 0.0f ? 0.0f : oldComponents[1] - value;
newComponents[2] = oldComponents[2] - value < 0.0f ? 0.0f : oldComponents[2] - value;
newComponents[3] = oldComponents[3];
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef newColor = CGColorCreate(colorSpace, newComponents);
CGColorSpaceRelease(colorSpace);
UIColor *retColor = [UIColor colorWithCGColor:newColor];
CGColorRelease(newColor);
return retColor;
}
@end

View File

@ -49,7 +49,7 @@ public class ContactShareViewModel: NSObject {
}
@objc
public func getAvatarImage(diameter: CGFloat, contactsManager: OWSContactsManager) -> UIImage {
public func getAvatarImage(diameter: CGFloat, contactsManager: OWSContactsManager) -> UIImage? {
if let avatarImage = avatarImage {
return avatarImage
}

View File

@ -200,10 +200,9 @@ const CGFloat kContactCellAvatarTextMargin = 12;
return [TSThread stableConversationColorNameForString:self.recipientId];
}
}();
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithSignalId:recipientId
color:color
colorName:colorName
diameter:kContactCellAvatarSize
contactsManager:contactsManager] build];
}

View File

@ -31,12 +31,10 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - ConversationColor
+ (nullable UIColor *)ows_conversationColorForColorName:(NSString *)colorName
NS_SWIFT_NAME(ows_conversationColor(colorName:));
+ (nullable NSString *)ows_conversationColorNameForColor:(UIColor *)color
NS_SWIFT_NAME(ows_conversationColorName(color:));
isShaded:(BOOL)isShaded
NS_SWIFT_NAME(ows_conversationColor(colorName:isShaded:));
@property (class, readonly, nonatomic) NSArray<NSString *> *ows_conversationColorNames;
@property (class, readonly, nonatomic) NSArray<UIColor *> *ows_conversationColors;
- (UIColor *)blendWithColor:(UIColor *)otherColor alpha:(CGFloat)alpha;
@ -73,6 +71,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (class, readonly, nonatomic) UIColor *ows_grey600Color;
@property (class, readonly, nonatomic) UIColor *ows_darkSkyBlueColor;
+ (NSString *)ows_defaultConversationColorName;
@end
NS_ASSUME_NONNULL_END

View File

@ -251,7 +251,29 @@ NS_ASSUME_NONNULL_BEGIN
return [UIColor colorWithRed:32.f / 255.f green:144.f / 255.f blue:234.f / 255.f alpha:1.f];
}
+ (NSDictionary<NSString *, UIColor *> *)ows_conversationColorMap
+ (NSDictionary<NSString *, UIColor *> *)ows_conversationColorMapLight
{
static NSDictionary<NSString *, UIColor *> *colorMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
colorMap = @{
@"red" : self.ows_red700Color,
@"pink" : self.ows_pink600Color,
@"purple" : self.ows_purple600Color,
@"indigo" : self.ows_indigo600Color,
@"blue" : self.ows_blue700Color,
@"cyan" : self.ows_cyan800Color,
@"teal" : self.ows_teal700Color,
@"green" : self.ows_green800Color,
@"deep_orange" : self.ows_deepOrange900Color,
@"grey" : self.ows_grey600Color
};
});
return colorMap;
}
+ (NSDictionary<NSString *, UIColor *> *)ows_conversationColorMapDark
{
static NSDictionary<NSString *, UIColor *> *colorMap;
static dispatch_once_t onceToken;
@ -275,24 +297,22 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSArray<NSString *> *)ows_conversationColorNames
{
return self.ows_conversationColorMap.allKeys;
OWSAssertDebug(
[self.ows_conversationColorMapLight.allKeys isEqualToArray:self.ows_conversationColorMapDark.allKeys]);
return self.ows_conversationColorMapLight.allKeys;
}
+ (NSArray<UIColor *> *)ows_conversationColors
{
return self.ows_conversationColorMap.allValues;
}
+ (nullable UIColor *)ows_conversationColorForColorName:(NSString *)colorName
+ (nullable UIColor *)ows_conversationColorForColorName:(NSString *)colorName isShaded:(BOOL)isShaded
{
OWSAssertDebug(colorName.length > 0);
return [self.ows_conversationColorMap objectForKey:colorName];
return (isShaded ? self.ows_conversationColorMapDark : self.ows_conversationColorMapLight)[colorName];
}
+ (nullable NSString *)ows_conversationColorNameForColor:(UIColor *)color
+ (NSString *)ows_defaultConversationColorName
{
return [self.ows_conversationColorMap allKeysForObject:color].firstObject;
return @"teal";
}
@end

View File

@ -136,7 +136,8 @@ public class ConversationStyle: NSObject {
return self.defaultBubbleColorIncoming
}
guard let color = UIColor.ows_conversationColor(colorName: colorName) else {
guard let color = UIColor.ows_conversationColor(colorName: colorName,
isShaded: Theme.isDarkThemeEnabled) else {
return self.defaultBubbleColorIncoming
}

View File

@ -1,24 +1,33 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@class TSThread;
@class OWSContactsManager;
@class TSThread;
@class UIImage;
@interface OWSAvatarBuilder : NSObject
+ (UIImage *)buildImageForThread:(TSThread *)thread
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager NS_SWIFT_NAME(buildImage(thread:diameter:contactsManager:));
+ (nullable UIImage *)buildImageForThread:(TSThread *)thread
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager
NS_SWIFT_NAME(buildImage(thread:diameter:contactsManager:));
+ (UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter;
+ (nullable UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter;
- (nullable UIImage *)buildSavedImage;
- (UIImage *)buildDefaultImage;
- (UIImage *)build;
- (nullable UIImage *)buildDefaultImage;
- (nullable UIImage *)build;
+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials
backgroundColor:(UIColor *)backgroundColor
diameter:(NSUInteger)diameter;
+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon
backgroundColor:(UIColor *)backgroundColor
diameter:(NSUInteger)diameter;
@end

View File

@ -3,20 +3,22 @@
//
#import "OWSAvatarBuilder.h"
#import "JSQMessagesAvatarImageFactory.h"
#import "OWSContactAvatarBuilder.h"
#import "OWSGroupAvatarBuilder.h"
#import "TSContactThread.h"
#import "TSGroupThread.h"
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
NS_ASSUME_NONNULL_BEGIN
typedef void (^OWSAvatarDrawBlock)(CGContextRef context);
@implementation OWSAvatarBuilder
+ (UIImage *)buildImageForThread:(TSThread *)thread
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager
+ (nullable UIImage *)buildImageForThread:(TSThread *)thread
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager
{
OWSAssertDebug(thread);
OWSAssertDebug(contactsManager);
@ -25,9 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
if ([thread isKindOfClass:[TSContactThread class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
NSString *colorName = thread.conversationColorName;
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:contactThread.contactIdentifier
color:color
colorName:colorName
diameter:diameter
contactsManager:contactsManager];
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
@ -38,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
return [avatarBuilder build];
}
+ (UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter
+ (nullable UIImage *)buildRandomAvatarWithDiameter:(NSUInteger)diameter
{
NSArray<NSString *> *eyes = @[ @":", @"=", @"8", @"B" ];
NSArray<NSString *> *mouths = @[ @"3", @")", @"(", @"|", @"\\", @"P", @"D", @"o" ];
@ -50,34 +51,194 @@ NS_ASSUME_NONNULL_BEGIN
NSString *randomEyebrow = eyebrows[arc4random_uniform((uint32_t)eyebrows.count)];
NSString *face = [NSString stringWithFormat:@"%@%@%@", randomEyebrow, randomEye, randomMouth];
CGFloat fontSize = (CGFloat)(diameter / 2.4);
UIColor *backgroundColor = [UIColor colorWithRGBHex:0xaca6633];
UIImage *srcImage =
[[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:face
backgroundColor:[UIColor colorWithRGBHex:0xaca6633]
textColor:[UIColor whiteColor]
font:[UIFont boldSystemFontOfSize:fontSize]
diameter:diameter] avatarImage];
return [self avatarImageWithDiameter:diameter
backgroundColor:backgroundColor
drawBlock:^(CGContextRef context) {
CGContextTranslateCTM(context, diameter / 2, diameter / 2);
CGContextRotateCTM(context, (CGFloat)M_PI_2);
CGContextTranslateCTM(context, -diameter / 2, -diameter / 2);
UIGraphicsBeginImageContext(srcImage.size);
[self drawInitialsInAvatar:face
textColor:self.avatarForegroundColor
font:self.avatarTextFont
diameter:diameter];
}];
}
CGContextRef context = UIGraphicsGetCurrentContext();
+ (UIColor *)avatarForegroundColor
{
return (Theme.isDarkThemeEnabled ? UIColor.ows_gray05Color : UIColor.ows_whiteColor);
}
CGFloat width = srcImage.size.width;
+ (UIFont *)avatarTextFont
{
return [UIFont ows_mediumFontWithSize:20.f];
}
// Rotate
CGContextTranslateCTM(context, width / 2, width / 2);
CGContextRotateCTM(context, (CGFloat)M_PI_2);
CGContextTranslateCTM(context, -width / 2, -width / 2);
+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials
backgroundColor:(UIColor *)backgroundColor
diameter:(NSUInteger)diameter
{
return [self avatarImageWithInitials:initials
backgroundColor:backgroundColor
textColor:self.avatarForegroundColor
font:self.avatarTextFont
diameter:diameter];
}
[srcImage drawAtPoint:CGPointMake(0, 0)];
+ (nullable UIImage *)avatarImageWithInitials:(NSString *)initials
backgroundColor:(UIColor *)backgroundColor
textColor:(UIColor *)textColor
font:(UIFont *)font
diameter:(NSUInteger)diameter
{
OWSAssertDebug(initials);
OWSAssertDebug(textColor);
OWSAssertDebug(font);
return [self avatarImageWithDiameter:diameter
backgroundColor:backgroundColor
drawBlock:^(CGContextRef context) {
[self drawInitialsInAvatar:initials textColor:textColor font:font diameter:diameter];
}];
}
+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon
backgroundColor:(UIColor *)backgroundColor
diameter:(NSUInteger)diameter
{
return [self avatarImageWithIcon:icon
iconColor:self.avatarForegroundColor
backgroundColor:backgroundColor
diameter:diameter];
}
+ (nullable UIImage *)avatarImageWithIcon:(UIImage *)icon
iconColor:(UIColor *)iconColor
backgroundColor:(UIColor *)backgroundColor
diameter:(NSUInteger)diameter
{
OWSAssertDebug(icon);
OWSAssertDebug(iconColor);
return [self avatarImageWithDiameter:diameter
backgroundColor:backgroundColor
drawBlock:^(CGContextRef context) {
[self drawIconInAvatar:icon iconColor:iconColor diameter:diameter];
}];
}
+ (nullable UIImage *)avatarImageWithDiameter:(NSUInteger)diameter
backgroundColor:(UIColor *)backgroundColor
drawBlock:(OWSAvatarDrawBlock)drawBlock
{
OWSAssertDebug(drawBlock);
OWSAssertDebug(backgroundColor);
OWSAssertDebug(diameter > 0);
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
UIGraphicsBeginImageContextWithOptions(frame.size, NO, [UIScreen mainScreen].scale);
CGContextRef _Nullable context = UIGraphicsGetCurrentContext();
if (!context) {
return nil;
}
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, frame);
// Gradient
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat gradientLocations[] = { 0.0, 1.0 };
CGGradientRef _Nullable gradient = CGGradientCreateWithColors(colorspace,
(__bridge CFArrayRef) @[
(id)[UIColor colorWithWhite:0.f alpha:0.f].CGColor,
(id)[UIColor colorWithWhite:0.f alpha:0.15f].CGColor,
],
gradientLocations);
if (!gradient) {
return nil;
}
CGPoint startPoint = CGPointMake(diameter * 0.5f, 0);
CGPoint endPoint = CGPointMake(diameter * 0.5f, diameter);
CGContextDrawLinearGradient(context,
gradient,
startPoint,
endPoint,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CFRelease(gradient);
CGContextSaveGState(context);
drawBlock(context);
CGContextRestoreGState(context);
UIImage *_Nullable image = UIGraphicsGetImageFromCurrentImageContext();
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)build
+ (void)drawInitialsInAvatar:(NSString *)initials
textColor:(UIColor *)textColor
font:(UIFont *)font
diameter:(NSUInteger)diameter
{
OWSAssertDebug(initials);
OWSAssertDebug(textColor);
OWSAssertDebug(font);
OWSAssertDebug(diameter > 0);
CGRect frame = CGRectMake(0.0f, 0.0f, diameter, diameter);
NSDictionary *textAttributes = @{
NSFontAttributeName : font,
NSForegroundColorAttributeName : textColor,
};
CGSize textSize =
[initials boundingRectWithSize:frame.size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:textAttributes
context:nil]
.size;
// Ensure that the text fits within the avatar bounds, with a margin.
if (textSize.width > 0 && textSize.height > 0) {
CGFloat textDiameter = (CGFloat)sqrt(textSize.width * textSize.width + textSize.height * textSize.height);
// Leave a 10% margin.
CGFloat maxTextDiameter = diameter * 0.9f;
if (textDiameter > maxTextDiameter) {
font = [font fontWithSize:font.pointSize * maxTextDiameter / textDiameter];
textAttributes = @{
NSFontAttributeName : font,
NSForegroundColorAttributeName : textColor,
};
textSize =
[initials boundingRectWithSize:frame.size
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:textAttributes
context:nil]
.size;
}
}
CGPoint drawPoint = CGPointMake((diameter - textSize.width) * 0.5f, (diameter - textSize.height) * 0.5f);
[initials drawAtPoint:drawPoint withAttributes:textAttributes];
}
+ (void)drawIconInAvatar:(UIImage *)icon iconColor:(UIColor *)iconColor diameter:(NSUInteger)diameter
{
OWSAssertDebug(icon);
OWSAssertDebug(iconColor);
OWSAssertDebug(diameter > 0);
CGPoint drawPoint = CGPointMake((diameter - icon.size.width) * 0.5f, (diameter - icon.size.height) * 0.5f);
[icon drawAtPoint:drawPoint];
}
- (nullable UIImage *)build
{
UIImage *_Nullable savedImage = [self buildSavedImage];
if (savedImage) {
@ -93,10 +254,10 @@ NS_ASSUME_NONNULL_BEGIN
return nil;
}
- (UIImage *)buildDefaultImage
- (nullable UIImage *)buildDefaultImage
{
OWSAbstractMethod();
return [UIImage new];
return nil;
}
@end

View File

@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)initWithSignalId:(NSString *)signalId
color:(UIColor *)color
colorName:(NSString *)colorName
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager;

View File

@ -10,7 +10,6 @@
#import "UIColor+OWS.h"
#import "UIFont+OWS.h"
#import <SignalMessaging/SignalMessaging-Swift.h>
#import "JSQMessagesAvatarImageFactory.h"
NS_ASSUME_NONNULL_BEGIN
@ -19,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, readonly) NSString *signalId;
@property (nonatomic, readonly) NSString *contactName;
@property (nonatomic, readonly) UIColor *color;
@property (nonatomic, readonly) NSString *colorName;
@property (nonatomic, readonly) NSUInteger diameter;
@end
@ -30,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithContactId:(NSString *)contactId
name:(NSString *)name
color:(UIColor *)color
colorName:(NSString *)colorName
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager
{
@ -39,9 +38,11 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
OWSAssertDebug(colorName.length > 0);
_signalId = contactId;
_contactName = name;
_color = color;
_colorName = colorName;
_diameter = diameter;
_contactsManager = contactsManager;
@ -49,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (instancetype)initWithSignalId:(NSString *)signalId
color:(UIColor *)color
colorName:(NSString *)colorName
diameter:(NSUInteger)diameter
contactsManager:(OWSContactsManager *)contactsManager
{
@ -61,7 +62,11 @@ NS_ASSUME_NONNULL_BEGIN
if (name.length == 0) {
name = signalId;
}
return [self initWithContactId:signalId name:name color:color diameter:diameter contactsManager:contactsManager];
return [self initWithContactId:signalId
name:name
colorName:colorName
diameter:diameter
contactsManager:contactsManager];
}
- (instancetype)initWithNonSignalName:(NSString *)nonSignalName
@ -71,11 +76,9 @@ NS_ASSUME_NONNULL_BEGIN
{
NSString *colorName = [TSThread stableConversationColorNameForString:colorSeed];
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
OWSAssertDebug(color);
return [self initWithContactId:colorSeed
name:nonSignalName
color:color
colorName:(NSString *)colorName
diameter:diameter
contactsManager:contactsManager];
}
@ -87,10 +90,15 @@ NS_ASSUME_NONNULL_BEGIN
return [self.contactsManager imageForPhoneIdentifier:self.signalId];
}
- (UIImage *)buildDefaultImage
- (id)cacheKey
{
return [NSString stringWithFormat:@"%@-%d", self.signalId, Theme.isDarkThemeEnabled];
}
- (nullable UIImage *)buildDefaultImage
{
UIImage *cachedAvatar =
[self.contactsManager.avatarCache imageForKey:self.signalId diameter:(CGFloat)self.diameter];
[self.contactsManager.avatarCache imageForKey:self.cacheKey diameter:(CGFloat)self.diameter];
if (cachedAvatar) {
return cachedAvatar;
}
@ -121,19 +129,19 @@ NS_ASSUME_NONNULL_BEGIN
[initials appendString:@"#"];
}
CGFloat fontSize = (CGFloat)self.diameter / 2.8f;
UIImage *image = [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
backgroundColor:self.color
textColor:[UIColor whiteColor]
font:[UIFont ows_boldFontWithSize:fontSize]
diameter:self.diameter] avatarImage];
UIColor *color = [UIColor ows_conversationColorForColorName:self.colorName isShaded:Theme.isDarkThemeEnabled];
OWSAssertDebug(color);
[self.contactsManager.avatarCache setImage:image forKey:self.signalId diameter:self.diameter];
UIImage *_Nullable image =
[OWSAvatarBuilder avatarImageWithInitials:initials backgroundColor:color diameter:self.diameter];
if (!image) {
return nil;
}
[self.contactsManager.avatarCache setImage:image forKey:self.cacheKey diameter:self.diameter];
return image;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
return self.thread.groupModel.groupImage;
}
- (UIImage *)buildDefaultImage
- (nullable UIImage *)buildDefaultImage
{
return self.class.defaultGroupImage;
}