Explorar o código

Merge pull request #633 from deltachat/share_extension

Share extension
cyBerta %!s(int64=5) %!d(string=hai) anos
pai
achega
7d559ec284
Modificáronse 30 ficheiros con 1382 adicións e 228 borrados
  1. 28 0
      DcCore/DcCore.xcodeproj/project.pbxproj
  2. 4 0
      DcCore/DcCore/DC/Wrapper.swift
  3. 3 0
      DcCore/DcCore/Extensions/UIColor+Extensions.swift
  4. 77 0
      DcCore/DcCore/Extensions/UIImage+Extensions.swift
  5. 230 0
      DcCore/DcCore/Extensions/UIView+Extensions.swift
  6. 13 9
      DcCore/DcCore/Helper/DatabaseHelper.swift
  7. 59 0
      DcCore/DcCore/Helper/DcColors.swift
  8. 60 0
      DcCore/DcCore/Helper/DcUtils.swift
  9. 13 11
      DcCore/DcCore/Views/InitialsBadge.swift
  10. 24 0
      DcShare/Base.lproj/MainInterface.storyboard
  11. 61 0
      DcShare/Controller/ChatListController.swift
  12. 80 0
      DcShare/Controller/SendingController.swift
  13. 179 0
      DcShare/Controller/ShareViewController.swift
  14. 10 0
      DcShare/DcShare.entitlements
  15. 174 0
      DcShare/Helper/ShareAttachment.swift
  16. 48 0
      DcShare/Info.plist
  17. 91 0
      DcShare/View/ChatListCell.swift
  18. 213 13
      deltachat-ios.xcodeproj/project.pbxproj
  19. 1 1
      deltachat-ios/Controller/ChatListController.swift
  20. 3 3
      deltachat-ios/Controller/ChatViewController.swift
  21. 1 1
      deltachat-ios/DC/DcMsg+Extension.swift
  22. 4 0
      deltachat-ios/Extensions/UIColor+Extensions.swift
  23. 1 73
      deltachat-ios/Extensions/UIImage+Extension.swift
  24. 0 69
      deltachat-ios/Helper/Colors.swift
  25. 0 47
      deltachat-ios/Helper/Utils.swift
  26. 1 0
      deltachat-ios/MessageKit/Views/Cells/InfoMessageCell.swift
  27. 1 0
      deltachat-ios/View/ChatTitleView.swift
  28. 1 0
      deltachat-ios/View/ContactDetailHeader.swift
  29. 1 0
      deltachat-ios/View/ProviderInfoCell.swift
  30. 1 1
      deltachat-ios/ViewModel/ContactCellViewModel.swift

+ 28 - 0
DcCore/DcCore.xcodeproj/project.pbxproj

@@ -21,7 +21,12 @@
 		30421964243F0B8400516852 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421963243F0B8400516852 /* String+Extensions.swift */; };
 		30421964243F0B8400516852 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421963243F0B8400516852 /* String+Extensions.swift */; };
 		30421986243F209E00516852 /* events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421985243F209E00516852 /* events.swift */; };
 		30421986243F209E00516852 /* events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421985243F209E00516852 /* events.swift */; };
 		30421988243F23E500516852 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421987243F23E500516852 /* Constants.swift */; };
 		30421988243F23E500516852 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30421987243F23E500516852 /* Constants.swift */; };
+		304F5E41244F2F3200462538 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304F5E40244F2F3200462538 /* UIImage+Extensions.swift */; };
 		306C324824460CDE001D89F3 /* DateUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306C324724460CDE001D89F3 /* DateUtils.swift */; };
 		306C324824460CDE001D89F3 /* DateUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 306C324724460CDE001D89F3 /* DateUtils.swift */; };
