mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
e6e7d52ebf
commit
dc0d4d25bf
@ -899,7 +899,9 @@ public protocol SharedAccountContext: AnyObject {
|
||||
|
||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], loadedStickerPacks: [LoadedStickerPack], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||
|
||||
func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
func makeMediaPickerScreen(context: AccountContext, completion: @escaping (Any) -> Void) -> ViewController
|
||||
|
||||
func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
|
||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import SwiftSignalKit
|
||||
import AVFoundation
|
||||
import CoreImage
|
||||
import TelegramCore
|
||||
|
||||
final class CameraSession {
|
||||
private let singleSession: AVCaptureSession?
|
||||
@ -83,10 +84,8 @@ final class CameraDeviceContext {
|
||||
private var preferredMaxDimensions: CMVideoDimensions {
|
||||
if self.additional {
|
||||
//if case .iPhoneXS = DeviceModel.current {
|
||||
return CMVideoDimensions(width: 1440, height: 1080)
|
||||
//} else {
|
||||
// return CMVideoDimensions(width: 1920, height: 1440)
|
||||
//}
|
||||
// return CMVideoDimensions(width: 1440, height: 1080)
|
||||
return CMVideoDimensions(width: 1920, height: 1440)
|
||||
} else {
|
||||
return CMVideoDimensions(width: 1920, height: 1080)
|
||||
}
|
||||
@ -198,13 +197,22 @@ private final class CameraContext {
|
||||
self.mainDeviceContext.output.processCodes = { [weak self] codes in
|
||||
self?.detectedCodesPipe.putNext(codes)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(self.sessionRuntimeError),
|
||||
name: .AVCaptureSessionRuntimeError,
|
||||
object: self.session.session
|
||||
)
|
||||
}
|
||||
|
||||
private var isSessionRunning = false
|
||||
func startCapture() {
|
||||
guard !self.session.session.isRunning else {
|
||||
return
|
||||
}
|
||||
self.session.session.startRunning()
|
||||
self.isSessionRunning = self.session.session.isRunning
|
||||
}
|
||||
|
||||
func stopCapture(invalidate: Bool = false) {
|
||||
@ -516,6 +524,27 @@ private final class CameraContext {
|
||||
var detectedCodes: Signal<[CameraCode], NoError> {
|
||||
return self.detectedCodesPipe.signal()
|
||||
}
|
||||
|
||||
@objc private func sessionInterruptionEnded(notification: NSNotification) {
|
||||
}
|
||||
|
||||
@objc private func sessionRuntimeError(notification: NSNotification) {
|
||||
guard let errorValue = notification.userInfo?[AVCaptureSessionErrorKey] as? NSError else {
|
||||
return
|
||||
}
|
||||
|
||||
let error = AVError(_nsError: errorValue)
|
||||
Logger.shared.log("Camera", "Runtime error: \(error)")
|
||||
|
||||
if error.code == .mediaServicesWereReset {
|
||||
self.queue.async {
|
||||
if self.isSessionRunning {
|
||||
self.session.session.startRunning()
|
||||
self.isSessionRunning = self.session.session.isRunning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class Camera {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import AVFoundation
|
||||
import TelegramCore
|
||||
|
||||
class CameraInput {
|
||||
var videoInput: AVCaptureDeviceInput?
|
||||
@ -32,6 +33,8 @@ class CameraInput {
|
||||
} else {
|
||||
session.session.addInput(videoInput)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add video input")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,6 +48,8 @@ class CameraInput {
|
||||
self.audioInput = audioInput
|
||||
if session.session.canAddInput(audioInput) {
|
||||
session.session.addInput(audioInput)
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add audio input")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import SwiftSignalKit
|
||||
import CoreImage
|
||||
import Vision
|
||||
import VideoToolbox
|
||||
import TelegramCore
|
||||
|
||||
public enum VideoCaptureResult: Equatable {
|
||||
case finished((String, UIImage, Bool), (String, UIImage, Bool)?, Double, [(Bool, Double)], Double)
|
||||
@ -124,6 +125,8 @@ final class CameraOutput: NSObject {
|
||||
session.session.addOutput(self.videoOutput)
|
||||
}
|
||||
self.videoOutput.setSampleBufferDelegate(self, queue: self.queue)
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add video output")
|
||||
}
|
||||
if audio, session.session.canAddOutput(self.audioOutput) {
|
||||
session.session.addOutput(self.audioOutput)
|
||||
@ -135,6 +138,8 @@ final class CameraOutput: NSObject {
|
||||
} else {
|
||||
session.session.addOutput(self.photoOutput)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add photo output")
|
||||
}
|
||||
if metadata, session.session.canAddOutput(self.metadataOutput) {
|
||||
session.session.addOutput(self.metadataOutput)
|
||||
@ -152,6 +157,8 @@ final class CameraOutput: NSObject {
|
||||
if session.session.canAddConnection(previewConnection) {
|
||||
session.session.addConnection(previewConnection)
|
||||
self.previewConnection = previewConnection
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add preview connection")
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,6 +166,8 @@ final class CameraOutput: NSObject {
|
||||
if session.session.canAddConnection(videoConnection) {
|
||||
session.session.addConnection(videoConnection)
|
||||
self.videoConnection = videoConnection
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't add video connection")
|
||||
}
|
||||
|
||||
if photo {
|
||||
@ -168,6 +177,8 @@ final class CameraOutput: NSObject {
|
||||
self.photoConnection = photoConnection
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Camera", "Can't get video port")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
375
submodules/DrawingUI/Sources/DrawingEntitySnapTool.swift
Normal file
375
submodules/DrawingUI/Sources/DrawingEntitySnapTool.swift
Normal file
@ -0,0 +1,375 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
private let snapTimeout = 1.0
|
||||
|
||||
class DrawingEntitySnapTool {
|
||||
enum SnapType {
|
||||
case centerX
|
||||
case centerY
|
||||
case top
|
||||
case left
|
||||
case right
|
||||
case bottom
|
||||
case rotation(CGFloat?)
|
||||
|
||||
static var allPositionTypes: [SnapType] {
|
||||
return [
|
||||
.centerX,
|
||||
.centerY,
|
||||
.top,
|
||||
.left,
|
||||
.right,
|
||||
.bottom
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct SnapState {
|
||||
let skipped: CGFloat
|
||||
let waitForLeave: Bool
|
||||
}
|
||||
|
||||
private var topEdgeState: SnapState?
|
||||
private var leftEdgeState: SnapState?
|
||||
private var rightEdgeState: SnapState?
|
||||
private var bottomEdgeState: SnapState?
|
||||
|
||||
private var xState: SnapState?
|
||||
private var yState: SnapState?
|
||||
|
||||
private var rotationState: (angle: CGFloat, skipped: CGFloat, waitForLeave: Bool)?
|
||||
|
||||
var onSnapUpdated: (SnapType, Bool) -> Void = { _, _ in }
|
||||
|
||||
var previousTopEdgeSnapTimestamp: Double?
|
||||
var previousLeftEdgeSnapTimestamp: Double?
|
||||
var previousRightEdgeSnapTimestamp: Double?
|
||||
var previousBottomEdgeSnapTimestamp: Double?
|
||||
|
||||
var previousXSnapTimestamp: Double?
|
||||
var previousYSnapTimestamp: Double?
|
||||
var previousRotationSnapTimestamp: Double?
|
||||
|
||||
func reset() {
|
||||
self.topEdgeState = nil
|
||||
self.leftEdgeState = nil
|
||||
self.rightEdgeState = nil
|
||||
self.bottomEdgeState = nil
|
||||
self.xState = nil
|
||||
self.yState = nil
|
||||
|
||||
for type in SnapType.allPositionTypes {
|
||||
self.onSnapUpdated(type, false)
|
||||
}
|
||||
}
|
||||
|
||||
func rotationReset() {
|
||||
self.rotationState = nil
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
|
||||
func maybeSkipFromStart(entityView: DrawingEntityView, position: CGPoint) {
|
||||
self.topEdgeState = nil
|
||||
self.leftEdgeState = nil
|
||||
self.rightEdgeState = nil
|
||||
self.bottomEdgeState = nil
|
||||
|
||||
self.xState = nil
|
||||
self.yState = nil
|
||||
|
||||
let snapXDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
let snapYDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
|
||||
if let snapLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() {
|
||||
if position.x > snapLocation.x - snapXDelta && position.x < snapLocation.x + snapXDelta {
|
||||
self.xState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||
}
|
||||
|
||||
if position.y > snapLocation.y - snapYDelta && position.y < snapLocation.y + snapYDelta {
|
||||
self.yState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(entityView: DrawingEntityView, velocity: CGPoint, delta: CGPoint, updatedPosition: CGPoint, size: CGSize) -> CGPoint {
|
||||
var updatedPosition = updatedPosition
|
||||
|
||||
guard let snapCenterLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() else {
|
||||
return updatedPosition
|
||||
}
|
||||
let snapEdgeLocations = (entityView.superview as? DrawingEntitiesView)?.getEntityEdgePositions()
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
|
||||
let snapDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
let snapVelocity: CGFloat = snapDelta * 12.0
|
||||
let snapSkipTranslation: CGFloat = snapDelta * 2.0
|
||||
|
||||
let topPoint = updatedPosition.y - size.height / 2.0
|
||||
let leftPoint = updatedPosition.x - size.width / 2.0
|
||||
let rightPoint = updatedPosition.x + size.width / 2.0
|
||||
let bottomPoint = updatedPosition.y + size.height / 2.0
|
||||
|
||||
func process(
|
||||
state: SnapState?,
|
||||
velocity: CGFloat,
|
||||
delta: CGFloat,
|
||||
value: CGFloat,
|
||||
snapVelocity: CGFloat,
|
||||
snapToValue: CGFloat?,
|
||||
snapDelta: CGFloat,
|
||||
snapSkipTranslation: CGFloat,
|
||||
previousSnapTimestamp: Double?,
|
||||
onSnapUpdated: (Bool) -> Void
|
||||
) -> (
|
||||
value: CGFloat,
|
||||
state: SnapState?,
|
||||
snapTimestamp: Double?
|
||||
) {
|
||||
var updatedValue = value
|
||||
var updatedState = state
|
||||
var updatedPreviousSnapTimestamp = previousSnapTimestamp
|
||||
if abs(velocity) < snapVelocity || state?.waitForLeave == true {
|
||||
if let snapToValue {
|
||||
if let state {
|
||||
let skipped = state.skipped
|
||||
let waitForLeave = state.waitForLeave
|
||||
if waitForLeave {
|
||||
if value > snapToValue - snapDelta * 2.0 && value < snapToValue + snapDelta * 2.0 {
|
||||
|
||||
} else {
|
||||
updatedState = nil
|
||||
}
|
||||
} else if abs(skipped) < snapSkipTranslation {
|
||||
updatedState = SnapState(skipped: skipped + delta, waitForLeave: false)
|
||||
updatedValue = snapToValue
|
||||
} else {
|
||||
updatedState = SnapState(skipped: snapSkipTranslation, waitForLeave: true)
|
||||
onSnapUpdated(false)
|
||||
}
|
||||
} else {
|
||||
if value > snapToValue - snapDelta && value < snapToValue + snapDelta {
|
||||
if let previousSnapTimestamp, currentTimestamp - previousSnapTimestamp < snapTimeout {
|
||||
|
||||
} else {
|
||||
updatedPreviousSnapTimestamp = currentTimestamp
|
||||
updatedState = SnapState(skipped: 0.0, waitForLeave: false)
|
||||
updatedValue = snapToValue
|
||||
onSnapUpdated(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updatedState = nil
|
||||
onSnapUpdated(false)
|
||||
}
|
||||
return (updatedValue, updatedState, updatedPreviousSnapTimestamp)
|
||||
}
|
||||
|
||||
let (updatedXValue, updatedXState, updatedXPreviousTimestamp) = process(
|
||||
state: self.xState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: updatedPosition.x,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapCenterLocation.x,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousXSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.centerX, snapped)
|
||||
}
|
||||
)
|
||||
self.xState = updatedXState
|
||||
self.previousXSnapTimestamp = updatedXPreviousTimestamp
|
||||
|
||||
let (updatedYValue, updatedYState, updatedYPreviousTimestamp) = process(
|
||||
state: self.yState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: updatedPosition.y,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapCenterLocation.y,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousYSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.centerY, snapped)
|
||||
}
|
||||
)
|
||||
self.yState = updatedYState
|
||||
self.previousYSnapTimestamp = updatedYPreviousTimestamp
|
||||
|
||||
if let snapEdgeLocations {
|
||||
if updatedXState == nil {
|
||||
let (updatedXLeftEdgeValue, updatedLeftEdgeState, updatedLeftEdgePreviousTimestamp) = process(
|
||||
state: self.leftEdgeState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: leftPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.left,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousLeftEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.left, snapped)
|
||||
}
|
||||
)
|
||||
self.leftEdgeState = updatedLeftEdgeState
|
||||
self.previousLeftEdgeSnapTimestamp = updatedLeftEdgePreviousTimestamp
|
||||
|
||||
if updatedLeftEdgeState != nil {
|
||||
updatedPosition.x = updatedXLeftEdgeValue + size.width / 2.0
|
||||
|
||||
self.rightEdgeState = nil
|
||||
self.previousRightEdgeSnapTimestamp = nil
|
||||
} else {
|
||||
let (updatedXRightEdgeValue, updatedRightEdgeState, updatedRightEdgePreviousTimestamp) = process(
|
||||
state: self.rightEdgeState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: rightPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.right,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousRightEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.right, snapped)
|
||||
}
|
||||
)
|
||||
self.rightEdgeState = updatedRightEdgeState
|
||||
self.previousRightEdgeSnapTimestamp = updatedRightEdgePreviousTimestamp
|
||||
|
||||
updatedPosition.x = updatedXRightEdgeValue - size.width / 2.0
|
||||
}
|
||||
} else {
|
||||
updatedPosition.x = updatedXValue
|
||||
}
|
||||
|
||||
if updatedYState == nil {
|
||||
let (updatedYTopEdgeValue, updatedTopEdgeState, updatedTopEdgePreviousTimestamp) = process(
|
||||
state: self.topEdgeState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: topPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.top,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousTopEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.top, snapped)
|
||||
}
|
||||
)
|
||||
self.topEdgeState = updatedTopEdgeState
|
||||
self.previousTopEdgeSnapTimestamp = updatedTopEdgePreviousTimestamp
|
||||
|
||||
if updatedTopEdgeState != nil {
|
||||
updatedPosition.y = updatedYTopEdgeValue + size.height / 2.0
|
||||
|
||||
self.bottomEdgeState = nil
|
||||
self.previousBottomEdgeSnapTimestamp = nil
|
||||
} else {
|
||||
let (updatedYBottomEdgeValue, updatedBottomEdgeState, updatedBottomEdgePreviousTimestamp) = process(
|
||||
state: self.bottomEdgeState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: bottomPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.bottom,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousBottomEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.bottom, snapped)
|
||||
}
|
||||
)
|
||||
self.bottomEdgeState = updatedBottomEdgeState
|
||||
self.previousBottomEdgeSnapTimestamp = updatedBottomEdgePreviousTimestamp
|
||||
|
||||
updatedPosition.y = updatedYBottomEdgeValue - size.height / 2.0
|
||||
}
|
||||
} else {
|
||||
updatedPosition.y = updatedYValue
|
||||
}
|
||||
} else {
|
||||
updatedPosition.x = updatedXValue
|
||||
updatedPosition.y = updatedYValue
|
||||
}
|
||||
|
||||
return updatedPosition
|
||||
}
|
||||
|
||||
private let snapRotations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
||||
func maybeSkipFromStart(entityView: DrawingEntityView, rotation: CGFloat) {
|
||||
self.rotationState = nil
|
||||
|
||||
let snapDelta: CGFloat = 0.25
|
||||
for snapRotation in self.snapRotations {
|
||||
let snapRotation = snapRotation * .pi
|
||||
if rotation > snapRotation - snapDelta && rotation < snapRotation + snapDelta {
|
||||
self.rotationState = (snapRotation, 0.0, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(entityView: DrawingEntityView, velocity: CGFloat, delta: CGFloat, updatedRotation: CGFloat) -> CGFloat {
|
||||
var updatedRotation = updatedRotation
|
||||
if updatedRotation < 0.0 {
|
||||
updatedRotation = 2.0 * .pi + updatedRotation
|
||||
} else if updatedRotation > 2.0 * .pi {
|
||||
while updatedRotation > 2.0 * .pi {
|
||||
updatedRotation -= 2.0 * .pi
|
||||
}
|
||||
}
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
|
||||
let snapDelta: CGFloat = 0.01
|
||||
let snapVelocity: CGFloat = snapDelta * 35.0
|
||||
let snapSkipRotation: CGFloat = snapDelta * 40.0
|
||||
|
||||
if abs(velocity) < snapVelocity || self.rotationState?.waitForLeave == true {
|
||||
if let (snapRotation, skipped, waitForLeave) = self.rotationState {
|
||||
if waitForLeave {
|
||||
if updatedRotation > snapRotation - snapDelta * 2.0 && updatedRotation < snapRotation + snapDelta {
|
||||
|
||||
} else {
|
||||
self.rotationState = nil
|
||||
}
|
||||
} else if abs(skipped) < snapSkipRotation {
|
||||
self.rotationState = (snapRotation, skipped + delta, false)
|
||||
updatedRotation = snapRotation
|
||||
} else {
|
||||
self.rotationState = (snapRotation, snapSkipRotation, true)
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
} else {
|
||||
for snapRotation in self.snapRotations {
|
||||
let snapRotation = snapRotation * .pi
|
||||
if updatedRotation > snapRotation - snapDelta && updatedRotation < snapRotation + snapDelta {
|
||||
if let previousRotationSnapTimestamp, currentTimestamp - previousRotationSnapTimestamp < snapTimeout {
|
||||
|
||||
} else {
|
||||
self.previousRotationSnapTimestamp = currentTimestamp
|
||||
self.rotationState = (snapRotation, 0.0, false)
|
||||
updatedRotation = snapRotation
|
||||
self.onSnapUpdated(.rotation(snapRotation), true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.rotationState = nil
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
|
||||
return updatedRotation
|
||||
}
|
||||
}
|
@ -3172,39 +3172,41 @@ public final class DrawingToolsInteraction {
|
||||
}
|
||||
|
||||
func presentEyedropper(retryLaterForVideo: Bool = true, dismissed: @escaping () -> Void) {
|
||||
// self.entitiesView.pause()
|
||||
//
|
||||
// if self.isVideo && retryLaterForVideo {
|
||||
// self.updateVideoPlayback(false)
|
||||
// Queue.mainQueue().after(0.1) {
|
||||
// self.presentEyedropper(retryLaterForVideo: false, dismissed: dismissed)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// guard let currentImage = self.getCurrentImage() else {
|
||||
// self.entitiesView.play()
|
||||
// self.updateVideoPlayback(true)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let sourceImage = generateImage(self.drawingView.imageSize, contextGenerator: { size, context in
|
||||
// let bounds = CGRect(origin: .zero, size: size)
|
||||
// if let cgImage = currentImage.cgImage {
|
||||
// context.draw(cgImage, in: bounds)
|
||||
// }
|
||||
// if let cgImage = self.drawingView.drawingImage?.cgImage {
|
||||
// context.draw(cgImage, in: bounds)
|
||||
// }
|
||||
// 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)
|
||||
// self.entitiesView.layer.render(in: context)
|
||||
// }, opaque: true, scale: 1.0)
|
||||
// guard let sourceImage = sourceImage else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
self.entitiesView.pause()
|
||||
|
||||
if self.isVideo && retryLaterForVideo {
|
||||
self.updateVideoPlayback(false)
|
||||
Queue.mainQueue().after(0.1) {
|
||||
self.presentEyedropper(retryLaterForVideo: false, dismissed: dismissed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let currentImage = self.getCurrentImage() else {
|
||||
self.entitiesView.play()
|
||||
self.updateVideoPlayback(true)
|
||||
return
|
||||
}
|
||||
|
||||
let sourceImage = generateImage(self.drawingView.imageSize, contextGenerator: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
if let cgImage = currentImage.cgImage {
|
||||
context.draw(cgImage, in: bounds)
|
||||
}
|
||||
if let cgImage = self.drawingView.drawingImage?.cgImage {
|
||||
context.draw(cgImage, in: bounds)
|
||||
}
|
||||
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)
|
||||
self.entitiesView.layer.render(in: context)
|
||||
}, opaque: true, scale: 1.0)
|
||||
guard let sourceImage = sourceImage else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = sourceImage
|
||||
|
||||
// let eyedropperView = EyedropperView(containerSize: controller.contentWrapperView.frame.size, drawingView: self.drawingView, sourceImage: sourceImage)
|
||||
// eyedropperView.completed = { [weak self] color in
|
||||
// if let self {
|
||||
|
@ -179,12 +179,46 @@ public final class DrawingStickerEntityView: DrawingEntityView {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
} else if let image = self.image {
|
||||
func drawImageWithOrientation(_ image: UIImage, size: CGSize, in context: CGContext) {
|
||||
let imageSize: CGSize
|
||||
|
||||
switch image.imageOrientation {
|
||||
case .left, .leftMirrored, .right, .rightMirrored:
|
||||
imageSize = CGSize(width: size.height, height: size.width)
|
||||
default:
|
||||
imageSize = size
|
||||
}
|
||||
|
||||
let imageRect = CGRect(origin: .zero, size: imageSize)
|
||||
|
||||
switch image.imageOrientation {
|
||||
case .down, .downMirrored:
|
||||
context.translateBy(x: imageSize.width, y: imageSize.height)
|
||||
context.rotate(by: CGFloat.pi)
|
||||
case .left, .leftMirrored:
|
||||
context.translateBy(x: imageSize.width, y: 0)
|
||||
context.rotate(by: CGFloat.pi / 2)
|
||||
case .right, .rightMirrored:
|
||||
context.translateBy(x: 0, y: imageSize.height)
|
||||
context.rotate(by: -CGFloat.pi / 2)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// switch image.imageOrientation {
|
||||
// case .leftMirrored, .rightMirrored:
|
||||
// context.scaleBy(x: -1, y: 1)
|
||||
// default:
|
||||
// context.scaleBy(x: 1, y: -1)
|
||||
// }
|
||||
|
||||
context.draw(image.cgImage!, in: imageRect)
|
||||
}
|
||||
|
||||
self.imageNode.setSignal(.single({ arguments -> DrawingContext? in
|
||||
let context = DrawingContext(size: arguments.drawingSize, opaque: false, clear: true)
|
||||
context?.withFlippedContext({ ctx in
|
||||
if let cgImage = image.cgImage {
|
||||
ctx.draw(cgImage, in: CGRect(origin: .zero, size: arguments.drawingSize))
|
||||
}
|
||||
drawImageWithOrientation(image, size: arguments.drawingSize, in: ctx)
|
||||
})
|
||||
return context
|
||||
}))
|
||||
@ -644,385 +678,15 @@ final class DrawingStickerEntititySelectionView: DrawingEntitySelectionView {
|
||||
self.border.lineWidth = 2.0 / self.scale
|
||||
|
||||
if entity.isRectangle {
|
||||
let aspectRatio = entity.baseSize.width / entity.baseSize.height
|
||||
|
||||
let width: CGFloat = self.bounds.width - inset * 2.0
|
||||
let height: CGFloat = self.bounds.height - inset * 2.0
|
||||
let height: CGFloat = self.bounds.height / aspectRatio - inset * 2.0
|
||||
|
||||
let cornerRadius: CGFloat = 12.0 - self.scale
|
||||
self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath
|
||||
self.border.path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - width) / 2.0), y: floorToScreenPixels((self.bounds.height - height) / 2.0)), size: CGSize(width: width, height: height)), cornerRadius: cornerRadius).cgPath
|
||||
} else {
|
||||
self.border.path = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: inset, y: inset), size: CGSize(width: self.bounds.width - inset * 2.0, height: self.bounds.height - inset * 2.0))).cgPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let snapTimeout = 1.0
|
||||
|
||||
class DrawingEntitySnapTool {
|
||||
enum SnapType {
|
||||
case centerX
|
||||
case centerY
|
||||
case top
|
||||
case left
|
||||
case right
|
||||
case bottom
|
||||
case rotation(CGFloat?)
|
||||
|
||||
static var allPositionTypes: [SnapType] {
|
||||
return [
|
||||
.centerX,
|
||||
.centerY,
|
||||
.top,
|
||||
.left,
|
||||
.right,
|
||||
.bottom
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct SnapState {
|
||||
let skipped: CGFloat
|
||||
let waitForLeave: Bool
|
||||
}
|
||||
|
||||
private var topEdgeState: SnapState?
|
||||
private var leftEdgeState: SnapState?
|
||||
private var rightEdgeState: SnapState?
|
||||
private var bottomEdgeState: SnapState?
|
||||
|
||||
private var xState: SnapState?
|
||||
private var yState: SnapState?
|
||||
|
||||
private var rotationState: (angle: CGFloat, skipped: CGFloat, waitForLeave: Bool)?
|
||||
|
||||
var onSnapUpdated: (SnapType, Bool) -> Void = { _, _ in }
|
||||
|
||||
var previousTopEdgeSnapTimestamp: Double?
|
||||
var previousLeftEdgeSnapTimestamp: Double?
|
||||
var previousRightEdgeSnapTimestamp: Double?
|
||||
var previousBottomEdgeSnapTimestamp: Double?
|
||||
|
||||
var previousXSnapTimestamp: Double?
|
||||
var previousYSnapTimestamp: Double?
|
||||
var previousRotationSnapTimestamp: Double?
|
||||
|
||||
func reset() {
|
||||
self.topEdgeState = nil
|
||||
self.leftEdgeState = nil
|
||||
self.rightEdgeState = nil
|
||||
self.bottomEdgeState = nil
|
||||
self.xState = nil
|
||||
self.yState = nil
|
||||
|
||||
for type in SnapType.allPositionTypes {
|
||||
self.onSnapUpdated(type, false)
|
||||
}
|
||||
}
|
||||
|
||||
func rotationReset() {
|
||||
self.rotationState = nil
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
|
||||
func maybeSkipFromStart(entityView: DrawingEntityView, position: CGPoint) {
|
||||
self.topEdgeState = nil
|
||||
self.leftEdgeState = nil
|
||||
self.rightEdgeState = nil
|
||||
self.bottomEdgeState = nil
|
||||
|
||||
self.xState = nil
|
||||
self.yState = nil
|
||||
|
||||
let snapXDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
let snapYDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
|
||||
if let snapLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() {
|
||||
if position.x > snapLocation.x - snapXDelta && position.x < snapLocation.x + snapXDelta {
|
||||
self.xState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||
}
|
||||
|
||||
if position.y > snapLocation.y - snapYDelta && position.y < snapLocation.y + snapYDelta {
|
||||
self.yState = SnapState(skipped: 0.0, waitForLeave: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(entityView: DrawingEntityView, velocity: CGPoint, delta: CGPoint, updatedPosition: CGPoint, size: CGSize) -> CGPoint {
|
||||
var updatedPosition = updatedPosition
|
||||
|
||||
guard let snapCenterLocation = (entityView.superview as? DrawingEntitiesView)?.getEntityCenterPosition() else {
|
||||
return updatedPosition
|
||||
}
|
||||
let snapEdgeLocations = (entityView.superview as? DrawingEntitiesView)?.getEntityEdgePositions()
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
|
||||
let snapDelta: CGFloat = (entityView.superview?.frame.width ?? 0.0) * 0.02
|
||||
let snapVelocity: CGFloat = snapDelta * 12.0
|
||||
let snapSkipTranslation: CGFloat = snapDelta * 2.0
|
||||
|
||||
let topPoint = updatedPosition.y - size.height / 2.0
|
||||
let leftPoint = updatedPosition.x - size.width / 2.0
|
||||
let rightPoint = updatedPosition.x + size.width / 2.0
|
||||
let bottomPoint = updatedPosition.y + size.height / 2.0
|
||||
|
||||
func process(
|
||||
state: SnapState?,
|
||||
velocity: CGFloat,
|
||||
delta: CGFloat,
|
||||
value: CGFloat,
|
||||
snapVelocity: CGFloat,
|
||||
snapToValue: CGFloat?,
|
||||
snapDelta: CGFloat,
|
||||
snapSkipTranslation: CGFloat,
|
||||
previousSnapTimestamp: Double?,
|
||||
onSnapUpdated: (Bool) -> Void
|
||||
) -> (
|
||||
value: CGFloat,
|
||||
state: SnapState?,
|
||||
snapTimestamp: Double?
|
||||
) {
|
||||
var updatedValue = value
|
||||
var updatedState = state
|
||||
var updatedPreviousSnapTimestamp = previousSnapTimestamp
|
||||
if abs(velocity) < snapVelocity || state?.waitForLeave == true {
|
||||
if let snapToValue {
|
||||
if let state {
|
||||
let skipped = state.skipped
|
||||
let waitForLeave = state.waitForLeave
|
||||
if waitForLeave {
|
||||
if value > snapToValue - snapDelta * 2.0 && value < snapToValue + snapDelta * 2.0 {
|
||||
|
||||
} else {
|
||||
updatedState = nil
|
||||
}
|
||||
} else if abs(skipped) < snapSkipTranslation {
|
||||
updatedState = SnapState(skipped: skipped + delta, waitForLeave: false)
|
||||
updatedValue = snapToValue
|
||||
} else {
|
||||
updatedState = SnapState(skipped: snapSkipTranslation, waitForLeave: true)
|
||||
onSnapUpdated(false)
|
||||
}
|
||||
} else {
|
||||
if value > snapToValue - snapDelta && value < snapToValue + snapDelta {
|
||||
if let previousSnapTimestamp, currentTimestamp - previousSnapTimestamp < snapTimeout {
|
||||
|
||||
} else {
|
||||
updatedPreviousSnapTimestamp = currentTimestamp
|
||||
updatedState = SnapState(skipped: 0.0, waitForLeave: false)
|
||||
updatedValue = snapToValue
|
||||
onSnapUpdated(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updatedState = nil
|
||||
onSnapUpdated(false)
|
||||
}
|
||||
return (updatedValue, updatedState, updatedPreviousSnapTimestamp)
|
||||
}
|
||||
|
||||
let (updatedXValue, updatedXState, updatedXPreviousTimestamp) = process(
|
||||
state: self.xState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: updatedPosition.x,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapCenterLocation.x,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousXSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.centerX, snapped)
|
||||
}
|
||||
)
|
||||
self.xState = updatedXState
|
||||
self.previousXSnapTimestamp = updatedXPreviousTimestamp
|
||||
|
||||
let (updatedYValue, updatedYState, updatedYPreviousTimestamp) = process(
|
||||
state: self.yState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: updatedPosition.y,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapCenterLocation.y,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousYSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.centerY, snapped)
|
||||
}
|
||||
)
|
||||
self.yState = updatedYState
|
||||
self.previousYSnapTimestamp = updatedYPreviousTimestamp
|
||||
|
||||
if let snapEdgeLocations {
|
||||
if updatedXState == nil {
|
||||
let (updatedXLeftEdgeValue, updatedLeftEdgeState, updatedLeftEdgePreviousTimestamp) = process(
|
||||
state: self.leftEdgeState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: leftPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.left,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousLeftEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.left, snapped)
|
||||
}
|
||||
)
|
||||
self.leftEdgeState = updatedLeftEdgeState
|
||||
self.previousLeftEdgeSnapTimestamp = updatedLeftEdgePreviousTimestamp
|
||||
|
||||
if updatedLeftEdgeState != nil {
|
||||
updatedPosition.x = updatedXLeftEdgeValue + size.width / 2.0
|
||||
|
||||
self.rightEdgeState = nil
|
||||
self.previousRightEdgeSnapTimestamp = nil
|
||||
} else {
|
||||
let (updatedXRightEdgeValue, updatedRightEdgeState, updatedRightEdgePreviousTimestamp) = process(
|
||||
state: self.rightEdgeState,
|
||||
velocity: velocity.x,
|
||||
delta: delta.x,
|
||||
value: rightPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.right,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousRightEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.right, snapped)
|
||||
}
|
||||
)
|
||||
self.rightEdgeState = updatedRightEdgeState
|
||||
self.previousRightEdgeSnapTimestamp = updatedRightEdgePreviousTimestamp
|
||||
|
||||
updatedPosition.x = updatedXRightEdgeValue - size.width / 2.0
|
||||
}
|
||||
} else {
|
||||
updatedPosition.x = updatedXValue
|
||||
}
|
||||
|
||||
if updatedYState == nil {
|
||||
let (updatedYTopEdgeValue, updatedTopEdgeState, updatedTopEdgePreviousTimestamp) = process(
|
||||
state: self.topEdgeState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: topPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.top,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousTopEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.top, snapped)
|
||||
}
|
||||
)
|
||||
self.topEdgeState = updatedTopEdgeState
|
||||
self.previousTopEdgeSnapTimestamp = updatedTopEdgePreviousTimestamp
|
||||
|
||||
if updatedTopEdgeState != nil {
|
||||
updatedPosition.y = updatedYTopEdgeValue + size.height / 2.0
|
||||
|
||||
self.bottomEdgeState = nil
|
||||
self.previousBottomEdgeSnapTimestamp = nil
|
||||
} else {
|
||||
let (updatedYBottomEdgeValue, updatedBottomEdgeState, updatedBottomEdgePreviousTimestamp) = process(
|
||||
state: self.bottomEdgeState,
|
||||
velocity: velocity.y,
|
||||
delta: delta.y,
|
||||
value: bottomPoint,
|
||||
snapVelocity: snapVelocity,
|
||||
snapToValue: snapEdgeLocations.bottom,
|
||||
snapDelta: snapDelta,
|
||||
snapSkipTranslation: snapSkipTranslation,
|
||||
previousSnapTimestamp: self.previousBottomEdgeSnapTimestamp,
|
||||
onSnapUpdated: { [weak self] snapped in
|
||||
self?.onSnapUpdated(.bottom, snapped)
|
||||
}
|
||||
)
|
||||
self.bottomEdgeState = updatedBottomEdgeState
|
||||
self.previousBottomEdgeSnapTimestamp = updatedBottomEdgePreviousTimestamp
|
||||
|
||||
updatedPosition.y = updatedYBottomEdgeValue - size.height / 2.0
|
||||
}
|
||||
} else {
|
||||
updatedPosition.y = updatedYValue
|
||||
}
|
||||
} else {
|
||||
updatedPosition.x = updatedXValue
|
||||
updatedPosition.y = updatedYValue
|
||||
}
|
||||
|
||||
return updatedPosition
|
||||
}
|
||||
|
||||
private let snapRotations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
||||
func maybeSkipFromStart(entityView: DrawingEntityView, rotation: CGFloat) {
|
||||
self.rotationState = nil
|
||||
|
||||
let snapDelta: CGFloat = 0.25
|
||||
for snapRotation in self.snapRotations {
|
||||
let snapRotation = snapRotation * .pi
|
||||
if rotation > snapRotation - snapDelta && rotation < snapRotation + snapDelta {
|
||||
self.rotationState = (snapRotation, 0.0, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(entityView: DrawingEntityView, velocity: CGFloat, delta: CGFloat, updatedRotation: CGFloat) -> CGFloat {
|
||||
var updatedRotation = updatedRotation
|
||||
if updatedRotation < 0.0 {
|
||||
updatedRotation = 2.0 * .pi + updatedRotation
|
||||
} else if updatedRotation > 2.0 * .pi {
|
||||
while updatedRotation > 2.0 * .pi {
|
||||
updatedRotation -= 2.0 * .pi
|
||||
}
|
||||
}
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime()
|
||||
|
||||
let snapDelta: CGFloat = 0.01
|
||||
let snapVelocity: CGFloat = snapDelta * 35.0
|
||||
let snapSkipRotation: CGFloat = snapDelta * 40.0
|
||||
|
||||
if abs(velocity) < snapVelocity || self.rotationState?.waitForLeave == true {
|
||||
if let (snapRotation, skipped, waitForLeave) = self.rotationState {
|
||||
if waitForLeave {
|
||||
if updatedRotation > snapRotation - snapDelta * 2.0 && updatedRotation < snapRotation + snapDelta {
|
||||
|
||||
} else {
|
||||
self.rotationState = nil
|
||||
}
|
||||
} else if abs(skipped) < snapSkipRotation {
|
||||
self.rotationState = (snapRotation, skipped + delta, false)
|
||||
updatedRotation = snapRotation
|
||||
} else {
|
||||
self.rotationState = (snapRotation, snapSkipRotation, true)
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
} else {
|
||||
for snapRotation in self.snapRotations {
|
||||
let snapRotation = snapRotation * .pi
|
||||
if updatedRotation > snapRotation - snapDelta && updatedRotation < snapRotation + snapDelta {
|
||||
if let previousRotationSnapTimestamp, currentTimestamp - previousRotationSnapTimestamp < snapTimeout {
|
||||
|
||||
} else {
|
||||
self.previousRotationSnapTimestamp = currentTimestamp
|
||||
self.rotationState = (snapRotation, 0.0, false)
|
||||
updatedRotation = snapRotation
|
||||
self.onSnapUpdated(.rotation(snapRotation), true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.rotationState = nil
|
||||
self.onSnapUpdated(.rotation(nil), false)
|
||||
}
|
||||
|
||||
return updatedRotation
|
||||
}
|
||||
}
|
||||
|
@ -912,7 +912,13 @@ public class StickerPickerScreen: ViewController {
|
||||
useOpaqueTheme: false,
|
||||
hideBackground: true,
|
||||
stateContext: nil,
|
||||
addImage: nil
|
||||
addImage: { [weak self] in
|
||||
if let self {
|
||||
self.controller?.completion(nil)
|
||||
self.controller?.dismiss(animated: true)
|
||||
self.controller?.presentGallery()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var stickerPeekBehavior: EmojiContentPeekBehaviorImpl?
|
||||
@ -1170,8 +1176,12 @@ public class StickerPickerScreen: ViewController {
|
||||
useOpaqueTheme: false,
|
||||
hideBackground: true,
|
||||
stateContext: nil,
|
||||
addImage: {
|
||||
|
||||
addImage: { [weak self] in
|
||||
if let self {
|
||||
self.controller?.completion(nil)
|
||||
self.controller?.dismiss(animated: true)
|
||||
self.controller?.presentGallery()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ -1627,6 +1637,8 @@ public class StickerPickerScreen: ViewController {
|
||||
|
||||
public var completion: (DrawingStickerEntity.Content?) -> Void = { _ in }
|
||||
|
||||
public var presentGallery: () -> Void = { }
|
||||
|
||||
public init(context: AccountContext, inputData: Signal<StickerPickerInputData, NoError>) {
|
||||
self.context = context
|
||||
self.theme = defaultDarkColorPresentationTheme
|
||||
|
@ -137,6 +137,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
case `default`
|
||||
case wallpaper
|
||||
case story
|
||||
case addImage
|
||||
}
|
||||
|
||||
case assets(PHAssetCollection?, AssetsMode)
|
||||
@ -250,7 +251,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.presentationData = controller.presentationData
|
||||
|
||||
var assetType: PHAssetMediaType?
|
||||
if case let .assets(_, mode) = controller.subject, case .wallpaper = mode {
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .addImage].contains(mode) {
|
||||
assetType = .image
|
||||
}
|
||||
let mediaAssetsContext = MediaAssetsContext(assetType: assetType)
|
||||
@ -404,7 +405,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.gridNode.scrollView.alwaysBounceVertical = true
|
||||
self.gridNode.scrollView.showsVerticalScrollIndicator = false
|
||||
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper, .story].contains(mode) {
|
||||
if case let .assets(_, mode) = controller.subject, [.wallpaper .addImage, .story].contains(mode) {
|
||||
|
||||
} else {
|
||||
let selectionGesture = MediaPickerGridSelectionGesture<TGMediaSelectableItem>()
|
||||
@ -1379,6 +1380,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
self.titleView.title = presentationData.strings.Attachment_Gallery
|
||||
case .wallpaper:
|
||||
self.titleView.title = presentationData.strings.Conversation_Theme_ChooseWallpaperTitle
|
||||
case .addImage:
|
||||
//TODO:localize
|
||||
self.titleView.title = "Add Image"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -2184,6 +2188,28 @@ public func wallpaperMediaPickerController(
|
||||
return controller
|
||||
}
|
||||
|
||||
public func mediaPickerController(
|
||||
context: AccountContext,
|
||||
completion: @escaping (Any) -> Void
|
||||
) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
let updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>) = (presentationData, .single(presentationData))
|
||||
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: nil, buttons: [.standalone], initialButton: .standalone, fromMenu: false, hasTextInput: false, makeEntityInputView: {
|
||||
return nil
|
||||
})
|
||||
controller.requestController = { _, present in
|
||||
let mediaPickerController = MediaPickerScreen(context: context, updatedPresentationData: updatedPresentationData, peer: nil, threadTitle: nil, chatLocation: nil, bannedSendPhotos: nil, bannedSendVideos: nil, subject: .assets(nil, .addImage), mainButtonState: nil, mainButtonAction: nil)
|
||||
mediaPickerController.customSelection = { controller, result in
|
||||
completion(result)
|
||||
controller.dismiss(animated: true)
|
||||
}
|
||||
present(mediaPickerController, mediaPickerController.mediaPickerContext)
|
||||
}
|
||||
controller.navigationPresentation = .flatModal
|
||||
controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
return controller
|
||||
}
|
||||
|
||||
public func storyMediaPickerController(
|
||||
context: AccountContext,
|
||||
getSourceRect: @escaping () -> CGRect,
|
||||
|
@ -113,6 +113,7 @@ swift_library(
|
||||
"//submodules/MediaPickerUI:MediaPickerUI",
|
||||
"//submodules/ImageBlur:ImageBlur",
|
||||
"//submodules/AttachmentUI:AttachmentUI",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -53,6 +53,8 @@ extension SettingsSearchableItemIcon {
|
||||
return PresentationResourcesSettings.devices
|
||||
case .premium:
|
||||
return PresentationResourcesSettings.premium
|
||||
case .stories:
|
||||
return PresentationResourcesSettings.stories
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import NotificationPeerExceptionController
|
||||
import QrCodeUI
|
||||
import PremiumUI
|
||||
import StorageUsageScreen
|
||||
import PeerInfoStoryGridScreen
|
||||
|
||||
enum SettingsSearchableItemIcon {
|
||||
case profile
|
||||
@ -41,6 +42,7 @@ enum SettingsSearchableItemIcon {
|
||||
case deleteAccount
|
||||
case devices
|
||||
case premium
|
||||
case stories
|
||||
}
|
||||
|
||||
public enum SettingsSearchableItemId: Hashable {
|
||||
@ -62,6 +64,7 @@ public enum SettingsSearchableItemId: Hashable {
|
||||
case deleteAccount(Int32)
|
||||
case devices(Int32)
|
||||
case premium(Int32)
|
||||
case stories(Int32)
|
||||
|
||||
private var namespace: Int32 {
|
||||
switch self {
|
||||
@ -101,6 +104,8 @@ public enum SettingsSearchableItemId: Hashable {
|
||||
return 18
|
||||
case .premium:
|
||||
return 19
|
||||
case .stories:
|
||||
return 20
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,7 +128,8 @@ public enum SettingsSearchableItemId: Hashable {
|
||||
let .chatFolders(id),
|
||||
let .deleteAccount(id),
|
||||
let .devices(id),
|
||||
let .premium(id):
|
||||
let .premium(id),
|
||||
let .stories(id):
|
||||
return id
|
||||
}
|
||||
}
|
||||
@ -172,6 +178,8 @@ public enum SettingsSearchableItemId: Hashable {
|
||||
self = .devices(id)
|
||||
case 19:
|
||||
self = .premium(id)
|
||||
case 20:
|
||||
self = .stories(id)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -340,6 +348,25 @@ private func premiumSearchableItems(context: AccountContext) -> [SettingsSearcha
|
||||
return result
|
||||
}
|
||||
|
||||
private func storiesSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
||||
let icon: SettingsSearchableItemIcon = .stories
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
|
||||
var result: [SettingsSearchableItem] = []
|
||||
|
||||
//TODO:localize
|
||||
result.append(SettingsSearchableItem(id: .stories(0), title: "My Stories", alternate: synonyms(strings.SettingsSearch_Synonyms_Premium), icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
present(.push, PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: .saved))
|
||||
}))
|
||||
|
||||
result.append(SettingsSearchableItem(id: .stories(1), title: "Stories Archive", alternate: synonyms(strings.SettingsSearch_Synonyms_Premium), icon: icon, breadcrumbs: [], present: { context, _, present in
|
||||
present(.push, PeerInfoStoryGridScreen(context: context, peerId: context.account.peerId, scope: .archive))
|
||||
}))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
private func callSearchableItems(context: AccountContext) -> [SettingsSearchableItem] {
|
||||
let icon: SettingsSearchableItemIcon = .calls
|
||||
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
|
||||
@ -1008,6 +1035,9 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
|
||||
|
||||
let premiumItems = premiumSearchableItems(context: context)
|
||||
allItems.append(contentsOf: premiumItems)
|
||||
|
||||
let storiesItems = storiesSearchableItems(context: context)
|
||||
allItems.append(contentsOf: storiesItems)
|
||||
|
||||
if watchAppInstalled {
|
||||
let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: synonyms(strings.SettingsSearch_Synonyms_Watch), icon: .watch, breadcrumbs: [], present: { context, _, present in
|
||||
|
@ -1370,7 +1370,7 @@ public class CameraScreen: ViewController {
|
||||
|
||||
private var isDismissing = false
|
||||
@objc private func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
|
||||
guard let controller = self.controller else {
|
||||
guard let controller = self.controller, let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
let translation = gestureRecognizer.translation(in: gestureRecognizer.view)
|
||||
@ -1380,7 +1380,7 @@ public class CameraScreen: ViewController {
|
||||
case .changed:
|
||||
if self.componentExternalState.isRecording {
|
||||
|
||||
} else {
|
||||
} else if case .compact = layout.metrics.widthClass {
|
||||
if translation.x < -10.0 || self.isDismissing {
|
||||
self.isDismissing = true
|
||||
let transitionFraction = 1.0 - max(0.0, translation.x * -1.0) / self.frame.width
|
||||
@ -2179,7 +2179,7 @@ public class CameraScreen: ViewController {
|
||||
if let current = self.galleryController {
|
||||
controller = current
|
||||
} else {
|
||||
controller = self.context.sharedContext.makeMediaPickerScreen(context: self.context, getSourceRect: { [weak self] in
|
||||
controller = self.context.sharedContext.makeStoryMediaPickerScreen(context: self.context, getSourceRect: { [weak self] in
|
||||
if let self {
|
||||
if let galleryButton = self.node.componentHost.findTaggedView(tag: galleryButtonTag) {
|
||||
return galleryButton.convert(galleryButton.bounds, to: self.view).offsetBy(dx: 0.0, dy: -15.0)
|
||||
|
@ -588,6 +588,18 @@ public final class EntityKeyboardComponent: Component {
|
||||
}
|
||||
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||
}
|
||||
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(Button(
|
||||
content: AnyComponent(BundleIconComponent(
|
||||
name: "Media Editor/AddImage",
|
||||
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||
maxSize: nil
|
||||
)),
|
||||
action: {
|
||||
addImage()
|
||||
}
|
||||
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||
}
|
||||
}
|
||||
|
||||
let deleteBackwards = component.emojiContent?.inputInteractionHolder.inputInteraction?.deleteBackwards
|
||||
@ -689,9 +701,8 @@ public final class EntityKeyboardComponent: Component {
|
||||
component.switchToTextInput()
|
||||
}
|
||||
).minSize(CGSize(width: 38.0, height: 38.0)))))
|
||||
}
|
||||
if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "image", component: AnyComponent(Button(
|
||||
} else if let addImage = component.emojiContent?.inputInteractionHolder.inputInteraction?.addImage {
|
||||
contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(Button(
|
||||
content: AnyComponent(BundleIconComponent(
|
||||
name: "Media Editor/AddImage",
|
||||
tintColor: component.theme.chat.inputMediaPanel.panelIconColor,
|
||||
|
@ -1843,6 +1843,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
switch subject {
|
||||
case .image, .video:
|
||||
isSavingAvailable = !controller.isEditingStory
|
||||
case .draft:
|
||||
isSavingAvailable = true
|
||||
default:
|
||||
isSavingAvailable = false
|
||||
}
|
||||
@ -2704,34 +2706,27 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
self.saveTooltip = tooltipController
|
||||
}
|
||||
}
|
||||
|
||||
private weak var storyArchiveTooltip: ViewController?
|
||||
func presentStoryArchiveTooltip(sourceView: UIView) {
|
||||
|
||||
func presentGallery() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
|
||||
if let storyArchiveTooltip = self.storyArchiveTooltip {
|
||||
storyArchiveTooltip.dismiss(animated: true)
|
||||
self.storyArchiveTooltip = nil
|
||||
}
|
||||
|
||||
let parentFrame = self.view.convert(self.bounds, to: nil)
|
||||
let absoluteFrame = sourceView.convert(sourceView.bounds, to: nil).offsetBy(dx: -parentFrame.minX, dy: 0.0)
|
||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 5.0), size: CGSize())
|
||||
|
||||
let text: String
|
||||
if controller.state.privacy.pin {
|
||||
text = "Story will be kept on your page."
|
||||
} else {
|
||||
text = "Story will disappear in 24 hours."
|
||||
}
|
||||
|
||||
let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), location: .point(location, .bottom), displayDuration: .default, inset: 7.0, cornerRadius: 9.0, shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
let galleryController = self.context.sharedContext.makeMediaPickerScreen(context: self.context, completion: { [weak self] result in
|
||||
guard let self, let asset = result as? PHAsset else {
|
||||
return
|
||||
}
|
||||
|
||||
let options = PHImageRequestOptions()
|
||||
options.deliveryMode = .highQualityFormat
|
||||
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { [weak self] image, _ in
|
||||
if let self, let image {
|
||||
Queue.mainQueue().async {
|
||||
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, true)), scale: 2.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
self.storyArchiveTooltip = tooltipController
|
||||
self.controller?.present(tooltipController, in: .current)
|
||||
controller.push(galleryController)
|
||||
}
|
||||
|
||||
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -2886,6 +2881,11 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
self.updateModalTransitionFactor(transitionFactor, transition: transition)
|
||||
}
|
||||
}
|
||||
controller.presentGallery = { [weak self] in
|
||||
if let self {
|
||||
self.presentGallery()
|
||||
}
|
||||
}
|
||||
self.stickerScreen = controller
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
return
|
||||
|
@ -1476,7 +1476,7 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
let inset: CGFloat
|
||||
if case let .stories(editing) = component.stateContext.subject {
|
||||
if editing {
|
||||
inset = 430.0
|
||||
inset = 446.0
|
||||
} else {
|
||||
inset = 605.0
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "photo_24.pdf",
|
||||
"filename" : "image_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
160
submodules/TelegramUI/Images.xcassets/Media Editor/AddImage.imageset/image_30.pdf
vendored
Normal file
160
submodules/TelegramUI/Images.xcassets/Media Editor/AddImage.imageset/image_30.pdf
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
%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 5.335022 5.334991 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
7.065000 19.330017 m
|
||||
7.035460 19.330017 l
|
||||
5.940372 19.330021 5.077796 19.330025 4.383656 19.273312 c
|
||||
3.675523 19.215456 3.084329 19.095276 2.547134 18.821562 c
|
||||
1.669358 18.374313 0.955704 17.660660 0.508455 16.782883 c
|
||||
0.234740 16.245687 0.114562 15.654494 0.056705 14.946362 c
|
||||
-0.000008 14.252222 -0.000005 13.389645 0.000000 12.294557 c
|
||||
0.000000 12.265017 l
|
||||
0.000000 7.065017 l
|
||||
0.000000 7.035477 l
|
||||
-0.000005 5.940390 -0.000008 5.077813 0.056705 4.383673 c
|
||||
0.114562 3.675540 0.234740 3.084345 0.508455 2.547152 c
|
||||
0.955704 1.669374 1.669358 0.955721 2.547134 0.508471 c
|
||||
3.084329 0.234756 3.675523 0.114578 4.383656 0.056721 c
|
||||
5.077746 0.000010 5.940250 0.000013 7.035228 0.000017 c
|
||||
7.035275 0.000017 l
|
||||
7.035323 0.000017 l
|
||||
7.035370 0.000017 l
|
||||
7.065000 0.000017 l
|
||||
12.265000 0.000017 l
|
||||
12.294630 0.000017 l
|
||||
12.294676 0.000017 l
|
||||
12.294722 0.000017 l
|
||||
12.294767 0.000017 l
|
||||
13.389750 0.000013 14.252252 0.000010 14.946344 0.056721 c
|
||||
15.654477 0.114578 16.245672 0.234756 16.782866 0.508471 c
|
||||
17.660643 0.955721 18.374296 1.669374 18.821547 2.547152 c
|
||||
19.095261 3.084345 19.215439 3.675540 19.273296 4.383673 c
|
||||
19.330008 5.077765 19.330004 5.940269 19.330000 7.035251 c
|
||||
19.330000 7.035296 l
|
||||
19.330000 7.035342 l
|
||||
19.330000 7.035388 l
|
||||
19.330000 7.065018 l
|
||||
19.330000 12.265018 l
|
||||
19.330000 12.294647 l
|
||||
19.330000 12.294695 l
|
||||
19.330000 12.294742 l
|
||||
19.330000 12.294788 l
|
||||
19.330004 13.389767 19.330008 14.252271 19.273296 14.946362 c
|
||||
19.215439 15.654494 19.095261 16.245687 18.821547 16.782883 c
|
||||
18.374296 17.660660 17.660643 18.374313 16.782866 18.821562 c
|
||||
16.245672 19.095276 15.654477 19.215456 14.946344 19.273312 c
|
||||
14.252204 19.330025 13.389627 19.330021 12.294539 19.330017 c
|
||||
12.264999 19.330017 l
|
||||
7.065000 19.330017 l
|
||||
h
|
||||
3.150942 17.636524 m
|
||||
3.469394 17.798782 3.866076 17.896591 4.491960 17.947729 c
|
||||
5.125607 17.999500 5.933921 18.000017 7.065000 18.000017 c
|
||||
12.264999 18.000017 l
|
||||
13.396078 18.000017 14.204392 17.999500 14.838039 17.947729 c
|
||||
15.463923 17.896591 15.860606 17.798782 16.179058 17.636524 c
|
||||
16.806580 17.316786 17.316771 16.806597 17.636507 16.179075 c
|
||||
17.798767 15.860623 17.896576 15.463942 17.947712 14.838057 c
|
||||
17.999483 14.204411 18.000000 13.396095 18.000000 12.265018 c
|
||||
18.000000 7.065018 l
|
||||
18.000000 6.309615 17.999769 5.698176 17.984194 5.186587 c
|
||||
15.466902 7.867111 l
|
||||
14.693245 8.690934 13.378143 8.669102 12.632261 7.820047 c
|
||||
11.520865 6.554921 l
|
||||
6.709533 11.633550 l
|
||||
5.937637 12.448328 4.633699 12.427637 3.888045 11.588776 c
|
||||
1.330000 8.710975 l
|
||||
1.330000 12.265017 l
|
||||
1.330000 13.396095 1.330517 14.204410 1.382288 14.838057 c
|
||||
1.433425 15.463942 1.531234 15.860623 1.693493 16.179075 c
|
||||
2.013231 16.806597 2.523421 17.316786 3.150942 17.636524 c
|
||||
h
|
||||
14.497394 6.956641 m
|
||||
17.768957 3.472939 l
|
||||
17.730608 3.355179 17.686632 3.249336 17.636507 3.150959 c
|
||||
17.316771 2.523438 16.806580 2.013247 16.179058 1.693510 c
|
||||
16.167240 1.687489 16.155315 1.681555 16.143274 1.675711 c
|
||||
12.439418 5.585338 l
|
||||
13.631460 6.942265 l
|
||||
13.859314 7.201635 14.261054 7.208306 14.497394 6.956641 c
|
||||
h
|
||||
1.330082 6.709152 m
|
||||
1.330711 5.761131 1.336117 5.057091 1.382288 4.491978 c
|
||||
1.433425 3.866094 1.531234 3.469411 1.693493 3.150959 c
|
||||
2.013231 2.523438 2.523421 2.013247 3.150942 1.693510 c
|
||||
3.469394 1.531250 3.866076 1.433441 4.491960 1.382305 c
|
||||
5.125607 1.330534 5.933922 1.330017 7.065000 1.330017 c
|
||||
12.265000 1.330017 l
|
||||
13.261761 1.330017 14.007863 1.330418 14.604662 1.365944 c
|
||||
5.744016 10.718849 l
|
||||
5.508215 10.967751 5.109884 10.961430 4.882100 10.705172 c
|
||||
1.330082 6.709152 l
|
||||
h
|
||||
14.040000 12.165017 m
|
||||
15.075534 12.165017 15.915000 13.004483 15.915000 14.040017 c
|
||||
15.915000 15.075551 15.075534 15.915017 14.040000 15.915017 c
|
||||
13.004466 15.915017 12.165000 15.075551 12.165000 14.040017 c
|
||||
12.165000 13.004483 13.004466 12.165017 14.040000 12.165017 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3865
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.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
|
||||
0000003955 00000 n
|
||||
0000003978 00000 n
|
||||
0000004151 00000 n
|
||||
0000004225 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
4284
|
||||
%%EOF
|
@ -1,154 +0,0 @@
|
||||
%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 3.835083 3.834961 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
5.465000 16.330017 m
|
||||
5.436178 16.330017 l
|
||||
5.436174 16.330017 l
|
||||
4.620535 16.330023 3.967872 16.330027 3.440454 16.286936 c
|
||||
2.899074 16.242702 2.431364 16.149773 2.001125 15.930555 c
|
||||
1.311511 15.579180 0.750837 15.018506 0.399461 14.328892 c
|
||||
0.180244 13.898653 0.087314 13.430943 0.043081 12.889563 c
|
||||
-0.000010 12.362144 -0.000006 11.709482 0.000000 10.893843 c
|
||||
0.000000 10.893839 l
|
||||
0.000000 10.865017 l
|
||||
0.000000 5.465017 l
|
||||
0.000000 5.436195 l
|
||||
-0.000006 4.620555 -0.000010 3.967890 0.043081 3.440471 c
|
||||
0.087314 2.899090 0.180244 2.431380 0.399461 2.001142 c
|
||||
0.750837 1.311528 1.311511 0.750854 2.001125 0.399478 c
|
||||
2.431364 0.180260 2.899074 0.087330 3.440454 0.043098 c
|
||||
3.967863 0.000008 4.620512 0.000011 5.436130 0.000017 c
|
||||
5.436194 0.000017 l
|
||||
5.465001 0.000017 l
|
||||
10.865000 0.000017 l
|
||||
10.893806 0.000017 l
|
||||
10.893871 0.000017 l
|
||||
11.709489 0.000011 12.362138 0.000008 12.889546 0.043098 c
|
||||
13.430927 0.087330 13.898637 0.180260 14.328876 0.399478 c
|
||||
15.018489 0.750854 15.579163 1.311528 15.930539 2.001142 c
|
||||
16.149757 2.431380 16.242687 2.899091 16.286919 3.440471 c
|
||||
16.330009 3.967880 16.330006 4.620529 16.330000 5.436146 c
|
||||
16.330000 5.436212 l
|
||||
16.330000 5.465017 l
|
||||
16.330000 10.865017 l
|
||||
16.330000 10.893824 l
|
||||
16.330000 10.893888 l
|
||||
16.330006 11.709505 16.330009 12.362154 16.286919 12.889563 c
|
||||
16.242687 13.430943 16.149757 13.898653 15.930539 14.328892 c
|
||||
15.579163 15.018506 15.018489 15.579180 14.328876 15.930555 c
|
||||
13.898637 16.149773 13.430926 16.242702 12.889546 16.286936 c
|
||||
12.362126 16.330029 11.709461 16.330023 10.893822 16.330017 c
|
||||
10.865000 16.330017 l
|
||||
5.465000 16.330017 l
|
||||
h
|
||||
2.604932 14.745517 m
|
||||
2.816429 14.853280 3.089626 14.923841 3.548759 14.961352 c
|
||||
4.015654 14.999499 4.613948 15.000017 5.465000 15.000017 c
|
||||
10.865000 15.000017 l
|
||||
11.716052 15.000017 12.314346 14.999499 12.781241 14.961352 c
|
||||
13.240374 14.923841 13.513572 14.853280 13.725068 14.745517 c
|
||||
14.164426 14.521652 14.521636 14.164443 14.745501 13.725084 c
|
||||
14.853263 13.513588 14.923823 13.240391 14.961336 12.781259 c
|
||||
14.999483 12.314363 15.000000 11.716068 15.000000 10.865017 c
|
||||
15.000000 5.465017 l
|
||||
15.000000 5.137442 14.999924 4.847313 14.997654 4.587710 c
|
||||
12.903418 6.817746 l
|
||||
12.230759 7.534022 11.087341 7.515037 10.438833 6.776826 c
|
||||
9.645941 5.874260 l
|
||||
5.897148 9.831320 l
|
||||
5.226022 10.539732 4.092310 10.521740 3.444000 9.792391 c
|
||||
1.330000 7.414142 l
|
||||
1.330000 10.865017 l
|
||||
1.330000 11.716068 1.330518 12.314363 1.368664 12.781259 c
|
||||
1.406177 13.240391 1.476737 13.513588 1.584500 13.725084 c
|
||||
1.808364 14.164443 2.165574 14.521652 2.604932 14.745517 c
|
||||
h
|
||||
11.933909 5.907277 m
|
||||
14.833861 2.819281 l
|
||||
14.807792 2.739742 14.778378 2.669474 14.745501 2.604949 c
|
||||
14.521636 2.165591 14.164426 1.808381 13.725068 1.584517 c
|
||||
13.714809 1.579343 l
|
||||
10.564494 4.904676 l
|
||||
11.438032 5.899043 l
|
||||
11.568513 6.047573 11.798571 6.051393 11.933909 5.907277 c
|
||||
h
|
||||
1.330001 5.412228 m
|
||||
1.330038 4.589128 1.331311 4.005958 1.368664 3.548775 c
|
||||
1.406177 3.089643 1.476737 2.816445 1.584500 2.604949 c
|
||||
1.808364 2.165591 2.165574 1.808381 2.604932 1.584517 c
|
||||
2.816429 1.476754 3.089626 1.406194 3.548759 1.368681 c
|
||||
4.015654 1.330534 4.613949 1.330017 5.465001 1.330017 c
|
||||
10.865000 1.330017 l
|
||||
11.357291 1.330017 11.765009 1.330190 12.111642 1.337719 c
|
||||
4.931631 8.916620 l
|
||||
4.796600 9.059153 4.568496 9.055533 4.438055 8.908787 c
|
||||
1.330001 5.412228 l
|
||||
h
|
||||
11.664969 10.165024 m
|
||||
12.493397 10.165024 13.164969 10.836597 13.164969 11.665024 c
|
||||
13.164969 12.493451 12.493397 13.165024 11.664969 13.165024 c
|
||||
10.836542 13.165024 10.164969 12.493451 10.164969 11.665024 c
|
||||
10.164969 10.836597 10.836542 10.165024 11.664969 10.165024 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3694
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.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
|
||||
0000003784 00000 n
|
||||
0000003807 00000 n
|
||||
0000003980 00000 n
|
||||
0000004054 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
4113
|
||||
%%EOF
|
@ -1843,7 +1843,11 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, loadedStickerPacks: loadedStickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||
}
|
||||
|
||||
public func makeMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
||||
public func makeMediaPickerScreen(context: AccountContext, completion: @escaping (Any) -> Void) -> ViewController {
|
||||
return mediaPickerController(context: context, completion: completion)
|
||||
}
|
||||
|
||||
public func makeStoryMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController {
|
||||
return storyMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user