diff --git a/Telegram/LegacyWidget/Info.plist b/Telegram/LegacyWidget/Info.plist new file mode 100644 index 0000000000..6917b6ac76 --- /dev/null +++ b/Telegram/LegacyWidget/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ${APP_NAME} + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + $(PRODUCT_BUNDLE_SHORT_VERSION) + CFBundleVersion + ${BUILD_NUMBER} + NSExtension + + NSExtensionPointIdentifier + com.apple.widget-extension + NSExtensionPrincipalClass + TodayViewController + + + diff --git a/Telegram/LegacyWidget/PeerNode.swift b/Telegram/LegacyWidget/PeerNode.swift new file mode 100644 index 0000000000..b89deba9e6 --- /dev/null +++ b/Telegram/LegacyWidget/PeerNode.swift @@ -0,0 +1,172 @@ +import Foundation +import UIKit +import WidgetItems + +private extension UIColor { + convenience init(rgb: UInt32) { + self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: 1.0) + } +} + +private let UIScreenScale = UIScreen.main.scale +private func floorToScreenPixels(_ value: CGFloat) -> CGFloat { + return floor(value * UIScreenScale) / UIScreenScale +} + +private let gradientColors: [NSArray] = [ + [UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor], + [UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor], + [UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor], + [UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor], + [UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor], + [UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor], + [UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor], +] + +private func avatarRoundImage(size: CGSize, source: UIImage) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + let context = UIGraphicsGetCurrentContext() + + context?.beginPath() + context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) + context?.clip() + + source.draw(in: CGRect(origin: CGPoint(), size: size)) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image +} + +private let deviceColorSpace: CGColorSpace = { + if #available(iOSApplicationExtension 9.3, *) { + if let colorSpace = CGColorSpace(name: CGColorSpace.displayP3) { + return colorSpace + } else { + return CGColorSpaceCreateDeviceRGB() + } + } else { + return CGColorSpaceCreateDeviceRGB() + } +}() + +private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId: Int64, letters: [String]) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + let context = UIGraphicsGetCurrentContext() + + context?.beginPath() + context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) + context?.clip() + + let colorIndex = abs(Int(accountPeerId + peerId)) + + let colorsArray = gradientColors[colorIndex % gradientColors.count] + var locations: [CGFloat] = [1.0, 0.0] + let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)! + + context?.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) + + context?.setBlendMode(.normal) + + let string = letters.count == 0 ? "" : (letters[0] + (letters.count == 1 ? "" : letters[1])) + let attributedString = NSAttributedString(string: string, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20.0), NSAttributedString.Key.foregroundColor: UIColor.white]) + + let line = CTLineCreateWithAttributedString(attributedString) + let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds) + + let lineOffset = CGPoint(x: string == "B" ? 1.0 : 0.0, y: 0.0) + let lineOrigin = CGPoint(x: floorToScreenPixels(-lineBounds.origin.x + (size.width - lineBounds.size.width) / 2.0) + lineOffset.x, y: floorToScreenPixels(-lineBounds.origin.y + (size.height - lineBounds.size.height) / 2.0)) + + context?.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context?.scaleBy(x: 1.0, y: -1.0) + context?.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + + context?.translateBy(x: lineOrigin.x, y: lineOrigin.y) + if let context = context { + CTLineDraw(line, context) + } + context?.translateBy(x: -lineOrigin.x, y: -lineOrigin.y) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image +} + +private let avatarSize = CGSize(width: 50.0, height: 50.0) + +private final class AvatarView: UIImageView { + init(accountPeerId: Int64, peer: WidgetDataPeer, size: CGSize) { + super.init(frame: CGRect()) + + if let path = peer.avatarPath, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) { + self.image = roundImage + } else { + self.image = avatarViewLettersImage(size: size, peerId: peer.id, accountPeerId: accountPeerId, letters: peer.letters) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +final class PeerView: UIView { + let peer: WidgetDataPeer + private let avatarView: AvatarView + private let titleLabel: UILabel + + private let tapped: () -> Void + + init(primaryColor: UIColor, accountPeerId: Int64, peer: WidgetDataPeer, tapped: @escaping () -> Void) { + self.peer = peer + self.tapped = tapped + self.avatarView = AvatarView(accountPeerId: accountPeerId, peer: peer, size: avatarSize) + + self.titleLabel = UILabel() + var title = peer.name + if let lastName = peer.lastName, !lastName.isEmpty { + title.append("\n") + title.append(lastName) + } + + let systemFontSize = UIFont.preferredFont(forTextStyle: .body).pointSize + let fontSize = floor(systemFontSize * 11.0 / 17.0) + + self.titleLabel.text = title + if #available(iOSApplicationExtension 13.0, *) { + self.titleLabel.textColor = UIColor.label + } else { + self.titleLabel.textColor = primaryColor + } + self.titleLabel.font = UIFont.systemFont(ofSize: fontSize) + self.titleLabel.lineBreakMode = .byTruncatingTail + self.titleLabel.numberOfLines = 2 + self.titleLabel.textAlignment = .center + + super.init(frame: CGRect()) + + self.addSubview(self.avatarView) + self.addSubview(self.titleLabel) + + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func updateLayout(size: CGSize) { + self.avatarView.frame = CGRect(origin: CGPoint(x: floor((size.width - avatarSize.width) / 2.0), y: 0.0), size: avatarSize) + + var titleSize = self.titleLabel.sizeThatFits(size) + titleSize.width = min(size.width - 6.0, ceil(titleSize.width)) + titleSize.height = ceil(titleSize.height) + self.titleLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: avatarSize.height + 5.0), size: titleSize) + } + + @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.tapped() + } + } +} diff --git a/Telegram/LegacyWidget/TodayViewController.swift b/Telegram/LegacyWidget/TodayViewController.swift new file mode 100644 index 0000000000..f99f7903ce --- /dev/null +++ b/Telegram/LegacyWidget/TodayViewController.swift @@ -0,0 +1,171 @@ +import UIKit +import NotificationCenter +import BuildConfig +import WidgetItems +import AppLockState + +private func rootPathForBasePath(_ appGroupPath: String) -> String { + return appGroupPath + "/telegram-data" +} + +@objc(TodayViewController) +class TodayViewController: UIViewController, NCWidgetProviding { + private var initializedInterface = false + + private var buildConfig: BuildConfig? + + private var primaryColor: UIColor = .black + private var placeholderLabel: UILabel? + + override func viewDidLoad() { + super.viewDidLoad() + + let appBundleIdentifier = Bundle.main.bundleIdentifier! + guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { + return + } + let baseAppBundleId = String(appBundleIdentifier[.. Void)) { + completionHandler(.newData) + } + + @available(iOSApplicationExtension 10.0, *) + func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) { + + } + + private var widgetData: WidgetData? + + private func setWidgetData(widgetData: WidgetData, presentationData: WidgetPresentationData) { + self.widgetData = widgetData + self.peerViews.forEach { + $0.removeFromSuperview() + } + self.peerViews = [] + switch widgetData { + case .notAuthorized, .disabled: + break + case let .peers(peers): + for peer in peers.peers { + let peerView = PeerView(primaryColor: self.primaryColor, accountPeerId: peers.accountPeerId, peer: peer, tapped: { [weak self] in + if let strongSelf = self, let buildConfig = strongSelf.buildConfig { + if let url = URL(string: "\(buildConfig.appSpecificUrlScheme)://localpeer?id=\(peer.id)") { + strongSelf.extensionContext?.open(url, completionHandler: nil) + } + } + }) + self.view.addSubview(peerView) + self.peerViews.append(peerView) + } + } + + if self.peerViews.isEmpty { + self.setPlaceholderText(presentationData.applicationStartRequiredString) + } else { + self.placeholderLabel?.removeFromSuperview() + self.placeholderLabel = nil + } + + if let size = self.validLayout { + self.updateLayout(size: size) + } + } + + private var validLayout: CGSize? + + private var peerViews: [PeerView] = [] + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + self.updateLayout(size: self.view.bounds.size) + } + + private func updateLayout(size: CGSize) { + self.validLayout = size + + if let placeholderLabel = self.placeholderLabel { + placeholderLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - placeholderLabel.bounds.width) / 2.0), y: floor((size.height - placeholderLabel.bounds.height) / 2.0)), size: placeholderLabel.bounds.size) + } + + let peerSize = CGSize(width: 70.0, height: 100.0) + + var peerFrames: [CGRect] = [] + + var offset: CGFloat = 0.0 + for _ in self.peerViews { + let peerFrame = CGRect(origin: CGPoint(x: offset, y: 10.0), size: peerSize) + offset += peerFrame.size.width + if peerFrame.maxX > size.width { + break + } + peerFrames.append(peerFrame) + } + + var totalSize: CGFloat = 0.0 + for i in 0 ..< peerFrames.count { + totalSize += peerFrames[i].width + } + + let spacing: CGFloat = floor((size.width - totalSize) / CGFloat(peerFrames.count)) + offset = floor(spacing / 2.0) + for i in 0 ..< peerFrames.count { + let peerView = self.peerViews[i] + peerView.frame = CGRect(origin: CGPoint(x: offset, y: 16.0), size: peerFrames[i].size) + peerView.updateLayout(size: peerFrames[i].size) + offset += peerFrames[i].width + spacing + } + } +} diff --git a/Telegram/LegacyWidget/Widget-AppStore.entitlements b/Telegram/LegacyWidget/Widget-AppStore.entitlements new file mode 100644 index 0000000000..5e963c4f0f --- /dev/null +++ b/Telegram/LegacyWidget/Widget-AppStore.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.telegram.TelegramHD + + + diff --git a/Telegram/LegacyWidget/Widget-AppStoreLLC.entitlements b/Telegram/LegacyWidget/Widget-AppStoreLLC.entitlements new file mode 100644 index 0000000000..c9a9054223 --- /dev/null +++ b/Telegram/LegacyWidget/Widget-AppStoreLLC.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.ph.telegra.Telegraph + + + diff --git a/Telegram/LegacyWidget/Widget-Bridging-Header.h b/Telegram/LegacyWidget/Widget-Bridging-Header.h new file mode 100644 index 0000000000..16747def3f --- /dev/null +++ b/Telegram/LegacyWidget/Widget-Bridging-Header.h @@ -0,0 +1,4 @@ +#ifndef Widget_Bridging_Header_h +#define Widget_Bridging_Header_h + +#endif diff --git a/Telegram/LegacyWidget/Widget-Fork.entitlements b/Telegram/LegacyWidget/Widget-Fork.entitlements new file mode 100644 index 0000000000..eb39a047f1 --- /dev/null +++ b/Telegram/LegacyWidget/Widget-Fork.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.fork.telegram.Telegram-iOS + + + diff --git a/Telegram/LegacyWidget/Widget-HockeyApp.entitlements b/Telegram/LegacyWidget/Widget-HockeyApp.entitlements new file mode 100644 index 0000000000..65f2a19d32 --- /dev/null +++ b/Telegram/LegacyWidget/Widget-HockeyApp.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.telegram.Telegram-iOS + + + diff --git a/Telegram/LegacyWidget/ar.lproj/InfoPlist.strings b/Telegram/LegacyWidget/ar.lproj/InfoPlist.strings new file mode 100644 index 0000000000..07394dd6c9 --- /dev/null +++ b/Telegram/LegacyWidget/ar.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "الأشخاص"; diff --git a/Telegram/LegacyWidget/de.lproj/InfoPlist.strings b/Telegram/LegacyWidget/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..1ad24433c2 --- /dev/null +++ b/Telegram/LegacyWidget/de.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Leute"; diff --git a/Telegram/LegacyWidget/en.lproj/InfoPlist.strings b/Telegram/LegacyWidget/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..b1ddd1ac39 --- /dev/null +++ b/Telegram/LegacyWidget/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "People"; diff --git a/Telegram/LegacyWidget/en.lproj/Localizable.strings b/Telegram/LegacyWidget/en.lproj/Localizable.strings new file mode 100644 index 0000000000..c90696d0fb --- /dev/null +++ b/Telegram/LegacyWidget/en.lproj/Localizable.strings @@ -0,0 +1,2 @@ +"Widget.NoUsers" = "No users here yet..."; +"Widget.AuthRequired" = "Open Telegram and log in."; diff --git a/Telegram/LegacyWidget/es.lproj/InfoPlist.strings b/Telegram/LegacyWidget/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..3d5094963a --- /dev/null +++ b/Telegram/LegacyWidget/es.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Personas"; diff --git a/Telegram/LegacyWidget/it.lproj/InfoPlist.strings b/Telegram/LegacyWidget/it.lproj/InfoPlist.strings new file mode 100644 index 0000000000..f118d25a4d --- /dev/null +++ b/Telegram/LegacyWidget/it.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Persone"; diff --git a/Telegram/LegacyWidget/ko.lproj/InfoPlist.strings b/Telegram/LegacyWidget/ko.lproj/InfoPlist.strings new file mode 100644 index 0000000000..e1bc831c53 --- /dev/null +++ b/Telegram/LegacyWidget/ko.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "사람"; diff --git a/Telegram/LegacyWidget/nl.lproj/InfoPlist.strings b/Telegram/LegacyWidget/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..a23cbfc4a2 --- /dev/null +++ b/Telegram/LegacyWidget/nl.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Mensen"; diff --git a/Telegram/LegacyWidget/pt.lproj/InfoPlist.strings b/Telegram/LegacyWidget/pt.lproj/InfoPlist.strings new file mode 100644 index 0000000000..a6c032d0ed --- /dev/null +++ b/Telegram/LegacyWidget/pt.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Pessoas"; diff --git a/Telegram/LegacyWidget/ru.lproj/InfoPlist.strings b/Telegram/LegacyWidget/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000000..689e714f47 --- /dev/null +++ b/Telegram/LegacyWidget/ru.lproj/InfoPlist.strings @@ -0,0 +1 @@ +"CFBundleDisplayName" = "Люди"; diff --git a/Telegram/NotificationServiceNext/NotificationServiceNext.swift b/Telegram/NotificationServiceNext/NotificationServiceNext.swift deleted file mode 100644 index cbf5c7df64..0000000000 --- a/Telegram/NotificationServiceNext/NotificationServiceNext.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation -import UserNotifications - -@available(iOSApplicationExtension 10.0, *) -@objc(NotificationService) -public final class NotificationService: UNNotificationServiceExtension { - -}