+		30E8F2212447357500CE2C90 /* DatabaseHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2202447357500CE2C90 /* DatabaseHelper.swift */; };
+		30E8F2482449C98600CE2C90 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */; };
+		30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */; };
+		30E8F24D2449D30200CE2C90 /* DcColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24C2449D30200CE2C90 /* DcColors.swift */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXContainerItemProxy section */
@@ -52,7 +57,12 @@
 		30421963243F0B8400516852 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
 		30421963243F0B8400516852 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
 		30421985243F209E00516852 /* events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = events.swift; path = ../../DcCore/DcCore/DC/events.swift; sourceTree = "<group>"; };
 		30421985243F209E00516852 /* events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = events.swift; path = ../../DcCore/DcCore/DC/events.swift; sourceTree = "<group>"; };
 		30421987243F23E500516852 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
 		30421987243F23E500516852 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
+		304F5E40244F2F3200462538 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
 		306C324724460CDE001D89F3 /* DateUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUtils.swift; sourceTree = "<group>"; };
 		306C324724460CDE001D89F3 /* DateUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUtils.swift; sourceTree = "<group>"; };
+		30E8F2202447357500CE2C90 /* DatabaseHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseHelper.swift; sourceTree = "<group>"; };
+		30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
+		30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialsBadge.swift; sourceTree = "<group>"; };
+		30E8F24C2449D30200CE2C90 /* DcColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DcColors.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
@@ -98,6 +108,7 @@
 		30421933243DE0F200516852 /* DcCore */ = {
 		30421933243DE0F200516852 /* DcCore */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				30E8F2492449CF2F00CE2C90 /* Views */,
 				30421977243F1AF400516852 /* Helper */,
 				30421977243F1AF400516852 /* Helper */,
 				3042195E243E255000516852 /* Extensions */,
 				3042195E243E255000516852 /* Extensions */,
 				3042194B243DE15D00516852 /* DC */,
 				3042194B243DE15D00516852 /* DC */,
@@ -142,6 +153,8 @@
 			children = (
 			children = (
 				3042195F243E257100516852 /* UIColor+Extensions.swift */,
 				3042195F243E257100516852 /* UIColor+Extensions.swift */,
 				30421963243F0B8400516852 /* String+Extensions.swift */,
 				30421963243F0B8400516852 /* String+Extensions.swift */,
+				30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */,
+				304F5E40244F2F3200462538 /* UIImage+Extensions.swift */,
 			);
 			);
 			path = Extensions;
 			path = Extensions;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -149,13 +162,23 @@
 		30421977243F1AF400516852 /* Helper */ = {
 		30421977243F1AF400516852 /* Helper */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				30E8F2202447357500CE2C90 /* DatabaseHelper.swift */,
 				30421987243F23E500516852 /* Constants.swift */,
 				30421987243F23E500516852 /* Constants.swift */,
 				3042195C243E23F100516852 /* DcUtils.swift */,
 				3042195C243E23F100516852 /* DcUtils.swift */,
 				306C324724460CDE001D89F3 /* DateUtils.swift */,
 				306C324724460CDE001D89F3 /* DateUtils.swift */,
+				30E8F24C2449D30200CE2C90 /* DcColors.swift */,
 			);
 			);
 			path = Helper;
 			path = Helper;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		30E8F2492449CF2F00CE2C90 /* Views */ = {
+			isa = PBXGroup;
+			children = (
+				30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */,
+			);
+			path = Views;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXHeadersBuildPhase section */
 /* Begin PBXHeadersBuildPhase section */
@@ -289,14 +312,19 @@
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 				30421988243F23E500516852 /* Constants.swift in Sources */,
 				30421988243F23E500516852 /* Constants.swift in Sources */,
+				30E8F24D2449D30200CE2C90 /* DcColors.swift in Sources */,
 				30421962243E26C800516852 /* Logger.swift in Sources */,
 				30421962243E26C800516852 /* Logger.swift in Sources */,
 				30421986243F209E00516852 /* events.swift in Sources */,
 				30421986243F209E00516852 /* events.swift in Sources */,
 				30421951243DE15D00516852 /* Wrapper.swift in Sources */,
 				30421951243DE15D00516852 /* Wrapper.swift in Sources */,
 				306C324824460CDE001D89F3 /* DateUtils.swift in Sources */,
 				306C324824460CDE001D89F3 /* DateUtils.swift in Sources */,
 				30421952243DE15D00516852 /* wrapper.c in Sources */,
 				30421952243DE15D00516852 /* wrapper.c in Sources */,
+				30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */,
+				30E8F2212447357500CE2C90 /* DatabaseHelper.swift in Sources */,
 				3042195D243E23F100516852 /* DcUtils.swift in Sources */,
 				3042195D243E23F100516852 /* DcUtils.swift in Sources */,
 				30421964243F0B8400516852 /* String+Extensions.swift in Sources */,
 				30421964243F0B8400516852 /* String+Extensions.swift in Sources */,
 				30421960243E257100516852 /* UIColor+Extensions.swift in Sources */,
 				30421960243E257100516852 /* UIColor+Extensions.swift in Sources */,
+				30E8F2482449C98600CE2C90 /* UIView+Extensions.swift in Sources */,
+				304F5E41244F2F3200462538 /* UIImage+Extensions.swift in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 4 - 0
DcCore/DcCore/DC/Wrapper.swift

@@ -202,6 +202,10 @@ public class DcContext {
         dc_perform_mvbox_idle(contextPointer)
         dc_perform_mvbox_idle(contextPointer)
     }
     }
 
 
+    public func performSmtpJobs() {
+        dc_perform_smtp_jobs(contextPointer)
+    }
+    
     public func performSmtp() {
     public func performSmtp() {
         dc_perform_smtp_jobs(contextPointer)
         dc_perform_smtp_jobs(contextPointer)
         dc_perform_smtp_idle(contextPointer)
         dc_perform_smtp_idle(contextPointer)

+ 3 - 0
DcCore/DcCore/Extensions/UIColor+Extensions.swift

@@ -60,5 +60,8 @@ public extension UIColor {
         return UIColor(hexString: lightHex)
         return UIColor(hexString: lightHex)
     }
     }
 
 
+    static func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
+        return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
+    }
 
 
 }
 }

+ 77 - 0
DcCore/DcCore/Extensions/UIImage+Extensions.swift

@@ -0,0 +1,77 @@
+import UIKit
+
+public extension UIImage {
+
+    private func getResizedRectangle(toMax: Float) -> CGRect {
+        var actualHeight = Float(size.height)
+        var actualWidth = Float(size.width)
+        let maxHeight: Float = toMax
+        let maxWidth: Float = toMax
+        var imgRatio: Float = actualWidth / actualHeight
+        let maxRatio: Float = maxWidth / maxHeight
+        if actualHeight > maxHeight || actualWidth > maxWidth {
+            if imgRatio < maxRatio {
+                //adjust width according to maxHeight
+                imgRatio = maxHeight / actualHeight
+                actualWidth = imgRatio * actualWidth
+                actualHeight = maxHeight
+            } else if imgRatio > maxRatio {
+                //adjust height according to maxWidth
+                imgRatio = maxWidth / actualWidth
+                actualHeight = imgRatio * actualHeight
+                actualWidth = maxWidth
+            } else {
+                actualHeight = maxHeight
+                actualWidth = maxWidth
+            }
+        }
+        return CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
+    }
+
+    func scaleDownImage(toMax: CGFloat) -> UIImage? {
+        let rect = getResizedRectangle(toMax: Float(toMax))
+        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
+        draw(in: rect)
+        let newImage = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return newImage
+    }
+
+    func dcCompress(toMax target: Float = 1280) -> UIImage? {
+        return scaleDownAndCompress(toMax: target)
+    }
+
+    func imageSizeInPixel() -> CGSize {
+        let heightInPoints = size.height
+        let heightInPixels = heightInPoints * scale
+        let widthInPoints = size.width
+        let widthInPixels = widthInPoints * scale
+        return CGSize(width: widthInPixels, height: heightInPixels)
+    }
+    
+    // if an image has an alpha channel we try to keep it, using PNG formatting instead of JPEG
+    // PNGs are less compressed than JPEGs - to keep the message sizes small,
+    // the size of PNG imgaes will be scaled down
+    func scaleDownAndCompress(toMax: Float) -> UIImage? {
+        let rect = getResizedRectangle(toMax: self.isTransparent() ?
+            min(Float(self.size.width) / 2, toMax / 2) :
+            toMax)
+
+        UIGraphicsBeginImageContextWithOptions(rect.size, !self.isTransparent(), 0.0)
+        draw(in: rect)
+        let img = UIGraphicsGetImageFromCurrentImageContext()
+
+        let imageData = self.isTransparent() ?
+            img?.pngData() :
+            img?.jpegData(compressionQuality: 0.85)
+
+        UIGraphicsEndImageContext()
+        return UIImage(data: imageData!)
+    }
+
+    func isTransparent() -> Bool {
+      guard let alpha: CGImageAlphaInfo = self.cgImage?.alphaInfo else { return false }
+      return alpha == .first || alpha == .last || alpha == .premultipliedFirst || alpha == .premultipliedLast
+    }
+
+}

+ 230 - 0
DcCore/DcCore/Extensions/UIView+Extensions.swift

@@ -0,0 +1,230 @@
+import UIKit
+
+// swiftlint:disable explicit_acl
+public extension UIView {
+
+    func makeBorder(color: UIColor = UIColor.red) {
+        self.layer.borderColor = color.cgColor
+        self.layer.borderWidth = 2
+    }
+
+    func alignLeadingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingLeading: CGFloat = 0.0) {
+        self.leadingAnchor.constraint(equalTo: anchor, constant: paddingLeading).isActive = true
+    }
+
+    func alignTrailingToAnchor(_ anchor: NSLayoutXAxisAnchor, paddingTrailing: CGFloat = 0.0) {
+        self.trailingAnchor.constraint(equalTo: anchor, constant: -paddingTrailing).isActive = true
+    }
+
+    func alignTopToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingTop: CGFloat = 0.0) {
+        self.topAnchor.constraint(equalTo: anchor, constant: paddingTop).isActive = true
+    }
+
+    func alignBottomToAnchor(_ anchor: NSLayoutYAxisAnchor, paddingBottom: CGFloat = 0.0) {
+        self.bottomAnchor.constraint(equalTo: anchor, constant: -paddingBottom).isActive = true
+    }
+
+    func fill(view: UIView, paddingLeading: CGFloat? = 0.0, paddingTrailing: CGFloat? = 0.0, paddingTop: CGFloat? = 0.0, paddingBottom: CGFloat? = 0.0) {
+        alignLeadingToAnchor(view.leadingAnchor, paddingLeading: paddingLeading ??  0.0)
+        alignTrailingToAnchor(view.trailingAnchor, paddingTrailing: paddingTrailing ?? 0.0)
+        alignTopToAnchor(view.topAnchor, paddingTop: paddingTop ?? 0.0)
+        alignBottomToAnchor(view.bottomAnchor, paddingBottom: paddingBottom ?? 0.0)
+    }
+
+    func constraintAlignTopTo(_ view: UIView) -> NSLayoutConstraint {
+        return constraintAlignTopTo(view, paddingTop: 0.0)
+    }
+
+    func constraintAlignTopTo(_ view: UIView, paddingTop: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .top,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .top,
+            multiplier: 1.0,
+            constant: paddingTop)
+    }
+
+    func constraintAlignBottomTo(_ view: UIView, paddingBottom: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .bottom,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .bottom,
+            multiplier: 1.0,
+            constant: -paddingBottom)
+    }
+
+    func constraintAlignLeadingTo(_ view: UIView, paddingLeading: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .leading,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .leading,
+            multiplier: 1.0,
+            constant: paddingLeading)
+    }
+
+    func constraintAlignTrailingTo(_ view: UIView, paddingTrailing: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .trailing,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .trailing,
+            multiplier: 1.0,
+            constant: -paddingTrailing)
+    }
+
+    func constraintToBottomOf(_ view: UIView, paddingTop: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .top,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .bottom,
+            multiplier: 1.0,
+            constant: paddingTop)
+    }
+
+    func constraintToTrailingOf(_ view: UIView, paddingLeading: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(
+            item: self,
+            attribute: .leading,
+            relatedBy: .equal,
+            toItem: view,
+            attribute: .trailing,
+            multiplier: 1.0,
+            constant: paddingLeading)
+    }
+
+
+    func constraintCenterXTo(_ view: UIView, paddingX: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(item: self,
+                                  attribute: .centerX,
+                                  relatedBy: .equal,
+                                  toItem: view,
+                                  attribute: .centerX,
+                                  multiplier: 1.0,
+                                  constant: paddingX)
+    }
+
+    func constraintCenterYTo(_ view: UIView, paddingY: CGFloat = 0.0) -> NSLayoutConstraint {
+        return NSLayoutConstraint(item: self,
+                                  attribute: .centerY,
+                                  relatedBy: .equal,
+                                  toItem: view,
+                                  attribute: .centerY,
+                                  multiplier: 1.0,
+                                  constant: paddingY)
+    }
+
+    func constraintHeightTo(_ height: CGFloat) -> NSLayoutConstraint {
+        return heightAnchor.constraint(equalToConstant: height)
+    }
+
+    func constraintWidthTo(_ width: CGFloat) -> NSLayoutConstraint {
+       return  widthAnchor.constraint(equalToConstant: width)
+    }
+
+    func fillSuperview() {
+        guard let superview = self.superview else {
+            return
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+
+        let constraints: [NSLayoutConstraint] = [
+            leftAnchor.constraint(equalTo: superview.leftAnchor),
+            rightAnchor.constraint(equalTo: superview.rightAnchor),
+            topAnchor.constraint(equalTo: superview.topAnchor),
+            bottomAnchor.constraint(equalTo: superview.bottomAnchor)]
+        NSLayoutConstraint.activate(constraints)
+    }
+
+    func centerInSuperview() {
+        guard let superview = self.superview else {
+            return
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+        let constraints: [NSLayoutConstraint] = [
+            centerXAnchor.constraint(equalTo: superview.centerXAnchor),
+            centerYAnchor.constraint(equalTo: superview.centerYAnchor)
+        ]
+        NSLayoutConstraint.activate(constraints)
+    }
+
+    func constraint(equalTo size: CGSize) {
+        guard superview != nil else { return }
+        translatesAutoresizingMaskIntoConstraints = false
+        let constraints: [NSLayoutConstraint] = [
+            widthAnchor.constraint(equalToConstant: size.width),
+            heightAnchor.constraint(equalToConstant: size.height)
+        ]
+        NSLayoutConstraint.activate(constraints)
+
+    }
+
+    @discardableResult
+    func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil, centerX: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, centerYConstant: CGFloat = 0, centerXConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] {
+
+        if self.superview == nil {
+            return []
+        }
+        translatesAutoresizingMaskIntoConstraints = false
+
+        var constraints = [NSLayoutConstraint]()
+
+        if let top = top {
+            let constraint = topAnchor.constraint(equalTo: top, constant: topConstant)
+            constraint.identifier = "top"
+            constraints.append(constraint)
+        }
+        if let left = left {
+            let constraint = leftAnchor.constraint(equalTo: left, constant: leftConstant)
+            constraint.identifier = "left"
+            constraints.append(constraint)
+        }
+
+        if let bottom = bottom {
+            let constraint = bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)
+            constraint.identifier = "bottom"
+            constraints.append(constraint)
+        }
+
+        if let right = right {
+            let constraint = rightAnchor.constraint(equalTo: right, constant: -rightConstant)
+            constraint.identifier = "right"
+            constraints.append(constraint)
+        }
+
+        if let centerY = centerY {
+            let constraint = centerYAnchor.constraint(equalTo: centerY, constant: centerYConstant)
+            constraint.identifier = "centerY"
+            constraints.append(constraint)
+        }
+
+        if let centerX = centerX {
+            let constraint = centerXAnchor.constraint(equalTo: centerX, constant: centerXConstant)
+            constraint.identifier = "centerX"
+            constraints.append(constraint)
+        }
+
+        if widthConstant > 0 {
+            let constraint = widthAnchor.constraint(equalToConstant: widthConstant)
+            constraint.identifier = "width"
+            constraints.append(constraint)
+        }
+
+        if heightConstant > 0 {
+            let constraint = heightAnchor.constraint(equalToConstant: heightConstant)
+            constraint.identifier = "height"
+            constraints.append(constraint)
+        }
+
+        NSLayoutConstraint.activate(constraints)
+        return constraints
+    }
+}

+ 13 - 9
deltachat-ios/Helper/DatabaseHelper.swift → DcCore/DcCore/Helper/DatabaseHelper.swift

@@ -1,11 +1,11 @@
 import Foundation
 import Foundation
-class DatabaseHelper {
+public class DatabaseHelper {
 
 
     /// The application group identifier defines a group of apps or extensions that have access to a shared container.
     /// The application group identifier defines a group of apps or extensions that have access to a shared container.
     /// The ID is created in the apple developer portal and can be changed there.
     /// The ID is created in the apple developer portal and can be changed there.
     static let applicationGroupIdentifier = "group.chat.delta.ios"
     static let applicationGroupIdentifier = "group.chat.delta.ios"
 
 
-    var sharedDbFile: String {
+    public var sharedDbFile: String {
         guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: DatabaseHelper.applicationGroupIdentifier) else {
         guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: DatabaseHelper.applicationGroupIdentifier) else {
             return ""
             return ""
         }
         }
@@ -33,7 +33,7 @@ class DatabaseHelper {
         return URL(fileURLWithPath: paths[0], isDirectory: true)
         return URL(fileURLWithPath: paths[0], isDirectory: true)
     }
     }
 
 
-    var currentDatabaseLocation: String {
+    public var currentDatabaseLocation: String {
         let filemanager = FileManager.default
         let filemanager = FileManager.default
         if filemanager.fileExists(atPath: localDbFile) {
         if filemanager.fileExists(atPath: localDbFile) {
             return localDbFile
             return localDbFile
@@ -49,6 +49,10 @@ class DatabaseHelper {
         return sharedDbBlobsDir
         return sharedDbBlobsDir
     }
     }
 
 
+    public init() {
+
+    }
+
     func clearDbBlobsDir(at path: String) {
     func clearDbBlobsDir(at path: String) {
         let fileManager = FileManager.default
         let fileManager = FileManager.default
         do {
         do {
@@ -61,7 +65,7 @@ class DatabaseHelper {
                 try fileManager.removeItem(atPath: path)
                 try fileManager.removeItem(atPath: path)
             }
             }
         } catch {
         } catch {
-          logger.error("Could not clean shared blobs dir, it might be it didn't exist")
+            DcContext.shared.logger?.error("Could not clean shared blobs dir, it might be it didn't exist")
         }
         }
     }
     }
 
 
@@ -71,12 +75,12 @@ class DatabaseHelper {
             do {
             do {
                 try filemanager.removeItem(atPath: path)
                 try filemanager.removeItem(atPath: path)
             } catch {
             } catch {
-                logger.error("Failed to delete db: \(error)")
+                DcContext.shared.logger?.error("Failed to delete db: \(error)")
             }
             }
         }
         }
     }
     }
 
 
-    func clearAccountData() {
+    public func clearAccountData() {
         clearDb(at: currentDatabaseLocation)
         clearDb(at: currentDatabaseLocation)
         clearDbBlobsDir(at: currentBlobsDirLocation)
         clearDbBlobsDir(at: currentBlobsDirLocation)
     }
     }
@@ -88,12 +92,12 @@ class DatabaseHelper {
                 clearDbBlobsDir(at: sharedDbBlobsDir)
                 clearDbBlobsDir(at: sharedDbBlobsDir)
                 try filemanager.moveItem(at: URL(fileURLWithPath: localDbBlobsDir), to: URL(fileURLWithPath: sharedDbBlobsDir))
                 try filemanager.moveItem(at: URL(fileURLWithPath: localDbBlobsDir), to: URL(fileURLWithPath: sharedDbBlobsDir))
             } catch let error {
             } catch let error {
-                logger.error("Could not move db blobs directory to shared space: \(error.localizedDescription)")
+                DcContext.shared.logger?.error("Could not move db blobs directory to shared space: \(error.localizedDescription)")
             }
             }
         }
         }
     }
     }
 
 
-    func updateDatabaseLocation() -> String? {
+    public func updateDatabaseLocation() -> String? {
       let filemanager = FileManager.default
       let filemanager = FileManager.default
       if filemanager.fileExists(atPath: localDbFile) {
       if filemanager.fileExists(atPath: localDbFile) {
           do {
           do {
@@ -101,7 +105,7 @@ class DatabaseHelper {
               try filemanager.moveItem(at: URL(fileURLWithPath: localDbFile), to: URL(fileURLWithPath: sharedDbFile))
               try filemanager.moveItem(at: URL(fileURLWithPath: localDbFile), to: URL(fileURLWithPath: sharedDbFile))
               moveBlobsFolder()
               moveBlobsFolder()
           } catch let error {
           } catch let error {
-              logger.error("Could not update DB location. Share extension will probably not work. \n\(error.localizedDescription)")
+              DcContext.shared.logger?.error("Could not update DB location. Share extension will probably not work. \n\(error.localizedDescription)")
               return localDbFile
               return localDbFile
           }
           }
       }
       }

+ 59 - 0
DcCore/DcCore/Helper/DcColors.swift

@@ -0,0 +1,59 @@
+import UIKit
+
+public struct DcColors {
+    public static let primary = UIColor.systemBlue
+    public static let colorDisabled = UIColor.themeColor(light: UIColor(white: 0.9, alpha: 1), dark: UIColor(white: 0.2, alpha: 1))
+    public static let messagePrimaryColor = UIColor.themeColor(light: UIColor.rgb(red: 220, green: 248, blue: 198),
+                                                        dark: UIColor.init(hexString: "224508"))
+    public static let messageSecondaryColor = UIColor.themeColor(light: UIColor.init(hexString: "ebebed"),
+                                                          dark: UIColor.init(hexString: "333333"))
+    public static let contactCellBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    public static let defaultBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
+    public static let chatBackgroundColor = UIColor.themeColor(light: UIColor(red: 255, green: 255, blue: 255, alpha: 0), dark: .black)
+    public static let checkmarkGreen = UIColor.themeColor(light: UIColor.rgb(red: 112, green: 177, blue: 92))
+    public static let defaultTextColor = UIColor.themeColor(light: .darkText, dark: .white)
+    public static let grayTextColor = UIColor.themeColor(light: .darkGray, dark: .lightGray)
+    public static let grayDateColor = UIColor.themeColor(lightHex: "999999", darkHex: "bbbbbb") // slight variations of lightGray (#aaaaaa)
+    public static let middleGray = UIColor(hexString: "848ba7")
+    public static let secondaryTextColor = UIColor.themeColor(lightHex: "848ba7", darkHex: "a5abc0")
+    public static let inputFieldColor =  UIColor.themeColor(light: UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1),
+                                                     dark: UIColor(red: 10 / 255, green: 10 / 255, blue: 10 / 255, alpha: 1))
+    public static let placeholderColor = UIColor.themeColor(light: UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1),
+                                                     dark: UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1))
+    public static let providerPreparationBackground = UIColor.themeColor(lightHex: "#fffdf7b2", darkHex: "##fffdf7b2")
+    public static let providerBrokenBackground = UIColor.themeColor(light: SystemColor.red.uiColor, dark: SystemColor.red.uiColor)
+    public static let systemMessageBackgroundColor = UIColor.themeColor(light: UIColor.rgb(red: 248, green: 248, blue: 248), dark: UIColor(white: 0.2, alpha: 0.5))
+    public static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "ebebec"), dark: UIColor.init(hexString: "1a1a1c"))
+}
+
+public enum SystemColor {
+    case red
+    case orange
+    case yellow
+    case green
+    case tealBlue
+    case blue
+    case purple
+    case pink
+
+    public var uiColor: UIColor {
+        switch self {
+        case .red:
+            return UIColor(red: 255 / 255, green: 59 / 255, blue: 48 / 255, alpha: 1)
+        case .orange:
+            return UIColor(red: 255 / 255, green: 149 / 255, blue: 0 / 255, alpha: 1)
+        case .yellow:
+            return UIColor(red: 255 / 255, green: 204 / 255, blue: 0 / 255, alpha: 1)
+        case .green:
+            return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
+        case .tealBlue:
+            return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
+        case .blue:
+            return UIColor(red: 0 / 255, green: 122 / 255, blue: 255 / 255, alpha: 1)
+        case .purple:
+            return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
+        case .pink:
+            return UIColor(red: 255 / 255, green: 45 / 255, blue: 85 / 255, alpha: 1)
+        }
+    }
+}

+ 60 - 0
DcCore/DcCore/Helper/DcUtils.swift

@@ -1,8 +1,18 @@
 import Foundation
 import Foundation
 import UIKit
 import UIKit
+import MobileCoreServices
+import AVFoundation
 
 
 public struct DcUtils {
 public struct DcUtils {
 
 
+    public static func getInitials(inputName: String) -> String {
+        if let firstLetter = inputName.first {
+            return firstLetter.uppercased()
+        } else {
+            return ""
+        }
+    }
+
     static func copyAndFreeArray(inputArray: OpaquePointer?) -> [Int] {
     static func copyAndFreeArray(inputArray: OpaquePointer?) -> [Int] {
         var acc: [Int] = []
         var acc: [Int] = []
         let len = dc_array_get_cnt(inputArray)
         let len = dc_array_get_cnt(inputArray)
@@ -51,4 +61,54 @@ public struct DcUtils {
         return acc
         return acc
     }
     }
 
 
+    // compression needs to be done before in UIImage.dcCompress()
+    public static func saveImage(image: UIImage) -> String? {
+        guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
+                                                           appropriateFor: nil, create: false) as NSURL else {
+                                                            return nil
+        }
+
+        guard let data = image.isTransparent() ? image.pngData() : image.jpegData(compressionQuality: 1.0) else {
+            return nil
+        }
+
+        do {
+            let timestamp = Double(Date().timeIntervalSince1970)
+            let path = directory.appendingPathComponent("\(timestamp).jpg")
+            try data.write(to: path!)
+            return path?.relativePath
+        } catch {
+            DcContext.shared.logger?.info(error.localizedDescription)
+            return nil
+        }
+    }
+
+    public static func getMimeTypeForPath(path: String) -> String {
+        let url = NSURL(fileURLWithPath: path)
+        let pathExtension = url.pathExtension
+
+        if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() {
+            if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
+                return mimetype as String
+            }
+        }
+        return "application/octet-stream"
+    }
+
+    public static func generateThumbnailFromVideo(url: URL?) -> UIImage? {
+           guard let url = url else {
+               return nil
+           }
+           do {
+               let asset = AVURLAsset(url: url)
+               let imageGenerator = AVAssetImageGenerator(asset: asset)
+               imageGenerator.appliesPreferredTrackTransform = true
+               let cgImage = try imageGenerator.copyCGImage(at: .zero, actualTime: nil)
+               return UIImage(cgImage: cgImage)
+           } catch {
+               print(error.localizedDescription)
+               return nil
+           }
+       }
+
 }
 }

+ 13 - 11
deltachat-ios/View/InitialsBadge.swift → DcCore/DcCore/Views/InitialsBadge.swift

@@ -1,6 +1,6 @@
 import UIKit
 import UIKit
 
 
-class InitialsBadge: UIView {
+public class InitialsBadge: UIView {
 
 
     private let verificationViewPadding: CGFloat = 2
     private let verificationViewPadding: CGFloat = 2
     private let size: CGFloat
     private let size: CGFloat
@@ -31,18 +31,20 @@ class InitialsBadge: UIView {
         return imageViewContainer
         return imageViewContainer
     }()
     }()
 
 
-    convenience init(name: String, color: UIColor, size: CGFloat, accessibilityLabel: String? = nil) {
+
+    public convenience init(name: String, color: UIColor, size: CGFloat, accessibilityLabel: String? = nil) {
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         setName(name)
         setName(name)
         setColor(color)
         setColor(color)
     }
     }
 
 
-    convenience init (image: UIImage, size: CGFloat, accessibilityLabel: String? = nil) {
+
+    public convenience init (image: UIImage, size: CGFloat, accessibilityLabel: String? = nil) {
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         self.init(size: size, accessibilityLabel: accessibilityLabel)
         setImage(image)
         setImage(image)
     }
     }
 
 
-    init(size: CGFloat, accessibilityLabel: String? = nil) {
+    public init(size: CGFloat, accessibilityLabel: String? = nil) {
         self.size = size
         self.size = size
         super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
         super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
         self.accessibilityLabel = accessibilityLabel
         self.accessibilityLabel = accessibilityLabel
@@ -80,32 +82,32 @@ class InitialsBadge: UIView {
         fatalError("init(coder:) has not been implemented")
         fatalError("init(coder:) has not been implemented")
     }
     }
 
 
-    func setName(_ name: String) {
-        label.text = Utils.getInitials(inputName: name)
+    public func setName(_ name: String) {
+        label.text = DcUtils.getInitials(inputName: name)
         label.isHidden = name.isEmpty
         label.isHidden = name.isEmpty
         imageView.isHidden = !name.isEmpty
         imageView.isHidden = !name.isEmpty
     }
     }
 
 
-    func setLabelFont(_ font: UIFont) {
+    public func setLabelFont(_ font: UIFont) {
         label.font = font
         label.font = font
     }
     }
 
 
-    func setImage(_ image: UIImage) {
+    public func setImage(_ image: UIImage) {
         self.imageView.image = image
         self.imageView.image = image
         self.imageView.contentMode = UIView.ContentMode.scaleAspectFill
         self.imageView.contentMode = UIView.ContentMode.scaleAspectFill
         self.imageView.isHidden = false
         self.imageView.isHidden = false
         self.label.isHidden = true
         self.label.isHidden = true
     }
     }
 
 
-    func showsInitials() -> Bool {
+    public func showsInitials() -> Bool {
         return !label.isHidden
         return !label.isHidden
     }
     }
 
 
-    func setColor(_ color: UIColor) {
+    public func setColor(_ color: UIColor) {
         backgroundColor = color
         backgroundColor = color
     }
     }
 
 
-    func setVerified(_ verified: Bool) {
+    public func setVerified(_ verified: Bool) {
         verifiedView.isHidden = !verified
         verifiedView.isHidden = !verified
     }
     }
 }
 }

+ 24 - 0
DcShare/Base.lproj/MainInterface.storyboard

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Share View Controller-->
+        <scene sceneID="ceB-am-kn3">
+            <objects>
+                <viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>

+ 61 - 0
DcShare/Controller/ChatListController.swift

@@ -0,0 +1,61 @@
+import Foundation
+import UIKit
+import DcCore
+
+protocol ChatListDelegate: class {
+    func onChatSelected(chatId: Int)
+}
+
+class ChatListController: UITableViewController {
+    let dcContext: DcContext
+    var chatList: DcChatlist?
+    let contactCellReuseIdentifier = "contactCellReuseIdentifier"
+    weak var chatListDelegate: ChatListDelegate?
+
+    init(dcContext: DcContext, chatListDelegate: ChatListDelegate) {
+        self.dcContext = dcContext
+        self.chatListDelegate = chatListDelegate
+        super.init(style: .grouped)
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    override func viewWillAppear(_ animated: Bool) {
+        preferredContentSize = UIScreen.main.bounds.size
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        chatList = dcContext.getChatlist(flags: DC_GCL_ADD_ALLDONE_HINT | DC_GCL_FOR_FORWARDING | DC_GCL_NO_SPECIALS, queryString: nil, queryId: 0)
+        tableView.register(ChatListCell.self, forCellReuseIdentifier: contactCellReuseIdentifier)
+        tableView.rowHeight = 64
+        tableView.tableHeaderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 0.0, height: Double.leastNormalMagnitude))
+        tableView.tableFooterView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 0.0, height: Double.leastNormalMagnitude))
+
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return chatList?.length ?? 0
+    }
+
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: contactCellReuseIdentifier, for: indexPath) as? ChatListCell else {
+            fatalError("could not deque TableViewCell")
+        }
+
+        if let chatList = chatList {
+            cell.updateCell(chatId: chatList.getChatId(index: indexPath.row))
+        }
+
+        return cell
+    }
+
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if let chatList = chatList {
+            chatListDelegate?.onChatSelected(chatId: chatList.getChatId(index: indexPath.row))
+        }
+    }
+
+}

+ 80 - 0
DcShare/Controller/SendingController.swift

@@ -0,0 +1,80 @@
+import UIKit
+import DcCore
+
+protocol SendingControllerDelegate: class {
+    func onSendingAttemptFinished()
+}
+
+class SendingController: UIViewController {
+
+    private let dcMsgs: [DcMsg]
+    private let chatId: Int
+    private let dcContext: DcContext
+    weak var delegate: SendingControllerDelegate?
+
+    private var progressLabel: UILabel = {
+        let view = UILabel()
+        view.translatesAutoresizingMaskIntoConstraints = false
+        /// TODO: translation!
+        view.text = "Sending..."
+        return view
+    }()
+
+    private var activityIndicator: UIActivityIndicatorView = {
+        let view: UIActivityIndicatorView
+        if #available(iOS 13, *) {
+             view = UIActivityIndicatorView(style: .large)
+        } else {
+            view = UIActivityIndicatorView(style: .whiteLarge)
+            view.color = UIColor.gray
+        }
+        view.startAnimating()
+        view.translatesAutoresizingMaskIntoConstraints = false
+        return view
+    }()
+
+    init(chatId: Int, dcMsgs: [DcMsg], dcContext: DcContext) {
+        self.chatId = chatId
+        self.dcMsgs = dcMsgs
+        self.dcContext = dcContext
+        super.init(nibName: nil, bundle: nil)
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+
+    override func viewDidLoad() {
+        view.backgroundColor = DcColors.defaultBackgroundColor
+        setupViews()
+        sendMessage()
+    }
+
+    private func setupViews() {
+        view.addSubview(progressLabel)
+        view.addSubview(activityIndicator)
+        view.addConstraints([
+            progressLabel.constraintCenterXTo(view),
+            progressLabel.constraintAlignTopTo(view, paddingTop: 25),
+            activityIndicator.constraintCenterXTo(view),
+            activityIndicator.constraintCenterYTo(view)
+        ])
+        setupNavigationBar()
+    }
+
+    private func setupNavigationBar() {
+        self.navigationItem.leftBarButtonItem = UIBarButtonItem()
+        self.navigationItem.titleView = UIImageView(image: UIImage(named: "ic_chat")?.scaleDownImage(toMax: 26))
+    }
+
+    private func sendMessage() {
+        DispatchQueue.global(qos: .utility).async {
+            for message in self.dcMsgs {
+                message.sendInChat(id: self.chatId)
+            }
+            self.dcContext.performSmtpJobs()
+            self.delegate?.onSendingAttemptFinished()
+        }
+    }
+}

+ 179 - 0
DcShare/Controller/ShareViewController.swift

@@ -0,0 +1,179 @@
+import UIKit
+import Social
+import DcCore
+import MobileCoreServices
+
+
+class ShareViewController: SLComposeServiceViewController {
+
+    class SimpleLogger: Logger {
+        func verbose(_ message: String) {
+            print("ShareViewController", "verbose", message)
+        }
+
+        func debug(_ message: String) {
+            print("ShareViewController", "debug", message)
+        }
+
+        func info(_ message: String) {
+            print("ShareViewController", "info", message)
+        }
+
+        func warning(_ message: String) {
+            print("ShareViewController", "warning", message)
+        }
+
+        func error(_ message: String) {
+            print("ShareViewController", "error", message)
+        }
+    }
+
+    let logger = SimpleLogger()
+    let dcContext = DcContext.shared
+    var selectedChatId: Int?
+    var selectedChat: DcChat?
+    let dbHelper = DatabaseHelper()
+    var shareAttachment: ShareAttachment?
+
+    lazy var preview: UIImageView? = {
+        let imageView = UIImageView(frame: .zero)
+        imageView.clipsToBounds = true
+        imageView.shouldGroupAccessibilityChildren = true
+        imageView.isAccessibilityElement = false
+        return imageView
+    }()
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupNavigationBar()
+        // workaround for iOS13 bug
+        if #available(iOS 13.0, *) {
+            _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidShowNotification, object: nil, queue: .main) { (_) in
+                if let layoutContainerView = self.view.subviews.last {
+                    layoutContainerView.frame.size.height += 10
+                }
+            }
+        }
+
+        DispatchQueue.global(qos: .background).async {
+            self.shareAttachment = ShareAttachment(dcContext: self.dcContext, inputItems: self.extensionContext?.inputItems, delegate: self)
+        }
+    }
+
+    override func presentationAnimationDidFinish() {
+        if dbHelper.currentDatabaseLocation == dbHelper.sharedDbFile {
+            dcContext.logger = self.logger
+            dcContext.openDatabase(dbFile: dbHelper.sharedDbFile)
+            selectedChatId = dcContext.getChatIdByContactId(contactId: Int(DC_CONTACT_ID_SELF))
+            if let chatId = selectedChatId {
+                selectedChat = dcContext.getChat(chatId: chatId)
+            }
+            reloadConfigurationItems()
+        } else {
+            cancel()
+        }
+    }
+
+    override func loadPreviewView() -> UIView! {
+        return preview
+    }
+
+    override func isContentValid() -> Bool {
+        // Do validation of contentText and/or NSExtensionContext attachments here
+        return  !(contentText?.isEmpty ?? true) || !(self.shareAttachment?.isEmpty ?? true)
+    }
+
+    private func setupNavigationBar() {
+        guard let item = navigationController?.navigationBar.items?.first else { return }
+        let button = UIBarButtonItem(
+            title: String.localized("menu_send"),
+            style: .done,
+            target: self,
+            action: #selector(appendPostTapped))
+        item.rightBarButtonItem? = button
+        item.titleView = UIImageView(image: UIImage(named: "ic_chat")?.scaleDownImage(toMax: 26))
+    }
+
+    /// Invoked when the user wants to post.
+    @objc
+    private func appendPostTapped() {
+        if let chatId = self.selectedChatId {
+            guard var messages = shareAttachment?.messages else { return }
+            if !self.contentText.isEmpty {
+                if messages.count == 1 {
+                    messages[0].text?.append(self.contentText)
+                } else {
+                    let message = DcMsg(viewType: DC_MSG_TEXT)
+                    message.text = self.contentText
+                    messages.insert(message, at: 0)
+                }
+            }
+            let chatListController = SendingController(chatId: chatId, dcMsgs: messages, dcContext: dcContext)
+            chatListController.delegate = self
+            self.pushConfigurationViewController(chatListController)
+        }
+    }
+
+    func quit() {
+        if dbHelper.currentDatabaseLocation == dbHelper.sharedDbFile {
+            dcContext.closeDatabase()
+        }
+
+        // Inform the host that we're done, so it un-blocks its UI.
+        self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
+    }
+
+    override func configurationItems() -> [Any]! {
+        logger.debug("configurationItems")
+        // To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
+
+        let item = SLComposeSheetConfigurationItem()
+        item?.title = String.localized("forward_to")
+        item?.value = selectedChat?.name
+        logger.debug("configurationItems chat name: \(String(describing: selectedChat?.name))")
+        item?.tapHandler = {
+            let chatListController = ChatListController(dcContext: self.dcContext, chatListDelegate: self)
+            self.pushConfigurationViewController(chatListController)
+        }
+
+        return [item as Any]
+    }
+
+    override func didSelectCancel() {
+        quit()
+    }
+}
+
+extension ShareViewController: ChatListDelegate {
+    func onChatSelected(chatId: Int) {
+        selectedChatId = chatId
+        selectedChat = dcContext.getChat(chatId: chatId)
+        reloadConfigurationItems()
+        popConfigurationViewController()
+    }
+}
+
+extension ShareViewController: SendingControllerDelegate {
+    func onSendingAttemptFinished() {
+        DispatchQueue.main.async {
+            self.popConfigurationViewController()
+            self.quit()
+        }
+    }
+}
+
+extension ShareViewController: ShareAttachmentDelegate {
+    func onAttachmentChanged() {
+        DispatchQueue.main.async {
+            self.validateContent()
+        }
+    }
+
+    func onThumbnailChanged() {
+        DispatchQueue.main.async {
+            if let preview = self.preview {
+                preview.image = self.shareAttachment?.thumbnail ?? nil
+            }
+        }
+    }
+}

+ 10 - 0
DcShare/DcShare.entitlements

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.application-groups</key>
+	<array>
+		<string>group.chat.delta.ios</string>
+	</array>
+</dict>
+</plist>

+ 174 - 0
DcShare/Helper/ShareAttachment.swift

@@ -0,0 +1,174 @@
+import Foundation
+import MobileCoreServices
+import DcCore
+import UIKit
+import QuickLookThumbnailing
+
+protocol ShareAttachmentDelegate: class {
+    func onAttachmentChanged()
+    func onThumbnailChanged()
+}
+
+class ShareAttachment {
+
+    weak var delegate: ShareAttachmentDelegate?
+    let dcContext: DcContext
+    let thumbnailSize = CGFloat(96)
+
+    var inputItems: [Any]?
+    var messages: [DcMsg] = []
+
+    private var imageThumbnail: UIImage?
+    private var attachmentThumbnail: UIImage?
+
+    var thumbnail: UIImage? {
+        return self.imageThumbnail ?? self.attachmentThumbnail
+    }
+
+    var isEmpty: Bool {
+        return messages.isEmpty
+    }
+
+    init(dcContext: DcContext, inputItems: [Any]?, delegate: ShareAttachmentDelegate) {
+        self.dcContext = dcContext
+        self.inputItems = inputItems
+        self.delegate = delegate
+        createMessages()
+    }
+
+
+    func createMessages() {
+        guard let items = inputItems as? [NSExtensionItem] else { return }
+        for item in items {
+            if let attachments = item.attachments {
+                createMessageFromDataRepresentaion(attachments)
+            }
+        }
+    }
+
+    func createMessageFromDataRepresentaion(_ attachments: [NSItemProvider]) {
+        for attachment in attachments {
+            if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
+                createImageMsg(attachment)
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
+                createMovieMsg(attachment)
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeAudio as String) {
+                createAudioMsg(attachment)
+            } else if attachment.hasItemConformingToTypeIdentifier(kUTTypeFileURL as String) {
+                createFileMsg(attachment)
+            }
+        }
+    }
+
+    func createImageMsg(_ item: NSItemProvider) {
+        item.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { data, error in
+            let result: UIImage?
+            switch data {
+            case let image as UIImage:
+                result = image
+            case let data as Data:
+                result = UIImage(data: data)
+            case let url as URL:
+                result = UIImage(contentsOfFile: url.path)
+            default:
+                self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
+                result = nil
+            }
+            if let result = result, let compressedImage = result.dcCompress() {
+                let pixelSize = compressedImage.imageSizeInPixel()
+                let path = DcUtils.saveImage(image: compressedImage)
+                let msg = DcMsg(viewType: DC_MSG_IMAGE)
+                msg.setFile(filepath: path, mimeType: "image/jpeg")
+                msg.setDimension(width: pixelSize.width, height: pixelSize.height)
+                self.delegate?.onAttachmentChanged()
+                self.messages.append(msg)
+                if self.imageThumbnail == nil {
+                    self.imageThumbnail = compressedImage.scaleDownImage(toMax: self.thumbnailSize)
+                    self.delegate?.onThumbnailChanged()
+                }
+            }
+            if error != nil {
+                self.dcContext.logger?.error(error?.localizedDescription ?? "Could not load share item as image")
+            }
+        }
+    }
+
+    func createMovieMsg(_ item: NSItemProvider) {
+        item.loadItem(forTypeIdentifier: kUTTypeMovie as String, options: nil) { data, error in
+            switch data {
+            case let url as URL:
+                self.addDcMsg(url: url, viewType: DC_MSG_VIDEO)
+                self.delegate?.onAttachmentChanged()
+                if self.imageThumbnail == nil {
+                    self.imageThumbnail = DcUtils.generateThumbnailFromVideo(url: url)?.scaleDownImage(toMax: self.thumbnailSize)
+                    self.delegate?.onThumbnailChanged()
+                }
+            default:
+                self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
+            }
+            if error != nil {
+                self.dcContext.logger?.error(error?.localizedDescription ?? "Could not load share item as video")
+            }
+        }
+    }
+
+    func createAudioMsg(_ item: NSItemProvider) {
+        createMessageFromItemURL(item: item, typeIdentifier: kUTTypeAudio, viewType: DC_MSG_AUDIO)
+    }
+
+    func createFileMsg(_ item: NSItemProvider) {
+        createMessageFromItemURL(item: item, typeIdentifier: kUTTypeFileURL, viewType: DC_MSG_FILE)
+    }
+
+    func createMessageFromItemURL(item: NSItemProvider, typeIdentifier: CFString, viewType: Int32) {
+        item.loadItem(forTypeIdentifier: typeIdentifier as String, options: nil) { data, error in
+            switch data {
+            case let url as URL:
+                self.addDcMsg(url: url, viewType: viewType)
+                self.delegate?.onAttachmentChanged()
+                if self.imageThumbnail == nil {
+                    self.generateThumbnailRepresentations(url: url)
+                }
+            default:
+                self.dcContext.logger?.debug("Unexpected data: \(type(of: data))")
+            }
+            if error != nil {
+                self.dcContext.logger?.error(error?.localizedDescription ?? "Could not load share item.")
+            }
+        }
+    }
+
+    func addDcMsg(url: URL, viewType: Int32) {
+        let msg = DcMsg(viewType: viewType)
+        msg.setFile(filepath: url.path, mimeType: DcUtils.getMimeTypeForPath(path: url.path))
+        self.messages.append(msg)
+    }
+
+    func generateThumbnailRepresentations(url: URL) {
+        let size: CGSize = CGSize(width: self.thumbnailSize * 2 / 3, height: self.thumbnailSize)
+        let scale = UIScreen.main.scale
+
+        if #available(iOSApplicationExtension 13.0, *) {
+            let request = QLThumbnailGenerator.Request(fileAt: url,
+                                                       size: size,
+                                                       scale: scale,
+                                                       representationTypes: .all)
+            let generator = QLThumbnailGenerator.shared
+            generator.generateRepresentations(for: request) { (thumbnail, _, error) in
+                DispatchQueue.main.async {
+                    if thumbnail == nil || error != nil {
+                        self.dcContext.logger?.warning(error?.localizedDescription ?? "Could not create thumbnail.")
+                    } else {
+                        self.attachmentThumbnail = thumbnail?.uiImage
+                        self.delegate?.onThumbnailChanged()
+                    }
+                }
+            }
+        } else {
+            let controller = UIDocumentInteractionController(url: url)
+            self.attachmentThumbnail = controller.icons.first
+            self.delegate?.onThumbnailChanged()
+        }
+    }
+
+}

+ 48 - 0
DcShare/Info.plist

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>DcShare</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>NSExtension</key>
+	<dict>
+		<key>NSExtensionAttributes</key>
+		<dict>
+			<key>NSExtensionActivationRule</key>
+			<string>SUBQUERY (
+extensionItems,
+$extensionItem,
+SUBQUERY (
+$extensionItem.attachments,
+$attachment,
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.audio" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ||
+ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
+).@count == $extensionItem.attachments.@count
+).@count >= 1</string>
+		</dict>
+		<key>NSExtensionMainStoryboard</key>
+		<string>MainInterface</string>
+		<key>NSExtensionPointIdentifier</key>
+		<string>com.apple.share-services</string>
+	</dict>
+</dict>
+</plist>

+ 91 - 0
DcShare/View/ChatListCell.swift

@@ -0,0 +1,91 @@
+import Foundation
+import UIKit
+import DcCore
+
+class ChatListCell: UITableViewCell {
+
+    let badgeSize: CGFloat = 54
+
+    lazy var avatar: InitialsBadge = {
+        let badge = InitialsBadge(size: badgeSize)
+        badge.setColor(UIColor.lightGray)
+        badge.isAccessibilityElement = false
+        return badge
+    }()
+
+    let titleLabel: UILabel = {
+        let label = UILabel()
+        label.font = UIFont.systemFont(ofSize: 16, weight: .medium)
+        label.lineBreakMode = .byTruncatingTail
+        label.textColor = DcColors.defaultTextColor
+        label.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1), for: NSLayoutConstraint.Axis.horizontal)
+        label.translatesAutoresizingMaskIntoConstraints = false
+        return label
+    }()
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        selectionStyle = .none
+        backgroundColor = DcColors.contactCellBackgroundColor
+        contentView.backgroundColor = DcColors.contactCellBackgroundColor
+        setupSubviews()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    private func setupSubviews() {
+        let margin: CGFloat = 10
+
+        avatar.translatesAutoresizingMaskIntoConstraints = false
+        contentView.addSubview(avatar)
+
+        contentView.addConstraints([
+            avatar.constraintWidthTo(badgeSize),
+            avatar.constraintHeightTo(badgeSize),
+            avatar.constraintAlignLeadingTo(contentView, paddingLeading: badgeSize / 4),
+            avatar.constraintCenterYTo(contentView),
+        ])
+
+        contentView.addSubview(titleLabel)
+        contentView.addConstraints([
+            titleLabel.constraintCenterYTo(contentView),
+            titleLabel.constraintToTrailingOf(avatar, paddingLeading: margin),
+            titleLabel.constraintAlignTrailingTo(contentView)
+        ])
+    }
+
+    private func setImage(_ img: UIImage) {
+        avatar.setImage(img)
+    }
+
+    private func resetBackupImage() {
+        avatar.setColor(UIColor.clear)
+        avatar.setName("")
+    }
+
+    private func setBackupImage(name: String, color: UIColor) {
+        avatar.setColor(color)
+        avatar.setName(name)
+    }
+
+    private func setColor(_ color: UIColor) {
+          avatar.setColor(color)
+      }
+
+    // use this update-method to update cell in cellForRowAt whenever it is possible - other set-methods will be set private in progress
+    func updateCell(chatId: Int) {
+        let chat = DcContext.shared.getChat(chatId: chatId)
+        titleLabel.text = chat.name
+        backgroundColor = DcColors.contactCellBackgroundColor
+        contentView.backgroundColor = DcColors.contactCellBackgroundColor
+
+        if let img = chat.profileImage {
+            resetBackupImage()
+            setImage(img)
+        } else {
+            setBackupImage(name: chat.name, color: chat.color)
+        }
+    }
+}

+ 213 - 13
deltachat-ios.xcodeproj/project.pbxproj

@@ -12,6 +12,7 @@
 		30149D9322F21129003C12B5 /* QrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30149D9222F21129003C12B5 /* QrViewController.swift */; };
 		30149D9322F21129003C12B5 /* QrViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30149D9222F21129003C12B5 /* QrViewController.swift */; };
 		3015634423A003BA00E9DEF4 /* AudioRecorderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3015634323A003BA00E9DEF4 /* AudioRecorderController.swift */; };
 		3015634423A003BA00E9DEF4 /* AudioRecorderController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3015634323A003BA00E9DEF4 /* AudioRecorderController.swift */; };
 		3022E6BE22E8768800763272 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3022E6C022E8768800763272 /* InfoPlist.strings */; };
 		3022E6BE22E8768800763272 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3022E6C022E8768800763272 /* InfoPlist.strings */; };
