diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h
index 694ed0779..0c93f10df 100644
--- a/Signal/src/Signal-Bridging-Header.h
+++ b/Signal/src/Signal-Bridging-Header.h
@@ -7,6 +7,7 @@
 
 // Separate iOS Frameworks from other imports.
 #import "AppSettingsViewController.h"
+#import "ConversationHeaderView.h"
 #import "ConversationViewItem.h"
 #import "DateUtil.h"
 #import "DebugUIPage.h"
diff --git a/Signal/src/ViewControllers/MediaPageViewController.swift b/Signal/src/ViewControllers/MediaPageViewController.swift
index 471acaeb1..eae541762 100644
--- a/Signal/src/ViewControllers/MediaPageViewController.swift
+++ b/Signal/src/ViewControllers/MediaPageViewController.swift
@@ -52,6 +52,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
                 return
             }
 
+            self.updateTitle(item: newValue)
             self.setViewControllers([galleryPage], direction: .forward, animated: false, completion: nil)
         }
     }
@@ -61,6 +62,8 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
     private let showAllMediaButton: Bool
     private let sliderEnabled: Bool
 
+    private let navItemTitleView: ConversationHeaderView!
+
     init(initialItem: MediaGalleryItem, mediaGalleryDataSource: MediaGalleryDataSource, uiDatabaseConnection: YapDatabaseConnection, options: MediaGalleryOption) {
         assert(uiDatabaseConnection.isInLongLivedReadTransaction())
         self.uiDatabaseConnection = uiDatabaseConnection
@@ -68,6 +71,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
         self.sliderEnabled = options.contains(.sliderEnabled)
         self.mediaGalleryDataSource = mediaGalleryDataSource
 
+        let headerView =  ConversationHeaderView()
+        self.navItemTitleView = headerView
+
         let kSpacingBetweenItems: CGFloat = 20
 
         super.init(transitionStyle: .scroll,
@@ -109,6 +115,15 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
         let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton))
         self.navigationItem.leftBarButtonItem = backButton
 
+        navItemTitleView.titleLabel = headerNameLabel
+        navItemTitleView.subtitleLabel = headerDateLabel
+        navItemTitleView.addSubview(headerNameLabel)
+        navItemTitleView.addSubview(headerDateLabel)
+        navItemTitleView.frame = CGRect(origin: .zero, size: CGSize(width: 150, height: 35))
+        navItemTitleView.layoutSubviews()
+        self.navigationItem.titleView = navItemTitleView
+        self.updateTitle()
+
         if showAllMediaButton {
             self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: MediaStrings.allMedia, style: .plain, target: self, action: #selector(didPressAllMediaButton))
         }
@@ -132,7 +147,6 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
         // one "Page" so the bounce doesn't make sense.
         pagerScrollView.isScrollEnabled = sliderEnabled
 
-        // FIXME dynamic title with sender/date
         self.title = "Attachment"
 
         // Views
@@ -337,6 +351,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
 
             // Do any cleanup for the no-longer visible view controller
             if transitionCompleted {
+                updateTitle()
                 previousPage.zoomOut(animated: false)
                 previousPage.stopAnyVideo()
                 updateFooterBarButtonItems(isPlayingVideo: false)
@@ -450,4 +465,69 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
         self.shouldHideToolbars = isPlayingVideo
         self.updateFooterBarButtonItems(isPlayingVideo: isPlayingVideo)
     }
+
+    // MARK: Dynamic Header
+
+    private var contactsManager: OWSContactsManager {
+        return Environment.current().contactsManager
+    }
+
+    private func senderName(message: TSMessage) -> String {
+        switch message {
+        case let incomingMessage as TSIncomingMessage:
+            return self.contactsManager.displayName(forPhoneIdentifier: incomingMessage.authorId)
+        case is TSOutgoingMessage:
+            return NSLocalizedString("MEDIA_GALLERY_SENDER_NAME_YOU", comment: "Short sender label for media sent by you")
+        default:
+            owsFail("\(logTag) Unknown message type: \(type(of: message))")
+            return ""
+        }
+    }
+
+    private lazy var dateFormatter: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.dateStyle = .short
+        formatter.timeStyle = .short
+
+        return formatter
+    }()
+
+    lazy private var headerNameLabel: UILabel = {
+        let label = UILabel()
+        label.textColor = .white
+        label.font = .ows_dynamicTypeBody()
+        label.textAlignment = .center
+        label.adjustsFontSizeToFitWidth = true
+        label.minimumScaleFactor = 0.8
+
+        return label
+    }()
+
+    lazy private var headerDateLabel: UILabel = {
+        let label = UILabel()
+        label.textColor = .white
+        label.font = UIFont.preferredFont(forTextStyle: .caption1)
+        label.textAlignment = .center
+        label.adjustsFontSizeToFitWidth = true
+
+        return label
+    }()
+
+    private func updateTitle() {
+        guard let currentItem = self.currentItem else {
+            owsFail("\(logTag) currentItem was unexpectedly nil")
+            return
+        }
+        updateTitle(item: currentItem)
+    }
+
+    private func updateTitle(item: MediaGalleryItem) {
+        let name = senderName(message: item.message)
+        headerNameLabel.text = name
+
+        // use sent date
+        let date = Date(timeIntervalSince1970: Double(item.message.timestamp) / 1000)
+        let formattedDate = dateFormatter.string(from: date)
+        headerDateLabel.text = formattedDate
+    }
 }
diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings
index a20dbba54..dc32e2d7e 100644
--- a/Signal/translations/en.lproj/Localizable.strings
+++ b/Signal/translations/en.lproj/Localizable.strings
@@ -175,9 +175,6 @@
 /* Indicates that the database data is being exported. */
 "BACKUP_EXPORT_PHASE_DATABASE_EXPORT" = "Exporting Data";
 
-/* Indicates that the backup export data is being finalized. */
-"BACKUP_EXPORT_PHASE_DATABASE_FINALIZED" = "Finalizing Data";
-
 /* Indicates that the backup export data is being exported. */
 "BACKUP_EXPORT_PHASE_EXPORT" = "Exporting Backup";
 
@@ -980,6 +977,9 @@
 /* media picker option to choose from library */
 "MEDIA_FROM_LIBRARY_BUTTON" = "Photo Library";
 
+/* Short sender label for media sent by you */
+"MEDIA_GALLERY_SENDER_NAME_YOU" = "You";
+
 /* Section header in media gallery collection view */
 "MEDIA_GALLERY_THIS_MONTH_HEADER" = "This Month";