2017-05-15 12:27:07 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright 2017 Google
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
|
2017-06-22 13:08:35 -07:00
|
|
|
import FirebaseCommunity
|
2017-05-15 12:27:07 -07:00
|
|
|
|
|
|
|
|
enum Row: String {
|
|
|
|
|
case apnsToken = "apnsToken"
|
|
|
|
|
case apnsStatus = "apnsStatus"
|
|
|
|
|
case requestAPNSPermissions = "requestAPNSPermissions"
|
|
|
|
|
case fcmToken = "fcmToken"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum PermissionsButtonTitle: String {
|
|
|
|
|
case requestPermissions = "Request User Notifications"
|
|
|
|
|
case noAPNS = "Cannot Request Permissions (No APNs)"
|
|
|
|
|
case alreadyRequested = "Already Requested Permissions"
|
|
|
|
|
case simulator = "Cannot Request Permissions (Simulator)"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MessagingViewController: UIViewController {
|
|
|
|
|
|
|
|
|
|
let tableView: UITableView
|
|
|
|
|
|
|
|
|
|
var sections = [[Row]]()
|
|
|
|
|
var sectionHeaderTitles = [String?]()
|
|
|
|
|
|
|
|
|
|
var allowedNotificationTypes: [NotificationsControllerAllowedNotificationType]?
|
|
|
|
|
|
|
|
|
|
// Cached rows by Row type. Since this is largely a fixed table view, we'll
|
|
|
|
|
// keep track of our created cells and UI, rather than have all the logic
|
|
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
|
tableView = UITableView(frame: CGRect.zero, style: .grouped)
|
|
|
|
|
tableView.rowHeight = UITableViewAutomaticDimension
|
|
|
|
|
tableView.estimatedRowHeight = 44
|
|
|
|
|
// Allow UI Controls within the table to be immediately responsive
|
|
|
|
|
tableView.delaysContentTouches = false
|
|
|
|
|
super.init(coder: aDecoder)
|
|
|
|
|
tableView.dataSource = self
|
|
|
|
|
tableView.delegate = self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func loadView() {
|
|
|
|
|
super.loadView()
|
|
|
|
|
view = UIView(frame: CGRect.zero)
|
|
|
|
|
view.addSubview(self.tableView)
|
|
|
|
|
// Ensure that the tableView always is the size of the view
|
|
|
|
|
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
|
super.viewDidLoad()
|
|
|
|
|
let center = NotificationCenter.default
|
|
|
|
|
center.addObserver(self,
|
|
|
|
|
selector: #selector(onAPNSTokenReceived) ,
|
|
|
|
|
name: APNSTokenReceivedNotification,
|
|
|
|
|
object: nil)
|
|
|
|
|
center.addObserver(self,
|
|
|
|
|
selector: #selector(onUserNotificationSettingsChanged),
|
|
|
|
|
name: UserNotificationsChangedNotification,
|
|
|
|
|
object: nil)
|
|
|
|
|
center.addObserver(self,
|
|
|
|
|
selector: #selector(onFCMTokenRefreshed),
|
|
|
|
|
name: Notification.Name.MessagingRegistrationTokenRefreshed,
|
|
|
|
|
object: nil)
|
|
|
|
|
updateAllowedNotificationTypes {
|
|
|
|
|
self.resetTableContents()
|
|
|
|
|
self.tableView.reloadData()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func onAPNSTokenReceived() {
|
|
|
|
|
// Reload the appropriate cells
|
|
|
|
|
updateAllowedNotificationTypes {
|
|
|
|
|
if let tokenPath = self.indexPathFor(.apnsToken),
|
|
|
|
|
let statusPath = self.indexPathFor(.apnsStatus),
|
|
|
|
|
let requestPath = self.indexPathFor(.requestAPNSPermissions) {
|
|
|
|
|
self.updateIndexPaths(indexPaths: [tokenPath, statusPath, requestPath])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func onFCMTokenRefreshed() {
|
|
|
|
|
if let indexPath = indexPathFor(.fcmToken) {
|
|
|
|
|
updateIndexPaths(indexPaths: [indexPath])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func onUserNotificationSettingsChanged() {
|
|
|
|
|
updateAllowedNotificationTypes {
|
|
|
|
|
if let statusPath = self.indexPathFor(.apnsStatus),
|
|
|
|
|
let requestPath = self.indexPathFor(.requestAPNSPermissions) {
|
|
|
|
|
self.updateIndexPaths(indexPaths: [statusPath, requestPath])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func updateIndexPaths(indexPaths: [IndexPath]) {
|
|
|
|
|
tableView.beginUpdates()
|
|
|
|
|
tableView.reloadRows(at: indexPaths, with: .none)
|
|
|
|
|
tableView.endUpdates()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileprivate func updateAllowedNotificationTypes(_ completion: (() -> Void)?) {
|
|
|
|
|
NotificationsController.shared.getAllowedNotificationTypes { (types) in
|
|
|
|
|
self.allowedNotificationTypes = types
|
|
|
|
|
self.updateRequestAPNSButton()
|
|
|
|
|
completion?()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileprivate func updateRequestAPNSButton() {
|
|
|
|
|
guard !Environment.isSimulator else {
|
|
|
|
|
requestPermissionsButton.isEnabled = false
|
|
|
|
|
requestPermissionsButton.setTitle(PermissionsButtonTitle.simulator.rawValue, for: .normal)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guard let allowedTypes = allowedNotificationTypes else {
|
|
|
|
|
requestPermissionsButton.isEnabled = false
|
|
|
|
|
requestPermissionsButton.setTitle(PermissionsButtonTitle.noAPNS.rawValue, for: .normal)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
requestPermissionsButton.isEnabled =
|
|
|
|
|
(allowedTypes.count == 1 && allowedTypes.first! == .silent)
|
|
|
|
|
|
|
|
|
|
let title: PermissionsButtonTitle =
|
|
|
|
|
(requestPermissionsButton.isEnabled ? .requestPermissions : .alreadyRequested)
|
|
|
|
|
requestPermissionsButton.setTitle(title.rawValue, for: .normal)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: UI (Cells and Buttons) Defined as lazy properties
|
|
|
|
|
lazy var apnsTableCell: UITableViewCell = {
|
|
|
|
|
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.apnsToken.rawValue)
|
|
|
|
|
cell.textLabel?.numberOfLines = 0
|
|
|
|
|
cell.textLabel?.lineBreakMode = .byWordWrapping
|
|
|
|
|
return cell
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
lazy var apnsStatusTableCell: UITableViewCell = {
|
|
|
|
|
let cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: Row.apnsStatus.rawValue)
|
|
|
|
|
cell.textLabel?.text = "Allowed:"
|
|
|
|
|
cell.detailTextLabel?.numberOfLines = 0
|
|
|
|
|
cell.detailTextLabel?.lineBreakMode = .byWordWrapping
|
|
|
|
|
return cell
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
lazy var requestPermissionsButton: UIButton = {
|
|
|
|
|
let button = UIButton(type: .system)
|
|
|
|
|
button.setTitle(PermissionsButtonTitle.requestPermissions.rawValue, for: .normal)
|
|
|
|
|
button.setTitleColor(UIColor.gray, for: .highlighted)
|
|
|
|
|
button.setTitleColor(UIColor.gray, for: .disabled)
|
|
|
|
|
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
|
|
|
|
|
button.addTarget(self,
|
|
|
|
|
action: #selector(onRequestUserNotificationsButtonTapped),
|
|
|
|
|
for: .touchUpInside)
|
|
|
|
|
return button
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
lazy var apnsRequestPermissionsTableCell: UITableViewCell = {
|
|
|
|
|
let cell = UITableViewCell(style: .default,
|
|
|
|
|
reuseIdentifier: Row.requestAPNSPermissions.rawValue)
|
|
|
|
|
cell.selectionStyle = .none
|
|
|
|
|
cell.contentView.addSubview(self.requestPermissionsButton)
|
|
|
|
|
self.requestPermissionsButton.frame = cell.contentView.bounds
|
|
|
|
|
self.requestPermissionsButton.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
|
|
|
return cell
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
lazy var fcmTokenTableCell: UITableViewCell = {
|
|
|
|
|
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: Row.fcmToken.rawValue)
|
|
|
|
|
cell.textLabel?.numberOfLines = 0
|
|
|
|
|
cell.textLabel?.lineBreakMode = .byCharWrapping
|
|
|
|
|
return cell
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Configuring the table view and cells with information
|
|
|
|
|
extension MessagingViewController {
|
|
|
|
|
func resetTableContents() {
|
|
|
|
|
sections.removeAll()
|
|
|
|
|
sectionHeaderTitles.removeAll()
|
|
|
|
|
|
|
|
|
|
// APNS
|
|
|
|
|
let apnsSection: [Row] = [.apnsToken, .apnsStatus, .requestAPNSPermissions]
|
|
|
|
|
sections.append(apnsSection)
|
|
|
|
|
sectionHeaderTitles.append("APNs")
|
|
|
|
|
|
|
|
|
|
// FCM
|
|
|
|
|
let fcmSection: [Row] = [.fcmToken]
|
|
|
|
|
sections.append(fcmSection)
|
|
|
|
|
sectionHeaderTitles.append("FCM Token")
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func indexPathFor(_ rowId: Row) -> IndexPath? {
|
|
|
|
|
var sectionIndex = 0
|
|
|
|
|
for section in sections {
|
|
|
|
|
var rowIndex = 0
|
|
|
|
|
for row in section {
|
|
|
|
|
if row == rowId {
|
|
|
|
|
return IndexPath(row: rowIndex, section: sectionIndex)
|
|
|
|
|
}
|
|
|
|
|
rowIndex += 1
|
|
|
|
|
}
|
|
|
|
|
sectionIndex += 1
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func configureCell(_ cell: UITableViewCell, withAPNSToken apnsToken: Data?) {
|
|
|
|
|
guard !Environment.isSimulator else {
|
|
|
|
|
cell.textLabel?.text = "APNs notifications are not supported in the simulator."
|
|
|
|
|
cell.detailTextLabel?.text = nil
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if let apnsToken = apnsToken {
|
|
|
|
|
cell.textLabel?.text = apnsToken.hexByteString
|
|
|
|
|
cell.detailTextLabel?.text = "Tap to Share"
|
|
|
|
|
} else {
|
|
|
|
|
cell.textLabel?.text = "None"
|
|
|
|
|
cell.detailTextLabel?.text = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func configureCellWithAPNSStatus(_ cell: UITableViewCell) {
|
|
|
|
|
if let allowedNotificationTypes = allowedNotificationTypes {
|
|
|
|
|
let displayableTypes: [String] = allowedNotificationTypes.map { return $0.rawValue }
|
|
|
|
|
cell.detailTextLabel?.text = displayableTypes.joined(separator: ", ")
|
|
|
|
|
} else {
|
|
|
|
|
cell.detailTextLabel?.text = "Retrieving..."
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func configureCell(_ cell: UITableViewCell, withFCMToken fcmToken: String?) {
|
|
|
|
|
if let fcmToken = fcmToken {
|
|
|
|
|
cell.textLabel?.text = fcmToken
|
|
|
|
|
cell.detailTextLabel?.text = "Tap to Share"
|
|
|
|
|
} else {
|
|
|
|
|
cell.textLabel?.text = "None"
|
|
|
|
|
cell.detailTextLabel?.text = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - UITableViewDataSource
|
|
|
|
|
extension MessagingViewController: UITableViewDataSource {
|
|
|
|
|
func numberOfSections(in tableView: UITableView) -> Int {
|
|
|
|
|
return sections.count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
|
|
return sections[section].count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func tableView(_ tableView: UITableView,
|
|
|
|
|
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
|
|
let row = sections[indexPath.section][indexPath.row]
|
|
|
|
|
|
|
|
|
|
let cell: UITableViewCell
|
|
|
|
|
switch row {
|
|
|
|
|
case .apnsToken:
|
|
|
|
|
cell = apnsTableCell
|
|
|
|
|
configureCell(cell, withAPNSToken: Messaging.messaging().apnsToken)
|
|
|
|
|
case .apnsStatus:
|
|
|
|
|
cell = apnsStatusTableCell
|
|
|
|
|
configureCellWithAPNSStatus(cell)
|
|
|
|
|
case .requestAPNSPermissions:
|
|
|
|
|
cell = apnsRequestPermissionsTableCell
|
|
|
|
|
case .fcmToken:
|
|
|
|
|
cell = fcmTokenTableCell
|
|
|
|
|
configureCell(cell, withFCMToken: Messaging.messaging().fcmToken)
|
|
|
|
|
}
|
|
|
|
|
return cell
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
|
|
|
return sectionHeaderTitles[section]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - UITableViewDelegate
|
|
|
|
|
extension MessagingViewController: UITableViewDelegate {
|
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
|
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
|
|
|
|
|
|
|
|
let row = sections[indexPath.section][indexPath.row]
|
|
|
|
|
switch row {
|
|
|
|
|
case .apnsToken:
|
|
|
|
|
if let apnsToken = Messaging.messaging().apnsToken {
|
|
|
|
|
showActivityViewControllerFor(sharedItem: apnsToken.hexByteString)
|
|
|
|
|
}
|
|
|
|
|
case .fcmToken:
|
|
|
|
|
if let fcmToken = Messaging.messaging().fcmToken {
|
|
|
|
|
showActivityViewControllerFor(sharedItem: fcmToken)
|
|
|
|
|
}
|
|
|
|
|
default: break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - UI Controls
|
|
|
|
|
extension MessagingViewController {
|
|
|
|
|
func onRequestUserNotificationsButtonTapped(sender: UIButton) {
|
|
|
|
|
NotificationsController.shared.registerForUserFacingNotificationsFor(UIApplication.shared)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Activity View Controller
|
|
|
|
|
extension MessagingViewController {
|
|
|
|
|
func showActivityViewControllerFor(sharedItem: Any) {
|
|
|
|
|
let activityViewController = UIActivityViewController(activityItems: [sharedItem],
|
|
|
|
|
applicationActivities: nil)
|
|
|
|
|
present(activityViewController, animated: true, completion: nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|