+		302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302589FE2452FA280086C1CD /* ShareAttachment.swift */; };
 		30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */; };
 		30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */; };
 		302B84C6239676F0001C261F /* AvatarHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AC265E237F1807002A943F /* AvatarHelper.swift */; };
 		302B84C6239676F0001C261F /* AvatarHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AC265E237F1807002A943F /* AvatarHelper.swift */; };
 		302B84C72396770B001C261F /* RelayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B84C42396627F001C261F /* RelayHelper.swift */; };
 		302B84C72396770B001C261F /* RelayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B84C42396627F001C261F /* RelayHelper.swift */; };
@@ -21,6 +22,7 @@
 		3040F462234F550300FA34D5 /* AudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3040F461234F550300FA34D5 /* AudioPlayerView.swift */; };
 		3040F462234F550300FA34D5 /* AudioPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3040F461234F550300FA34D5 /* AudioPlayerView.swift */; };
 		304219D3243F588500516852 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		304219D3243F588500516852 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		304219D92440734A00516852 /* DcMsg+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304219D82440734A00516852 /* DcMsg+Extension.swift */; };
 		304219D92440734A00516852 /* DcMsg+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304219D82440734A00516852 /* DcMsg+Extension.swift */; };
+		304F5E44244F571C00462538 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A9FB14A1FB061E2001FEA36 /* Assets.xcassets */; };
 		305961CC2346125100C80F33 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961822346125000C80F33 /* UIView+Extensions.swift */; };
 		305961CC2346125100C80F33 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961822346125000C80F33 /* UIView+Extensions.swift */; };
 		305961CD2346125100C80F33 /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */; };
 		305961CD2346125100C80F33 /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961832346125000C80F33 /* UIEdgeInsets+Extensions.swift */; };
 		305961CF2346125100C80F33 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961852346125000C80F33 /* UIColor+Extensions.swift */; };
 		305961CF2346125100C80F33 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 305961852346125000C80F33 /* UIColor+Extensions.swift */; };
