diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index df5604a3dd..4e9d8ec336 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10886,6 +10886,7 @@ Sorry for the inconvenience."; "Call.StatusWeakSignal" = "Weak network signal"; "Conversation.ContactAddContact" = "ADD"; +"Conversation.ContactAddContactLong" = "ADD CONTACT"; "Conversation.ContactMessage" = "MESSAGE"; "Chat.PlayOnceVideoMessageTooltip" = "This video message can only be played once."; diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index ff09574e62..f07f8cbee7 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -304,31 +304,35 @@ final class CameraOutput: NSObject { self.currentMode = mode self.lastSampleTimestamp = nil - let codecType: AVVideoCodecType - if case .roundVideo = mode { - codecType = .h264 - } else { - if hasHEVCHardwareEncoder { - codecType = .hevc - } else { - codecType = .h264 - } - } - - guard var videoSettings = self.videoOutput.recommendedVideoSettings(forVideoCodecType: codecType, assetWriterOutputFileType: .mp4) else { - return .complete() - } - - var dimensions: CGSize = CGSize(width: 1080, height: 1920) - if orientation == .landscapeLeft || orientation == .landscapeRight { - dimensions = CGSize(width: 1920, height: 1080) - } var orientation = orientation + let dimensions: CGSize + let videoSettings: [String: Any] if case .roundVideo = mode { - videoSettings[AVVideoWidthKey] = 400 - videoSettings[AVVideoHeightKey] = 400 - dimensions = CGSize(width: 400, height: 400) + dimensions = videoMessageDimensions.cgSize orientation = .landscapeRight + + let compressionProperties: [String: Any] = [ + AVVideoAverageBitRateKey: 1000 * 1000, + AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel, + AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC + ] + videoSettings = [ + AVVideoCodecKey: AVVideoCodecType.h264, + AVVideoCompressionPropertiesKey: compressionProperties, + AVVideoWidthKey: Int(dimensions.width), + AVVideoHeightKey: Int(dimensions.height) + ] + } else { + let codecType: AVVideoCodecType = hasHEVCHardwareEncoder ? .hevc : .h264 + if orientation == .landscapeLeft || orientation == .landscapeRight { + dimensions = CGSize(width: 1920, height: 1080) + } else { + dimensions = CGSize(width: 1080, height: 1920) + } + guard let settings = self.videoOutput.recommendedVideoSettings(forVideoCodecType: codecType, assetWriterOutputFileType: .mp4) else { + return .complete() + } + videoSettings = settings } let audioSettings = self.audioOutput.recommendedAudioSettingsForAssetWriter(writingTo: .mp4) ?? [:] @@ -514,10 +518,10 @@ final class CameraOutput: NSObject { let extensions = CMFormatDescriptionGetExtensions(formatDescription) as! [String: Any] var updatedExtensions = extensions - updatedExtensions["CVBytesPerRow"] = 400 * 4 + updatedExtensions["CVBytesPerRow"] = videoMessageDimensions.width * 4 var newFormatDescription: CMFormatDescription? - var status = CMVideoFormatDescriptionCreate(allocator: nil, codecType: mediaSubType, width: 400, height: 400, extensions: updatedExtensions as CFDictionary, formatDescriptionOut: &newFormatDescription) + var status = CMVideoFormatDescriptionCreate(allocator: nil, codecType: mediaSubType, width: videoMessageDimensions.width, height: videoMessageDimensions.height, extensions: updatedExtensions as CFDictionary, formatDescriptionOut: &newFormatDescription) guard status == noErr, let newFormatDescription else { return nil } diff --git a/submodules/Camera/Sources/CameraRoundVideoFilter.swift b/submodules/Camera/Sources/CameraRoundVideoFilter.swift index 83d8d9f1b6..6e0a4252fd 100644 --- a/submodules/Camera/Sources/CameraRoundVideoFilter.swift +++ b/submodules/Camera/Sources/CameraRoundVideoFilter.swift @@ -5,6 +5,9 @@ import CoreMedia import CoreVideo import Metal import Display +import TelegramCore + +let videoMessageDimensions = PixelDimensions(width: 400, height: 400) func allocateOutputBufferPool(with inputFormatDescription: CMFormatDescription, outputRetainedBufferCountHint: Int) -> ( outputBufferPool: CVPixelBufferPool?, @@ -114,8 +117,7 @@ class CameraRoundVideoFilter { } self.inputFormatDescription = formatDescription - let diameter: CGFloat = 400.0 - let circleImage = generateImage(CGSize(width: diameter, height: diameter), opaque: false, scale: 1.0, rotatedContext: { size, context in + let circleImage = generateImage(videoMessageDimensions.cgSize, opaque: false, scale: 1.0, rotatedContext: { size, context in let bounds = CGRect(origin: .zero, size: size) context.clear(bounds) context.setFillColor(UIColor.white.cgColor) @@ -158,7 +160,7 @@ class CameraRoundVideoFilter { var sourceImage = CIImage(cvImageBuffer: pixelBuffer) sourceImage = sourceImage.oriented(additional ? .leftMirrored : .right) - let scale = 400.0 / min(sourceImage.extent.width, sourceImage.extent.height) + let scale = CGFloat(videoMessageDimensions.width) / min(sourceImage.extent.width, sourceImage.extent.height) resizeFilter.setValue(sourceImage, forKey: kCIInputImageKey) resizeFilter.setValue(scale, forKey: kCIInputScaleKey) @@ -203,18 +205,14 @@ class CameraRoundVideoFilter { guard let finalImage else { return nil } - - if finalImage.extent.width != 400 { - print("wtf: \(finalImage)") - } - + var pbuf: CVPixelBuffer? CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, outputPixelBufferPool!, &pbuf) guard let outputPixelBuffer = pbuf else { return nil } - self.ciContext.render(finalImage, to: outputPixelBuffer, bounds: CGRect(origin: .zero, size: CGSize(width: 400, height: 400)), colorSpace: outputColorSpace) + self.ciContext.render(finalImage, to: outputPixelBuffer, bounds: CGRect(origin: .zero, size: videoMessageDimensions.cgSize), colorSpace: outputColorSpace) return outputPixelBuffer } diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index 5dcf593bd1..c0e84873b1 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -47,6 +47,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem { let title: String let titleColor: ItemListDisclosureItemTitleColor let titleFont: ItemListDisclosureItemTitleFont + let titleIcon: UIImage? let enabled: Bool let label: String let labelStyle: ItemListDisclosureLabelStyle @@ -59,7 +60,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem { public let tag: ItemListItemTag? public let shimmeringIndex: Int? - public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) { + public init(presentationData: ItemListPresentationData, icon: UIImage? = nil, context: AccountContext? = nil, iconPeer: EnginePeer? = nil, title: String, enabled: Bool = true, titleColor: ItemListDisclosureItemTitleColor = .primary, titleFont: ItemListDisclosureItemTitleFont = .regular, titleIcon: UIImage? = nil, label: String, labelStyle: ItemListDisclosureLabelStyle = .text, additionalDetailLabel: String? = nil, sectionId: ItemListSectionId, style: ItemListStyle, disclosureStyle: ItemListDisclosureStyle = .arrow, action: (() -> Void)?, clearHighlightAutomatically: Bool = true, tag: ItemListItemTag? = nil, shimmeringIndex: Int? = nil) { self.presentationData = presentationData self.icon = icon self.context = context @@ -67,6 +68,7 @@ public class ItemListDisclosureItem: ListViewItem, ItemListItem { self.title = title self.titleColor = titleColor self.titleFont = titleFont + self.titleIcon = titleIcon self.enabled = enabled self.labelStyle = labelStyle self.label = label @@ -138,6 +140,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { var avatarNode: AvatarNode? let iconNode: ASImageNode let titleNode: TextNode + let titleIconNode: ASImageNode public let labelNode: TextNode var additionalDetailLabelNode: TextNode? let arrowNode: ASImageNode @@ -184,6 +187,10 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { self.titleNode = TextNode() self.titleNode.isUserInteractionEnabled = false + self.titleIconNode = ASImageNode() + self.titleIconNode.displayWithoutProcessing = true + self.titleIconNode.displaysAsynchronously = false + self.labelNode = TextNode() self.labelNode.isUserInteractionEnabled = false @@ -626,6 +633,19 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { strongSelf.additionalDetailLabelNode = nil additionalDetailLabelNode.removeFromSupernode() } + + if let titleIcon = item.titleIcon { + if strongSelf.titleIconNode.supernode == nil { + strongSelf.addSubnode(strongSelf.titleIconNode) + } + + strongSelf.titleIconNode.image = titleIcon + strongSelf.titleIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 5.0, y: floor((layout.contentSize.height - titleIcon.size.height) / 2.0) - 1.0), size: titleIcon.size) + } else { + if strongSelf.titleIconNode.supernode != nil { + strongSelf.titleIconNode.removeFromSupernode() + } + } if case .textWithIcon = item.labelStyle { if let updatedLabelImage = updatedLabelImage { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index a100512a62..86651729cb 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -97,7 +97,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { case forwardPrivacy(PresentationTheme, String, String) case groupPrivacy(PresentationTheme, String, String) case voiceMessagePrivacy(PresentationTheme, String, String, Bool) - case messagePrivacy(Bool) + case messagePrivacy(PresentationTheme, Bool, Bool) case bioPrivacy(PresentationTheme, String, String) case selectivePrivacyInfo(PresentationTheme, String) case passcode(PresentationTheme, String, Bool, String) @@ -240,14 +240,14 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { } else { return false } - case let .voiceMessagePrivacy(lhsTheme, lhsText, lhsValue, lhsLocked): - if case let .voiceMessagePrivacy(rhsTheme, rhsText, rhsValue, rhsLocked) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsLocked == rhsLocked { + case let .voiceMessagePrivacy(lhsTheme, lhsText, lhsValue, lhsHasPremium): + if case let .voiceMessagePrivacy(rhsTheme, rhsText, rhsValue, rhsHasPremium) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsHasPremium == rhsHasPremium { return true } else { return false } - case let .messagePrivacy(value): - if case .messagePrivacy(value) = rhs { + case let .messagePrivacy(lhsTheme, lhsValue, lhsHasPremium): + if case let .messagePrivacy(rhsTheme, rhsValue, rhsHasPremium) = rhs, lhsTheme === rhsTheme, lhsValue == rhsValue, lhsHasPremium == rhsHasPremium { return true } else { return false @@ -390,12 +390,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, action: { arguments.openGroupsPrivacy() }) - case let .voiceMessagePrivacy(_, text, value, locked): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, labelStyle: locked ? .textWithIcon(UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon")!.precomposed()) : .text, sectionId: self.section, style: .blocks, action: { + case let .voiceMessagePrivacy(theme, text, value, hasPremium): + return ItemListDisclosureItem(presentationData: presentationData, title: text, titleIcon: hasPremium ? PresentationResourcesItemList.premiumIcon(theme) : nil, label: value, labelStyle: !hasPremium ? .textWithIcon(UIImage(bundleImageName: "Chat/Input/Accessory Panels/TextLockIcon")!.precomposed()) : .text, sectionId: self.section, style: .blocks, action: { arguments.openVoiceMessagePrivacy() }) - case let .messagePrivacy(value): - return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.Settings_Privacy_Messages, label: !value ? presentationData.strings.Settings_Privacy_Messages_ValueEveryone : presentationData.strings.Settings_Privacy_Messages_ValueContactsAndPremium, sectionId: self.section, style: .blocks, action: { + case let .messagePrivacy(theme, value, hasPremium): + return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.Settings_Privacy_Messages, titleIcon: hasPremium ? PresentationResourcesItemList.premiumIcon(theme) : nil, label: !value ? presentationData.strings.Settings_Privacy_Messages_ValueEveryone : presentationData.strings.Settings_Privacy_Messages_ValueContactsAndPremium, sectionId: self.section, style: .blocks, action: { arguments.openMessagePrivacy() }) case let .bioPrivacy(_, text, value): @@ -591,8 +591,8 @@ private func privacyAndSecurityControllerEntries( entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceCalls))) entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.groupInvitations))) if !isPremiumDisabled { - entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceMessages), !isPremium)) - entries.append(.messagePrivacy(privacySettings.globalSettings.nonContactChatsRequirePremium)) + entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.voiceMessages), isPremium)) + entries.append(.messagePrivacy(presentationData.theme, privacySettings.globalSettings.nonContactChatsRequirePremium, isPremium)) } } else { entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, presentationData.strings.Channel_NotificationLoading)) @@ -603,7 +603,7 @@ private func privacyAndSecurityControllerEntries( entries.append(.voiceCallPrivacy(presentationData.theme, presentationData.strings.Privacy_Calls, presentationData.strings.Channel_NotificationLoading)) entries.append(.groupPrivacy(presentationData.theme, presentationData.strings.Privacy_GroupsAndChannels, presentationData.strings.Channel_NotificationLoading)) if !isPremiumDisabled { - entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, presentationData.strings.Channel_NotificationLoading, !isPremium)) + entries.append(.voiceMessagePrivacy(presentationData.theme, presentationData.strings.Privacy_VoiceMessages, presentationData.strings.Channel_NotificationLoading, isPremium)) } //entries.append(.selectivePrivacyInfo(presentationData.theme, presentationData.strings.PrivacyLastSeenSettings_GroupsAndChannelsHelp)) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 588fc81f74..82cb5b1475 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -72,6 +72,7 @@ public enum PresentationResourceKey: Int32 { case itemListCloudIcon case itemListTopicArrowIcon case itemListAddBoostsIcon + case itemListPremiumIcon case statsReactionsIcon case statsForwardsIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index c25c954ed6..67873fb57e 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -282,6 +282,30 @@ public struct PresentationResourcesItemList { }) } + public static func premiumIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.itemListPremiumIcon.rawValue, { theme in + return generateImage(CGSize(width: 16.0, height: 16.0), contextGenerator: { size, context in + let bounds = CGRect(origin: .zero, size: size) + context.clear(bounds) + + let image = UIImage(bundleImageName: "Item List/PremiumIcon")! + context.clip(to: bounds, mask: image.cgImage!) + + let colorsArray: [CGColor] = [ + UIColor(rgb: 0x6b93ff).cgColor, + UIColor(rgb: 0x6b93ff).cgColor, + UIColor(rgb: 0x8d77ff).cgColor, + UIColor(rgb: 0xb56eec).cgColor, + UIColor(rgb: 0xb56eec).cgColor + ] + var locations: [CGFloat] = [0.0, 0.3, 0.5, 0.7, 1.0] + let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions()) + }) + }) + } + public static func cornersImage(_ theme: PresentationTheme, top: Bool, bottom: Bool) -> UIImage? { if !top && !bottom { return nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index 3ae837295c..2007ed9f71 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -307,7 +307,18 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } let (messageButtonWidth, messageContinueLayout) = makeMessageButtonLayout(constrainedSize.width, nil, false, item.presentationData.strings.Conversation_ContactMessage.uppercased(), mainColor, false, false) - let (addButtonWidth, addContinueLayout) = makeAddButtonLayout(constrainedSize.width, nil, false, !canMessage && !canAdd ? item.presentationData.strings.Conversation_ViewContactDetails.uppercased() : item.presentationData.strings.Conversation_ContactAddContact.uppercased(), mainColor, false, false) + + let addTitle: String + if !canMessage && !canAdd { + addTitle = item.presentationData.strings.Conversation_ViewContactDetails + } else { + if canMessage { + addTitle = item.presentationData.strings.Conversation_ContactAddContact + } else { + addTitle = item.presentationData.strings.Conversation_ContactAddContactLong + } + } + let (addButtonWidth, addContinueLayout) = makeAddButtonLayout(constrainedSize.width, nil, false, addTitle.uppercased(), mainColor, false, false) let maxButtonWidth = max(messageButtonWidth, addButtonWidth) var maxContentWidth: CGFloat = avatarSize.width + 7.0 @@ -327,7 +338,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { let lineWidth: CGFloat = 3.0 var buttonCount = 1 - if canMessage { + if canMessage && canAdd { buttonCount += 1 } var buttonWidth = floor((boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0 - lineWidth)) @@ -387,7 +398,7 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.messageButtonNode.isHidden = !canMessage let backgroundInsets = layoutConstants.text.bubbleInsets - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: contentWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: layoutSize.height - 34.0)) + let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top + 5.0), size: CGSize(width: boundingWidth - layoutConstants.text.bubbleInsets.right * 2.0, height: layoutSize.height - 34.0)) if let statusSizeAndApply = statusSizeAndApply { strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: layoutConstants.text.bubbleInsets.left, y: backgroundFrame.maxY + 3.0), size: statusSizeAndApply.0) diff --git a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift index 2f5cfb86f1..fe945eb091 100644 --- a/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift +++ b/submodules/TelegramUI/Components/VideoMessageCameraScreen/Sources/VideoMessageCameraScreen.swift @@ -532,7 +532,7 @@ public class VideoMessageCameraScreen: ViewController { fileprivate var liveUploadInterface: LegacyLiveUploadInterface? private var currentLiveUploadPath: String? fileprivate var currentLiveUploadData: LegacyLiveUploadInterfaceResult? - + fileprivate let backgroundView: UIVisualEffectView fileprivate let containerView: UIView fileprivate let componentHost: ComponentView @@ -689,16 +689,27 @@ public class VideoMessageCameraScreen: ViewController { } func withReadyCamera(isFirstTime: Bool = false, _ f: @escaping () -> Void) { + guard let controller = self.controller else { + return + } if #available(iOS 13.0, *) { - let _ = ((self.cameraState.isDualCameraEnabled ? self.additionalPreviewView.isPreviewing : self.mainPreviewView.isPreviewing) - |> filter { $0 } - |> take(1)).startStandalone(next: { _ in + let _ = (combineLatest(queue: Queue.mainQueue(), + self.cameraState.isDualCameraEnabled ? self.additionalPreviewView.isPreviewing : self.mainPreviewView.isPreviewing, + controller.audioSessionReady.get() + ) + |> filter { $0 && $1 } + |> take(1)).startStandalone(next: { _, _ in f() }) } else { - Queue.mainQueue().after(0.35) { + let _ = (combineLatest(queue: Queue.mainQueue(), + .single(true) |> delay(0.35, queue: Queue.mainQueue()), + controller.audioSessionReady.get() + ) + |> filter { $0 && $1 } + |> take(1)).startStandalone(next: { _, _ in f() - } + }) } } @@ -1241,6 +1252,7 @@ public class VideoMessageCameraScreen: ViewController { fileprivate let completion: (EnqueueMessage?, Bool?, Int32?) -> Void private var audioSessionDisposable: Disposable? + fileprivate let audioSessionReady = ValuePromise(false) private let hapticFeedback = HapticFeedback() @@ -1484,11 +1496,13 @@ public class VideoMessageCameraScreen: ViewController { finalDuration = duration } + let dimensions = PixelDimensions(width: 400, height: 400) + var thumbnailImage = video.thumbnail if startTime > 0.0 { let composition = composition(with: results) let imageGenerator = AVAssetImageGenerator(asset: composition) - imageGenerator.maximumSize = CGSize(width: 400, height: 400) + imageGenerator.maximumSize = dimensions.cgSize imageGenerator.appliesPreferredTrackTransform = true if let cgImage = try? imageGenerator.copyCGImage(at: CMTime(seconds: startTime, preferredTimescale: composition.duration.timescale), actualTime: nil) { @@ -1496,7 +1510,7 @@ public class VideoMessageCameraScreen: ViewController { } } - let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: PixelDimensions(width: 400, height: 400), cropOffset: .zero, cropRect: CGRect(origin: .zero, size: CGSize(width: 400.0, height: 400.0)), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage) + let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage) var resourceAdjustments: VideoMediaResourceAdjustments? = nil if let valuesData = try? JSONEncoder().encode(values) { @@ -1614,10 +1628,13 @@ public class VideoMessageCameraScreen: ViewController { } private func requestAudioSession() { - self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { _ in + self.audioSessionDisposable = self.context.sharedContext.mediaManager.audioSession.push(audioSessionType: .recordWithOthers, activate: { [weak self] _ in if #available(iOS 13.0, *) { try? AVAudioSession.sharedInstance().setAllowHapticsAndSystemSoundsDuringRecording(true) } + if let self { + self.audioSessionReady.set(true) + } }, deactivate: { _ in return .single(Void()) }) diff --git a/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/Contents.json new file mode 100644 index 0000000000..f34bc3513d --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "premiumstar_16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/premiumstar_16.pdf b/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/premiumstar_16.pdf new file mode 100644 index 0000000000..117a2e4bfb --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/PremiumIcon.imageset/premiumstar_16.pdf @@ -0,0 +1,97 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.494995 cm +0.000000 0.000000 0.000000 scn +7.573102 2.542415 m +3.972436 0.336632 l +3.598035 0.107271 3.108589 0.224851 2.879229 0.599252 c +2.767209 0.782110 2.733811 1.002467 2.786615 1.210307 c +3.343997 3.404178 l +3.545203 4.196128 4.087134 4.858135 4.823744 5.211795 c +8.751891 7.097767 l +8.935022 7.185692 9.012203 7.405426 8.924278 7.588558 c +8.853073 7.736866 8.692046 7.819856 8.529942 7.791792 c +4.157411 7.034797 l +3.268577 6.880917 2.357086 7.126360 1.665707 7.705756 c +0.284388 8.863339 l +-0.052136 9.145357 -0.096324 9.646784 0.185694 9.983309 c +0.322857 10.146982 0.520100 10.248593 0.732998 10.265255 c +4.953338 10.595538 l +5.251494 10.618872 5.511330 10.807558 5.625789 11.083856 c +7.253917 15.014055 l +7.421958 15.419697 7.887019 15.612309 8.292661 15.444268 c +8.487435 15.363581 8.642185 15.208831 8.722873 15.014055 c +10.351001 11.083856 l +10.465460 10.807558 10.725295 10.618872 11.023451 10.595538 c +15.266980 10.263440 l +15.704712 10.229183 16.031794 9.846561 15.997537 9.408829 c +15.981057 9.198256 15.881458 9.002894 15.720723 8.865864 c +12.484364 6.106792 l +12.256535 5.912563 12.157140 5.606812 12.227205 5.315742 c +13.222160 1.182478 l +13.324918 0.755602 13.062167 0.326249 12.635291 0.223492 c +12.430174 0.174116 12.213841 0.208297 12.033939 0.318506 c +8.403688 2.542415 l +8.148836 2.698538 7.827954 2.698538 7.573102 2.542415 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1440 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001530 00000 n +0000001553 00000 n +0000001726 00000 n +0000001800 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1859 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift index 2e31756982..8a6c84f630 100644 --- a/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecordingPreviewInputPanelNode.swift @@ -350,11 +350,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode { } } } - - if isFirstTime, !self.viewOnceButton.isHidden { - self.maybePresentViewOnceTooltip() - } - + let panelHeight = defaultHeight(metrics: metrics) transition.updateFrame(node: self.deleteButton, frame: CGRect(origin: CGPoint(x: leftInset + 2.0 - UIScreenPixel, y: 1), size: CGSize(width: 40.0, height: 40))) @@ -488,6 +484,10 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode { } } + if isFirstTime, !self.viewOnceButton.isHidden { + self.maybePresentViewOnceTooltip() + } + return panelHeight } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 361bed480d..5353e783f2 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -126,6 +126,29 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { } if !pathComponents.isEmpty && !pathComponents[0].isEmpty { let peerName: String = pathComponents[0] + + if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") { + let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+") + if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil { + var attach: String? + var startAttach: String? + if let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "attach" { + attach = value + } else if queryItem.name == "startattach" { + startAttach = value + } + } + } + } + + return .phone(component.replacingOccurrences(of: "+", with: ""), attach, startAttach) + } else { + return .join(String(component.dropFirst())) + } + } if pathComponents.count == 1 { if let queryItems = components.queryItems { if peerName == "socks" || peerName == "proxy" { @@ -288,27 +311,6 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { } } else if pathComponents[0].hasPrefix(phonebookUsernamePathPrefix), let idValue = Int64(String(pathComponents[0][pathComponents[0].index(pathComponents[0].startIndex, offsetBy: phonebookUsernamePathPrefix.count)...])) { return .peerId(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(idValue))) - } else if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") { - let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+") - if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil { - var attach: String? - var startAttach: String? - if let queryItems = components.queryItems { - for queryItem in queryItems { - if let value = queryItem.value { - if queryItem.name == "attach" { - attach = value - } else if queryItem.name == "startattach" { - startAttach = value - } - } - } - } - - return .phone(component.replacingOccurrences(of: "+", with: ""), attach, startAttach) - } else { - return .join(String(component.dropFirst())) - } } else if pathComponents[0].hasPrefix("$") || pathComponents[0].hasPrefix("%24") { var component = pathComponents[0].replacingOccurrences(of: "%24", with: "$") if component.hasPrefix("$") {