@@ -94,7 +96,15 @@
 		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3095A350237DD1F700AB07F7 /* MediaPicker.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
 		30A4D9AE2332672700544344 /* QrInviteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A4D9AD2332672600544344 /* QrInviteViewController.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
 		30C0D49D237C4908008E2A0E /* CertificateCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */; };
-		30ECA5472436026F006F2E7A /* DatabaseHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30ECA5462436026F006F2E7A /* DatabaseHelper.swift */; };
+		30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2122447285600CE2C90 /* ShareViewController.swift */; };
+		30E8F2162447285600CE2C90 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 30E8F2142447285600CE2C90 /* MainInterface.storyboard */; };
+		30E8F21A2447285600CE2C90 /* DcShare.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 30E8F2102447285600CE2C90 /* DcShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+		30E8F2252447622300CE2C90 /* DcCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; };
+		30E8F2262447622300CE2C90 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		30E8F2422448B77600CE2C90 /* ChatListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2412448B77600CE2C90 /* ChatListController.swift */; };
+		30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2432449C64100CE2C90 /* ChatListCell.swift */; };
+		30E8F2512449EA0E00CE2C90 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3060119E22DDE24000C1CE6F /* Localizable.strings */; };
+		30E8F253244DAD0E00CE2C90 /* SendingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F252244DAD0E00CE2C90 /* SendingController.swift */; };
 		30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F9B9EB235F2116006E7ACF /* MessageCounter.swift */; };
 		30F9B9EC235F2116006E7ACF /* MessageCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F9B9EB235F2116006E7ACF /* MessageCounter.swift */; };
 		6795F63A82E94FF7CD2CEC0F /* Pods_deltachat_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F7009234DB9408201A6CDCB /* Pods_deltachat_iosTests.framework */; };
 		6795F63A82E94FF7CD2CEC0F /* Pods_deltachat_iosTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F7009234DB9408201A6CDCB /* Pods_deltachat_iosTests.framework */; };
 		7070FB9B2101ECBB000DC258 /* NewGroupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7070FB9A2101ECBB000DC258 /* NewGroupController.swift */; };
 		7070FB9B2101ECBB000DC258 /* NewGroupController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7070FB9A2101ECBB000DC258 /* NewGroupController.swift */; };
@@ -125,7 +135,6 @@
 		AE1988AB23EB3C7600B4CD5F /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = AE1988AA23EB3C7600B4CD5F /* Assets */; };
 		AE1988AB23EB3C7600B4CD5F /* Assets in Resources */ = {isa = PBXBuildFile; fileRef = AE1988AA23EB3C7600B4CD5F /* Assets */; };
 		AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */; };
 		AE25F09022807AD800CDEA66 /* AvatarSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
 		AE38B31822672DFC00EC37A1 /* ActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B31722672DFC00EC37A1 /* ActionCell.swift */; };
-		AE38B31A2267328200EC37A1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE38B3192267328200EC37A1 /* Colors.swift */; };
 		AE406EF0240FF8FF005F7022 /* ProfileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE406EEF240FF8FF005F7022 /* ProfileCell.swift */; };
 		AE406EF0240FF8FF005F7022 /* ProfileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE406EEF240FF8FF005F7022 /* ProfileCell.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
 		AE4AEE3522B1030D000AA495 /* PreviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE4AEE3422B1030D000AA495 /* PreviewController.swift */; };
 		AE52EA19229EB53C00C586C9 /* ContactDetailHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */; };
 		AE52EA19229EB53C00C586C9 /* ContactDetailHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */; };
@@ -139,7 +148,6 @@
 		AE851AC5227C755A00ED86F0 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC4227C755A00ED86F0 /* Protocols.swift */; };
 		AE851AC5227C755A00ED86F0 /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC4227C755A00ED86F0 /* Protocols.swift */; };
 		AE851AC7227C776400ED86F0 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC6227C776400ED86F0 /* Location.swift */; };
 		AE851AC7227C776400ED86F0 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC6227C776400ED86F0 /* Location.swift */; };
 		AE851AC9227C77CF00ED86F0 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC8227C77CF00ED86F0 /* Media.swift */; };
 		AE851AC9227C77CF00ED86F0 /* Media.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851AC8227C77CF00ED86F0 /* Media.swift */; };
-		AE851ACE227CA54400ED86F0 /* InitialsBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */; };
 		AE851AD0227DF50900ED86F0 /* GroupChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */; };
 		AE851AD0227DF50900ED86F0 /* GroupChatDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */; };
 		AE9DAF0D22C1215D004C9591 /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0C22C1215D004C9591 /* EditContactController.swift */; };
 		AE9DAF0D22C1215D004C9591 /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0C22C1215D004C9591 /* EditContactController.swift */; };
 		AE9DAF0F22C278C6004C9591 /* ChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */; };
 		AE9DAF0F22C278C6004C9591 /* ChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */; };
@@ -167,6 +175,13 @@
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXContainerItemProxy section */
+		30E8F2182447285600CE2C90 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 7A9FB1381FB061E2001FEA36 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 30E8F20F2447285600CE2C90;
+			remoteInfo = DcShare;
+		};
 		AE851A06227AECDF00ED86F0 /* PBXContainerItemProxy */ = {
 		AE851A06227AECDF00ED86F0 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			isa = PBXContainerItemProxy;
 			containerPortal = 7A9FB1381FB061E2001FEA36 /* Project object */;
 			containerPortal = 7A9FB1381FB061E2001FEA36 /* Project object */;
@@ -188,6 +203,28 @@
 			name = "Embed Frameworks";
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
+		30E8F21B2447285600CE2C90 /* Embed App Extensions */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 13;
+			files = (
+				30E8F21A2447285600CE2C90 /* DcShare.appex in Embed App Extensions */,
+			);
+			name = "Embed App Extensions";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		30E8F2272447622400CE2C90 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				30E8F2262447622300CE2C90 /* DcCore.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXCopyFilesBuildPhase section */
 /* End PBXCopyFilesBuildPhase section */
 
 
 /* Begin PBXFileReference section */
 /* Begin PBXFileReference section */
@@ -217,6 +254,7 @@
 		3022E6D122E8769E00763272 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		3022E6D122E8769E00763272 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		3022E6D222E8769F00763272 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		3022E6D222E8769F00763272 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		3022E6D322E876A100763272 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		3022E6D322E876A100763272 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		302589FE2452FA280086C1CD /* ShareAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAttachment.swift; sourceTree = "<group>"; };
 		30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineTextFieldCell.swift; sourceTree = "<group>"; };
 		30260CA6238F02F700D8D52C /* MultilineTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineTextFieldCell.swift; sourceTree = "<group>"; };
 		302B84C42396627F001C261F /* RelayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayHelper.swift; sourceTree = "<group>"; };
 		302B84C42396627F001C261F /* RelayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayHelper.swift; sourceTree = "<group>"; };
 		302B84CD2397F6CD001C261F /* URL+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extension.swift"; sourceTree = "<group>"; };
 		302B84CD2397F6CD001C261F /* URL+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extension.swift"; sourceTree = "<group>"; };
@@ -338,7 +376,14 @@
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
 		30A4D9AD2332672600544344 /* QrInviteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrInviteViewController.swift; sourceTree = "<group>"; };
 		30AC265E237F1807002A943F /* AvatarHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHelper.swift; sourceTree = "<group>"; };
 		30AC265E237F1807002A943F /* AvatarHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarHelper.swift; sourceTree = "<group>"; };
 		30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateCheckController.swift; sourceTree = "<group>"; };
 		30C0D49C237C4908008E2A0E /* CertificateCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateCheckController.swift; sourceTree = "<group>"; };
-		30ECA5462436026F006F2E7A /* DatabaseHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseHelper.swift; sourceTree = "<group>"; };
+		30E8F2102447285600CE2C90 /* DcShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = DcShare.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+		30E8F2122447285600CE2C90 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
+		30E8F2152447285600CE2C90 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
+		30E8F2172447285600CE2C90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		30E8F21F24472AAE00CE2C90 /* DcShare.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DcShare.entitlements; sourceTree = "<group>"; };
+		30E8F2412448B77600CE2C90 /* ChatListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListController.swift; sourceTree = "<group>"; };
+		30E8F2432449C64100CE2C90 /* ChatListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListCell.swift; sourceTree = "<group>"; };
+		30E8F252244DAD0E00CE2C90 /* SendingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendingController.swift; sourceTree = "<group>"; };
 		30F9B9EB235F2116006E7ACF /* MessageCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCounter.swift; sourceTree = "<group>"; };
 		30F9B9EB235F2116006E7ACF /* MessageCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCounter.swift; sourceTree = "<group>"; };
 		6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		6241BE1534A653E79AD5D01D /* Pods_deltachat_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_deltachat_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		7070FB9A2101ECBB000DC258 /* NewGroupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGroupController.swift; sourceTree = "<group>"; };
 		7070FB9A2101ECBB000DC258 /* NewGroupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewGroupController.swift; sourceTree = "<group>"; };
@@ -374,7 +419,6 @@
 		AE1988AA23EB3C7600B4CD5F /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Assets; sourceTree = "<group>"; };
 		AE1988AA23EB3C7600B4CD5F /* Assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Assets; sourceTree = "<group>"; };
 		AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSelectionCell.swift; sourceTree = "<group>"; };
 		AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSelectionCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
 		AE38B31722672DFC00EC37A1 /* ActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCell.swift; sourceTree = "<group>"; };
-		AE38B3192267328200EC37A1 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
 		AE406EEF240FF8FF005F7022 /* ProfileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCell.swift; sourceTree = "<group>"; };
 		AE406EEF240FF8FF005F7022 /* ProfileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCell.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
 		AE4AEE3422B1030D000AA495 /* PreviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewController.swift; sourceTree = "<group>"; };
 		AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailHeader.swift; sourceTree = "<group>"; };
 		AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailHeader.swift; sourceTree = "<group>"; };
@@ -390,7 +434,6 @@
 		AE851AC4227C755A00ED86F0 /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
 		AE851AC4227C755A00ED86F0 /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
 		AE851AC6227C776400ED86F0 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
 		AE851AC6227C776400ED86F0 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
 		AE851AC8227C77CF00ED86F0 /* Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = "<group>"; };
 		AE851AC8227C77CF00ED86F0 /* Media.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Media.swift; sourceTree = "<group>"; };
-		AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialsBadge.swift; sourceTree = "<group>"; };
 		AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatDetailViewController.swift; sourceTree = "<group>"; };
 		AE851ACF227DF50900ED86F0 /* GroupChatDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatDetailViewController.swift; sourceTree = "<group>"; };
 		AE9DAF0C22C1215D004C9591 /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
 		AE9DAF0C22C1215D004C9591 /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
 		AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTitleView.swift; sourceTree = "<group>"; };
 		AE9DAF0E22C278C6004C9591 /* ChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTitleView.swift; sourceTree = "<group>"; };
@@ -455,6 +498,14 @@
 /* End PBXFileReference section */
 /* End PBXFileReference section */
 
 
 /* Begin PBXFrameworksBuildPhase section */
 /* Begin PBXFrameworksBuildPhase section */
+		30E8F20D2447285600CE2C90 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30E8F2252447622300CE2C90 /* DcCore.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		7A9FB13D1FB061E2001FEA36 /* Frameworks */ = {
 		7A9FB13D1FB061E2001FEA36 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -483,6 +534,32 @@
 			path = DC;
 			path = DC;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		304F5E452451D27500462538 /* Helper */ = {
+			isa = PBXGroup;
+			children = (
+				302589FE2452FA280086C1CD /* ShareAttachment.swift */,
+			);
+			path = Helper;
+			sourceTree = "<group>";
+		};
+		304F5E462451D2AA00462538 /* Controller */ = {
+			isa = PBXGroup;
+			children = (
+				30E8F2122447285600CE2C90 /* ShareViewController.swift */,
+				30E8F2412448B77600CE2C90 /* ChatListController.swift */,
+				30E8F252244DAD0E00CE2C90 /* SendingController.swift */,
+			);
+			path = Controller;
+			sourceTree = "<group>";
+		};
+		304F5E472451D2CA00462538 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				30E8F2432449C64100CE2C90 /* ChatListCell.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		3059617E234610A800C80F33 /* MessageKit */ = {
 		3059617E234610A800C80F33 /* MessageKit */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -635,6 +712,19 @@
 			path = Layout;
 			path = Layout;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		30E8F2112447285600CE2C90 /* DcShare */ = {
+			isa = PBXGroup;
+			children = (
+				304F5E472451D2CA00462538 /* View */,
+				304F5E462451D2AA00462538 /* Controller */,
+				304F5E452451D27500462538 /* Helper */,
+				30E8F21F24472AAE00CE2C90 /* DcShare.entitlements */,
+				30E8F2142447285600CE2C90 /* MainInterface.storyboard */,
+				30E8F2172447285600CE2C90 /* Info.plist */,
+			);
+			path = DcShare;
+			sourceTree = "<group>";
+		};
 		7A9FB1371FB061E2001FEA36 = {
 		7A9FB1371FB061E2001FEA36 = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -644,6 +734,7 @@
 				7837B63F21E54DC600CDE126 /* .swiftlint.yml */,
 				7837B63F21E54DC600CDE126 /* .swiftlint.yml */,
 				7A9FB1421FB061E2001FEA36 /* deltachat-ios */,
 				7A9FB1421FB061E2001FEA36 /* deltachat-ios */,
 				AE851A02227AECDE00ED86F0 /* deltachat-iosTests */,
 				AE851A02227AECDE00ED86F0 /* deltachat-iosTests */,
+				30E8F2112447285600CE2C90 /* DcShare */,
 				7A9FB1411FB061E2001FEA36 /* Products */,
 				7A9FB1411FB061E2001FEA36 /* Products */,
 				7A9FB4F81FB084E6001FEA36 /* Frameworks */,
 				7A9FB4F81FB084E6001FEA36 /* Frameworks */,
 				7DB2DC4CCB84D323E5130F99 /* Pods */,
 				7DB2DC4CCB84D323E5130F99 /* Pods */,
@@ -658,6 +749,7 @@
 			children = (
 			children = (
 				7A9FB1401FB061E2001FEA36 /* deltachat-ios.app */,
 				7A9FB1401FB061E2001FEA36 /* deltachat-ios.app */,
 				AE851A01227AECDE00ED86F0 /* deltachat-iosTests.xctest */,
 				AE851A01227AECDE00ED86F0 /* deltachat-iosTests.xctest */,
+				30E8F2102447285600CE2C90 /* DcShare.appex */,
 			);
 			);
 			name = Products;
 			name = Products;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -819,7 +911,6 @@
 			children = (
 			children = (
 				AEACE2E21FB32B5C00DCDD78 /* Constants.swift */,
 				AEACE2E21FB32B5C00DCDD78 /* Constants.swift */,
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
 				AEACE2E41FB32E1900DCDD78 /* Utils.swift */,
-				AE38B3192267328200EC37A1 /* Colors.swift */,
 				AE851AC4227C755A00ED86F0 /* Protocols.swift */,
 				AE851AC4227C755A00ED86F0 /* Protocols.swift */,
 				3095A350237DD1F700AB07F7 /* MediaPicker.swift */,
 				3095A350237DD1F700AB07F7 /* MediaPicker.swift */,
 				30AC265E237F1807002A943F /* AvatarHelper.swift */,
 				30AC265E237F1807002A943F /* AvatarHelper.swift */,
@@ -827,7 +918,6 @@
 				AE1988A423EB2FBA00B4CD5F /* Errors.swift */,
 				AE1988A423EB2FBA00B4CD5F /* Errors.swift */,
 				AEFBE23023FF09B20045327A /* TypeAlias.swift */,
 				AEFBE23023FF09B20045327A /* TypeAlias.swift */,
 				307D822D241669C7006D2490 /* LocationManager.swift */,
 				307D822D241669C7006D2490 /* LocationManager.swift */,
-				30ECA5462436026F006F2E7A /* DatabaseHelper.swift */,
 			);
 			);
 			path = Helper;
 			path = Helper;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -844,7 +934,6 @@
 				78E45E3B21D3D03700D4B15E /* TextFieldTableViewCell.swift */,
 				78E45E3B21D3D03700D4B15E /* TextFieldTableViewCell.swift */,
 				789E879C21D6DF86003ED1C5 /* ProgressHud.swift */,
 				789E879C21D6DF86003ED1C5 /* ProgressHud.swift */,
 				AE38B31722672DFC00EC37A1 /* ActionCell.swift */,
 				AE38B31722672DFC00EC37A1 /* ActionCell.swift */,
-				AE851ACD227CA54300ED86F0 /* InitialsBadge.swift */,
 				AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */,
 				AE25F08F22807AD800CDEA66 /* AvatarSelectionCell.swift */,
 				AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
 				AE728F14229D5C390047565B /* PhotoPickerAlertAction.swift */,
 				AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
 				AE52EA18229EB53C00C586C9 /* ContactDetailHeader.swift */,
@@ -870,6 +959,24 @@
 /* End PBXGroup section */
 /* End PBXGroup section */
 
 
 /* Begin PBXNativeTarget section */
 /* Begin PBXNativeTarget section */
+		30E8F20F2447285600CE2C90 /* DcShare */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 30E8F21E2447285600CE2C90 /* Build configuration list for PBXNativeTarget "DcShare" */;
+			buildPhases = (
+				30E8F20C2447285600CE2C90 /* Sources */,
+				30E8F20D2447285600CE2C90 /* Frameworks */,
+				30E8F20E2447285600CE2C90 /* Resources */,
+				30E8F2272447622400CE2C90 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = DcShare;
+			productName = DcShare;
+			productReference = 30E8F2102447285600CE2C90 /* DcShare.appex */;
+			productType = "com.apple.product-type.app-extension";
+		};
 		7A9FB13F1FB061E2001FEA36 /* deltachat-ios */ = {
 		7A9FB13F1FB061E2001FEA36 /* deltachat-ios */ = {
 			isa = PBXNativeTarget;
 			isa = PBXNativeTarget;
 			buildConfigurationList = 7A9FB1521FB061E2001FEA36 /* Build configuration list for PBXNativeTarget "deltachat-ios" */;
 			buildConfigurationList = 7A9FB1521FB061E2001FEA36 /* Build configuration list for PBXNativeTarget "deltachat-ios" */;
@@ -883,10 +990,12 @@
 				7837B63821E54CB400CDE126 /* Run Script */,
 				7837B63821E54CB400CDE126 /* Run Script */,
 				7837B64621E5532B00CDE126 /* ShellScript */,
 				7837B64621E5532B00CDE126 /* ShellScript */,
 				304219D4243F588600516852 /* Embed Frameworks */,
 				304219D4243F588600516852 /* Embed Frameworks */,
+				30E8F21B2447285600CE2C90 /* Embed App Extensions */,
 			);
 			);
 			buildRules = (
 			buildRules = (
 			);
 			);
 			dependencies = (
 			dependencies = (
+				30E8F2192447285600CE2C90 /* PBXTargetDependency */,
 			);
 			);
 			name = "deltachat-ios";
 			name = "deltachat-ios";
 			productName = "deltachat-ios";
 			productName = "deltachat-ios";
@@ -918,10 +1027,14 @@
 		7A9FB1381FB061E2001FEA36 /* Project object */ = {
 		7A9FB1381FB061E2001FEA36 /* Project object */ = {
 			isa = PBXProject;
 			isa = PBXProject;
 			attributes = {
 			attributes = {
-				LastSwiftUpdateCheck = 1020;
+				LastSwiftUpdateCheck = 1140;
 				LastUpgradeCheck = 0930;
 				LastUpgradeCheck = 0930;
 				ORGANIZATIONNAME = "Jonas Reinsch";
 				ORGANIZATIONNAME = "Jonas Reinsch";
 				TargetAttributes = {
 				TargetAttributes = {
+					30E8F20F2447285600CE2C90 = {
+						CreatedOnToolsVersion = 11.4;
+						ProvisioningStyle = Automatic;
+					};
 					7A9FB13F1FB061E2001FEA36 = {
 					7A9FB13F1FB061E2001FEA36 = {
 						CreatedOnToolsVersion = 9.1;
 						CreatedOnToolsVersion = 9.1;
 						LastSwiftMigration = 1030;
 						LastSwiftMigration = 1030;
@@ -994,11 +1107,22 @@
 			targets = (
 			targets = (
 				7A9FB13F1FB061E2001FEA36 /* deltachat-ios */,
 				7A9FB13F1FB061E2001FEA36 /* deltachat-ios */,
 				AE851A00227AECDE00ED86F0 /* deltachat-iosTests */,
 				AE851A00227AECDE00ED86F0 /* deltachat-iosTests */,
+				30E8F20F2447285600CE2C90 /* DcShare */,
 			);
 			);
 		};
 		};
 /* End PBXProject section */
 /* End PBXProject section */
 
 
 /* Begin PBXResourcesBuildPhase section */
 /* Begin PBXResourcesBuildPhase section */
+		30E8F20E2447285600CE2C90 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				30E8F2162447285600CE2C90 /* MainInterface.storyboard in Resources */,
+				304F5E44244F571C00462538 /* Assets.xcassets in Resources */,
+				30E8F2512449EA0E00CE2C90 /* Localizable.strings in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		7A9FB13E1FB061E2001FEA36 /* Resources */ = {
 		7A9FB13E1FB061E2001FEA36 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -1151,6 +1275,18 @@
 /* End PBXShellScriptBuildPhase section */
 /* End PBXShellScriptBuildPhase section */
 
 
 /* Begin PBXSourcesBuildPhase section */
 /* Begin PBXSourcesBuildPhase section */
+		30E8F20C2447285600CE2C90 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				302589FF2452FA280086C1CD /* ShareAttachment.swift in Sources */,
+				30E8F2442449C64100CE2C90 /* ChatListCell.swift in Sources */,
+				30E8F2132447285600CE2C90 /* ShareViewController.swift in Sources */,
+				30E8F253244DAD0E00CE2C90 /* SendingController.swift in Sources */,
+				30E8F2422448B77600CE2C90 /* ChatListController.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		7A9FB13C1FB061E2001FEA36 /* Sources */ = {
 		7A9FB13C1FB061E2001FEA36 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -1183,7 +1319,6 @@
 				3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */,
 				3040F45E234DFBC000FA34D5 /* Audio.swift in Sources */,
 				305961FE2346125100C80F33 /* InsetLabel.swift in Sources */,
 				305961FE2346125100C80F33 /* InsetLabel.swift in Sources */,
 				3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */,
 				3095A351237DD1F700AB07F7 /* MediaPicker.swift in Sources */,
-				30ECA5472436026F006F2E7A /* DatabaseHelper.swift in Sources */,
 				B21005DB23383664004C70C5 /* SettingsClassicViewController.swift in Sources */,
 				B21005DB23383664004C70C5 /* SettingsClassicViewController.swift in Sources */,
 				305961F62346125100C80F33 /* MessageContentCell.swift in Sources */,
 				305961F62346125100C80F33 /* MessageContentCell.swift in Sources */,
 				305961E42346125100C80F33 /* MessageKitDateFormatter.swift in Sources */,
 				305961E42346125100C80F33 /* MessageKitDateFormatter.swift in Sources */,
@@ -1261,14 +1396,12 @@
 				305961F32346125100C80F33 /* MediaMessageCell.swift in Sources */,
 				305961F32346125100C80F33 /* MediaMessageCell.swift in Sources */,
 				305962102346154D00C80F33 /* String+Extension.swift in Sources */,
 				305962102346154D00C80F33 /* String+Extension.swift in Sources */,
 				78E45E4C21D404AE00D4B15E /* InfoMessageCell.swift in Sources */,
 				78E45E4C21D404AE00D4B15E /* InfoMessageCell.swift in Sources */,
-				AE38B31A2267328200EC37A1 /* Colors.swift in Sources */,
 				789E879621D6CB58003ED1C5 /* QrCodeReaderController.swift in Sources */,
 				789E879621D6CB58003ED1C5 /* QrCodeReaderController.swift in Sources */,
 				305961D22346125100C80F33 /* CGRect+Extensions.swift in Sources */,
 				305961D22346125100C80F33 /* CGRect+Extensions.swift in Sources */,
 				305961E12346125100C80F33 /* LocationItem.swift in Sources */,
 				305961E12346125100C80F33 /* LocationItem.swift in Sources */,
 				305961E72346125100C80F33 /* AccessoryPosition.swift in Sources */,
 				305961E72346125100C80F33 /* AccessoryPosition.swift in Sources */,
 				30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */,
 				30260CA7238F02F700D8D52C /* MultilineTextFieldCell.swift in Sources */,
 				305961DE2346125100C80F33 /* MessageType.swift in Sources */,
 				305961DE2346125100C80F33 /* MessageType.swift in Sources */,
-				AE851ACE227CA54400ED86F0 /* InitialsBadge.swift in Sources */,
 				305961DA2346125100C80F33 /* MediaItem.swift in Sources */,
 				305961DA2346125100C80F33 /* MediaItem.swift in Sources */,
 				305961EB2346125100C80F33 /* MessageKitError.swift in Sources */,
 				305961EB2346125100C80F33 /* MessageKitError.swift in Sources */,
 				70B8882E2091B8550074812E /* ContactCell.swift in Sources */,
 				70B8882E2091B8550074812E /* ContactCell.swift in Sources */,
@@ -1314,6 +1447,11 @@
 /* End PBXSourcesBuildPhase section */
 /* End PBXSourcesBuildPhase section */
 
 
 /* Begin PBXTargetDependency section */
 /* Begin PBXTargetDependency section */
+		30E8F2192447285600CE2C90 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 30E8F20F2447285600CE2C90 /* DcShare */;
+			targetProxy = 30E8F2182447285600CE2C90 /* PBXContainerItemProxy */;
+		};
 		AE851A07227AECDF00ED86F0 /* PBXTargetDependency */ = {
 		AE851A07227AECDF00ED86F0 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			isa = PBXTargetDependency;
 			target = 7A9FB13F1FB061E2001FEA36 /* deltachat-ios */;
 			target = 7A9FB13F1FB061E2001FEA36 /* deltachat-ios */;
@@ -1442,6 +1580,14 @@
 			path = "deltachat-ios";
 			path = "deltachat-ios";
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		30E8F2142447285600CE2C90 /* MainInterface.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				30E8F2152447285600CE2C90 /* Base */,
+			);
+			name = MainInterface.storyboard;
+			sourceTree = "<group>";
+		};
 		7A9FB14C1FB061E2001FEA36 /* LaunchScreen.storyboard */ = {
 		7A9FB14C1FB061E2001FEA36 /* LaunchScreen.storyboard */ = {
 			isa = PBXVariantGroup;
 			isa = PBXVariantGroup;
 			children = (
 			children = (
@@ -1453,6 +1599,49 @@
 /* End PBXVariantGroup section */
 /* End PBXVariantGroup section */
 
 
 /* Begin XCBuildConfiguration section */
 /* Begin XCBuildConfiguration section */
+		30E8F21C2447285600CE2C90 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CODE_SIGN_ENTITLEMENTS = DcShare/DcShare.entitlements;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = DcShare/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcShare;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SWIFT_VERSION = 4.2;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		30E8F21D2447285600CE2C90 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CODE_SIGN_ENTITLEMENTS = DcShare/DcShare.entitlements;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = 8Y86453UA8;
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = DcShare/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = chat.delta.DcShare;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SWIFT_VERSION = 4.2;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
 		7A9FB1501FB061E2001FEA36 /* Debug */ = {
 		7A9FB1501FB061E2001FEA36 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
@@ -1570,6 +1759,7 @@
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 8DE110C607A0E4485C43B5FA /* Pods-deltachat-ios.debug.xcconfig */;
 			baseConfigurationReference = 8DE110C607A0E4485C43B5FA /* Pods-deltachat-ios.debug.xcconfig */;
 			buildSettings = {
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
@@ -1643,6 +1833,7 @@
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = A8615D4600859851E53CAA9C /* Pods-deltachat-ios.release.xcconfig */;
 			baseConfigurationReference = A8615D4600859851E53CAA9C /* Pods-deltachat-ios.release.xcconfig */;
 			buildSettings = {
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
 				CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
@@ -1753,6 +1944,15 @@
 /* End XCBuildConfiguration section */
 /* End XCBuildConfiguration section */
 
 
 /* Begin XCConfigurationList section */
 /* Begin XCConfigurationList section */
+		30E8F21E2447285600CE2C90 /* Build configuration list for PBXNativeTarget "DcShare" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				30E8F21C2447285600CE2C90 /* Debug */,
+				30E8F21D2447285600CE2C90 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		7A9FB13B1FB061E2001FEA36 /* Build configuration list for PBXProject "deltachat-ios" */ = {
 		7A9FB13B1FB061E2001FEA36 /* Build configuration list for PBXProject "deltachat-ios" */ = {
 			isa = XCConfigurationList;
 			isa = XCConfigurationList;
 			buildConfigurations = (
 			buildConfigurations = (

+ 1 - 1
deltachat-ios/Controller/ChatListController.swift

@@ -279,7 +279,7 @@ class ChatListController: UITableViewController {
         }
         }
         archiveCell.textLabel?.textAlignment = .center
         archiveCell.textLabel?.textAlignment = .center
         archiveCell.textLabel?.text = title
         archiveCell.textLabel?.text = title
-        archiveCell.textLabel?.textColor = .systemBlue
+        archiveCell.textLabel?.textColor = UIColor.systemBlue
         return archiveCell
         return archiveCell
     }
     }
 
 

+ 3 - 3
deltachat-ios/Controller/ChatViewController.swift

@@ -707,7 +707,7 @@ extension ChatViewController: MessagesDataSource {
     func avatar(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> Avatar {
     func avatar(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> Avatar {
         let message = messageList[indexPath.section]
         let message = messageList[indexPath.section]
         let contact = message.fromContact
         let contact = message.fromContact
-        return Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.displayName))
+        return Avatar(image: contact.profileImage, initials: DcUtils.getInitials(inputName: contact.displayName))
     }
     }
 
 
     func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
     func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
@@ -974,7 +974,7 @@ extension ChatViewController: MessagesDataSource {
             if let compressedImage = image.dcCompress() {
             if let compressedImage = image.dcCompress() {
                 // at this point image is compressed by 85% by default
                 // at this point image is compressed by 85% by default
                 let pixelSize = compressedImage.imageSizeInPixel()
                 let pixelSize = compressedImage.imageSizeInPixel()
-                let path = Utils.saveImage(image: compressedImage)
+                let path = DcUtils.saveImage(image: compressedImage)
                 let msg = DcMsg(viewType: DC_MSG_IMAGE)
                 let msg = DcMsg(viewType: DC_MSG_IMAGE)
                 msg.setFile(filepath: path, mimeType: "image/jpeg")
                 msg.setFile(filepath: path, mimeType: "image/jpeg")
                 msg.setDimension(width: pixelSize.width, height: pixelSize.height)
                 msg.setDimension(width: pixelSize.width, height: pixelSize.height)
@@ -1076,7 +1076,7 @@ extension ChatViewController: MessagesDisplayDelegate {
     func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) {
     func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) {
         let message = messageList[indexPath.section]
         let message = messageList[indexPath.section]
         let contact = message.fromContact
         let contact = message.fromContact
-        let avatar = Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.displayName))
+        let avatar = Avatar(image: contact.profileImage, initials: DcUtils.getInitials(inputName: contact.displayName))
         avatarView.set(avatar: avatar)
         avatarView.set(avatar: avatar)
         avatarView.isHidden = isAvatarHidden(at: indexPath)
         avatarView.isHidden = isAvatarHidden(at: indexPath)
         avatarView.backgroundColor = contact.color
         avatarView.backgroundColor = contact.color

+ 1 - 1
deltachat-ios/DC/DcMsg+Extension.swift

@@ -46,7 +46,7 @@ extension DcMsg: MessageType {
     }
     }
 
 
     internal func createVideoMessage(text: String) -> MessageKind {
     internal func createVideoMessage(text: String) -> MessageKind {
-        let thumbnail = Utils.generateThumbnailFromVideo(url: fileURL)
+        let thumbnail = DcUtils.generateThumbnailFromVideo(url: fileURL)
         if text.isEmpty {
         if text.isEmpty {
             return MessageKind.video(Media(url: fileURL, image: thumbnail))
             return MessageKind.video(Media(url: fileURL, image: thumbnail))
         }
         }

+ 4 - 0
deltachat-ios/Extensions/UIColor+Extensions.swift

@@ -35,5 +35,9 @@ internal extension UIColor {
     static let inputBarGray = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
     static let inputBarGray = UIColor(red: 247/255, green: 247/255, blue: 247/255, alpha: 1.0)
     static let playButtonLightGray = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1.0)
     static let playButtonLightGray = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1.0)
     static let sendButtonBlue = UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0)
     static let sendButtonBlue = UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0)
+
+    static var systemBlue: UIColor {
+        return UIButton(type: .system).tintColor
+    }
     
     
 }
 }

+ 1 - 73
deltachat-ios/Extensions/UIImage+Extension.swift

@@ -1,4 +1,5 @@
 import UIKit
 import UIKit
+import DcCore
 
 
 extension UIImage {
 extension UIImage {
 
 
@@ -23,79 +24,6 @@ extension UIImage {
         self.init(cgImage: cgImage)
         self.init(cgImage: cgImage)
     }
     }
 
 
-
-    func dcCompress(toMax target: Float = 1280) -> UIImage? {
-        return scaleDownAndCompress(toMax: target)
-    }
-
-    func imageSizeInPixel() -> CGSize {
-        let heightInPoints = size.height
-        let heightInPixels = heightInPoints * scale
-        let widthInPoints = size.width
-        let widthInPixels = widthInPoints * scale
-        return CGSize(width: widthInPixels, height: heightInPixels)
-    }
-
-    private func getResizedRectangle(toMax: Float) -> CGRect {
-        var actualHeight = Float(size.height)
-        var actualWidth = Float(size.width)
-        let maxHeight: Float = toMax
-        let maxWidth: Float = toMax
-        var imgRatio: Float = actualWidth / actualHeight
-        let maxRatio: Float = maxWidth / maxHeight
-        if actualHeight > maxHeight || actualWidth > maxWidth {
-            if imgRatio < maxRatio {
-                //adjust width according to maxHeight
-                imgRatio = maxHeight / actualHeight
-                actualWidth = imgRatio * actualWidth
-                actualHeight = maxHeight
-            } else if imgRatio > maxRatio {
-                //adjust height according to maxWidth
-                imgRatio = maxWidth / actualWidth
-                actualHeight = imgRatio * actualHeight
-                actualWidth = maxWidth
-            } else {
-                actualHeight = maxHeight
-                actualWidth = maxWidth
-            }
-        }
-        return CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
-    }
-
-    func scaleDownImage(toMax: CGFloat) -> UIImage? {
-        let rect = getResizedRectangle(toMax: Float(toMax))
-        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
-        draw(in: rect)
-        let newImage = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-        return newImage
-    }
-
-    // if an image has an alpha channel we try to keep it, using PNG formatting instead of JPEG
-    // PNGs are less compressed than JPEGs - to keep the message sizes small,
-    // the size of PNG imgaes will be scaled down
-    func scaleDownAndCompress(toMax: Float) -> UIImage? {
-        let rect = getResizedRectangle(toMax: self.isTransparent() ?
-            min(Float(self.size.width) / 2, toMax / 2) :
-            toMax)
-
-        UIGraphicsBeginImageContextWithOptions(rect.size, !self.isTransparent(), 0.0)
-        draw(in: rect)
-        let img = UIGraphicsGetImageFromCurrentImageContext()
-
-        let imageData = self.isTransparent() ?
-            img?.pngData() :
-            img?.jpegData(compressionQuality: 0.85)
-
-        UIGraphicsEndImageContext()
-        return UIImage(data: imageData!)
-    }
-
-    public func isTransparent() -> Bool {
-        guard let alpha: CGImageAlphaInfo = self.cgImage?.alphaInfo else { return false }
-        return alpha == .first || alpha == .last || alpha == .premultipliedFirst || alpha == .premultipliedLast
-      }
-
 }
 }
 
 
 public enum ImageType: String {
 public enum ImageType: String {

+ 0 - 69
deltachat-ios/Helper/Colors.swift

@@ -1,69 +0,0 @@
-import UIKit
-
-struct DcColors {
-    static let primary = UIColor.systemBlue
-    static let colorDisabled = UIColor.themeColor(light: UIColor(white: 0.9, alpha: 1), dark: UIColor(white: 0.2, alpha: 1))
-    static let messagePrimaryColor = UIColor.themeColor(light: UIColor.rgb(red: 220, green: 248, blue: 198),
-                                                        dark: UIColor.init(hexString: "224508"))
-    static let messageSecondaryColor = UIColor.themeColor(light: UIColor.init(hexString: "ebebed"),
-                                                          dark: UIColor.init(hexString: "333333"))
-    static let contactCellBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
-    static let defaultBackgroundColor = UIColor.themeColor(light: .white, dark: .black)
-    static let chatBackgroundColor = UIColor.themeColor(light: UIColor(red: 255, green: 255, blue: 255, alpha: 0), dark: .black)
-    static let checkmarkGreen = UIColor.themeColor(light: UIColor.rgb(red: 112, green: 177, blue: 92))
-    static let defaultTextColor = UIColor.themeColor(light: .darkText, dark: .white)
-    static let grayTextColor = UIColor.themeColor(light: .darkGray, dark: .lightGray)
-    static let grayDateColor = UIColor.themeColor(lightHex: "999999", darkHex: "bbbbbb") // slight variations of lightGray (#aaaaaa)
-    static let middleGray = UIColor(hexString: "848ba7")
-    static let secondaryTextColor = UIColor.themeColor(lightHex: "848ba7", darkHex: "a5abc0")
-    static let inputFieldColor =  UIColor.themeColor(light: UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1),
-                                                     dark: UIColor(red: 10 / 255, green: 10 / 255, blue: 10 / 255, alpha: 1))
-    static let placeholderColor = UIColor.themeColor(light: UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1),
-                                                     dark: UIColor(red: 0.4, green: 0.4, blue: 0.4, alpha: 1))
-    static let providerPreparationBackground = UIColor.themeColor(lightHex: "#fffdf7b2", darkHex: "##fffdf7b2")
-    static let providerBrokenBackground = UIColor.themeColor(light: SystemColor.red.uiColor, dark: SystemColor.red.uiColor)
-    static let systemMessageBackgroundColor = UIColor.themeColor(light: UIColor.rgb(red: 248, green: 248, blue: 248), dark: UIColor(white: 0.2, alpha: 0.5))
-    static let deaddropBackground = UIColor.themeColor(light: UIColor.init(hexString: "ebebec"), dark: UIColor.init(hexString: "1a1a1c"))
-}
-
-enum SystemColor {
-    case red
-    case orange
-    case yellow
-    case green
-    case tealBlue
-    case blue
-    case purple
-    case pink
-
-    var uiColor: UIColor {
-        switch self {
-        case .red:
-            return UIColor(red: 255 / 255, green: 59 / 255, blue: 48 / 255, alpha: 1)
-        case .orange:
-            return UIColor(red: 255 / 255, green: 149 / 255, blue: 0 / 255, alpha: 1)
-        case .yellow:
-            return UIColor(red: 255 / 255, green: 204 / 255, blue: 0 / 255, alpha: 1)
-        case .green:
-            return UIColor(red: 76 / 255, green: 217 / 255, blue: 100 / 255, alpha: 1)
-        case .tealBlue:
-            return UIColor(red: 90 / 255, green: 200 / 255, blue: 250 / 255, alpha: 1)
-        case .blue:
-            return UIColor(red: 0 / 255, green: 122 / 255, blue: 255 / 255, alpha: 1)
-        case .purple:
-            return UIColor(red: 88 / 255, green: 86 / 255, blue: 214 / 255, alpha: 1)
-        case .pink:
-            return UIColor(red: 255 / 255, green: 45 / 255, blue: 85 / 255, alpha: 1)
-        }
-    }
-}
-
-extension UIColor {
-    static func rgb(red: CGFloat, green: CGFloat, blue: CGFloat) -> UIColor {
-        return UIColor(red: red / 255, green: green / 255, blue: blue / 255, alpha: 1)
-    }
-
-    static var systemBlue: UIColor {
-        return UIButton(type: .system).tintColor
-    }
-}

+ 0 - 47
deltachat-ios/Helper/Utils.swift

@@ -1,18 +1,9 @@
 import Foundation
 import Foundation
 import UIKit
 import UIKit
-import AVFoundation
 import DcCore
 import DcCore
 
 
 struct Utils {
 struct Utils {
 
 
-    static func getInitials(inputName: String) -> String {
-        if let firstLetter = inputName.first {
-            return firstLetter.uppercased()
-        } else {
-            return ""
-        }
-    }
-
     static func isValid(email: String) -> Bool {
     static func isValid(email: String) -> Bool {
         let emailRegEx = "(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
         let emailRegEx = "(?:[a-z0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[a-z0-9!#$%\\&'*+/=?\\^_`{|}"
             + "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"
             + "~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\"
@@ -61,49 +52,11 @@ struct Utils {
         return addressParts.joined(separator: ", ")
         return addressParts.joined(separator: ", ")
     }
     }
 
 
-    // compression needs to be done before in UIImage.dcCompress()
-    static func saveImage(image: UIImage) -> String? {
-        guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask,
-            appropriateFor: nil, create: false) as NSURL else {
-            return nil
-        }
-
-        guard let data = image.isTransparent() ? image.pngData() : image.jpegData(compressionQuality: 1.0) else {
-            return nil
-        }
-
-        do {
-            let timestamp = Double(Date().timeIntervalSince1970)
-            let path = directory.appendingPathComponent("\(timestamp).jpg")
-            try data.write(to: path!)
-            return path?.relativePath
-        } catch {
-            logger.info(error.localizedDescription)
-            return nil
-        }
-    }
-
     static func hasAudioSuffix(url: URL) -> Bool {
     static func hasAudioSuffix(url: URL) -> Bool {
         ///TODO: add more file suffixes
         ///TODO: add more file suffixes
         return url.absoluteString.hasSuffix("wav")
         return url.absoluteString.hasSuffix("wav")
     }
     }
 
 
-    static func generateThumbnailFromVideo(url: URL?) -> UIImage? {
-        guard let url = url else {
-            return nil
-        }
-        do {
-            let asset = AVURLAsset(url: url)
-            let imageGenerator = AVAssetImageGenerator(asset: asset)
-            imageGenerator.appliesPreferredTrackTransform = true
-            let cgImage = try imageGenerator.copyCGImage(at: .zero, actualTime: nil)
-            return UIImage(cgImage: cgImage)
-        } catch {
-            print(error.localizedDescription)
-            return nil
-        }
-    }
-
     static func getDeviceLanguage() -> String? {
     static func getDeviceLanguage() -> String? {
         // some device languages have suffixes (like en-aus etc.) so we want to cut suffixes off
         // some device languages have suffixes (like en-aus etc.) so we want to cut suffixes off
         guard let lang = Locale.preferredLanguages.first?.split(separator: "-").first else {
         guard let lang = Locale.preferredLanguages.first?.split(separator: "-").first else {

+ 1 - 0
deltachat-ios/MessageKit/Views/Cells/InfoMessageCell.swift

@@ -1,4 +1,5 @@
 import UIKit
 import UIKit
+import DcCore
 
 
 open class InfoMessageCell: UICollectionViewCell {
 open class InfoMessageCell: UICollectionViewCell {
     let label = MessageLabel()
     let label = MessageLabel()

+ 1 - 0
deltachat-ios/View/ChatTitleView.swift

@@ -1,4 +1,5 @@
 import UIKit
 import UIKit
+import DcCore
 
 
 class ChatTitleView: UIView {
 class ChatTitleView: UIView {
 
 

+ 1 - 0
deltachat-ios/View/ContactDetailHeader.swift

@@ -1,4 +1,5 @@
 import UIKit
 import UIKit
+import DcCore
 
 
 class ContactDetailHeader: UIView {
 class ContactDetailHeader: UIView {
 
 

+ 1 - 0
deltachat-ios/View/ProviderInfoCell.swift

@@ -1,4 +1,5 @@
 import UIKit
 import UIKit
+import DcCore
 
 
 enum ProviderInfoStatus: Int {
 enum ProviderInfoStatus: Int {
     case preparation = 2
     case preparation = 2

+ 1 - 1
deltachat-ios/ViewModel/ContactCellViewModel.swift

@@ -45,7 +45,7 @@ class ContactCellViewModel: AvatarCellViewModel {
     }
     }
 
 
     var avartarTitle: String {
     var avartarTitle: String {
-        return Utils.getInitials(inputName: title)
+        return DcUtils.getInitials(inputName: title)
     }
     }
 
 
     var titleHighlightIndexes: [Int]
     var titleHighlightIndexes: [Int]