Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2019-07-15 19:12:23 +02:00
commit ee44c0627a
283 changed files with 9411 additions and 8383 deletions

View file

@ -15,7 +15,7 @@ internal:
except:
- tags
script:
- sh buildbox/build-telegram.sh hockeyapp
- bash buildbox/build-telegram.sh hockeyapp
environment:
name: internal
@ -28,6 +28,6 @@ beta_testflight:
except:
- tags
script:
- sh buildbox/build-telegram.sh appstore
- bash buildbox/build-telegram.sh appstore
environment:
name: testflight_llc

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -185,7 +185,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Before After
Before After

View file

@ -4464,3 +4464,10 @@ Any member of this group will be able to see messages in the channel.";
"CreateGroup.ChannelsTooMuch" = "Sorry, you are a member of too many groups and channels. Please leave some before creating a new one.";
"Join.ChannelsTooMuch" = "Sorry, you are a member of too many groups and channels. Please leave some before joining one.";
"Invite.ChannelsTooMuch" = "Sorry, the target user is a member of too many groups and channels. Please ask them to leave some first.";
"Appearance.TintAllColors" = "Tint All Colors";
"Contacts.DeselectAll" = "Deselect All";
"Channel.TooMuchBots" = "Sorry, there are already too many bots in this group. Please remove some of the bots you're not using first.";
"Channel.BotDoesntSupportGroups" = "Sorry, this bot is telling us it doesn't want to be added to groups. You can't add this bot unless its developers change their mind.";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>UISupportedInterfaceOrientations</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.9</string>
<string>5.9.1</string>
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>NSExtension</key>

View file

@ -1,9 +1,37 @@
#!/bin/sh
#!/bin/bash
set -e
BUILD_TELEGRAM_VERSION="1"
MACOS_VERSION="10.14"
XCODE_VERSION="10.1"
VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)"
case "$(uname -s)" in
Linux*) BUILD_MACHINE=linux;;
Darwin*) BUILD_MACHINE=macOS;;
*) BUILD_MACHINE=""
esac
if [ "$BUILD_MACHINE" == "linux" ]; then
for MACHINE in $(virsh list --all --name); do
if [ "$MACHINE" == "$VM_BASE_NAME" ]; then
FOUND_BASE_MACHINE="1"
break
fi
done
if [ -z "$FOUND_BASE_MACHINE" ]; then
echo "Virtual machine $VM_BASE_NAME not found"
exit 1
fi
elif [ "$BUILD_MACHINE" == "macOS" ]; then
echo "Building on macOS"
else
echo "Unknown build machine $(uname -s)"
fi
if [ `which cleanup-telegram-build-vms.sh` ]; then
cleanup-telegram-build-vms.sh
fi
@ -66,27 +94,39 @@ if [ ! -d "$BUILDBOX_DIR/$CODESIGNING_SUBPATH" ]; then
fi
SOURCE_DIR=$(basename "$BASE_DIR")
cd ..
rm -f "$SOURCE_DIR/$BUILDBOX_DIR/transient-data/source.tar"
tar cf "$SOURCE_DIR/$BUILDBOX_DIR/transient-data/source.tar" --exclude "$SOURCE_DIR/$BUILDBOX_DIR" "$SOURCE_DIR"
cd "$BASE_DIR"
VM_BASE_NAME="macos10_14_3_Xcode10_1"
SNAPSHOT_ID=$(prlctl snapshot-list "$VM_BASE_NAME" | grep -Eo '\{(\d|[a-f]|-)*\}' | tr '\n' '\0')
if [ -z "$SNAPSHOT_ID" ]; then
echo "$VM_BASE_NAME is required to have one snapshot"
exit 1
fi
rm -f "$BUILDBOX_DIR/transient-data/source.tar"
tar cf "$BUILDBOX_DIR/transient-data/source.tar" --exclude "$BUILDBOX_DIR" --exclude ".git" "."
PROCESS_ID="$$"
VM_NAME="$VM_BASE_NAME-$(openssl rand -hex 10)-build-telegram-$PROCESS_ID"
prlctl clone "$VM_BASE_NAME" --name "$VM_NAME"
prlctl snapshot-switch "$VM_NAME" -i "$SNAPSHOT_ID"
if [ "$BUILD_MACHINE" == "linux" ]; then
virt-clone --original "$VM_BASE_NAME" --name "$VM_NAME" --auto-clone
virsh start "$VM_NAME"
VM_IP=$(prlctl exec "$VM_NAME" "ifconfig | grep inet | grep broadcast | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1 | tr '\n' '\0'")
echo "Getting VM IP"
while [ 1 ]; do
TEST_IP=$(virsh domifaddr "$VM_NAME" 2>/dev/null | egrep -o 'ipv4.*' | sed -e 's/ipv4\s*//g' | sed -e 's|/.*||g')
if [ ! -z "$TEST_IP" ]; then
RESPONSE=$(ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null telegram@"$TEST_IP" -o ServerAliveInterval=60 -t "echo -n 1")
if [ "$RESPONSE" == "1" ]; then
VM_IP="$TEST_IP"
break
fi
fi
sleep 1
done
elif [ "$BUILD_MACHINE" == "macOS" ]; then
SNAPSHOT_ID=$(prlctl snapshot-list "$VM_BASE_NAME" | grep -Eo '\{(\d|[a-f]|-)*\}' | tr '\n' '\0')
if [ -z "$SNAPSHOT_ID" ]; then
echo "$VM_BASE_NAME is required to have one snapshot"
exit 1
fi
prlctl clone "$VM_BASE_NAME" --name "$VM_NAME"
prlctl snapshot-switch "$VM_NAME" -i "$SNAPSHOT_ID"
VM_IP=$(prlctl exec "$VM_NAME" "ifconfig | grep inet | grep broadcast | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1 | tr '\n' '\0'")
fi
scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr "$BUILDBOX_DIR/$CODESIGNING_SUBPATH" telegram@"$VM_IP":codesigning_data
@ -118,5 +158,10 @@ elif [ "$BUILD_CONFIGURATION" == "verify" ]; then
scp -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -pr telegram@"$VM_IP":telegram-ios/Telegram-iOS-AppStoreLLC.ipa "./$VERIFY_IPA"
fi
prlctl stop "$VM_NAME" --kill
prlctl delete "$VM_NAME"
if [ "$BUILD_MACHINE" == "linux" ]; then
virsh destroy "$VM_NAME"
virsh undefine "$VM_NAME" --remove-all-storage --nvram
elif [ "$BUILD_MACHINE" == "macOS" ]; then
prlctl stop "$VM_NAME" --kill
prlctl delete "$VM_NAME"
fi

View file

@ -74,9 +74,32 @@ else
exit 1
fi
echo "Unpacking files..."
tar -xf "source.tar"
mkdir "$SOURCE_PATH"
SIZE_IN_BLOCKS=$((10*1024*1024*1024/512))
DEV=`hdid -nomount ram://$SIZE_IN_BLOCKS`
if [ $? -eq 0 ]; then
newfs_hfs -v 'ram disk' $DEV
eval `/usr/bin/stat -s "$SOURCE_PATH"`
mount -t hfs -o union -o nobrowse -o nodev -o noatime $DEV "$SOURCE_PATH"
chown $st_uid:$st_gid "$SOURCE_PATH"
chmod $st_mode "$SOURCE_PATH"
else
echo "Error creating ramdisk"
exit 1
fi
echo "Unpacking files..."
mkdir "$SOURCE_PATH"
BASE_DIR=$(pwd)
cd "$SOURCE_PATH"
tar -xf "../source.tar"
FASTLANE_PASSWORD="$FASTLANE_PASSWORD" FASTLANE_ITC_TEAM_NAME="$FASTLANE_ITC_TEAM_NAME" fastlane "$FASTLANE_BUILD_CONFIGURATION" build_number:"$BUILD_NUMBER" commit_hash:"$COMMIT_ID" commit_author:"$COMMIT_AUTHOR"
cd "$BASE_DIR"
umount -f "$SOURCE_PATH"
hdiutil detach "$DEV"
fi

View file

@ -2760,6 +2760,7 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_CODE_COVERAGE = NO;
CLANG_WARN_UNREACHABLE_CODE = YES;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
@ -2768,6 +2769,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
@ -2793,6 +2795,7 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_CODE_COVERAGE = NO;
CLANG_WARN_UNREACHABLE_CODE = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
@ -2802,6 +2805,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";
@ -2877,6 +2881,7 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_CODE_COVERAGE = NO;
CLANG_WARN_UNREACHABLE_CODE = YES;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
@ -2885,6 +2890,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
@ -3021,6 +3027,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@ -3158,6 +3165,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = "";
@ -3232,6 +3240,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREPROCESSOR_DEFINITIONS = "";
@ -3341,6 +3350,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@ -3456,6 +3466,7 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_CODE_COVERAGE = NO;
CLANG_WARN_UNREACHABLE_CODE = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
@ -3465,6 +3476,7 @@
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist";

View file

@ -362,6 +362,7 @@ API_AVAILABLE(ios(10))
}
if (signature.data != nil) {
_dataDict[@"data"] = [MTSha1(signature.data) base64EncodedStringWithOptions:0];
_dataDict[@"data1"] = [signature.data base64EncodedStringWithOptions:0];
}
}
return self;

View file

@ -6,6 +6,10 @@ public enum ContainedViewLayoutTransitionCurve {
case easeInOut
case spring
case custom(Float, Float, Float, Float)
public static var slide: ContainedViewLayoutTransitionCurve {
return .custom(0.33, 0.52, 0.25, 0.99)
}
}
public extension ContainedViewLayoutTransitionCurve {

View file

@ -36,15 +36,15 @@ public struct LayoutMetrics: Equatable {
}
public struct ContainerViewLayout: Equatable {
public let size: CGSize
public let metrics: LayoutMetrics
public let intrinsicInsets: UIEdgeInsets
public let safeInsets: UIEdgeInsets
public let statusBarHeight: CGFloat?
public var size: CGSize
public var metrics: LayoutMetrics
public var intrinsicInsets: UIEdgeInsets
public var safeInsets: UIEdgeInsets
public var statusBarHeight: CGFloat?
public var inputHeight: CGFloat?
public let standardInputHeight: CGFloat
public let inputHeightIsInteractivellyChanging: Bool
public let inVoiceOver: Bool
public var standardInputHeight: CGFloat
public var inputHeightIsInteractivellyChanging: Bool
public var inVoiceOver: Bool
public init(size: CGSize, metrics: LayoutMetrics, intrinsicInsets: UIEdgeInsets, safeInsets: UIEdgeInsets, statusBarHeight: CGFloat?, inputHeight: CGFloat?, standardInputHeight: CGFloat, inputHeightIsInteractivellyChanging: Bool, inVoiceOver: Bool) {
self.size = size
@ -63,7 +63,7 @@ public struct ContainerViewLayout: Equatable {
if let statusBarHeight = self.statusBarHeight , options.contains(.statusBar) {
insets.top += statusBarHeight
}
if let inputHeight = self.inputHeight , options.contains(.input) {
if let inputHeight = self.inputHeight, options.contains(.input) {
insets.bottom = max(inputHeight, insets.bottom)
}
return insets

View file

@ -939,12 +939,13 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
if let offset = offset {
let timingFunction = curve.timingFunction
let mediaTimingFunction = curve.mediaTimingFunction
for (index, itemNode) in self.itemNodes where existingItemIndices.contains(index) {
itemNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
itemNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, additive: true)
}
for (wrappedSection, sectionNode) in self.sectionNodes where existingSections.contains(wrappedSection) {
sectionNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
sectionNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, additive: true)
}
for index in self.itemNodes.keys {
@ -953,7 +954,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
self.removeItemNodeWithIndex(index, removeNode: false)
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset), to: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset), to: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
itemNode?.removeFromSupernode()
})
} else {
@ -965,7 +966,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
for itemNode in removedNodes {
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
itemNode?.removeFromSupernode()
})
} else {
@ -979,7 +980,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: sectionNode)] {
self.removeSectionNodeWithSection(wrappedSection, removeNode: false)
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
sectionNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak sectionNode] _ in
sectionNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak sectionNode] _ in
sectionNode?.removeFromSupernode()
})
} else {
@ -1006,6 +1007,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
}
} else if let previousItemFrames = previousItemFrames, case let .animated(duration, curve) = itemTransition {
let timingFunction = curve.timingFunction
let mediaTimingFunction = curve.mediaTimingFunction
let contentOffset = self.scrollView.contentOffset
for index in self.itemNodes.keys {
@ -1021,7 +1023,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
self.removeItemNodeWithIndex(index, removeNode: true)
}
} else if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
itemNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: itemNode.layer.position, duration: duration, timingFunction: timingFunction)
itemNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: itemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
} else {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12, timingFunction: kCAMediaTimingFunctionEaseIn)
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
@ -1051,7 +1053,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
self.removeSectionNodeWithSection(wrappedSection, removeNode: true)
}
} else if let previousFrame = previousItemFrames[WrappedGridItemNode(node: sectionNode)] {
sectionNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: sectionNode.layer.position, duration: duration, timingFunction: timingFunction)
sectionNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: sectionNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
} else {
sectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseIn)
}

View file

@ -2789,7 +2789,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
reverseAnimation = reverseBasicAnimation
} else {
let basicAnimation = CABasicAnimation(keyPath: "sublayerTransform")
basicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99)
basicAnimation.timingFunction = ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction
basicAnimation.duration = (duration ?? 0.3) * UIView.animationDurationFactor()
basicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, -offset, 0.0))
basicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity)
@ -2797,7 +2797,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
basicAnimation.isAdditive = true
let reverseBasicAnimation = CABasicAnimation(keyPath: "sublayerTransform")
reverseBasicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99)
reverseBasicAnimation.timingFunction = ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction
reverseBasicAnimation.duration = (duration ?? 0.3) * UIView.animationDurationFactor()
reverseBasicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, offset, 0.0))
reverseBasicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity)
@ -2966,7 +2966,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: p1, p2, p3, p4))
case .easeInOut:
if transition.1 {
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99))
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction)
} else {
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut))
}

View file

@ -751,7 +751,7 @@ open class NavigationController: UINavigationController, ContainableController,
}
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
navigationTransitionCoordinator.updateProgress()
navigationTransitionCoordinator.updateProgress(transition: transition)
}
}
@ -840,14 +840,14 @@ open class NavigationController: UINavigationController, ContainableController,
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
}
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar, didUpdateProgress: { [weak self] progress in
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar, didUpdateProgress: { [weak self] progress, transition in
if let strongSelf = self {
for i in 0 ..< strongSelf._viewControllers.count {
if let controller = strongSelf._viewControllers[i].controller as? ViewController {
if i < strongSelf._viewControllers.count - 1 {
controller.updateNavigationCustomData((strongSelf.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0 - progress, transition: .immediate)
controller.updateNavigationCustomData((strongSelf.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0 - progress, transition: transition)
} else {
controller.updateNavigationCustomData(nil, progress: 1.0 - progress, transition: .immediate)
controller.updateNavigationCustomData(nil, progress: 1.0 - progress, transition: transition)
}
}
}

View file

@ -21,7 +21,7 @@ class NavigationTransitionCoordinator {
}
set(value) {
self._progress = value
self.updateProgress()
self.updateProgress(transition: .immediate)
}
}
@ -39,8 +39,9 @@ class NavigationTransitionCoordinator {
private(set) var animatingCompletion = false
private var currentCompletion: (() -> Void)?
private var didUpdateProgress:((CGFloat)->Void)?
init(transition: NavigationTransition, container: UIView, topView: UIView, topNavigationBar: NavigationBar?, bottomView: UIView, bottomNavigationBar: NavigationBar?, didUpdateProgress: ((CGFloat) -> Void)? = nil) {
private var didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
init(transition: NavigationTransition, container: UIView, topView: UIView, topNavigationBar: NavigationBar?, bottomView: UIView, bottomNavigationBar: NavigationBar?, didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)? = nil) {
self.transition = transition
self.container = container
self.didUpdateProgress = didUpdateProgress
@ -79,14 +80,14 @@ class NavigationTransitionCoordinator {
self.viewSuperview?.insertSubview(self.shadowView, belowSubview: dimView)
self.maybeCreateNavigationBarTransition()
self.updateProgress()
self.updateProgress(transition: .immediate)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateProgress() {
func updateProgress(transition: ContainedViewLayoutTransition) {
let position: CGFloat
switch self.transition {
case .Push:
@ -111,7 +112,7 @@ class NavigationTransitionCoordinator {
self.updateNavigationBarTransition()
self.didUpdateProgress?(self.progress)
self.didUpdateProgress?(self.progress, transition)
}
func updateNavigationBarTransition() {

View file

@ -14,7 +14,7 @@ public enum PresentationContextType {
case window(PresentationSurfaceLevel)
}
final class PresentationContext {
public final class PresentationContext {
private var _view: UIView?
var view: UIView? {
get {

View file

@ -107,6 +107,14 @@ open class TabBarController: ViewController {
var currentController: ViewController?
open override var navigationBarRequiresEntireLayoutUpdate: Bool {
if let currentController = currentController {
return currentController.navigationBarRequiresEntireLayoutUpdate
} else {
return false
}
}
private let pendingControllerDisposable = MetaDisposable()
private var theme: TabBarControllerTheme
@ -168,7 +176,23 @@ open class TabBarController: ViewController {
}
}
if let validLayout = strongSelf.validLayout {
strongSelf.controllers[index].containerLayoutUpdated(validLayout.addedInsets(insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 49.0, right: 0.0)), transition: .immediate)
var updatedLayout = validLayout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if validLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = validLayout.insets(options: options).bottom
if !validLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
strongSelf.controllers[index].containerLayoutUpdated(updatedLayout, transition: .immediate)
}
let startTime = CFAbsoluteTimeGetCurrent()
strongSelf.pendingControllerDisposable.set((strongSelf.controllers[index].ready.get()
@ -263,7 +287,23 @@ open class TabBarController: ViewController {
if let currentController = self.currentController {
currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size)
currentController.containerLayoutUpdated(layout.addedInsets(insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 49.0, right: 0.0)), transition: transition)
var updatedLayout = layout
var tabBarHeight: CGFloat
var options: ContainerViewLayoutInsetOptions = []
if updatedLayout.metrics.widthClass == .regular {
options.insert(.input)
}
let bottomInset: CGFloat = updatedLayout.insets(options: options).bottom
if !updatedLayout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
updatedLayout.intrinsicInsets.bottom = tabBarHeight
currentController.containerLayoutUpdated(updatedLayout, transition: transition)
}
}

View file

@ -115,6 +115,16 @@ public extension UIColor {
return UIColor(hue: hue, saturation: saturation, brightness: max(0.0, min(1.0, brightness * factor)), alpha: alpha)
}
func withMultiplied(hue: CGFloat, saturation: CGFloat, brightness: CGFloat) -> UIColor {
var hueValue: CGFloat = 0.0
var saturationValue: CGFloat = 0.0
var brightnessValue: CGFloat = 0.0
var alphaValue: CGFloat = 0.0
self.getHue(&hueValue, saturation: &saturationValue, brightness: &brightnessValue, alpha: &alphaValue)
return UIColor(hue: max(0.0, min(1.0, hueValue * hue)), saturation: max(0.0, min(1.0, saturationValue * saturation)), brightness: max(0.0, min(1.0, brightnessValue * brightness)), alpha: alphaValue)
}
func mixedWith(_ other: UIColor, alpha: CGFloat) -> UIColor {
let alpha = min(1.0, max(0.0, alpha))
let oneMinusAlpha = 1.0 - alpha

View file

@ -65,7 +65,7 @@ open class ViewControllerPresentationArguments {
return self.validLayout
}
private let presentationContext: PresentationContext
public let presentationContext: PresentationContext
public final var supportedOrientations: ViewControllerSupportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
public final var lockedOrientation: UIInterfaceOrientationMask?
@ -81,6 +81,7 @@ open class ViewControllerPresentationArguments {
public final var isOpaqueWhenInOverlay: Bool = false
public final var blocksBackgroundWhenInOverlay: Bool = false
public final var automaticallyControlPresentationContextLayout: Bool = true
public func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations {
return self.supportedOrientations
@ -140,6 +141,9 @@ open class ViewControllerPresentationArguments {
private var previewingContext: Any?
public var displayNavigationBar = true
open var navigationBarRequiresEntireLayoutUpdate: Bool {
return true
}
private weak var activeInputViewCandidate: UIResponder?
private weak var activeInputView: UIResponder?
@ -258,7 +262,11 @@ open class ViewControllerPresentationArguments {
}
self.navigationBar?.requestContainerLayout = { [weak self] transition in
if let strongSelf = self, strongSelf.isNodeLoaded, let validLayout = strongSelf.validLayout {
strongSelf.updateNavigationBarLayout(validLayout, transition: transition)
if strongSelf.navigationBarRequiresEntireLayoutUpdate {
strongSelf.containerLayoutUpdated(validLayout, transition: transition)
} else {
strongSelf.updateNavigationBarLayout(validLayout, transition: transition)
}
}
}
self.navigationBar?.item = self.navigationItem
@ -325,7 +333,9 @@ open class ViewControllerPresentationArguments {
self.updateNavigationBarLayout(layout, transition: transition)
self.presentationContext.containerLayoutUpdated(layout, transition: transition)
if self.automaticallyControlPresentationContextLayout {
self.presentationContext.containerLayoutUpdated(layout, transition: transition)
}
if let scrollToTopView = self.scrollToTopView {
scrollToTopView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 10.0)

View file

@ -10,8 +10,8 @@ FOUNDATION_EXPORT const unsigned char GZipVersionString[];
extern "C" {
#endif
NSData *TGGZipData(NSData *data, float level);
NSData * _Nullable TGGUnzipData(NSData *data);
NSData * _Nonnull TGGZipData(NSData * _Nonnull data, float level);
NSData * _Nullable TGGUnzipData(NSData * _Nonnull data, uint sizeLimit);
#ifdef __cplusplus
}

View file

@ -42,7 +42,7 @@ NSData *TGGZipData(NSData *data, float level) {
return output;
}
NSData * _Nullable TGGUnzipData(NSData *data)
NSData * _Nullable TGGUnzipData(NSData *data, uint sizeLimit)
{
if (data.length == 0 || !TGIsGzippedData(data)) {
return nil;
@ -61,12 +61,20 @@ NSData * _Nullable TGGUnzipData(NSData *data)
int status = Z_OK;
output = [NSMutableData dataWithCapacity:data.length * 2];
while (status == Z_OK) {
if (sizeLimit > 0 && stream.total_out > sizeLimit) {
return nil;
}
if (stream.total_out >= output.length) {
output.length += data.length / 2;
NSUInteger length = output.length + data.length / 2;
if (sizeLimit > 0 && length > sizeLimit) {
return nil;
}
output.length = length;
}
stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out;
stream.avail_out = (uInt)(output.length - stream.total_out);
status = inflate (&stream, Z_SYNC_FLUSH);
status = inflate(&stream, Z_SYNC_FLUSH);
}
if (inflateEnd(&stream) == Z_OK) {
if (status == Z_STREAM_END) {

View file

@ -310,10 +310,8 @@ apple_library(
'LegacyComponents/TGPassportScanController.h',
'LegacyComponents/TGPassportOCR.h',
'LegacyComponents/TGPassportMRZ.h',
'LegacyComponents/TGPasscodeEntryController.h',
'LegacyComponents/TGPassportICloud.h',
'LegacyComponents/TGEmbedPlayerView.h',
'LegacyComponents/TGWallpaperInfo.h',
'LegacyComponents/LegacyHTTPRequestOperation.h',
'LegacyComponents/TGAttachmentCarouselItemView.h',
@ -342,14 +340,9 @@ apple_library(
'LegacyComponents/TGTooltipView.h',
'LegacyComponents/TGCheckButtonView.h',
'LegacyComponents/TGBuiltinWallpaperInfo.h',
'LegacyComponents/TGRemoteWallpaperInfo.h',
'LegacyComponents/TGColorWallpaperInfo.h',
'LegacyComponents/TGCustomImageWallpaperInfo.h',
'LegacyComponents/TGClipboardMenu.h',
'LegacyComponents/TGImagePickerController.h',
'LegacyComponents/TGLegacyCameraController.h',
'LegacyComponents/TGWallpaperController.h',
'LegacyComponents/TGProxyWindow.h',
'LegacyComponents/TGIconSwitchView.h',

View file

@ -295,10 +295,8 @@ FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[];
#import <LegacyComponents/TGPassportScanController.h>
#import <LegacyComponents/TGPassportOCR.h>
#import <LegacyComponents/TGPassportMRZ.h>
#import <LegacyComponents/TGPasscodeEntryController.h>
#import <LegacyComponents/TGPassportICloud.h>
#import <LegacyComponents/TGEmbedPlayerView.h>
#import <LegacyComponents/TGWallpaperInfo.h>
#import <LegacyComponents/TGMemoryImageCache.h>
#import <LegacyComponents/LegacyHTTPRequestOperation.h>
#import <LegacyComponents/LegacyComponentsAccessChecker.h>
@ -329,13 +327,8 @@ FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[];
#import <LegacyComponents/TGTooltipView.h>
#import <LegacyComponents/TGCheckButtonView.h>
#import <LegacyComponents/TGBuiltinWallpaperInfo.h>
#import <LegacyComponents/TGRemoteWallpaperInfo.h>
#import <LegacyComponents/TGColorWallpaperInfo.h>
#import <LegacyComponents/TGCustomImageWallpaperInfo.h>
#import <LegacyComponents/TGClipboardMenu.h>
#import <LegacyComponents/TGImagePickerController.h>
#import <LegacyComponents/TGLegacyCameraController.h>
#import <LegacyComponents/TGWallpaperController.h>
#import <LegacyComponents/TGProxyWindow.h>
#import <LegacyComponents/TGIconSwitchView.h>

View file

@ -9,7 +9,6 @@
@protocol SDisposable;
@class TGLocalization;
@class UIViewController;
@class TGWallpaperInfo;
@class TGMemoryImageCache;
@class TGImageMediaAttachment;
@class TGNavigationBarPallete;
@ -65,9 +64,6 @@ typedef enum {
- (id<SDisposable>)requestAudioSession:(TGAudioSessionType)type interrupted:(void (^)())interrupted;
- (TGWallpaperInfo *)currentWallpaperInfo;
- (UIImage *)currentWallpaperImage;
- (SThreadPool *)sharedMediaImageProcessingThreadPool;
- (TGMemoryImageCache *)sharedMediaMemoryImageCache;
- (SSignal *)squarePhotoThumbnail:(TGImageMediaAttachment *)imageAttachment ofSize:(CGSize)size threadPool:(SThreadPool *)threadPool memoryCache:(TGMemoryImageCache *)memoryCache pixelProcessingBlock:(void (^)(void *, int, int, int))pixelProcessingBlock downloadLargeImage:(bool)downloadLargeImage placeholder:(SSignal *)placeholder;

View file

@ -92,8 +92,10 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
- (void)handleEnteredBackground:(NSNotification *)__unused notification
{
if (self.isCapturing)
if (self.isCapturing) {
_wasCapturingOnEnterBackground = true;
[_previewView fadeOutAnimated:false];
}
[self stopCaptureForPause:true completion:nil];
}
@ -103,7 +105,13 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus";
if (_wasCapturingOnEnterBackground)
{
_wasCapturingOnEnterBackground = false;
[self startCaptureForResume:true completion:nil];
__weak PGCamera *weakSelf = self;
[self startCaptureForResume:true completion:^{
__strong PGCamera *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf->_previewView fadeInAnimated:true];
}
}];
}
}

View file

@ -1,13 +0,0 @@
#import <LegacyComponents/TGWallpaperInfo.h>
@interface TGBuiltinWallpaperInfo : TGWallpaperInfo
- (instancetype)initWithBuiltinId:(int)builtinId;
- (instancetype)initWithBuiltinId:(int)builtinId tintColor:(int)tintColor systemAlpha:(CGFloat)systemAlpha buttonsAlpha:(CGFloat)buttonsAlpha highlightedButtonAlpha:(CGFloat)highlightedButtonAlpha progressAlpha:(CGFloat)progressAlpha version:(int32_t)version;
- (BOOL)isDefault;
- (int32_t)version;
@end
extern const int32_t TGBuilitinWallpaperCurrentVersion;

View file

@ -1,140 +0,0 @@
#import "TGBuiltinWallpaperInfo.h"
const int32_t TGBuilitinWallpaperCurrentVersion = 1;
@interface TGBuiltinWallpaperInfo ()
{
int _builtinId;
int _tintColor;
int32_t _version;
CGFloat _systemAlpha;
CGFloat _buttonsAlpha;
CGFloat _highlightedButtonAlpha;
CGFloat _progressAlpha;
}
@end
@implementation TGBuiltinWallpaperInfo
- (instancetype)initWithBuiltinId:(int)builtinId
{
return [self initWithBuiltinId:builtinId tintColor:0x000000 systemAlpha:0.25f buttonsAlpha:0.35f highlightedButtonAlpha:0.50f progressAlpha:0.35f version:1];
}
- (instancetype)initWithBuiltinId:(int)builtinId tintColor:(int)tintColor systemAlpha:(CGFloat)systemAlpha buttonsAlpha:(CGFloat)buttonsAlpha highlightedButtonAlpha:(CGFloat)highlightedButtonAlpha progressAlpha:(CGFloat)progressAlpha version:(int32_t)version
{
self = [super init];
if (self != nil)
{
_builtinId = builtinId;
_tintColor = tintColor;
_systemAlpha = systemAlpha;
_buttonsAlpha = buttonsAlpha;
_highlightedButtonAlpha = highlightedButtonAlpha;
_progressAlpha = progressAlpha;
_version = version;
}
return self;
}
- (BOOL)isDefault
{
return _builtinId == 0;
}
- (int32_t)version
{
return _version;
}
- (NSString *)thumbnailUrl
{
return [[NSString alloc] initWithFormat:@"builtin-wallpaper://?id=%d&size=thumbnail", _builtinId];
}
- (NSString *)fullscreenUrl
{
return [[NSString alloc] initWithFormat:@"builtin-wallpaper://?id=%d", _builtinId];
}
- (int)tintColor
{
return _tintColor;
}
- (CGFloat)systemAlpha
{
return _systemAlpha;
}
- (CGFloat)buttonsAlpha
{
return _buttonsAlpha;
}
- (CGFloat)highlightedButtonAlpha
{
return _highlightedButtonAlpha;
}
- (CGFloat)progressAlpha
{
return _progressAlpha;
}
- (UIImage *)image
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithFormat:@"%@builtin-wallpaper-%d", [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? @"pad-" : @"", _builtinId] ofType:@"jpg"];
return [[UIImage alloc] initWithContentsOfFile:filePath];
}
- (NSData *)imageData
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithFormat:@"%@builtin-wallpaper-%d", [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? @"pad-" : @"", _builtinId] ofType:@"jpg"];
return [[NSData alloc] initWithContentsOfFile:filePath];
}
- (bool)hasData
{
return true;
}
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[TGBuiltinWallpaperInfo class]])
{
if (((TGBuiltinWallpaperInfo *)object)->_builtinId == _builtinId &&
((TGBuiltinWallpaperInfo *)object)->_tintColor == _tintColor &&
((TGBuiltinWallpaperInfo *)object)->_version == _version)
{
return true;
}
}
return false;
}
- (NSDictionary *)infoDictionary
{
return @{
@"_className": NSStringFromClass([self class]),
@"builtinId": @(_builtinId),
@"tintColor": @(_tintColor),
@"systemAlpha": @(_systemAlpha),
@"buttonsAlpha": @(_buttonsAlpha),
@"highlightedButtonAlpha": @(_highlightedButtonAlpha),
@"progressAlpha": @(_progressAlpha),
@"version": @(_version)
};
}
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)dict
{
return [[TGBuiltinWallpaperInfo alloc] initWithBuiltinId:[dict[@"builtinId"] intValue] tintColor:[dict[@"tintColor"] intValue] systemAlpha:[dict[@"systemAlpha"] floatValue] buttonsAlpha:[dict[@"buttonsAlpha"] floatValue] highlightedButtonAlpha:[dict[@"highlightedButtonAlpha"] floatValue] progressAlpha:[dict[@"progressAlpha"] floatValue] version:[dict[@"version"] intValue]];
}
@end

View file

@ -1,10 +0,0 @@
#import <LegacyComponents/TGWallpaperInfo.h>
@interface TGColorWallpaperInfo : TGWallpaperInfo
@property (nonatomic, readonly) uint32_t color;
- (instancetype)initWithColor:(uint32_t)color;
- (instancetype)initWithColor:(uint32_t)color tintColor:(int)tintColor systemAlpha:(CGFloat)systemAlpha buttonsAlpha:(CGFloat)buttonsAlpha highlightedButtonAlpha:(CGFloat)highlightedButtonAlpha progressAlpha:(CGFloat)progressAlpha;
@end

View file

@ -1,131 +0,0 @@
#import "TGColorWallpaperInfo.h"
#import "LegacyComponentsInternal.h"
@interface TGColorWallpaperInfo ()
{
int _tintColor;
CGFloat _systemAlpha;
CGFloat _buttonsAlpha;
CGFloat _highlightedButtonAlpha;
CGFloat _progressAlpha;
}
@end
@implementation TGColorWallpaperInfo
- (instancetype)initWithColor:(uint32_t)color
{
return [self initWithColor:color tintColor:0x000000 systemAlpha:0.25f buttonsAlpha:0.35f highlightedButtonAlpha:0.50f progressAlpha:0.35f];
}
- (instancetype)initWithColor:(uint32_t)color tintColor:(int)tintColor systemAlpha:(CGFloat)systemAlpha buttonsAlpha:(CGFloat)buttonsAlpha highlightedButtonAlpha:(CGFloat)highlightedButtonAlpha progressAlpha:(CGFloat)progressAlpha
{
self = [super init];
if (self != nil)
{
_color = color;
_tintColor = tintColor;
_systemAlpha = systemAlpha;
_buttonsAlpha = buttonsAlpha;
_highlightedButtonAlpha = highlightedButtonAlpha;
_progressAlpha = progressAlpha;
}
return self;
}
- (NSString *)thumbnailUrl
{
return [[NSString alloc] initWithFormat:@"color://?color=%d", (int)_color];
}
- (NSString *)fullscreenUrl
{
return [self thumbnailUrl];
}
- (int)tintColor
{
return _tintColor;
}
- (CGFloat)systemAlpha
{
return _systemAlpha;
}
- (CGFloat)buttonsAlpha
{
return _buttonsAlpha;
}
- (CGFloat)highlightedButtonAlpha
{
return _highlightedButtonAlpha;
}
- (CGFloat)progressAlpha
{
return _progressAlpha;
}
- (UIImage *)image
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), true, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGB(_color).CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 1.0f));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (NSData *)imageData
{
return nil;
}
- (bool)hasData
{
return false;
}
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[TGColorWallpaperInfo class]])
{
if (((TGColorWallpaperInfo *)object)->_color == _color &&
((TGColorWallpaperInfo *)object)->_tintColor == _tintColor)
{
return true;
}
}
return false;
}
- (NSDictionary *)infoDictionary
{
return @{
@"_className": NSStringFromClass([self class]),
@"color": @(_color),
@"tintColor": @(_tintColor),
@"systemAlpha": @(_systemAlpha),
@"buttonsAlpha": @(_buttonsAlpha),
@"highlightedButtonAlpha": @(_highlightedButtonAlpha),
@"progressAlpha": @(_progressAlpha)
};
}
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)dict
{
return [[TGColorWallpaperInfo alloc] initWithColor:[dict[@"color"] intValue] tintColor:[dict[@"tintColor"] intValue] systemAlpha:[dict[@"systemAlpha"] floatValue] buttonsAlpha:[dict[@"buttonsAlpha"] floatValue] highlightedButtonAlpha:[dict[@"highlightedButtonAlpha"] floatValue] progressAlpha:[dict[@"progressAlpha"] floatValue]];
}
@end

View file

@ -1,7 +0,0 @@
#import <LegacyComponents/TGWallpaperInfo.h>
@interface TGCustomImageWallpaperInfo : TGWallpaperInfo
- (instancetype)initWithImage:(UIImage *)image;
@end

View file

@ -1,77 +0,0 @@
#import "TGCustomImageWallpaperInfo.h"
@interface TGCustomImageWallpaperInfo ()
{
NSData *_imageData;
}
@end
@implementation TGCustomImageWallpaperInfo
- (instancetype)initWithImage:(UIImage *)image
{
self = [super init];
if (self != nil)
{
if (image != nil)
_imageData = UIImageJPEGRepresentation(image, 0.98f);
}
return self;
}
- (NSString *)thumbnailUrl
{
return nil;
}
- (NSString *)fullscreenUrl
{
return nil;
}
- (int)tintColor
{
return 0x000000;
}
- (UIImage *)image
{
return [[UIImage alloc] initWithData:_imageData];
}
- (NSData *)imageData
{
return _imageData;
}
- (bool)hasData
{
return true;
}
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[TGCustomImageWallpaperInfo class]])
{
return self == object;
}
return false;
}
- (NSDictionary *)infoDictionary
{
return @{
@"_className": NSStringFromClass([self class])
};
}
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)__unused dict
{
return [[TGCustomImageWallpaperInfo alloc] initWithImage:nil];
}
@end

View file

@ -1,5 +0,0 @@
#import <LegacyComponents/TGPasscodeBackground.h>
@interface TGDefaultPasscodeBackground : NSObject <TGPasscodeBackground>
@end

View file

@ -1,79 +0,0 @@
#import "TGDefaultPasscodeBackground.h"
#import "LegacyComponentsInternal.h"
#import <UIKit/UIKit.h>
@interface TGDefaultPasscodeBackground ()
{
CGSize _size;
UIImage *_backgroundImage;
}
@end
@implementation TGDefaultPasscodeBackground
- (instancetype)initWithSize:(CGSize)size
{
self = [super init];
if (self != nil)
{
_size = size;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(8.0f, _size.height), true, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef colors[2] = {
CGColorRetain(UIColorRGB(0x466f92).CGColor),
CGColorRetain(UIColorRGB(0x244f74).CGColor)
};
CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 2, NULL);
CGFloat locations[2] = {0.0f, 1.0f};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations);
CFRelease(colorsArray);
CFRelease(colors[0]);
CFRelease(colors[1]);
CGColorSpaceRelease(colorSpace);
CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0f, 0.0f), CGPointMake(0.0f, _size.height), 0);
CGGradientRelease(gradient);
_backgroundImage = [UIGraphicsGetImageFromCurrentImageContext() resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f) resizingMode:UIImageResizingModeTile];
UIGraphicsEndImageContext();
}
return self;
}
- (CGSize)size
{
return _size;
}
- (UIImage *)backgroundImage
{
return _backgroundImage;
}
- (UIImage *)foregroundImage
{
static UIImage *image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 1.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, UIColorRGBA(0xffffff, 0.5f).CGColor);
CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 1.0f));
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
});
return image;
}
@end

View file

@ -1,7 +0,0 @@
#import "TGPasscodeBackground.h"
@interface TGImageBasedPasscodeBackground : NSObject <TGPasscodeBackground>
- (instancetype)initWithImage:(UIImage *)image size:(CGSize)size;
@end

View file

@ -1,53 +0,0 @@
#import "TGImageBasedPasscodeBackground.h"
#import <LegacyComponents/TGImageBlur.h>
@interface TGImageBasedPasscodeBackground ()
{
CGSize _size;
UIImage *_backgroundImage;
UIImage *_foregroundImage;
}
@end
@implementation TGImageBasedPasscodeBackground
- (instancetype)initWithSize:(CGSize)size
{
return [self initWithImage:nil size:size];
}
- (instancetype)initWithImage:(UIImage *)image size:(CGSize)size
{
self = [super init];
if (self != nil)
{
_size = size;
if (image != nil)
{
NSArray *images = TGBlurredBackgroundImages(image, size);
_backgroundImage = images[0];
_foregroundImage = images[1];
}
}
return self;
}
- (CGSize)size
{
return _size;
}
- (UIImage *)backgroundImage
{
return _backgroundImage;
}
- (UIImage *)foregroundImage
{
return _foregroundImage;
}
@end

View file

@ -232,7 +232,10 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius
}
UIView *parentView = [_presentation view];
CGPoint centerPoint = [self.superview convertPoint:self.center toView:parentView];
CGPoint centerPointInSelfWindow = [self.window convertPoint:self.center fromView:self.superview];
CGPoint centerPointInParentViewWindow = [self.window convertPoint:centerPointInSelfWindow toWindow:parentView.window];
CGPoint centerPoint = [parentView.window convertPoint:centerPointInParentViewWindow toView:parentView];
centerPoint.x += _centerOffset.x;
centerPoint.y += _centerOffset.y;
_innerCircleView.center = centerPoint;

View file

@ -1,12 +0,0 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@protocol TGPasscodeBackground <NSObject>
- (instancetype)initWithSize:(CGSize)size;
- (CGSize)size;
- (UIImage *)backgroundImage;
- (UIImage *)foregroundImage;
@end

View file

@ -1,12 +0,0 @@
#import <LegacyComponents/TGModernButton.h>
#import "TGPasscodeBackground.h"
@interface TGPasscodeButtonView : TGModernButton
- (void)setAbsoluteOffset:(CGPoint)absoluteOffset;
- (void)setTitle:(NSString *)title subtitle:(NSString *)subtitle;
- (void)setBackground:(id<TGPasscodeBackground>)background;
@end

View file

@ -1,206 +0,0 @@
#import "TGPasscodeButtonView.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
#import "POPAnimation.h"
#import "POPBasicAnimation.h"
#import "TGImageUtils.h"
@interface TGPasscodeButtonView ()
{
id<TGPasscodeBackground> _background;
CGFloat _highlightAmount;
UIFont *_titleFont;
NSString *_title;
UIFont *_subtitleFont;
NSString *_subtitle;
CGPoint _absoluteOffset;
bool _highligted;
}
@end
@implementation TGPasscodeButtonView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.opaque = false;
self.backgroundColor = nil;
self.modernHighlight = false;
CGFloat titleFontSize = 0.0f;
CGFloat subtitleFontSize = 0.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1366)
{
titleFontSize = 38.0f;
subtitleFontSize = 10.0f;
}
else
{
titleFontSize = 36.0f;
subtitleFontSize = 9.0f;
}
_titleFont = TGUltralightSystemFontOfSize(titleFontSize);
_subtitleFont = TGMediumSystemFontOfSize(subtitleFontSize);
}
return self;
}
- (void)setTitle:(NSString *)title subtitle:(NSString *)subtitle
{
_title = title;
_subtitle = subtitle;
[self setNeedsDisplay];
}
- (void)setBackground:(id<TGPasscodeBackground>)background
{
_background = background;
[self setNeedsDisplay];
}
- (void)setFrame:(CGRect)frame
{
bool needsDisplay = !CGSizeEqualToSize(frame.size, self.frame.size);
[super setFrame:frame];
if (needsDisplay)
[self setNeedsDisplay];
}
- (void)setAbsoluteOffset:(CGPoint)absoluteOffset
{
if (!CGPointEqualToPoint(_absoluteOffset, absoluteOffset))
{
_absoluteOffset = absoluteOffset;
[self setNeedsDisplay];
}
}
- (void)_setHighligtedAnimated:(bool)highlighted animated:(bool)animated
{
if (_highligted != highlighted)
{
_highligted = highlighted;
[self pop_removeAllAnimations];
if (animated && !highlighted)
{
POPBasicAnimation *animation = [POPBasicAnimation animation];
animation.property = [POPMutableAnimatableProperty propertyWithName:@"highlightAmount" initializer:^(POPMutableAnimatableProperty *prop)
{
prop.readBlock = ^(TGPasscodeButtonView *view, CGFloat *values)
{
if (view != nil)
values[0] = view->_highlightAmount;
};
prop.writeBlock = ^(TGPasscodeButtonView *view, CGFloat const *values)
{
if (view != nil)
{
view->_highlightAmount = values[0];
[view setNeedsDisplay];
}
};
}];
animation.duration = 0.5;
animation.fromValue = @(_highlightAmount);
animation.toValue = @((CGFloat)0.0f);
[self pop_addAnimation:animation forKey:@"highlightAmount"];
}
else
{
_highlightAmount = highlighted ? 1.0f : 0.0f;
[self setNeedsDisplay];
}
}
}
- (void)drawRect:(CGRect)__unused rect
{
static CGFloat topOffset = 0.0f;
static CGFloat subtitleOffset = 0.0f;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1366)
{
topOffset = 1.0f;
subtitleOffset = 2.0f;
}
});
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize size = self.bounds.size;
[[_background backgroundImage] drawInRect:CGRectMake(-_absoluteOffset.x, -_absoluteOffset.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeCopy alpha:1.0f];
CGContextBeginPath(context);
CGContextAddEllipseInRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
CGContextClip(context);
[[_background foregroundImage] drawInRect:CGRectMake(-_absoluteOffset.x, -_absoluteOffset.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeNormal alpha:1.0f];
if (_highlightAmount < FLT_EPSILON)
{
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(1.5f, 1.5f, size.width - 3.0f, size.height - 3.0f));
}
else if (_highlightAmount < 1.0f - FLT_EPSILON)
{
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:0.0f alpha:_highlightAmount].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(1.5f, 1.5f, size.width - 3.0f, size.height - 3.0f));
}
CGSize titleSize = [_title sizeWithFont:_titleFont];
titleSize.width = CGCeil(titleSize.width);
titleSize.height = CGCeil(titleSize.height);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
[_title drawAtPoint:CGPointMake(CGFloor((size.width - titleSize.width) / 2.0f), 8.0f + TGRetinaPixel + ([_title isEqualToString:@"0"] ? (7.0f - TGRetinaPixel) : 0.0f) + topOffset) withFont:_titleFont];
if (iosMajorVersion() >= 7)
{
NSDictionary *subtitleAttributes = @{NSFontAttributeName: _subtitleFont, NSForegroundColorAttributeName: [UIColor whiteColor], NSKernAttributeName: @(2.0f)};
CGSize subtitleSize = [_subtitle sizeWithAttributes:subtitleAttributes];
[_subtitle drawAtPoint:CGPointMake(CGFloor((size.width - subtitleSize.width) / 2.0f) + 1.0f, 48.0f - TGRetinaPixel + topOffset + subtitleOffset) withAttributes:subtitleAttributes];
}
else
{
CGSize subtitleSize = [_subtitle sizeWithFont:_subtitleFont];
subtitleSize.width = CGCeil(subtitleSize.width);
subtitleSize.height = CGCeil(subtitleSize.height);
[_subtitle drawAtPoint:CGPointMake(CGFloor((size.width - subtitleSize.width) / 2.0f) + 1.0f, 48.0f - TGRetinaPixel + topOffset + subtitleOffset) withFont:_subtitleFont];
}
}
@end

View file

@ -1,48 +0,0 @@
#import <LegacyComponents/TGViewController.h>
#import <LegacyComponents/LegacyComponentsGlobals.h>
typedef enum {
TGPasscodeEntryControllerModeVerifySimple,
TGPasscodeEntryControllerModeVerifyComplex,
TGPasscodeEntryControllerModeSetupSimple,
TGPasscodeEntryControllerModeSetupComplex,
TGPasscodeEntryControllerModeChangeSimpleToComplex,
TGPasscodeEntryControllerModeChangeSimpleToSimple,
TGPasscodeEntryControllerModeChangeComplexToSimple,
TGPasscodeEntryControllerModeChangeComplexToComplex
} TGPasscodeEntryControllerMode;
typedef enum {
TGPasscodeEntryControllerStyleDefault,
TGPasscodeEntryControllerStyleTranslucent
} TGPasscodeEntryControllerStyle;
@interface TGPasscodeEntryAttemptData : NSObject
@property (nonatomic, readonly) NSInteger numberOfInvalidAttempts;
@property (nonatomic, readonly) double dateOfLastInvalidAttempt;
- (instancetype)initWithNumberOfInvalidAttempts:(NSInteger)numberOfInvalidAttempts dateOfLastInvalidAttempt:(double)dateOfLastInvalidAttempt;
@end
@interface TGPasscodeEntryController : TGViewController
@property (nonatomic, copy) void (^completion)(NSString *);
@property (nonatomic, copy) void (^touchIdCompletion)();
@property (nonatomic, copy) bool (^checkCurrentPasscode)(NSString *);
@property (nonatomic, copy) void (^updateAttemptData)(TGPasscodeEntryAttemptData *);
@property (nonatomic) bool allowTouchId;
+ (void)setGlobalsProvider:(id<LegacyComponentsGlobalsProvider>)provider;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context style:(TGPasscodeEntryControllerStyle)style mode:(TGPasscodeEntryControllerMode)mode cancelEnabled:(bool)cancelEnabled allowTouchId:(bool)allowTouchId attemptData:(TGPasscodeEntryAttemptData *)attemptData completion:(void (^)(NSString *))completion;
- (void)resetMode:(TGPasscodeEntryControllerMode)mode;
- (void)refreshTouchId;
- (NSInteger)invalidPasscodeAttempts;
- (void)prepareForAppear;
- (void)prepareForDisappear;
@end

View file

@ -1,602 +0,0 @@
#import "TGPasscodeEntryController.h"
#import "TGPasswordEntryView.h"
#import "TGStringUtils.h"
#import "TGTimerTarget.h"
#import "TGObserverProxy.h"
#import <AudioToolbox/AudioToolbox.h>
#import <LocalAuthentication/LocalAuthentication.h>
#import "LegacyComponentsInternal.h"
typedef enum {
TGPasscodeEntryControllerSubmodeEnteringCurrent,
TGPasscodeEntryControllerSubmodeEnteringNew,
TGPasscodeEntryControllerSubmodeReenteringNew
} TGPasscodeEntryControllerSubmode;
@implementation TGPasscodeEntryAttemptData
- (instancetype)initWithNumberOfInvalidAttempts:(NSInteger)numberOfInvalidAttempts dateOfLastInvalidAttempt:(double)dateOfLastInvalidAttempt {
self = [super init];
if (self != nil) {
_numberOfInvalidAttempts = numberOfInvalidAttempts;
_dateOfLastInvalidAttempt = dateOfLastInvalidAttempt;
}
return self;
}
@end
@interface TGPasscodeEntryController ()
{
TGPasscodeEntryControllerStyle _style;
TGPasscodeEntryControllerMode _mode;
TGPasscodeEntryControllerSubmode _submode;
bool _cancelEnabled;
NSString *_candidatePasscode;
TGPasswordEntryView *_view;
UIBarButtonItem *_nextItem;
bool _usingTouchId;
bool _alternativeMethodSelected;
NSTimer *_shouldWaitTimer;
bool _keepStatusBarStyle;
TGPasscodeEntryAttemptData *_attemptData;
}
@end
@implementation TGPasscodeEntryController
+ (void)setGlobalsProvider:(id<LegacyComponentsGlobalsProvider>)provider {
[LegacyComponentsGlobals setProvider:provider];
}
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context style:(TGPasscodeEntryControllerStyle)style mode:(TGPasscodeEntryControllerMode)mode cancelEnabled:(bool)cancelEnabled allowTouchId:(bool)allowTouchId attemptData:(TGPasscodeEntryAttemptData *)attemptData completion:(void (^)(NSString *))completion
{
self = [super initWithContext:context];
if (self != nil)
{
_style = style;
_mode = mode;
_completion = [completion copy];
_allowTouchId = allowTouchId;
_cancelEnabled = cancelEnabled;
_keepStatusBarStyle = true;
_attemptData = attemptData;
switch (_mode)
{
case TGPasscodeEntryControllerModeVerifySimple:
case TGPasscodeEntryControllerModeVerifyComplex:
{
break;
}
case TGPasscodeEntryControllerModeSetupSimple:
case TGPasscodeEntryControllerModeSetupComplex:
{
_submode = TGPasscodeEntryControllerSubmodeEnteringNew;
break;
}
case TGPasscodeEntryControllerModeChangeSimpleToSimple:
case TGPasscodeEntryControllerModeChangeSimpleToComplex:
case TGPasscodeEntryControllerModeChangeComplexToSimple:
case TGPasscodeEntryControllerModeChangeComplexToComplex:
{
_submode = TGPasscodeEntryControllerSubmodeEnteringCurrent;
break;
}
}
self.navigationBarShouldBeHidden = true;
if ([self invalidPasscodeAttempts] >= 6 && ![self shouldWaitBeforeAttempting])
{
[self resetInvalidPasscodeAttempts];
}
[self resetMode:_mode];
_shouldWaitTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(checkShouldWait) interval:1.0 repeat:true];
}
return self;
}
- (void)dealloc
{
[_shouldWaitTimer invalidate];
_shouldWaitTimer = nil;
}
- (void)cancelPressed
{
if (_completion)
_completion(nil);
[_view resignFirstResponder];
}
- (void)nextPressed
{
[self passcodeEntered:[_view passcode]];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return _keepStatusBarStyle ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}
- (TGPasswordEntryViewStyle)_passwordEntryViewStyleForStyle:(TGPasscodeEntryControllerStyle)style
{
switch (style)
{
case TGPasscodeEntryControllerStyleDefault:
return TGPasswordEntryViewStyleDefault;
case TGPasscodeEntryControllerStyleTranslucent:
return TGPasswordEntryViewStyleTranslucent;
}
}
- (void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor whiteColor];
CGSize screenSize = self.view.frame.size;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{
screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
}
_view = [[TGPasswordEntryView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height) style:[self _passwordEntryViewStyleForStyle:_style]];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
__weak TGPasscodeEntryController *weakSelf = self;
if (_cancelEnabled)
{
_view.cancel = ^
{
__strong TGPasscodeEntryController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf cancelPressed];
};
}
_view.simplePasscodeEntered = ^
{
__strong TGPasscodeEntryController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf passcodeEntered:[strongSelf->_view passcode]];
};
_view.complexPasscodeEntered = ^
{
__strong TGPasscodeEntryController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf passcodeEntered:[strongSelf->_view passcode]];
};
_view.passcodeChanged = ^(NSString *passcode)
{
__strong TGPasscodeEntryController *strongSelf = weakSelf;
if (strongSelf != nil)
strongSelf->_nextItem.enabled = passcode.length != 0;
};
[self.view addSubview:_view];
[self resetMode:_mode];
[_view becomeFirstResponder];
}
- (bool)supportsTouchId
{
if (iosMajorVersion() >= 8)
{
if ([[[LAContext alloc] init] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil])
return true;
}
return false;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
/*if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
UIView *snapshotView = [_view snapshotViewAfterScreenUpdates:false];
snapshotView.frame = _view.frame;
[self.view addSubview:snapshotView];
[UIView animateWithDuration:duration animations:^
{
snapshotView.alpha = 0.0f;
snapshotView.frame = (CGRect){CGPointZero, [self referenceViewSizeForOrientation:toInterfaceOrientation]};
} completion:^(__unused BOOL finished)
{
[snapshotView removeFromSuperview];
}];
}*/
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)prepareForAppear
{
[_view updateBackgroundIfNeeded];
_keepStatusBarStyle = true;
if (iosMajorVersion() >= 7)
[self setNeedsStatusBarAppearanceUpdate];
[_view becomeFirstResponder];
}
- (void)prepareForDisappear
{
_keepStatusBarStyle = false;
if (iosMajorVersion() >= 7)
[self setNeedsStatusBarAppearanceUpdate];
}
- (void)refreshTouchId
{
[_view resignFirstResponder];
[_view becomeFirstResponder];
if (!_usingTouchId && !_alternativeMethodSelected && _allowTouchId && [self supportsTouchId] && _touchIdCompletion)
{
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error])
{
_usingTouchId = true;
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:TGLocalized(@"EnterPasscode.TouchId") reply:^(BOOL success, NSError *error)
{
if (error != nil)
{
TGDispatchOnMainThread(^
{
_usingTouchId = false;
_alternativeMethodSelected = true;
});
}
else
{
if (success)
{
TGDispatchOnMainThread(^
{
_usingTouchId = false;
if (_touchIdCompletion)
_touchIdCompletion();
});
}
else
{
TGDispatchOnMainThread(^
{
_usingTouchId = false;
});
}
}
}];
}
}
}
- (NSInteger)invalidPasscodeAttempts {
return _attemptData.numberOfInvalidAttempts;
}
- (void)addInvalidPasscodeAttempt {
if (_attemptData == nil) {
_attemptData = [[TGPasscodeEntryAttemptData alloc] initWithNumberOfInvalidAttempts:1 dateOfLastInvalidAttempt:CFAbsoluteTimeGetCurrent()];
} else {
_attemptData = [[TGPasscodeEntryAttemptData alloc] initWithNumberOfInvalidAttempts:_attemptData.numberOfInvalidAttempts + 1 dateOfLastInvalidAttempt:CFAbsoluteTimeGetCurrent()];
}
if (_updateAttemptData) {
_updateAttemptData(_attemptData);
}
}
- (void)resetInvalidPasscodeAttempts {
_attemptData = nil;
if (_updateAttemptData) {
_updateAttemptData(_attemptData);
}
}
- (bool)shouldWaitBeforeAttempting
{
if (_attemptData == nil || [self invalidPasscodeAttempts] < 6)
return false;
NSTimeInterval invalidAttemptDate = _attemptData.dateOfLastInvalidAttempt;
NSTimeInterval waitInterval = 60.0;
#ifdef DEBUG
waitInterval = 5.0;
#endif
return CFAbsoluteTimeGetCurrent() - invalidAttemptDate < waitInterval;
}
- (NSTimeInterval)intervalSinceLastInvalidPasscodeAttempt
{
if (_attemptData == nil) {
return 9999999.0;
} else {
return CFAbsoluteTimeGetCurrent() - _attemptData.dateOfLastInvalidAttempt;
}
}
- (void)checkShouldWait
{
if ([self invalidPasscodeAttempts] >= 6 && ![self shouldWaitBeforeAttempting])
{
[self resetInvalidPasscodeAttempts];
}
[_view setErrorTitle:[self currentErrorText]];
}
- (NSString *)currentErrorText
{
NSInteger attemptCount = [self invalidPasscodeAttempts];
if (attemptCount == 0)
return @"";
else if (attemptCount < 6)
{
NSString *format = [TGStringUtils integerValueFormat:@"PasscodeSettings.FailedAttempts_" value:attemptCount];
return [[NSString alloc] initWithFormat:TGLocalized(format), [[NSString alloc] initWithFormat:@"%d",(int)attemptCount]];
}
else
return TGLocalized(@"PasscodeSettings.TryAgainIn1Minute");
}
- (void)resetMode:(TGPasscodeEntryControllerMode)mode
{
_candidatePasscode = nil;
_mode = mode;
switch (_mode)
{
case TGPasscodeEntryControllerModeVerifySimple:
case TGPasscodeEntryControllerModeVerifyComplex:
{
[_view setTitle:TGLocalized(@"EnterPasscode.EnterPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeVerifyComplex animated:false];
if (_mode == TGPasscodeEntryControllerModeVerifySimple)
{
if (_cancelEnabled)
{
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
}
}
else if (_mode == TGPasscodeEntryControllerModeVerifyComplex)
{
if (_cancelEnabled)
{
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
}
_nextItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Next") style:UIBarButtonItemStyleDone target:self action:@selector(nextPressed)];
[self setRightBarButtonItem:_nextItem];
_nextItem.enabled = false;
}
[_view setReturnKeyType:UIReturnKeyDone];
break;
}
case TGPasscodeEntryControllerModeSetupSimple:
case TGPasscodeEntryControllerModeSetupComplex:
{
[_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeNew") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:false];
[self setLeftBarButtonItem:nil];
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
[_view setReturnKeyType:UIReturnKeyNext];
break;
}
case TGPasscodeEntryControllerModeChangeComplexToSimple:
case TGPasscodeEntryControllerModeChangeComplexToComplex:
case TGPasscodeEntryControllerModeChangeSimpleToSimple:
case TGPasscodeEntryControllerModeChangeSimpleToComplex:
{
bool isComplex = _mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex;
[_view setTitle:TGLocalized(@"EnterPasscode.EnterCurrentPasscode") errorTitle:[self currentErrorText] isComplex:isComplex animated:false];
if (_mode == TGPasscodeEntryControllerModeChangeSimpleToSimple)
{
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
}
else
{
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
}
_nextItem = [[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Next") style:UIBarButtonItemStyleDone target:self action:@selector(nextPressed)];
if (_mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex)
{
[self setRightBarButtonItem:_nextItem];
}
_nextItem.enabled = false;
[_view setReturnKeyType:UIReturnKeyNext];
break;
}
}
[_view becomeFirstResponder];
}
- (void)passcodeEntered:(NSString *)passcode
{
if ([self shouldWaitBeforeAttempting])
{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
[_view resetPasscode];
return;
}
else if ([self invalidPasscodeAttempts] >= 6)
{
[self resetInvalidPasscodeAttempts];
}
switch (_mode)
{
case TGPasscodeEntryControllerModeVerifySimple:
case TGPasscodeEntryControllerModeVerifyComplex:
{
_alternativeMethodSelected = false;
if (_checkCurrentPasscode)
{
if (_checkCurrentPasscode(passcode))
{
[self resetInvalidPasscodeAttempts];
if (_completion)
_completion(passcode);
}
else
{
[self addInvalidPasscodeAttempt];
[_view setTitle:TGLocalized(@"EnterPasscode.EnterPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeVerifyComplex animated:false];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
}
else
{
if (_completion)
_completion(passcode);
}
break;
}
case TGPasscodeEntryControllerModeSetupSimple:
case TGPasscodeEntryControllerModeSetupComplex:
{
switch (_submode)
{
case TGPasscodeEntryControllerSubmodeEnteringNew:
{
_candidatePasscode = passcode;
_submode = TGPasscodeEntryControllerSubmodeReenteringNew;
[_view setTitle:TGLocalized(@"EnterPasscode.RepeatNewPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:true];
[_view setReturnKeyType:UIReturnKeyDone];
break;
}
case TGPasscodeEntryControllerSubmodeReenteringNew:
{
if ([passcode isEqualToString:_candidatePasscode])
{
if (_completion)
_completion(passcode);
}
else
{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
_candidatePasscode = nil;
_submode = TGPasscodeEntryControllerSubmodeEnteringNew;
[_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeNew") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeSetupComplex animated:true];
}
break;
}
default:
break;
}
break;
}
case TGPasscodeEntryControllerModeChangeSimpleToSimple:
case TGPasscodeEntryControllerModeChangeSimpleToComplex:
case TGPasscodeEntryControllerModeChangeComplexToSimple:
case TGPasscodeEntryControllerModeChangeComplexToComplex:
{
switch (_submode)
{
case TGPasscodeEntryControllerSubmodeEnteringCurrent:
{
if (_checkCurrentPasscode && _checkCurrentPasscode(passcode))
{
[self resetInvalidPasscodeAttempts];
_candidatePasscode = nil;
_submode = TGPasscodeEntryControllerSubmodeEnteringNew;
[_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeChange") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
[_view setReturnKeyType:UIReturnKeyNext];
if (_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex)
[self setRightBarButtonItem:_nextItem];
else
[self setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelPressed)]];
}
else
{
[self addInvalidPasscodeAttempt];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
_candidatePasscode = nil;
_submode = TGPasscodeEntryControllerSubmodeEnteringCurrent;
[_view setTitle:TGLocalized(@"EnterPasscode.EnterCurrentPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeComplexToSimple || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
[_view setReturnKeyType:UIReturnKeyNext];
}
break;
}
case TGPasscodeEntryControllerSubmodeEnteringNew:
{
_candidatePasscode = passcode;
_submode = TGPasscodeEntryControllerSubmodeReenteringNew;
[_view setTitle:TGLocalized(@"EnterPasscode.RepeatNewPasscode") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
[_view setReturnKeyType:UIReturnKeyDone];
break;
}
case TGPasscodeEntryControllerSubmodeReenteringNew:
{
if ([_candidatePasscode isEqualToString:passcode])
{
if (_completion)
_completion(passcode);
}
else
{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
_candidatePasscode = nil;
_submode = TGPasscodeEntryControllerSubmodeEnteringNew;
[_view setTitle:TGLocalized(@"EnterPasscode.EnterNewPasscodeChange") errorTitle:[self currentErrorText] isComplex:_mode == TGPasscodeEntryControllerModeChangeSimpleToComplex || _mode == TGPasscodeEntryControllerModeChangeComplexToComplex animated:true];
[_view setReturnKeyType:UIReturnKeyNext];
}
break;
}
}
break;
}
}
}
@end

View file

@ -1,11 +0,0 @@
#import <UIKit/UIKit.h>
#import "TGPasscodeBackground.h"
@interface TGPasscodeEntryKeyboardView : UIView
@property (nonatomic, copy) void (^characterEntered)(NSString *);
- (void)setBackground:(id<TGPasscodeBackground>)background;
@end

View file

@ -1,216 +0,0 @@
#import "TGPasscodeEntryKeyboardView.h"
#import <LegacyComponents/LegacyComponents.h>
#import "TGPasscodeButtonView.h"
@interface TGPasscodeEntryKeyboardView ()
{
id<TGPasscodeBackground> _background;
NSArray *_buttonViews;
}
@end
@implementation TGPasscodeEntryKeyboardView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
[self _generateButtonViews];
}
return self;
}
- (NSArray *)_buttonViewInfos
{
NSArray *infos = @[
@[@"1", @""],
@[@"2", @"ABC"],
@[@"3", @"DEF"],
@[@"4", @"GHI"],
@[@"5", @"JKL"],
@[@"6", @"MNO"],
@[@"7", @"PQRS"],
@[@"8", @"TUV"],
@[@"9", @"WXYZ"],
@[@"0", @""],
];
return infos;
}
- (void)_generateButtonViews
{
NSArray *infos = [self _buttonViewInfos];
NSMutableArray *buttonViews = [[NSMutableArray alloc] init];
for (NSArray *desc in infos)
{
TGPasscodeButtonView *buttonView = [[TGPasscodeButtonView alloc] init];
[buttonView addTarget:self action:@selector(buttonTouchDown:) forControlEvents:UIControlEventTouchDown];
[buttonView setTitle:desc[0] subtitle:desc[1]];
[self addSubview:buttonView];
[buttonViews addObject:buttonView];
}
_buttonViews = buttonViews;
[self _layoutButtons];
}
- (void)buttonTouchDown:(TGPasscodeButtonView *)buttonView
{
NSArray *infos = [self _buttonViewInfos];
NSUInteger buttonIndex = NSNotFound;
for (NSUInteger index = 0; index < _buttonViews.count; index++)
{
if (buttonView == _buttonViews[index])
{
buttonIndex = index;
break;
}
}
if (buttonIndex != NSNotFound && buttonIndex < infos.count)
{
if (_characterEntered)
_characterEntered(((NSArray *)infos[buttonIndex])[0]);
}
}
- (void)sizeToFit
{
CGSize size = CGSizeZero;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
size = CGSizeMake(293.0f, 384.0f);
else if ((int)screenSize.height == 736)
size = CGSizeMake(281.0f, 345.0f);
else if ((int)screenSize.height == 667)
size = CGSizeMake(281.0f, 345.0f);
else if ((int)screenSize.height == 568)
size = CGSizeMake(265.0f, 339.0f);
else
size = CGSizeMake(265.0f, 339.0f);
self.frame = (CGRect){self.frame.origin, size};
}
- (void)setBackground:(id<TGPasscodeBackground>)background
{
_background = background;
for (TGPasscodeButtonView *buttonView in _buttonViews)
{
[buttonView setBackground:background];
}
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
for (TGPasscodeButtonView *buttonView in _buttonViews)
{
[buttonView setAbsoluteOffset:CGPointMake(frame.origin.x + buttonView.frame.origin.x, frame.origin.y + buttonView.frame.origin.y)];
}
}
- (void)_layoutButtons
{
CGFloat buttonSize = 0.0f;
CGFloat horizontalSecond = 0.0f;
CGFloat horizontalThird = 0.0f;
CGFloat verticalSecond = 0.0f;
CGFloat verticalThird = 0.0f;
CGFloat verticalFourth = 0.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
{
buttonSize = 81.0f;
horizontalSecond = 106.0f;
horizontalThird = 212.0f;
verticalSecond = 100.0f + TGRetinaPixel;
verticalThird = 202.0f;
verticalFourth = 303.0f;
}
else if ((int)screenSize.height == 736)
{
buttonSize = 75.0f;
horizontalSecond = 103.5f;
horizontalThird = 206.0f;
verticalSecond = 90.0f;
verticalThird = 180.0f;
verticalFourth = 270.0f;
}
else if ((int)screenSize.height == 667)
{
buttonSize = 75.0f;
horizontalSecond = 103.5f;
horizontalThird = 206.0f;
verticalSecond = 90.0f;
verticalThird = 180.0f;
verticalFourth = 270.0f;
}
else if ((int)screenSize.height == 568)
{
buttonSize = 75.0f;
horizontalSecond = 95.0f;
horizontalThird = 190.0f;
verticalSecond = 88.0f;
verticalThird = 176.0f;
verticalFourth = 264.0f;
}
else
{
buttonSize = 75.0f;
horizontalSecond = 95.0f;
horizontalThird = 190.0f;
verticalSecond = 88.0f;
verticalThird = 176.0f;
verticalFourth = 264.0f;
}
for (NSUInteger i = 0; i < _buttonViews.count; i++)
{
CGPoint position = CGPointZero;
if (i % 3 == 0)
position.x = 0.0f;
else if (i % 3 == 1)
position.x = horizontalSecond;
else
position.x = horizontalThird;
if (i / 3 == 0)
position.y = 0.0f;
else if (i / 3 == 1)
position.y = verticalSecond;
else if (i / 3 == 2)
position.y = verticalThird;
else if (i / 3 == 3)
{
position.x = horizontalSecond;
position.y = verticalFourth;
}
[(TGPasscodeButtonView *)_buttonViews[i] setFrame:CGRectMake(position.x, position.y, buttonSize, buttonSize)];
}
}
@end

View file

@ -1,14 +0,0 @@
#import <UIKit/UIKit.h>
#import "TGPasscodeBackground.h"
@interface TGPasscodePinDotView : UIView
@property (nonatomic) bool filled;
- (void)setFilled:(bool)filled animated:(bool)animated;
- (void)setBackground:(id<TGPasscodeBackground>)background;
- (void)setAbsoluteOffset:(CGPoint)absoluteOffset;
@end

View file

@ -1,89 +0,0 @@
#import "TGPasscodePinDotView.h"
@interface TGPasscodePinDotView ()
{
id<TGPasscodeBackground> _background;
CGPoint _absoluteOffset;
bool _filled;
CGFloat _fillAmount;
}
@end
@implementation TGPasscodePinDotView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.opaque = false;
self.backgroundColor = nil;
}
return self;
}
- (bool)filled
{
return _filled;
}
- (void)setFilled:(bool)filled
{
[self setFilled:filled animated:false];
}
- (void)setFilled:(bool)filled animated:(bool)__unused animated
{
if (_filled != filled)
{
_filled = filled;
_fillAmount = _filled ? 1.0f : 0.0f;
[self setNeedsDisplay];
}
}
- (void)setBackground:(id<TGPasscodeBackground>)background
{
_background = background;
[self setNeedsDisplay];
}
- (void)setAbsoluteOffset:(CGPoint)absoluteOffset
{
if (!CGPointEqualToPoint(_absoluteOffset, absoluteOffset))
{
_absoluteOffset = absoluteOffset;
[self setNeedsDisplay];
}
}
- (void)drawRect:(CGRect)__unused rect
{
CGSize size = self.bounds.size;
CGContextRef context = UIGraphicsGetCurrentContext();
[[_background backgroundImage] drawInRect:CGRectMake(-_absoluteOffset.x, -_absoluteOffset.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeCopy alpha:1.0f];
CGContextBeginPath(context);
CGContextAddEllipseInRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height));
CGContextClip(context);
[[_background foregroundImage] drawInRect:CGRectMake(-_absoluteOffset.x, -_absoluteOffset.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeNormal alpha:1.0f];
if (_fillAmount < FLT_EPSILON)
{
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(1.0f, 1.0f, size.width - 2.0f, size.height - 2.0f));
}
else if (_fillAmount < 1.0f - FLT_EPSILON)
{
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:0.0f alpha:_fillAmount].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(1.0f, 1.0f, size.width - 2.0f, size.height - 2.0f));
}
}
@end

View file

@ -1,11 +0,0 @@
#import <UIKit/UIKit.h>
#import "TGPasscodeBackground.h"
@interface TGPasscodePinView : UIView
- (void)setBackground:(id<TGPasscodeBackground>)background;
- (void)setCharacterCount:(NSUInteger)characterCount maxCharacterCount:(NSUInteger)maxCharacterCount;
@end

View file

@ -1,224 +0,0 @@
#import "TGPasscodePinView.h"
#import "LegacyComponentsInternal.h"
#import "TGPasscodePinDotView.h"
@interface TGPasscodePinView ()
{
id<TGPasscodeBackground> _background;
NSArray *_dotViews;
NSUInteger _maxCharacterCount;
}
@end
@implementation TGPasscodePinView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.opaque = false;
self.backgroundColor = nil;
}
return self;
}
- (void)setBackground:(id<TGPasscodeBackground>)background
{
_background = background;
for (TGPasscodePinDotView *dotView in _dotViews)
{
[dotView setBackground:_background];
}
[self setNeedsDisplay];
}
- (void)setCharacterCount:(NSUInteger)characterCount maxCharacterCount:(NSUInteger)maxCharacterCount
{
if (_maxCharacterCount != maxCharacterCount)
[self setNeedsDisplay];
NSUInteger displayLimit = 0;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1366)
displayLimit = 22;
else if ((int)screenSize.height == 736)
displayLimit = 26;
else if ((int)screenSize.height == 667)
displayLimit = 23;
else if ((int)screenSize.height == 568)
displayLimit = 19;
else
displayLimit = 19;
_maxCharacterCount = maxCharacterCount;
NSUInteger dotCount = MIN(displayLimit, MAX(characterCount, maxCharacterCount));
NSMutableArray *dotViews = [[NSMutableArray alloc] initWithArray:_dotViews];
while (dotViews.count > dotCount)
{
TGPasscodePinDotView *dotView = dotViews.lastObject;
[dotView removeFromSuperview];
[dotViews removeLastObject];
}
for (NSUInteger i = dotViews.count; i < dotCount; i++)
{
TGPasscodePinDotView *dotView = [[TGPasscodePinDotView alloc] init];
[dotView setBackground:_background];
[self addSubview:dotView];
[dotViews addObject:dotView];
}
for (NSUInteger i = 0; i < dotViews.count; i++)
{
TGPasscodePinDotView *dotView = dotViews[i];
[dotView setFilled:maxCharacterCount == 0 || i < characterCount];
}
_dotViews = dotViews;
[self _layoutDots];
}
- (void)setFrame:(CGRect)frame
{
bool needsDotsLayout = !CGPointEqualToPoint(self.frame.origin, frame.origin);
[super setFrame:frame];
if (needsDotsLayout)
{
[self _layoutDots];
[self setNeedsDisplay];
}
}
- (void)_layoutDots
{
CGFloat dotSimpleSize = 0.0f;
CGFloat dotComplexSize = 0.0f;
CGFloat dotSimpleSpacing = 0.0f;
CGFloat dotComplexSpacing = 0.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1366)
{
dotSimpleSize = 16.0f;
dotComplexSize = 7.0f;
dotSimpleSpacing = 27.0f;
dotComplexSpacing = 7.0f;
}
else if ((int)screenSize.height == 736)
{
dotSimpleSize = 13.0f;
dotComplexSize = 7.0f;
dotSimpleSpacing = 24.0f;
dotComplexSpacing = 7.0f;
}
else if ((int)screenSize.height == 667)
{
dotSimpleSize = 13.0f;
dotComplexSize = 7.0f;
dotSimpleSpacing = 24.0f;
dotComplexSpacing = 7.0f;
}
else if ((int)screenSize.height == 568)
{
dotSimpleSize = 13.0f;
dotComplexSize = 7.0f;
dotSimpleSpacing = 24.0f;
dotComplexSpacing = 7.0f;
}
else
{
dotSimpleSize = 13.0f;
dotComplexSize = 7.0f;
dotSimpleSpacing = 24.0f;
dotComplexSpacing = 7.0f;
}
CGFloat dotSize = _maxCharacterCount == 0 ? dotComplexSize : dotSimpleSize;
CGFloat spacing = _maxCharacterCount == 0 ? dotComplexSpacing : dotSimpleSpacing;
CGFloat dotsWidth = _dotViews.count * dotSize + (_dotViews.count == 0 ? 0.0f : ((_dotViews.count - 1) * spacing));
CGPoint dotsPosition = CGPointMake(CGFloor((self.frame.size.width - dotsWidth) / 2.0f), CGFloor((self.frame.size.height - dotSize) / 2.0f));
NSUInteger index = 0;
for (TGPasscodePinDotView *dotView in _dotViews)
{
dotView.frame = CGRectMake(dotsPosition.x + index * (dotSize + spacing), dotsPosition.y, dotSize, dotSize);
[dotView setAbsoluteOffset:CGPointMake(self.frame.origin.x + dotView.frame.origin.x, self.frame.origin.y + dotView.frame.origin.y)];
index++;
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self _layoutDots];
}
- (void)drawRect:(CGRect)__unused rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize size = self.bounds.size;
[[_background backgroundImage] drawInRect:CGRectMake(-self.frame.origin.x, -self.frame.origin.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeCopy alpha:1.0f];
if (_maxCharacterCount == 0)
{
CGFloat padding = 0.0f;
CGFloat lineWidth = 1.0f;
CGFloat radius = 5.0f;
CGContextBeginPath(context);
CGContextMoveToPoint(context, padding, radius);
CGContextAddArcToPoint(context, padding, 0.0f, padding + radius, 0.0f, radius);
CGContextAddLineToPoint(context, size.width - padding - radius, 0.0f);
CGContextAddArcToPoint(context, size.width - padding, 0.0f, size.width - padding, radius, radius);
CGContextAddLineToPoint(context, size.width - padding, size.height - radius);
CGContextAddArcToPoint(context, size.width - padding, size.height, size.width - padding - radius, size.height, radius);
CGContextAddLineToPoint(context, padding + radius, size.height);
CGContextAddArcToPoint(context, padding, size.height, padding, size.height - radius, radius);
CGContextClosePath(context);
CGContextClip(context);
[[_background foregroundImage] drawInRect:CGRectMake(-self.frame.origin.x, -self.frame.origin.y, [_background size].width, [_background size].height) blendMode:kCGBlendModeNormal alpha:1.0f];
radius = 4.0f;
CGContextBeginPath(context);
CGContextMoveToPoint(context, padding + lineWidth, radius + lineWidth);
CGContextAddArcToPoint(context, padding + lineWidth, lineWidth, padding + radius + lineWidth, lineWidth, radius);
CGContextAddLineToPoint(context, size.width - padding - radius - lineWidth, lineWidth);
CGContextAddArcToPoint(context, size.width - padding - lineWidth, lineWidth, size.width - padding - lineWidth, radius + lineWidth, radius);
CGContextAddLineToPoint(context, size.width - padding - lineWidth, size.height - radius - lineWidth);
CGContextAddArcToPoint(context, size.width - padding - lineWidth, size.height - lineWidth, size.width - padding - radius - lineWidth, size.height - lineWidth, radius);
CGContextAddLineToPoint(context, padding + radius + lineWidth, size.height - lineWidth);
CGContextAddArcToPoint(context, padding + lineWidth, size.height - lineWidth, padding + lineWidth, size.height - radius - lineWidth, radius);
CGContextClosePath(context);
CGContextSetBlendMode(context, kCGBlendModeCopy);
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillPath(context);
}
}
@end

View file

@ -1,26 +0,0 @@
#import <UIKit/UIKit.h>
typedef enum {
TGPasswordEntryViewStyleDefault,
TGPasswordEntryViewStyleTranslucent
} TGPasswordEntryViewStyle;
@interface TGPasswordEntryView : UIView
@property (nonatomic, copy) void (^cancel)();
@property (nonatomic, copy) void (^simplePasscodeEntered)();
@property (nonatomic, copy) void (^complexPasscodeEntered)();
@property (nonatomic, copy) void (^passcodeChanged)(NSString *);
- (instancetype)initWithFrame:(CGRect)frame style:(TGPasswordEntryViewStyle)style;
- (void)setTitle:(NSString *)title errorTitle:(NSString *)errorTitle isComplex:(bool)isComplex animated:(bool)animated;
- (void)setErrorTitle:(NSString *)errorTitle;
- (NSString *)passcode;
- (void)resetPasscode;
- (void)becomeFirstResponder;
- (void)updateBackgroundIfNeeded;
- (void)setReturnKeyType:(UIReturnKeyType)keyType;
@end

View file

@ -1,696 +0,0 @@
#import "TGPasswordEntryView.h"
#import "LegacyComponentsInternal.h"
#import "TGFont.h"
#import "TGImageUtils.h"
#import "TGTextField.h"
#import "TGPasscodePinView.h"
#import "TGPasscodeEntryKeyboardView.h"
#import "TGDefaultPasscodeBackground.h"
#import "TGImageBasedPasscodeBackground.h"
#import <LegacyComponents/TGModernButton.h>
#import <AudioToolbox/AudioToolbox.h>
#import "TGBuiltinWallpaperInfo.h"
@interface TGPasswordEntryInputView : UIView <UIInputViewAudioFeedback>
@end
@implementation TGPasswordEntryInputView
- (BOOL)enableInputClicksWhenVisible
{
return true;
}
@end
@interface TGPasswordEntryView () <UITextFieldDelegate>
{
TGPasswordEntryViewStyle _style;
TGTextField *_textField;
UILabel *_titleLabel;
UILabel *_infoLabel;
NSString *_title;
NSString *_errorTitle;
bool _isComplex;
TGPasscodePinView *_pinView;
NSString *_passcode;
UIImageView *_backgroundView;
TGPasscodeEntryKeyboardView *_simpleKeyboardView;
TGModernButton *_simpleKeyboardCancelButton;
TGModernButton *_simpleKeyboardDeleteButton;
UIView *_inputView;
TGModernButton *_complexCancelButton;
TGModernButton *_complexNextButton;
TGWallpaperInfo *_currentWallpaperInfo;
}
@end
@implementation TGPasswordEntryView
- (instancetype)initWithFrame:(CGRect)frame style:(TGPasswordEntryViewStyle)style
{
self = [super initWithFrame:frame];
if (self != nil)
{
_style = style;
_backgroundView = [[UIImageView alloc] init];
[self addSubview:_backgroundView];
_inputView = [[TGPasswordEntryInputView alloc] init];
_textField = [[TGTextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f)];
_textField.clipsToBounds = true;
_textField.delegate = self;
_textField.secureTextEntry = true;
_textField.font = TGSystemFontOfSize(16.0f);
_textField.leftInset = 8.0f;
_textField.rightInset = 8.0f;
_textField.keyboardType = UIKeyboardTypeDefault;
_textField.returnKeyType = UIReturnKeyNext;
_textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
if (iosMajorVersion() >= 7)
_textField.keyboardAppearance = UIKeyboardAppearanceDark;
else
_textField.keyboardAppearance = UIKeyboardAppearanceAlert;
[self addSubview:_textField];
_pinView = [[TGPasscodePinView alloc] init];
[_pinView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pinViewTapped:)]];
[self addSubview:_pinView];
_titleLabel = [[UILabel alloc] init];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.textColor = [UIColor whiteColor];
_titleLabel.font = TGSystemFontOfSize([self titleFontSize]);
[self addSubview:_titleLabel];
_infoLabel = [[UILabel alloc] init];
_infoLabel.backgroundColor = [UIColor clearColor];
_infoLabel.textColor = [UIColor whiteColor];
_infoLabel.font = TGSystemFontOfSize(13.0f);
[self addSubview:_infoLabel];
_simpleKeyboardView = [[TGPasscodeEntryKeyboardView alloc] init];
[_simpleKeyboardView sizeToFit];
__weak TGPasswordEntryView *weakSelf = self;
_simpleKeyboardView.characterEntered = ^(NSString *text)
{
//AudioServicesPlaySystemSound(0x450);
__strong TGPasswordEntryView *strongSelf = weakSelf;
if (strongSelf != nil)
{
strongSelf->_textField.text = [strongSelf->_textField.text stringByAppendingString:text];
[strongSelf setPasscode:strongSelf->_textField.text];
if (!strongSelf->_isComplex)
{
if (strongSelf->_passcode.length == 4)
{
strongSelf.userInteractionEnabled = false;
TGDispatchAfter(0.2, dispatch_get_main_queue(), ^
{
strongSelf.userInteractionEnabled = true;
if (strongSelf->_simplePasscodeEntered)
strongSelf->_simplePasscodeEntered();
});
}
}
}
};
[self addSubview:_simpleKeyboardView];
CGFloat simpleButtonInsetHorizontal = 0.0f;
CGFloat simpleButtonInsetVertical = 0.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
{
simpleButtonInsetHorizontal = 26.0f;
simpleButtonInsetVertical = 21.0f;
}
else if ((int)screenSize.height == 896)
{
simpleButtonInsetHorizontal = 54.0f;
simpleButtonInsetVertical = 67.0f;
}
else if ((int)screenSize.height == 812)
{
simpleButtonInsetHorizontal = 54.0f;
simpleButtonInsetVertical = 67.0f;
}
else if ((int)screenSize.height == 736)
{
simpleButtonInsetHorizontal = 26.0f;
simpleButtonInsetVertical = 21.0f;
}
else if ((int)screenSize.height == 667)
{
simpleButtonInsetHorizontal = 54.0f;
simpleButtonInsetVertical = 33.0f;
}
else if ((int)screenSize.height == 568)
{
simpleButtonInsetHorizontal = 26.0f;
simpleButtonInsetVertical = 21.0f;
}
else
{
simpleButtonInsetHorizontal = 26.0f;
simpleButtonInsetVertical = 13.0f;
}
_complexCancelButton = [[TGModernButton alloc] init];
[_complexCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal];
[_complexCancelButton setTitleColor:[UIColor whiteColor]];
_complexCancelButton.contentEdgeInsets = UIEdgeInsetsMake(14.0f, 10.0f, 14.0f, 10.0f);
_complexCancelButton.titleLabel.font = TGSystemFontOfSize(18.0f);
[_complexCancelButton sizeToFit];
[_complexCancelButton addTarget:self action:@selector(simpleKeyboardCancel) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_complexCancelButton];
_complexNextButton = [[TGModernButton alloc] init];
[_complexNextButton setTitle:TGLocalized(@"Common.Next") forState:UIControlStateNormal];
[_complexNextButton setTitleColor:[UIColor whiteColor]];
_complexNextButton.contentEdgeInsets = UIEdgeInsetsMake(14.0f, 10.0f, 14.0f, 10.0f);
_complexNextButton.titleLabel.font = TGMediumSystemFontOfSize(18.0f);
[_complexNextButton sizeToFit];
[_complexNextButton addTarget:self action:@selector(simpleKeyboardNext) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_complexNextButton];
_simpleKeyboardCancelButton = [[TGModernButton alloc] init];
[_simpleKeyboardCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal];
[_simpleKeyboardCancelButton setTitleColor:[UIColor whiteColor]];
_simpleKeyboardCancelButton.contentEdgeInsets = UIEdgeInsetsMake(simpleButtonInsetVertical, simpleButtonInsetHorizontal, simpleButtonInsetVertical, simpleButtonInsetHorizontal);
_simpleKeyboardCancelButton.titleLabel.font = TGSystemFontOfSize(16.0f);
[_simpleKeyboardCancelButton sizeToFit];
[_simpleKeyboardCancelButton addTarget:self action:@selector(simpleKeyboardCancel) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_simpleKeyboardCancelButton];
_simpleKeyboardDeleteButton = [[TGModernButton alloc] init];
[_simpleKeyboardDeleteButton setTitle:TGLocalized(@"Common.Delete") forState:UIControlStateNormal];
[_simpleKeyboardDeleteButton setTitleColor:[UIColor whiteColor]];
_simpleKeyboardDeleteButton.contentEdgeInsets = UIEdgeInsetsMake(simpleButtonInsetVertical, simpleButtonInsetHorizontal, simpleButtonInsetVertical, simpleButtonInsetHorizontal);
_simpleKeyboardDeleteButton.titleLabel.font = TGSystemFontOfSize(16.0f);
[_simpleKeyboardDeleteButton sizeToFit];
[_simpleKeyboardDeleteButton addTarget:self action:@selector(simpleKeyboardDelete) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_simpleKeyboardDeleteButton];
[self _updateBackground:frame.size];
}
return self;
}
- (void)setReturnKeyType:(UIReturnKeyType)keyType {
_textField.returnKeyType = keyType;
}
- (CGFloat)titleFontSize
{
if (_isComplex)
return 20.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
return 21.0f;
else if ((int)screenSize.height == 736 || (int)screenSize.height == 667 || (int)screenSize.height == 812 || (int)screenSize.height == 896)
return 19.0f;
else
return 18.0f;
}
- (CGFloat)infoFontSize
{
if (_isComplex)
return 14.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
return 15.0f;
return 13.0f;
}
- (void)updateBackgroundIfNeeded
{
TGWallpaperInfo *wallpaperInfo = [[LegacyComponentsGlobals provider] currentWallpaperInfo];
if (!TGObjectCompare(_currentWallpaperInfo, wallpaperInfo))
[self _updateBackground:self.frame.size];
}
- (void)_updateBackground:(CGSize)size
{
TGWallpaperInfo *wallpaperInfo = [[LegacyComponentsGlobals provider] currentWallpaperInfo];
id<TGPasscodeBackground> background = nil;
if (wallpaperInfo == nil || [wallpaperInfo isKindOfClass:[TGBuiltinWallpaperInfo class]])
{
if (wallpaperInfo == nil || [((TGBuiltinWallpaperInfo *)wallpaperInfo) isDefault])
background = [[TGDefaultPasscodeBackground alloc] initWithSize:size];
}
if (background == nil)
{
background = [[TGImageBasedPasscodeBackground alloc] initWithImage:[[LegacyComponentsGlobals provider] currentWallpaperImage] size:size];
}
_currentWallpaperInfo = wallpaperInfo;
_backgroundView.image = [background backgroundImage];
[_pinView setBackground:background];
[_simpleKeyboardView setBackground:background];
}
- (void)setFrame:(CGRect)frame
{
bool updateBackground = false;
if ((self.frame.size.width < self.frame.size.height) != (frame.size.width < frame.size.height))
updateBackground = true;
[super setFrame:frame];
if (updateBackground)
[self _updateBackground:frame.size];
}
- (void)setCancel:(void (^)())cancel
{
_cancel = [cancel copy];
_simpleKeyboardCancelButton.hidden = _simpleKeyboardDeleteButton.hidden || cancel == nil;
_complexCancelButton.hidden = !_simpleKeyboardDeleteButton.hidden || cancel == nil;
_complexNextButton.hidden = !_isComplex;
}
- (void)simpleKeyboardCancel
{
if (_cancel)
_cancel();
}
- (void)simpleKeyboardNext
{
if (_passcode.length != 0 && _complexPasscodeEntered)
_complexPasscodeEntered();
}
- (void)simpleKeyboardDelete
{
NSString *text = _textField.text;
if (text.length != 0)
{
text = [text substringToIndex:text.length - 1];
_textField.text = text;
[self setPasscode:text];
}
}
- (void)setPasscode:(NSString *)passcode
{
_passcode = passcode;
[_pinView setCharacterCount:passcode.length maxCharacterCount:_isComplex ? 0 : 4];
}
- (void)pinViewTapped:(UITapGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded && ![_textField isFirstResponder] && _isComplex)
{
[_textField becomeFirstResponder];
}
}
- (bool)errorTitleReplacesTitle
{
if (_isComplex)
return false;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 480)
return true;
return false;
}
- (void)setTitle:(NSString *)title errorTitle:(NSString *)errorTitle isComplex:(bool)isComplex animated:(bool)animated
{
animated = false;
_title = title;
_errorTitle = errorTitle;
_isComplex = isComplex;
_titleLabel.font = TGSystemFontOfSize([self titleFontSize]);
_infoLabel.font = TGSystemFontOfSize([self infoFontSize]);
if (!animated)
{
if (errorTitle.length != 0)
{
if ([self errorTitleReplacesTitle])
_titleLabel.text = errorTitle;
else
{
_titleLabel.text = title;
_infoLabel.text = errorTitle;
}
[_titleLabel sizeToFit];
[_infoLabel sizeToFit];
}
else
{
_titleLabel.text = title;
[_titleLabel sizeToFit];
_infoLabel.text = errorTitle;
[_infoLabel sizeToFit];
}
_textField.text = @"";
if (_isComplex)
{
if (_textField.inputView != nil)
{
_textField.inputView = nil;
[_textField resignFirstResponder];
}
[_textField becomeFirstResponder];
_simpleKeyboardView.hidden = true;
_simpleKeyboardCancelButton.hidden = true;
_simpleKeyboardDeleteButton.hidden = true;
_complexCancelButton.hidden = _cancel == nil;
_complexNextButton.hidden = false;
}
else
{
if (_textField.inputView == nil)
{
_textField.inputView = _inputView;
[_textField resignFirstResponder];
}
[_textField becomeFirstResponder];
_simpleKeyboardView.hidden = false;
_simpleKeyboardCancelButton.hidden = _cancel == nil;
_simpleKeyboardDeleteButton.hidden = false;
_complexCancelButton.hidden = true;
_complexNextButton.hidden = true;
}
[_pinView setCharacterCount:0 maxCharacterCount:_isComplex ? 0 : 4];
[self setPasscode:@""];
[self setNeedsLayout];
if (_passcodeChanged)
_passcodeChanged(_passcode);
}
}
- (void)setErrorTitle:(NSString *)errorTitle
{
if (!TGStringCompare(_errorTitle, errorTitle))
{
_errorTitle = errorTitle;
if (_errorTitle.length != 0)
{
if ([self errorTitleReplacesTitle])
{
_titleLabel.text = _errorTitle;
[_titleLabel sizeToFit];
[self setNeedsLayout];
}
else
{
_infoLabel.text = _errorTitle;
[_infoLabel sizeToFit];
[self setNeedsLayout];
}
}
else
{
_titleLabel.text = _title;
[_titleLabel sizeToFit];
_infoLabel.text = _errorTitle;
[_infoLabel sizeToFit];
}
}
}
- (NSString *)passcode
{
return _passcode;
}
- (void)resetPasscode
{
_textField.text = @"";
[self setPasscode:@""];
}
- (void)becomeFirstResponder
{
[_textField becomeFirstResponder];
}
- (void)willMoveToWindow:(UIWindow *)window {
[super willMoveToWindow:window];
[_textField becomeFirstResponder];
dispatch_async(dispatch_get_main_queue(), ^{
[_textField becomeFirstResponder];
});
}
- (BOOL)resignFirstResponder
{
return [_textField resignFirstResponder];
}
- (void)layoutSubviews
{
[super layoutSubviews];
_backgroundView.frame = self.bounds;
CGFloat keyboardOffset = 0.0f;
CGFloat titleOffset = 0.0f;
CGFloat pinOffset = 0.0f;
CGFloat infoOffset = 0.0f;
CGFloat topOffset = 20.0f;
CGFloat bottomOffset = 0.0f;
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.width;
screenSize.width = screenSize.height;
screenSize.height = tmp;
}
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
{
keyboardOffset = CGFloor((self.frame.size.height - 500.0f) / 2.0f + 119.0f);
titleOffset = 122.0f;
pinOffset = 89.0f;
infoOffset = 7.0f;
}
else if ((int)screenSize.height == 896)
{
keyboardOffset = 330.0f;
titleOffset = 116.0f;
pinOffset = 79.0f + TGScreenPixel;
infoOffset = 6.0f - TGScreenPixel;
topOffset = 44.0f;
bottomOffset = 34.0f;
}
else if ((int)screenSize.height == 812)
{
keyboardOffset = 300.0f;
titleOffset = 116.0f;
pinOffset = 79.0f + TGScreenPixel;
infoOffset = 6.0f - TGScreenPixel;
topOffset = 44.0f;
bottomOffset = 34.0f;
}
else if ((int)screenSize.height == 736)
{
keyboardOffset = 246.0f;
titleOffset = 112.0f;
pinOffset = 79.0f + TGScreenPixel;
infoOffset = 6.0f - TGScreenPixel;
}
else if ((int)screenSize.height == 667)
{
keyboardOffset = 206.0f;
titleOffset = 112.0f;
pinOffset = 79.0f + TGScreenPixel;
infoOffset = 6.0f - TGScreenPixel;
}
else if ((int)screenSize.height == 568)
{
keyboardOffset = 163.0f;
titleOffset = 98.0f - TGScreenPixel;
pinOffset = 72.0f;
infoOffset = 0.0f;
}
else
{
keyboardOffset = 109.0f;
titleOffset = 68.0f;
pinOffset = 45.0f;
infoOffset = 0.0f;
}
if (_complexNextButton.hidden)
{
_complexCancelButton.frame = CGRectMake(self.frame.size.width - _complexCancelButton.frame.size.width, topOffset, _complexCancelButton.frame.size.width, _complexCancelButton.frame.size.height);
}
else
{
_complexNextButton.frame = CGRectMake(self.frame.size.width - _complexNextButton.frame.size.width, topOffset, _complexNextButton.frame.size.width, _complexNextButton.frame.size.height);
_complexCancelButton.frame = CGRectMake(0.0f, topOffset, _complexCancelButton.frame.size.width, _complexCancelButton.frame.size.height);
}
_simpleKeyboardView.frame = CGRectMake(CGFloor((self.frame.size.width - _simpleKeyboardView.frame.size.width) / 2.0f), keyboardOffset, _simpleKeyboardView.frame.size.width, _simpleKeyboardView.frame.size.height);
CGFloat textFieldWidth = 0.0f;
if ((int)screenSize.height == 1024 || (int)screenSize.height == 1194 || (int)screenSize.height == 1366)
{
textFieldWidth = 320.0f;
_simpleKeyboardCancelButton.frame = CGRectMake(CGRectGetMinX(_simpleKeyboardView.frame) - 23.0f, CGRectGetMaxY(_simpleKeyboardView.frame) - _simpleKeyboardCancelButton.frame.size.height - 10.0f, _simpleKeyboardCancelButton.frame.size.width, _simpleKeyboardCancelButton.frame.size.height);
_simpleKeyboardDeleteButton.frame = CGRectMake(CGRectGetMaxX(_simpleKeyboardView.frame) - _simpleKeyboardDeleteButton.frame.size.width + 23.0f, CGRectGetMaxY(_simpleKeyboardView.frame) - _simpleKeyboardDeleteButton.frame.size.height - 10.0f, _simpleKeyboardDeleteButton.frame.size.width, _simpleKeyboardDeleteButton.frame.size.height);
}
else
{
textFieldWidth = MAX(16.0f, self.frame.size.width - 17.0f * 2.0f);
_simpleKeyboardCancelButton.frame = CGRectMake(0.0f, self.frame.size.height - _simpleKeyboardCancelButton.frame.size.height, _simpleKeyboardCancelButton.frame.size.width, _simpleKeyboardCancelButton.frame.size.height);
_simpleKeyboardDeleteButton.frame = CGRectMake(self.frame.size.width - _simpleKeyboardDeleteButton.frame.size.width, self.frame.size.height - _simpleKeyboardDeleteButton.frame.size.height, _simpleKeyboardDeleteButton.frame.size.width, _simpleKeyboardDeleteButton.frame.size.height);
}
CGFloat topInset = topOffset + 44.0f;
CGFloat bottomInset = self.frame.size.width > self.frame.size.height ? 162.0f : 216.0f + bottomOffset;
CGFloat areaHeight = self.bounds.size.height - topInset - bottomInset;
CGSize titleSize = _titleLabel.frame.size;
CGSize infoSize = _infoLabel.frame.size;
CGFloat textFieldHeight = 41.0f;
if (_isComplex)
{
_titleLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - titleSize.width) / 2.0f), CGFloor(areaHeight / 2.0f) - titleSize.height - 1.0f, titleSize.width, titleSize.height);
_pinView.frame = CGRectMake(CGFloor((self.frame.size.width - textFieldWidth) / 2.0f), CGRectGetMaxY(_titleLabel.frame) + 23.0f, textFieldWidth, textFieldHeight);
_infoLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - infoSize.width) / 2.0f), CGRectGetMaxY(_pinView.frame) + 25.0f + TGRetinaPixel, infoSize.width, infoSize.height);
}
else
{
_titleLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - titleSize.width) / 2.0f), _simpleKeyboardView.frame.origin.y - titleOffset, titleSize.width, titleSize.height);
_pinView.frame = CGRectMake(0.0f, _simpleKeyboardView.frame.origin.y - pinOffset, self.frame.size.width, textFieldHeight);
_infoLabel.frame = CGRectMake(CGFloor((self.bounds.size.width - infoSize.width) / 2.0f), CGRectGetMaxY(_pinView.frame) + infoOffset, infoSize.width, infoSize.height);
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == _textField)
{
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (!_isComplex)
{
for (NSUInteger i = 0; i < text.length; i++)
{
unichar c = [text characterAtIndex:i];
if (!(c >= '0' && c <= '9'))
return false;
}
}
if (!_isComplex && text.length > 4)
return false;
if (!_isComplex)
[_pinView setCharacterCount:text.length maxCharacterCount:4];
[self setPasscode:text];
if (_passcodeChanged)
_passcodeChanged(_passcode);
if (!_isComplex && text.length == 4)
{
dispatch_async(dispatch_get_main_queue(), ^
{
if (_simplePasscodeEntered)
_simplePasscodeEntered();
});
}
}
return true;
}
- (BOOL)textFieldShouldReturn:(UITextField *)__unused textField
{
if (!_isComplex)
{
if (_passcode.length == 4 && _simplePasscodeEntered)
_simplePasscodeEntered();
}
else
{
if (_passcode.length != 0 && _complexPasscodeEntered)
_complexPasscodeEntered();
}
return false;
}
@end

View file

@ -360,6 +360,9 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
[_landscapeToolsWrapperView addSubview:_landscapeSettingsView];
[self setCurrentSwatch:_portraitSettingsView.swatch sender:nil];
if (![self _updateControllerInset:false])
[self controllerInsetUpdated:UIEdgeInsetsZero];
}
- (void)setupCanvas

View file

@ -1,7 +0,0 @@
#import <LegacyComponents/TGWallpaperInfo.h>
@interface TGRemoteWallpaperInfo : TGWallpaperInfo
- (instancetype)initWithRemoteId:(int)remoteId thumbnailUri:(NSString *)thumbnailUri fullscreenUri:(NSString *)fullscreenUri tintColor:(int)tintColor;
@end

View file

@ -1,103 +0,0 @@
#import "TGRemoteWallpaperInfo.h"
#import "LegacyComponentsInternal.h"
#import <LegacyComponents/TGRemoteImageView.h>
@interface TGRemoteWallpaperInfo ()
{
int _remoteId;
NSString *_thumbnailUri;
NSString *_fullscreenUri;
int _tintColor;
}
@end
@implementation TGRemoteWallpaperInfo
- (instancetype)initWithRemoteId:(int)remoteId thumbnailUri:(NSString *)thumbnailUri fullscreenUri:(NSString *)fullscreenUri tintColor:(int)tintColor
{
self = [super init];
if (self != nil)
{
_remoteId = remoteId;
_thumbnailUri = thumbnailUri;
_fullscreenUri = fullscreenUri;
_tintColor = tintColor;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[TGRemoteWallpaperInfo class]])
{
if (((TGRemoteWallpaperInfo *)object)->_remoteId == _remoteId &&
TGStringCompare(((TGRemoteWallpaperInfo *)object)->_thumbnailUri, _thumbnailUri) &&
TGStringCompare(((TGRemoteWallpaperInfo *)object)->_fullscreenUri, _fullscreenUri) &&
((TGRemoteWallpaperInfo *)object)->_tintColor == _tintColor)
{
return true;
}
}
return false;
}
- (NSString *)thumbnailUrl
{
return _thumbnailUri;
}
- (NSString *)fullscreenUrl
{
return _fullscreenUri;
}
- (int)tintColor
{
return _tintColor;
}
- (NSData *)imageData
{
NSString *trimmedUrl = [self fullscreenUrl];
NSArray *components = [trimmedUrl componentsSeparatedByString:@"_"];
if (components.count >= 5)
trimmedUrl = [NSString stringWithFormat:@"%@_%@_%@_%@", components[0], components[1], components[2], components[3]];
return [[NSData alloc] initWithContentsOfFile:[[TGRemoteImageView sharedCache] pathForCachedData:trimmedUrl]];
}
- (UIImage *)image
{
NSData *data = [self imageData];
if (data != nil)
return [[UIImage alloc] initWithData:data];
return nil;
}
- (bool)hasData
{
return true;
}
- (NSDictionary *)infoDictionary
{
return @{
@"_className": NSStringFromClass([self class]),
@"remoteId": @(_remoteId),
@"thumbnailUri": _thumbnailUri == nil ? @"" : _thumbnailUri,
@"fullscreenUri": _fullscreenUri == nil ? @"" : _fullscreenUri,
@"tintColor": @(_tintColor),
};
}
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)dict
{
return [[TGRemoteWallpaperInfo alloc] initWithRemoteId:[dict[@"remoteId"] intValue] thumbnailUri:dict[@"thumbnailUri"] fullscreenUri:dict[@"fullscreenUri"] tintColor:[dict[@"tintColor"] intValue]];
}
@end

View file

@ -155,7 +155,30 @@
_textureVertices[7] = 0.0f;
}
break;
case AVCaptureVideoOrientationPortraitUpsideDown:
if (!_mirror)
{
_textureVertices[0] = 1.0f - centerOffset;
_textureVertices[1] = 0.0f;
_textureVertices[2] = 1.0f - centerOffset;
_textureVertices[3] = 1.0f;
_textureVertices[4] = centerOffset;
_textureVertices[5] = 0.0f;
_textureVertices[6] = centerOffset;
_textureVertices[7] = 1.0f;
}
else
{
_textureVertices[0] = centerOffset;
_textureVertices[1] = 1.0f;
_textureVertices[2] = centerOffset;
_textureVertices[3] = 0.0f;
_textureVertices[4] = 1.0f - centerOffset;
_textureVertices[5] = 1.0f;
_textureVertices[6] = 1.0f - centerOffset;
_textureVertices[7] = 0.0f;
}
break;
default:
break;
}

View file

@ -280,7 +280,14 @@ typedef enum
[_wrapperView addSubview:_fadeView];
}
_circleWrapperView = [[UIView alloc] initWithFrame:CGRectMake((_wrapperView.frame.size.width - 216.0f - 38.0f) / 2.0f, _wrapperView.frame.size.height + 100.0f, 216.0f + 38.0f, 216.0f + 38.0f)];
CGFloat circleWrapperViewLength = 216.0f + 38.0f;
_circleWrapperView = [[UIView alloc] initWithFrame:(CGRect){
.origin.x = (_wrapperView.bounds.size.width - circleWrapperViewLength) / 2.0f,
.origin.y = _wrapperView.bounds.size.height + circleWrapperViewLength * 0.3f,
.size.width = circleWrapperViewLength,
.size.height = circleWrapperViewLength
}];
_circleWrapperView.alpha = 0.0f;
_circleWrapperView.clipsToBounds = false;
[_wrapperView addSubview:_circleWrapperView];
@ -305,14 +312,18 @@ typedef enum
_placeholderView.accessibilityIgnoresInvertColors = true;
}
_ringView = [[TGVideoMessageRingView alloc] initWithFrame:CGRectMake((_circleWrapperView.frame.size.width - 234.0f) / 2.0f, (_circleWrapperView.frame.size.height - 234.0f) / 2.0f, 234.0f, 234.0f)];
CGFloat ringViewLength = 234.0f;
_ringView = [[TGVideoMessageRingView alloc] initWithFrame:(CGRect){
.origin.x = (_circleWrapperView.bounds.size.width - ringViewLength) / 2.0f,
.origin.y = (_circleWrapperView.bounds.size.height - ringViewLength) / 2.0f,
.size.width = ringViewLength,
.size.height = ringViewLength
}];
_ringView.accentColor = self.pallete != nil ? self.pallete.buttonColor : TGAccentColor();
[_circleWrapperView addSubview:_ringView];
CGRect controlsFrame = _controlsFrame;
CGFloat height = TGIsPad() ? 56.0f : 45.0f;
controlsFrame.origin.y = CGRectGetMaxY(controlsFrame) - height;
controlsFrame.size.height = height;
controlsFrame.size.width = _wrapperView.frame.size.width;
_controlsView = [[TGVideoMessageControls alloc] initWithFrame:controlsFrame assets:_assets];
_controlsView.pallete = self.pallete;
@ -357,7 +368,7 @@ typedef enum
};
[self.view addSubview:_controlsView];
_separatorView = [[UIView alloc] initWithFrame:CGRectMake(_controlsView.frame.origin.x, _controlsFrame.origin.y - TGScreenPixel, _controlsView.frame.size.width, TGScreenPixel)];
_separatorView = [[UIView alloc] initWithFrame:CGRectMake(controlsFrame.origin.x, controlsFrame.origin.y - TGScreenPixel, controlsFrame.size.width, TGScreenPixel)];
_separatorView.backgroundColor = self.pallete != nil ? self.pallete.borderColor : UIColorRGB(0xb2b2b2);
_separatorView.userInteractionEnabled = false;
[self.view addSubview:_separatorView];
@ -508,31 +519,33 @@ typedef enum
_circleWrapperView.transform = CGAffineTransformMakeScale(0.3f, 0.3f);
CGPoint targetPosition = CGPointMake(_wrapperView.frame.size.width / 2.0f, _wrapperView.frame.size.height / 2.0f - _controlsView.frame.size.height);
CGPoint targetPosition = (CGPoint){
.x = _wrapperView.frame.size.width / 2.0f,
.y = _wrapperView.frame.size.height / 2.0f - _controlsView.frame.size.height
};
switch (self.interfaceOrientation)
{
case UIInterfaceOrientationLandscapeLeft:
targetPosition.x = MIN(_wrapperView.frame.size.width - _circleWrapperView.bounds.size.width / 2.0f - 20.0f, _wrapperView.frame.size.width / 4.0f * 3.0f);
targetPosition.y = self.view.frame.size.height / 2.0f;
break;
case UIInterfaceOrientationLandscapeRight:
targetPosition.x = MAX(_circleWrapperView.bounds.size.width / 2.0f + 20.0f, _wrapperView.frame.size.width / 4.0f);
targetPosition.y = self.view.frame.size.height / 2.0f;
break;
default:
if (self.view.frame.size.height > self.view.frame.size.width && fabs(_wrapperView.frame.size.height - self.view.frame.size.height) < 50.0f)
targetPosition.y = _wrapperView.frame.size.height / 3.0f - 20.0f;
targetPosition.y = MAX(_circleWrapperView.bounds.size.height / 2.0f + 40.0f, targetPosition.y);
break;
}
if (TGIsPad()) {
_circleWrapperView.center = targetPosition;
}
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.8f initialSpringVelocity:0.2f options:kNilOptions animations:^
{
_circleWrapperView.center = targetPosition;
if (!TGIsPad()) {
_circleWrapperView.center = targetPosition;
}
_circleWrapperView.transform = CGAffineTransformIdentity;
} completion:nil];

View file

@ -341,7 +341,7 @@ static CGRect viewFrame(UIView *view)
{
UIImage *deleteImage = _assets.actionDelete;
_deleteButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 45.0f, 45.0f)];
_deleteButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, (self.bounds.size.height - 45.0f) / 2.0f, 45.0f, 45.0f)];
[_deleteButton setImage:deleteImage forState:UIControlStateNormal];
_deleteButton.adjustsImageWhenDisabled = false;
_deleteButton.adjustsImageWhenHighlighted = false;
@ -491,7 +491,7 @@ static CGRect viewFrame(UIView *view)
}
setViewFrame(_sendButton, CGRectMake(self.frame.size.width - _sendButton.frame.size.width, 0.0f, _sendButton.frame.size.width, self.frame.size.height));
_deleteButton.center = CGPointMake(24.0f, 22.0f);
_deleteButton.center = CGPointMake(24.0f, self.bounds.size.height / 2.0f);
setViewFrame(_scrubberView, CGRectMake(46.0f, (self.frame.size.height - 33.0f) / 2.0f, self.frame.size.width - 46.0f * 2.0f, 33.0f));
}

View file

@ -1,29 +0,0 @@
#import <LegacyComponents/TGViewController.h>
#import <LegacyComponents/LegacyComponentsContext.h>
#import <LegacyComponents/ASWatcher.h>
@class TGWallpaperInfo;
@class TGWallpaperController;
@class TGPresentation;
@protocol TGWallpaperControllerDelegate <NSObject>
@optional
- (void)wallpaperController:(TGWallpaperController *)wallpaperController didSelectWallpaperWithInfo:(TGWallpaperInfo *)wallpaperInfo;
@end
@interface TGWallpaperController : TGViewController <ASWatcher>
@property (nonatomic, strong) ASHandle *actionHandle;
@property (nonatomic, weak) id<TGWallpaperControllerDelegate> delegate;
@property (nonatomic) bool enableWallpaperAdjustment;
@property (nonatomic, strong) TGPresentation *presentation;
@property (nonatomic, copy) void (^customDismiss)();
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context wallpaperInfo:(TGWallpaperInfo *)wallpaperInfo thumbnailImage:(UIImage *)thumbnailImage;
@end

View file

@ -1,378 +0,0 @@
#import "TGWallpaperController.h"
#import <LegacyComponents/LegacyComponents.h>
#import "LegacyComponentsInternal.h"
#import "TGModernButton.h"
#import <LegacyComponents/TGRemoteImageView.h>
#import <LegacyComponents/TGWallpaperInfo.h>
#import <LegacyComponents/TGCustomImageWallpaperInfo.h>
//#import "TGDefaultPresentationPallete.h"
#import "TGRemoteImageView.h"
@interface TGWallpaperController () <UIScrollViewDelegate>
{
TGWallpaperInfo *_wallpaperInfo;
UIImage *_thumbnailImage;
TGRemoteImageView *_imageView;
CGSize _adjustingImageSize;
CGFloat _adjustingImageScale;
UIScrollView *_scrollView;
TGModernButton *_setButton;
UIView *_panelView;
TGModernButton *_cancelButton;
UIView *_separatorView;
UIView *_bottomView;
id<LegacyComponentsContext> _context;
}
@end
@implementation TGWallpaperController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context wallpaperInfo:(TGWallpaperInfo *)wallpaperInfo thumbnailImage:(UIImage *)thumbnailImage
{
self = [super initWithContext:context];
if (self != nil)
{
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
_wallpaperInfo = wallpaperInfo;
_thumbnailImage = thumbnailImage;
[self setTitleText:TGLocalized(@"BackgroundPreview.Title")];
self.automaticallyManageScrollViewInsets = false;
}
return self;
}
- (void)dealloc
{
}
#pragma mark -
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.navigationController == nil && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[_context animateApplicationStatusBarAppearance:TGStatusBarAppearanceAnimationSlideUp delay:0.0 duration:0.5 completion:^
{
[_context setApplicationStatusBarAlpha:0.0f];
}];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.navigationController == nil && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[_context setApplicationStatusBarAlpha:1.0f];
[_context animateApplicationStatusBarAppearance:TGStatusBarAppearanceAnimationSlideDown duration:iosMajorVersion() >= 7 ? 0.23 : 0.3 completion:nil];
}
}
- (void)loadView
{
[super loadView];
self.view.clipsToBounds = true;
self.view.backgroundColor = [UIColor blackColor];
CGSize screenSize = self.view.bounds.size;
_imageView = [[TGRemoteImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height)];
bool imageLoading = false;
UIImage *immediateImage = [_wallpaperInfo image];
if (immediateImage != nil)
[_imageView loadImage:immediateImage];
else
{
_imageView.useCache = false;
_imageView.contentHints = TGRemoteImageContentHintLoadFromDiskSynchronously;
_imageView.fadeTransition = true;
_imageView.fadeTransitionDuration = 0.3;
imageLoading = true;
ASHandle *actionHandle = _actionHandle;
[_imageView setProgressHandler:^(TGRemoteImageView *imageView, float progress)
{
if (ABS(progress - 1.0f) < FLT_EPSILON && [imageView currentImage] != nil)
{
[actionHandle requestAction:@"imageLoaded" options:nil];
}
}];
[_imageView loadImage:[_wallpaperInfo fullscreenUrl] filter:nil placeholder:_thumbnailImage];
}
if (_enableWallpaperAdjustment && immediateImage != nil)
{
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height)];
[self.view addSubview:_scrollView];
_scrollView.showsHorizontalScrollIndicator = false;
_scrollView.showsVerticalScrollIndicator = false;
_scrollView.delegate = self;
_adjustingImageScale = immediateImage.scale;
_adjustingImageSize = CGSizeMake(immediateImage.size.width / _adjustingImageScale, immediateImage.size.height / _adjustingImageScale);
_imageView.frame = CGRectMake(0, 0, _adjustingImageSize.width, _adjustingImageSize.height);
_imageView.contentMode = UIViewContentModeScaleToFill;
[_scrollView addSubview:_imageView];
[self _adjustScrollView];
_scrollView.zoomScale = _scrollView.minimumZoomScale;
CGSize contentSize = _scrollView.contentSize;
CGSize viewSize = _scrollView.frame.size;
_scrollView.contentOffset = CGPointMake(MAX(0, CGFloor((contentSize.width - viewSize.width) / 2)), MAX(0, CGFloor((contentSize.height - viewSize.height) / 2)));
}
else
{
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:_imageView];
}
_panelView = [[UIView alloc] initWithFrame:CGRectMake(0, screenSize.height - 49, screenSize.width, 49)];
TGNavigationBarPallete *pallete = [[LegacyComponentsGlobals provider] navigationBarPallete];
if (iosMajorVersion() >= 7 && [TGViewController isWidescreen])
{
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:_panelView.bounds];
toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[_panelView addSubview:toolbar];
toolbar.barTintColor = pallete.backgroundColor;
}
else
{
_panelView.backgroundColor = pallete.backgroundColor;
_panelView.backgroundColor = [UIColor grayColor];
}
[self.view addSubview:_panelView];
CGFloat separatorWidth = TGScreenPixel;
_cancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, CGFloor(_panelView.frame.size.width / 2) - separatorWidth, _panelView.frame.size.height)];
_cancelButton.backgroundColor = [UIColor clearColor];
//_cancelButton.highlightBackgroundColor = self.presentation.pallete.selectionColor;
[_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_cancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal];
_cancelButton.titleLabel.font = TGSystemFontOfSize(17);
[_cancelButton setTitleColor:pallete.titleColor];
[_cancelButton setTitleShadowColor:[UIColor clearColor] forState:UIControlStateNormal];
[_panelView addSubview:_cancelButton];
_setButton = [[TGModernButton alloc] initWithFrame:CGRectMake(_cancelButton.frame.origin.x + _cancelButton.frame.size.width + separatorWidth, 0, CGFloor(_panelView.frame.size.width / 2), _panelView.frame.size.height)];
_setButton.backgroundColor = [UIColor clearColor];
//_setButton.highlightBackgroundColor = self.presentation.pallete.selectionColor;
_setButton.enabled = !imageLoading;
[_setButton addTarget:self action:@selector(doneButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[_setButton setTitle:TGLocalized(@"Wallpaper.Set") forState:UIControlStateNormal];
_setButton.titleLabel.font = TGSystemFontOfSize(17);
[_setButton setTitleColor:pallete.titleColor];
//[_setButton setTitleColor:[self.presentation.pallete.textColor colorWithAlphaComponent:0.4f] forState:UIControlStateDisabled];
[_setButton setTitleShadowColor:[UIColor clearColor] forState:UIControlStateNormal];
[_panelView addSubview:_setButton];
_separatorView = [[UIView alloc] initWithFrame:CGRectMake(CGFloor(_panelView.frame.size.width / 2) - separatorWidth, 0, separatorWidth, 49.0f)];
_separatorView.backgroundColor = pallete.separatorColor;
[_panelView addSubview:_separatorView];
_bottomView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 49.0f, _panelView.frame.size.width, separatorWidth)];
_bottomView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_bottomView.backgroundColor = pallete.separatorColor;
//[_panelView addSubview:_bottomView];
}
- (void)layoutControllerForSize:(CGSize)size duration:(NSTimeInterval)duration {
[super layoutControllerForSize:size duration:duration];
CGSize screenSize = self.view.bounds.size;
_imageView.frame = CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height);
UIImage *immediateImage = [_wallpaperInfo image];
if (_enableWallpaperAdjustment && immediateImage != nil)
{
_scrollView.frame = CGRectMake(0.0f, 0.0f, screenSize.width, screenSize.height);
_scrollView.showsHorizontalScrollIndicator = false;
_scrollView.showsVerticalScrollIndicator = false;
_scrollView.delegate = self;
_adjustingImageScale = immediateImage.scale;
_adjustingImageSize = CGSizeMake(immediateImage.size.width / _adjustingImageScale, immediateImage.size.height / _adjustingImageScale);
_imageView.frame = CGRectMake(0, 0, _adjustingImageSize.width, _adjustingImageSize.height);
_imageView.contentMode = UIViewContentModeScaleToFill;
[self _adjustScrollView];
_scrollView.zoomScale = _scrollView.minimumZoomScale;
CGSize contentSize = _scrollView.contentSize;
CGSize viewSize = _scrollView.frame.size;
_scrollView.contentOffset = CGPointMake(MAX(0, CGFloor((contentSize.width - viewSize.width) / 2)), MAX(0, CGFloor((contentSize.height - viewSize.height) / 2)));
}
else
{
_imageView.contentMode = UIViewContentModeScaleAspectFill;
}
_panelView.frame = CGRectMake(0, screenSize.height - 49.0f - self.controllerSafeAreaInset.bottom, screenSize.width, 49.0f + self.controllerSafeAreaInset.bottom);
CGFloat separatorWidth = TGScreenPixel;
_cancelButton.frame = CGRectMake(0, 0, CGFloor(_panelView.frame.size.width / 2) - separatorWidth, 49.0f);
_setButton.frame = CGRectMake(_cancelButton.frame.origin.x + _cancelButton.frame.size.width + separatorWidth, 0, CGFloor(_panelView.frame.size.width / 2), 49.0f);
_separatorView.frame = CGRectMake(CGFloor(_panelView.frame.size.width / 2) - separatorWidth, 0, separatorWidth, 49.0f + self.controllerSafeAreaInset.bottom);
}
- (void)controllerInsetUpdated:(UIEdgeInsets)previousInset
{
[super controllerInsetUpdated:previousInset];
_panelView.frame = CGRectMake(0, self.view.bounds.size.height - 49.0f - self.controllerSafeAreaInset.bottom, self.view.bounds.size.width, 49.0f + self.controllerSafeAreaInset.bottom);
CGFloat separatorWidth = TGScreenPixel;
_separatorView.frame = CGRectMake(CGFloor(_panelView.frame.size.width / 2) - separatorWidth, 0, separatorWidth, 49.0f + self.controllerSafeAreaInset.bottom);
}
- (BOOL)shouldAutorotate
{
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark -
- (void)scrollViewDidZoom:(UIScrollView *)__unused scrollView
{
[self _adjustScrollView];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)__unused scrollView withView:(UIView *)__unused view atScale:(CGFloat)__unused scale
{
[self _adjustScrollView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)__unused scrollView
{
return _imageView;
}
- (void)_adjustScrollView
{
CGSize imageSize = _adjustingImageSize;
CGFloat imageScale = _adjustingImageScale;
imageSize.width /= imageScale;
imageSize.height /= imageScale;
CGFloat scaleWidth = _scrollView.frame.size.width / imageSize.width;
CGFloat scaleHeight = _scrollView.frame.size.height / imageSize.height;
CGFloat minScale = MAX(scaleWidth, scaleHeight);
if (_scrollView.minimumZoomScale != minScale)
_scrollView.minimumZoomScale = minScale;
if (_scrollView.maximumZoomScale != minScale * 3.0f)
_scrollView.maximumZoomScale = minScale * 3.0f;
CGSize boundsSize = _scrollView.bounds.size;
CGRect contentsFrame = _imageView.frame;
if (boundsSize.width > contentsFrame.size.width)
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
else
contentsFrame.origin.x = 0;
if (boundsSize.height > contentsFrame.size.height)
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
else
contentsFrame.origin.y = 0;
_imageView.frame = contentsFrame;
}
#pragma mark
- (void)cancelButtonPressed
{
if (_customDismiss) {
_customDismiss();
} else {
if (self.navigationController != nil)
[self.navigationController popViewControllerAnimated:true];
else
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
}
}
- (void)doneButtonPressed
{
UIImage *currentImage = [_imageView currentImage];
if (currentImage != nil)
{
TGWallpaperInfo *selectedWallpaperInfo = _wallpaperInfo;
if (_enableWallpaperAdjustment)
{
CGSize screenSize = self.view.bounds.size;
CGFloat screenScale = [UIScreen mainScreen].scale;
screenSize.width *= screenScale;
screenSize.height *= screenScale;
CGFloat screenSide = MAX(screenSize.width, screenSize.height);
CGFloat scale = 1.0f / _scrollView.zoomScale;
CGRect visibleRect;
visibleRect.origin.x = _scrollView.contentOffset.x * scale;
visibleRect.origin.y = _scrollView.contentOffset.y * scale;
visibleRect.size.width = _scrollView.bounds.size.width * scale;
visibleRect.size.height = _scrollView.bounds.size.height * scale;
UIImage *croppedImage = TGFixOrientationAndCrop(currentImage, visibleRect, TGFitSize(visibleRect.size, CGSizeMake(screenSide, screenSide)));
if (croppedImage != nil)
selectedWallpaperInfo = [[TGCustomImageWallpaperInfo alloc] initWithImage:croppedImage];
}
id<TGWallpaperControllerDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(wallpaperController:didSelectWallpaperWithInfo:)])
[delegate wallpaperController:self didSelectWallpaperWithInfo:selectedWallpaperInfo];
}
}
- (void)actionStageActionRequested:(NSString *)action options:(id)__unused options
{
if ([action isEqualToString:@"imageLoaded"])
{
_setButton.enabled = true;
}
}
@end

View file

@ -1,21 +0,0 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface TGWallpaperInfo : NSObject
- (NSString *)thumbnailUrl;
- (NSString *)fullscreenUrl;
- (int)tintColor;
- (CGFloat)systemAlpha;
- (CGFloat)buttonsAlpha;
- (CGFloat)highlightedButtonAlpha;
- (CGFloat)progressAlpha;
- (UIImage *)image;
- (NSData *)imageData;
- (bool)hasData;
- (NSDictionary *)infoDictionary;
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)dict;
@end

View file

@ -1,68 +0,0 @@
#import "TGWallpaperInfo.h"
@implementation TGWallpaperInfo
- (NSString *)thumbnailUrl
{
return nil;
}
- (NSString *)fullscreenUrl
{
return nil;
}
- (int)tintColor
{
return 0;
}
- (CGFloat)systemAlpha
{
return 0.25f;
}
- (CGFloat)buttonsAlpha
{
return 0.35f;
}
- (CGFloat)highlightedButtonAlpha
{
return 0.50f;
}
- (CGFloat)progressAlpha
{
return 0.35f;
}
- (UIImage *)image
{
return nil;
}
- (NSData *)imageData
{
return nil;
}
- (bool)hasData
{
return false;
}
- (NSDictionary *)infoDictionary
{
return @{};
}
+ (TGWallpaperInfo *)infoWithDictionary:(NSDictionary *)dict
{
Class className = NSClassFromString(dict[@"_className"]);
return [className infoWithDictionary:dict];
}
@end

View file

@ -546,8 +546,6 @@
D04269121F586E430037ECE8 /* TGVideoCameraPipeline.m in Sources */ = {isa = PBXBuildFile; fileRef = D04269101F586E430037ECE8 /* TGVideoCameraPipeline.m */; };
D04269151F586EC80037ECE8 /* TGVideoMessageCaptureController.h in Headers */ = {isa = PBXBuildFile; fileRef = D04269131F586EC80037ECE8 /* TGVideoMessageCaptureController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D04269161F586EC80037ECE8 /* TGVideoMessageCaptureController.m in Sources */ = {isa = PBXBuildFile; fileRef = D04269141F586EC80037ECE8 /* TGVideoMessageCaptureController.m */; };
D07ABBA8202A193A003671DE /* TGWallpaperController.m in Sources */ = {isa = PBXBuildFile; fileRef = D07ABBA6202A1939003671DE /* TGWallpaperController.m */; };
D07ABBA9202A193A003671DE /* TGWallpaperController.h in Headers */ = {isa = PBXBuildFile; fileRef = D07ABBA7202A1939003671DE /* TGWallpaperController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BC6CF1F2A18B700ED97AA /* TGCameraMainPhoneView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BC6C91F2A18B700ED97AA /* TGCameraMainPhoneView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BC6D01F2A18B700ED97AA /* TGCameraMainPhoneView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BC6CA1F2A18B700ED97AA /* TGCameraMainPhoneView.m */; };
D07BC6D11F2A18B700ED97AA /* TGCameraMainTabletView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BC6CB1F2A18B700ED97AA /* TGCameraMainTabletView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -1061,35 +1059,8 @@
D07BCAFB1F2B517900ED97AA /* TGLegacyMediaPickerTipView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCAF91F2B517900ED97AA /* TGLegacyMediaPickerTipView.h */; };
D07BCAFC1F2B517900ED97AA /* TGLegacyMediaPickerTipView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCAFA1F2B517900ED97AA /* TGLegacyMediaPickerTipView.m */; };
D07BCB021F2B546400ED97AA /* LegacyComponentsContext.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB011F2B546400ED97AA /* LegacyComponentsContext.m */; };
D07BCB151F2B646A00ED97AA /* TGPasswordEntryView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB041F2B646A00ED97AA /* TGPasswordEntryView.h */; };
D07BCB161F2B646A00ED97AA /* TGPasswordEntryView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB051F2B646A00ED97AA /* TGPasswordEntryView.m */; };
D07BCB171F2B646A00ED97AA /* TGPasscodeEntryController.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB061F2B646A00ED97AA /* TGPasscodeEntryController.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB181F2B646A00ED97AA /* TGPasscodeEntryController.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB071F2B646A00ED97AA /* TGPasscodeEntryController.m */; };
D07BCB191F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB081F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.h */; };
D07BCB1A1F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB091F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.m */; };
D07BCB1B1F2B646A00ED97AA /* TGPasscodePinDotView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB0A1F2B646A00ED97AA /* TGPasscodePinDotView.h */; };
D07BCB1C1F2B646A00ED97AA /* TGPasscodePinDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB0B1F2B646A00ED97AA /* TGPasscodePinDotView.m */; };
D07BCB1D1F2B646A00ED97AA /* TGPasscodePinView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB0C1F2B646A00ED97AA /* TGPasscodePinView.h */; };
D07BCB1E1F2B646A00ED97AA /* TGPasscodePinView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB0D1F2B646A00ED97AA /* TGPasscodePinView.m */; };
D07BCB1F1F2B646A00ED97AA /* TGPasscodeButtonView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB0E1F2B646A00ED97AA /* TGPasscodeButtonView.h */; };
D07BCB201F2B646A00ED97AA /* TGPasscodeButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB0F1F2B646A00ED97AA /* TGPasscodeButtonView.m */; };
D07BCB211F2B646A00ED97AA /* TGPasscodeBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB101F2B646A00ED97AA /* TGPasscodeBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB221F2B646A00ED97AA /* TGTextField.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB111F2B646A00ED97AA /* TGTextField.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB231F2B646A00ED97AA /* TGTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB121F2B646A00ED97AA /* TGTextField.m */; };
D07BCB241F2B646A00ED97AA /* TGDefaultPasscodeBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB131F2B646A00ED97AA /* TGDefaultPasscodeBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB251F2B646A00ED97AA /* TGDefaultPasscodeBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB141F2B646A00ED97AA /* TGDefaultPasscodeBackground.m */; };
D07BCB281F2B652C00ED97AA /* TGImageBasedPasscodeBackground.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB261F2B652C00ED97AA /* TGImageBasedPasscodeBackground.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB291F2B652C00ED97AA /* TGImageBasedPasscodeBackground.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB271F2B652C00ED97AA /* TGImageBasedPasscodeBackground.m */; };
D07BCB351F2B65F100ED97AA /* TGBuiltinWallpaperInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB2B1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB361F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB2C1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m */; };
D07BCB371F2B65F100ED97AA /* TGColorWallpaperInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB2D1F2B65F100ED97AA /* TGColorWallpaperInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB381F2B65F100ED97AA /* TGColorWallpaperInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB2E1F2B65F100ED97AA /* TGColorWallpaperInfo.m */; };
D07BCB391F2B65F100ED97AA /* TGCustomImageWallpaperInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB2F1F2B65F100ED97AA /* TGCustomImageWallpaperInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB3A1F2B65F100ED97AA /* TGCustomImageWallpaperInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB301F2B65F100ED97AA /* TGCustomImageWallpaperInfo.m */; };
D07BCB3B1F2B65F100ED97AA /* TGRemoteWallpaperInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB311F2B65F100ED97AA /* TGRemoteWallpaperInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB3C1F2B65F100ED97AA /* TGRemoteWallpaperInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB321F2B65F100ED97AA /* TGRemoteWallpaperInfo.m */; };
D07BCB3D1F2B65F100ED97AA /* TGWallpaperInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB331F2B65F100ED97AA /* TGWallpaperInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB3E1F2B65F100ED97AA /* TGWallpaperInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB341F2B65F100ED97AA /* TGWallpaperInfo.m */; };
D07BCB5B1F2B6A5600ED97AA /* TGPIPAblePlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB401F2B6A5600ED97AA /* TGPIPAblePlayerView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB5C1F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = D07BCB411F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D07BCB5D1F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07BCB421F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.m */; };
@ -1746,8 +1717,6 @@
D04269101F586E430037ECE8 /* TGVideoCameraPipeline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGVideoCameraPipeline.m; sourceTree = "<group>"; };
D04269131F586EC80037ECE8 /* TGVideoMessageCaptureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGVideoMessageCaptureController.h; sourceTree = "<group>"; };
D04269141F586EC80037ECE8 /* TGVideoMessageCaptureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGVideoMessageCaptureController.m; sourceTree = "<group>"; };
D07ABBA6202A1939003671DE /* TGWallpaperController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGWallpaperController.m; sourceTree = "<group>"; };
D07ABBA7202A1939003671DE /* TGWallpaperController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGWallpaperController.h; sourceTree = "<group>"; };
D07BC6C91F2A18B700ED97AA /* TGCameraMainPhoneView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCameraMainPhoneView.h; sourceTree = "<group>"; };
D07BC6CA1F2A18B700ED97AA /* TGCameraMainPhoneView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGCameraMainPhoneView.m; sourceTree = "<group>"; };
D07BC6CB1F2A18B700ED97AA /* TGCameraMainTabletView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCameraMainTabletView.h; sourceTree = "<group>"; };
@ -2261,35 +2230,8 @@
D07BCAF91F2B517900ED97AA /* TGLegacyMediaPickerTipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLegacyMediaPickerTipView.h; sourceTree = "<group>"; };
D07BCAFA1F2B517900ED97AA /* TGLegacyMediaPickerTipView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLegacyMediaPickerTipView.m; sourceTree = "<group>"; };
D07BCB011F2B546400ED97AA /* LegacyComponentsContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyComponentsContext.m; sourceTree = "<group>"; };
D07BCB041F2B646A00ED97AA /* TGPasswordEntryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasswordEntryView.h; sourceTree = "<group>"; };
D07BCB051F2B646A00ED97AA /* TGPasswordEntryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasswordEntryView.m; sourceTree = "<group>"; };
D07BCB061F2B646A00ED97AA /* TGPasscodeEntryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodeEntryController.h; sourceTree = "<group>"; };
D07BCB071F2B646A00ED97AA /* TGPasscodeEntryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasscodeEntryController.m; sourceTree = "<group>"; };
D07BCB081F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodeEntryKeyboardView.h; sourceTree = "<group>"; };
D07BCB091F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasscodeEntryKeyboardView.m; sourceTree = "<group>"; };
D07BCB0A1F2B646A00ED97AA /* TGPasscodePinDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodePinDotView.h; sourceTree = "<group>"; };
D07BCB0B1F2B646A00ED97AA /* TGPasscodePinDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasscodePinDotView.m; sourceTree = "<group>"; };
D07BCB0C1F2B646A00ED97AA /* TGPasscodePinView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodePinView.h; sourceTree = "<group>"; };
D07BCB0D1F2B646A00ED97AA /* TGPasscodePinView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasscodePinView.m; sourceTree = "<group>"; };
D07BCB0E1F2B646A00ED97AA /* TGPasscodeButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodeButtonView.h; sourceTree = "<group>"; };
D07BCB0F1F2B646A00ED97AA /* TGPasscodeButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPasscodeButtonView.m; sourceTree = "<group>"; };
D07BCB101F2B646A00ED97AA /* TGPasscodeBackground.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPasscodeBackground.h; sourceTree = "<group>"; };
D07BCB111F2B646A00ED97AA /* TGTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGTextField.h; sourceTree = "<group>"; };
D07BCB121F2B646A00ED97AA /* TGTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGTextField.m; sourceTree = "<group>"; };
D07BCB131F2B646A00ED97AA /* TGDefaultPasscodeBackground.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDefaultPasscodeBackground.h; sourceTree = "<group>"; };
D07BCB141F2B646A00ED97AA /* TGDefaultPasscodeBackground.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDefaultPasscodeBackground.m; sourceTree = "<group>"; };
D07BCB261F2B652C00ED97AA /* TGImageBasedPasscodeBackground.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGImageBasedPasscodeBackground.h; sourceTree = "<group>"; };
D07BCB271F2B652C00ED97AA /* TGImageBasedPasscodeBackground.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGImageBasedPasscodeBackground.m; sourceTree = "<group>"; };
D07BCB2B1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBuiltinWallpaperInfo.h; sourceTree = "<group>"; };
D07BCB2C1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBuiltinWallpaperInfo.m; sourceTree = "<group>"; };
D07BCB2D1F2B65F100ED97AA /* TGColorWallpaperInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGColorWallpaperInfo.h; sourceTree = "<group>"; };
D07BCB2E1F2B65F100ED97AA /* TGColorWallpaperInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGColorWallpaperInfo.m; sourceTree = "<group>"; };
D07BCB2F1F2B65F100ED97AA /* TGCustomImageWallpaperInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCustomImageWallpaperInfo.h; sourceTree = "<group>"; };
D07BCB301F2B65F100ED97AA /* TGCustomImageWallpaperInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGCustomImageWallpaperInfo.m; sourceTree = "<group>"; };
D07BCB311F2B65F100ED97AA /* TGRemoteWallpaperInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGRemoteWallpaperInfo.h; sourceTree = "<group>"; };
D07BCB321F2B65F100ED97AA /* TGRemoteWallpaperInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGRemoteWallpaperInfo.m; sourceTree = "<group>"; };
D07BCB331F2B65F100ED97AA /* TGWallpaperInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGWallpaperInfo.h; sourceTree = "<group>"; };
D07BCB341F2B65F100ED97AA /* TGWallpaperInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGWallpaperInfo.m; sourceTree = "<group>"; };
D07BCB401F2B6A5600ED97AA /* TGPIPAblePlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPIPAblePlayerView.h; sourceTree = "<group>"; };
D07BCB411F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGEmbedInstagramPlayerView.h; sourceTree = "<group>"; };
D07BCB421F2B6A5600ED97AA /* TGEmbedInstagramPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGEmbedInstagramPlayerView.m; sourceTree = "<group>"; };
@ -2510,8 +2452,6 @@
D07BCA6F1F2B441F00ED97AA /* Media Assets Picker */,
D07BCAB41F2B4DD600ED97AA /* Attachment Menu */,
D07BC82C1F2A2CFE00ED97AA /* Secret Timer */,
D07BCB031F2B63D700ED97AA /* Passcode */,
D07BCB2A1F2B65C400ED97AA /* Wallpapers */,
D07BCB3F1F2B69D400ED97AA /* Embed Video */,
D02660331F34A7DA000E2DC5 /* Location */,
09750FAE1F30DAE1001B9886 /* Clipboard Menu */,
@ -2815,6 +2755,8 @@
D07BCC041F2B82D100ED97AA /* TGModernConversationTitleActivityIndicator.m */,
D02660801F34B986000E2DC5 /* TGListsTableView.h */,
D02660811F34B986000E2DC5 /* TGListsTableView.m */,
D07BCB111F2B646A00ED97AA /* TGTextField.h */,
D07BCB121F2B646A00ED97AA /* TGTextField.m */,
D02660841F34B9B1000E2DC5 /* TGSearchBar.h */,
D02660851F34B9B1000E2DC5 /* TGSearchBar.m */,
D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */,
@ -3742,51 +3684,6 @@
name = "Attachment Menu";
sourceTree = "<group>";
};
D07BCB031F2B63D700ED97AA /* Passcode */ = {
isa = PBXGroup;
children = (
D07BCB041F2B646A00ED97AA /* TGPasswordEntryView.h */,
D07BCB051F2B646A00ED97AA /* TGPasswordEntryView.m */,
D07BCB061F2B646A00ED97AA /* TGPasscodeEntryController.h */,
D07BCB071F2B646A00ED97AA /* TGPasscodeEntryController.m */,
D07BCB081F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.h */,
D07BCB091F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.m */,
D07BCB0A1F2B646A00ED97AA /* TGPasscodePinDotView.h */,
D07BCB0B1F2B646A00ED97AA /* TGPasscodePinDotView.m */,
D07BCB0C1F2B646A00ED97AA /* TGPasscodePinView.h */,
D07BCB0D1F2B646A00ED97AA /* TGPasscodePinView.m */,
D07BCB0E1F2B646A00ED97AA /* TGPasscodeButtonView.h */,
D07BCB0F1F2B646A00ED97AA /* TGPasscodeButtonView.m */,
D07BCB101F2B646A00ED97AA /* TGPasscodeBackground.h */,
D07BCB111F2B646A00ED97AA /* TGTextField.h */,
D07BCB121F2B646A00ED97AA /* TGTextField.m */,
D07BCB131F2B646A00ED97AA /* TGDefaultPasscodeBackground.h */,
D07BCB141F2B646A00ED97AA /* TGDefaultPasscodeBackground.m */,
D07BCB261F2B652C00ED97AA /* TGImageBasedPasscodeBackground.h */,
D07BCB271F2B652C00ED97AA /* TGImageBasedPasscodeBackground.m */,
);
name = Passcode;
sourceTree = "<group>";
};
D07BCB2A1F2B65C400ED97AA /* Wallpapers */ = {
isa = PBXGroup;
children = (
D07ABBA7202A1939003671DE /* TGWallpaperController.h */,
D07ABBA6202A1939003671DE /* TGWallpaperController.m */,
D07BCB2B1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.h */,
D07BCB2C1F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m */,
D07BCB2D1F2B65F100ED97AA /* TGColorWallpaperInfo.h */,
D07BCB2E1F2B65F100ED97AA /* TGColorWallpaperInfo.m */,
D07BCB2F1F2B65F100ED97AA /* TGCustomImageWallpaperInfo.h */,
D07BCB301F2B65F100ED97AA /* TGCustomImageWallpaperInfo.m */,
D07BCB311F2B65F100ED97AA /* TGRemoteWallpaperInfo.h */,
D07BCB321F2B65F100ED97AA /* TGRemoteWallpaperInfo.m */,
D07BCB331F2B65F100ED97AA /* TGWallpaperInfo.h */,
D07BCB341F2B65F100ED97AA /* TGWallpaperInfo.m */,
);
name = Wallpapers;
sourceTree = "<group>";
};
D07BCB3F1F2B69D400ED97AA /* Embed Video */ = {
isa = PBXGroup;
children = (
@ -3962,7 +3859,6 @@
D01778F71F20CDAC0044446D /* TGHacks.h in Headers */,
D01779161F20F4500044446D /* TGStaticBackdropImageData.h in Headers */,
D01779031F20D16B0044446D /* FreedomUIKit.h in Headers */,
D07BCB241F2B646A00ED97AA /* TGDefaultPasscodeBackground.h in Headers */,
D07BCA2D1F2A9A9600ED97AA /* TGModernGalleryImageItemView.h in Headers */,
D07BCBBD1F2B6F6300ED97AA /* CBCoubVideoSource.h in Headers */,
D07BC6D11F2A18B700ED97AA /* TGCameraMainTabletView.h in Headers */,
@ -4024,7 +3920,6 @@
D01778BE1F200A9A0044446D /* UIScrollView+TGHacks.h in Headers */,
D07BCBD31F2B6FFE00ED97AA /* LegacyHTTPRequestOperation.h in Headers */,
D0177A6F1F2201F00044446D /* TGModernGalleryDefaultFooterView.h in Headers */,
D07BCB371F2B65F100ED97AA /* TGColorWallpaperInfo.h in Headers */,
D01777F61F1F961D0044446D /* TGPeerIdAdapter.h in Headers */,
D07BC82A1F2A2C0B00ED97AA /* PGWarmthTool.h in Headers */,
D07BC7FC1F2A2C0B00ED97AA /* PGExposureTool.h in Headers */,
@ -4038,7 +3933,6 @@
D07BCADF1F2B4F5E00ED97AA /* TGLegacyCameraController.h in Headers */,
D017782F1F1F961D0044446D /* TGReplyMarkupAttachment.h in Headers */,
0916FEA720A1EA7B0084A755 /* TGPassportScanView.h in Headers */,
D07BCB211F2B646A00ED97AA /* TGPasscodeBackground.h in Headers */,
D01778371F1F961D0044446D /* TGAudioMediaAttachment.h in Headers */,
D07BC6F51F2A19A700ED97AA /* TGCameraSegmentsView.h in Headers */,
D04269051F586A070037ECE8 /* TGVideoMessageScrubber.h in Headers */,
@ -4061,7 +3955,6 @@
D07BC81A1F2A2C0B00ED97AA /* PGPhotoHistogramGenerator.h in Headers */,
D0177B181F2641B10044446D /* PGCameraDeviceAngleSampler.h in Headers */,
D0177A0A1F2139980044446D /* POPSpringSolver.h in Headers */,
D07BCB3D1F2B65F100ED97AA /* TGWallpaperInfo.h in Headers */,
D04268F91F58687D0037ECE8 /* TGVideoCameraGLView.h in Headers */,
D017794F1F2100280044446D /* TGMediaSelectionContext.h in Headers */,
D01778111F1F961D0044446D /* TGMessageEntityBold.h in Headers */,
@ -4155,9 +4048,7 @@
D017781F1F1F961D0044446D /* TGMessageEntityMentionName.h in Headers */,
D0177AD91F23D9B80044446D /* SGraphListNode.h in Headers */,
D07BCA571F2A9E1600ED97AA /* TGDraggableCollectionViewFlowLayout.h in Headers */,
D07BCB151F2B646A00ED97AA /* TGPasswordEntryView.h in Headers */,
D0177A041F2139980044446D /* POPPropertyAnimation.h in Headers */,
D07BCB281F2B652C00ED97AA /* TGImageBasedPasscodeBackground.h in Headers */,
D07BC8CB1F2A37EC00ED97AA /* TGPhotoPaintSelectionContainerView.h in Headers */,
D07BCB621F2B6A5600ED97AA /* TGEmbedPlayerControls.h in Headers */,
D07BC7371F2A2A7D00ED97AA /* PGPhotoEditorPicture.h in Headers */,
@ -4197,7 +4088,6 @@
D07BC85C1F2A2DBD00ED97AA /* TGMenuSheetItemView.h in Headers */,
D07BC8721F2A2F6500ED97AA /* TGInputTextTag.h in Headers */,
D07BCB721F2B6A5600ED97AA /* TGEmbedVKPlayerView.h in Headers */,
D07ABBA9202A193A003671DE /* TGWallpaperController.h in Headers */,
D07BC81C1F2A2C0B00ED97AA /* PGPhotoLookupFilterPass.h in Headers */,
D07BCACC1F2B4E7300ED97AA /* TGMediaAvatarMenuMixin.h in Headers */,
D01778CB1F200BAC0044446D /* TGRTLScreenEdgePanGestureRecognizer.h in Headers */,
@ -4233,7 +4123,6 @@
D07BC9FF1F2A9A2B00ED97AA /* TGMediaPickerGalleryPhotoItem.h in Headers */,
D07BC77E1F2A2B3700ED97AA /* TGPhotoEditorLinearBlurView.h in Headers */,
D01779711F2103FD0044446D /* TGPaintUndoManager.h in Headers */,
D07BCB1B1F2B646A00ED97AA /* TGPasscodePinDotView.h in Headers */,
D07BCAEF1F2B507600ED97AA /* TGAttachmentPhotoCell.h in Headers */,
D07BC7B61F2A2BBE00ED97AA /* PGBlurTool.h in Headers */,
D07BCA921F2B443700ED97AA /* TGMediaAssetsMomentsSectionHeader.h in Headers */,
@ -4242,7 +4131,6 @@
D01778311F1F961D0044446D /* TGInstantPage.h in Headers */,
D07BC96C1F2A43E300ED97AA /* TGPhotoStickersView.h in Headers */,
D07BC7A41F2A2B8900ED97AA /* GPUImageContext.h in Headers */,
D07BCB171F2B646A00ED97AA /* TGPasscodeEntryController.h in Headers */,
D07BC9FD1F2A9A2B00ED97AA /* TGMediaPickerGalleryModel.h in Headers */,
D017792F1F20FFAC0044446D /* TGMediaAssetsLegacyLibrary.h in Headers */,
D07BC87D1F2A365000ED97AA /* TGProgressSpinnerView.h in Headers */,
@ -4409,7 +4297,6 @@
D017782D1F1F961D0044446D /* TGBotReplyMarkupRow.h in Headers */,
D017775E1F1F8FE60044446D /* TGBotComandInfo.h in Headers */,
D07BC9071F2A380D00ED97AA /* TGPaintEllipticalBrush.h in Headers */,
D07BCB191F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.h in Headers */,
D07BC7701F2A2B3700ED97AA /* TGPhotoEditorCollectionView.h in Headers */,
D07BCA901F2B443700ED97AA /* TGMediaAssetsMomentsController.h in Headers */,
D07BC86B1F2A2F3800ED97AA /* HPGrowingTextView.h in Headers */,
@ -4421,11 +4308,9 @@
D0177A3C1F21F2E50044446D /* TGTimerTarget.h in Headers */,
D017798E1F2108130044446D /* PSLMDBKeyValueStore.h in Headers */,
D017784B1F1F961D0044446D /* TGUnsupportedMediaAttachment.h in Headers */,
D07BCB391F2B65F100ED97AA /* TGCustomImageWallpaperInfo.h in Headers */,
D01778461F1F961D0044446D /* TGDocumentAttributeAudio.h in Headers */,
D07BCBEB1F2B72DC00ED97AA /* STKAutoRecoveringHTTPDataSource.h in Headers */,
D01778AD1F1FFDB80044446D /* TGDateUtils.h in Headers */,
D07BCB351F2B65F100ED97AA /* TGBuiltinWallpaperInfo.h in Headers */,
D07BC9601F2A3F0A00ED97AA /* TGGradientLabel.h in Headers */,
D07BC80A1F2A2C0B00ED97AA /* PGPhotoEnhanceInterpolationFilter.h in Headers */,
D07BC8241F2A2C0B00ED97AA /* PGSharpenTool.h in Headers */,
@ -4480,7 +4365,6 @@
D07BCA9E1F2B443700ED97AA /* TGMediaAssetsVideoCell.h in Headers */,
D07BC70F1F2A25AE00ED97AA /* TGCameraPhotoPreviewController.h in Headers */,
D07BCA421F2A9C6600ED97AA /* TGModernMediaListItem.h in Headers */,
D07BCB1D1F2B646A00ED97AA /* TGPasscodePinView.h in Headers */,
D01778E91F20CAE60044446D /* TGViewController.h in Headers */,
D017781B1F1F961D0044446D /* TGMessageEntityItalic.h in Headers */,
D07BCBF11F2B72DC00ED97AA /* STKDataSourceWrapper.h in Headers */,
@ -4491,7 +4375,6 @@
D07BCB781F2B6DB900ED97AA /* TGEmbedCoubPlayerView.h in Headers */,
D07BC7271F2A2A5300ED97AA /* UICollectionView+Utils.h in Headers */,
D01777661F1F8FE60044446D /* TGStringUtils.h in Headers */,
D07BCB3B1F2B65F100ED97AA /* TGRemoteWallpaperInfo.h in Headers */,
D07BCBC01F2B6F6300ED97AA /* CBDownloadOperationDelegate.h in Headers */,
D07BC9B21F2A4B6600ED97AA /* TGStickerAssociation.h in Headers */,
D01778B21F1FFF810044446D /* TGLabel.h in Headers */,
@ -4540,7 +4423,6 @@
D07BC91F1F2A380D00ED97AA /* TGPaintShaderSet.h in Headers */,
D02660701F34A7F8000E2DC5 /* TGLocationUtils.h in Headers */,
D07BC6FF1F2A1A7700ED97AA /* TGMenuView.h in Headers */,
D07BCB1F1F2B646A00ED97AA /* TGPasscodeButtonView.h in Headers */,
D07BCA961F2B443700ED97AA /* TGMediaAssetsPhotoCell.h in Headers */,
D01778441F1F961D0044446D /* TGDocumentAttributeAnimated.h in Headers */,
D07BCABB1F2B4E2600ED97AA /* TGTransitionLayout.h in Headers */,
@ -4604,6 +4486,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = D017771D1F1F8F100044446D;
@ -4656,15 +4539,12 @@
D07BC90C1F2A380D00ED97AA /* TGPaintFaceDetector.m in Sources */,
D07BC9F21F2A9A2B00ED97AA /* TGMediaPickerCell.m in Sources */,
D07BC8051F2A2C0B00ED97AA /* PGPhotoBlurPass.m in Sources */,
D07BCB3C1F2B65F100ED97AA /* TGRemoteWallpaperInfo.m in Sources */,
D07BC7691F2A2B3700ED97AA /* TGPhotoEditorBlurAreaView.m in Sources */,
D07BC99E1F2A494000ED97AA /* TGStickerCollectionViewCell.m in Sources */,
D01779851F2107D70044446D /* mdb.c in Sources */,
D0177B171F2641B10044446D /* PGCameraCaptureSession.m in Sources */,
098C0E5B20245179007B4539 /* TGMessageEntityPhone.m in Sources */,
D07BCA8B1F2B443700ED97AA /* TGMediaAssetsGifCell.m in Sources */,
D07BCB1E1F2B646A00ED97AA /* TGPasscodePinView.m in Sources */,
D07BCB1A1F2B646A00ED97AA /* TGPasscodeEntryKeyboardView.m in Sources */,
D017784A1F1F961D0044446D /* TGDocumentMediaAttachment.m in Sources */,
D0177A0C1F2139980044446D /* POPVector.mm in Sources */,
D0177ADA1F23D9B80044446D /* SGraphListNode.m in Sources */,
@ -4699,7 +4579,6 @@
D07BC6F41F2A19A700ED97AA /* TGCameraModeControl.m in Sources */,
D017782C1F1F961D0044446D /* TGBotReplyMarkupButton.m in Sources */,
D07BCAB81F2B4DE200ED97AA /* TGAttachmentCarouselItemView.m in Sources */,
D07ABBA8202A193A003671DE /* TGWallpaperController.m in Sources */,
D026605B1F34A7F8000E2DC5 /* TGLocationMapModeControl.m in Sources */,
D01777771F1F92570044446D /* RMPhoneFormat.m in Sources */,
D07BCA221F2A9A5300ED97AA /* TGCheckButtonView.m in Sources */,
@ -4755,7 +4634,6 @@
D07BC76B1F2A2B3700ED97AA /* TGPhotoEditorBlurToolView.m in Sources */,
D0177AA41F2222990044446D /* TGKeyCommandController.m in Sources */,
D07BCA061F2A9A2B00ED97AA /* TGMediaPickerGalleryVideoItem.m in Sources */,
D07BCB1C1F2B646A00ED97AA /* TGPasscodePinDotView.m in Sources */,
D01778411F1F961D0044446D /* TGDocumentAttributeSticker.m in Sources */,
09750FCE1F30E53A001B9886 /* TGClipboardGalleryMixin.m in Sources */,
D07BC6F81F2A19A700ED97AA /* TGCameraShutterButton.m in Sources */,
@ -4886,7 +4764,6 @@
D017786A1F1F99180044446D /* NSInputStream+TL.m in Sources */,
D07BC7351F2A2A7D00ED97AA /* PGPhotoEditor.m in Sources */,
D02660671F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.m in Sources */,
D07BCB3A1F2B65F100ED97AA /* TGCustomImageWallpaperInfo.m in Sources */,
D07BC9731F2A467D00ED97AA /* TGPhotoTextSettingsView.m in Sources */,
D0177B191F2641B10044446D /* PGCameraDeviceAngleSampler.m in Sources */,
D07BC6D01F2A18B700ED97AA /* TGCameraMainPhoneView.m in Sources */,
@ -4903,7 +4780,6 @@
D07BC9BF1F2A722400ED97AA /* TGHistogramView.m in Sources */,
D07BCBDA1F2B72BD00ED97AA /* NSMutableArray+STKAudioPlayer.m in Sources */,
D07BCBBC1F2B6F6300ED97AA /* CBCoubPlayerContance.m in Sources */,
D07BCB291F2B652C00ED97AA /* TGImageBasedPasscodeBackground.m in Sources */,
D07BC92C1F2A3A3F00ED97AA /* matrix.m in Sources */,
D07BCA6E1F2B3CE700ED97AA /* TGCameraFocusCrosshairsControl.m in Sources */,
D07BC7AD1F2A2B8900ED97AA /* GPUImageOutput.m in Sources */,
@ -4971,7 +4847,6 @@
D07BC9001F2A380D00ED97AA /* TGPaintBrush.m in Sources */,
D0177A051F2139980044446D /* POPPropertyAnimation.mm in Sources */,
D07BCBCE1F2B6F6300ED97AA /* CBVideoPlayer.m in Sources */,
D07BCB381F2B65F100ED97AA /* TGColorWallpaperInfo.m in Sources */,
D0177A011F2139980044446D /* POPLayerExtras.mm in Sources */,
D01777671F1F8FE60044446D /* TGStringUtils.mm in Sources */,
D0177B151F2641B10044446D /* PGCamera.m in Sources */,
@ -4986,7 +4861,6 @@
D07BCBA71F2B6F6300ED97AA /* AVAsset+CBExtension.m in Sources */,
D017783C1F1F961D0044446D /* TGStickerPackReference.m in Sources */,
D026607B1F34A961000E2DC5 /* TGLocationVenue.m in Sources */,
D07BCB201F2B646A00ED97AA /* TGPasscodeButtonView.m in Sources */,
D07BC9261F2A380D00ED97AA /* TGPaintSwatch.m in Sources */,
D017799D1F2108670044446D /* TGMemoryImageCache.m in Sources */,
D07BCA461F2A9C6600ED97AA /* TGModernMediaListItemView.m in Sources */,
@ -4994,7 +4868,6 @@
D07BC6F21F2A19A700ED97AA /* TGCameraInterfaceAssets.m in Sources */,
D07BC8441F2A2D7A00ED97AA /* TGPhotoCaptionInputMixin.m in Sources */,
D01779911F2108130044446D /* PSLMDBKeyValueReaderWriter.m in Sources */,
D07BCB181F2B646A00ED97AA /* TGPasscodeEntryController.m in Sources */,
D0177A851F2218AB0044446D /* TGModernGalleryImageItemContainerView.m in Sources */,
D017775A1F1F8FE60044446D /* PSKeyValueEncoder.m in Sources */,
D07BC8661F2A2F1300ED97AA /* TGMediaPickerCaptionInputPanel.m in Sources */,
@ -5026,7 +4899,6 @@
D01779971F21082E0044446D /* PSLMDBTable.m in Sources */,
D07BCC061F2B82D100ED97AA /* TGModernConversationTitleActivityIndicator.m in Sources */,
D04269081F586A070037ECE8 /* TGVideoMessageScrubberThumbnailView.m in Sources */,
D07BCB361F2B65F100ED97AA /* TGBuiltinWallpaperInfo.m in Sources */,
D07BC90A1F2A380D00ED97AA /* TGPaintFaceDebugView.m in Sources */,
D017785A1F1F961D0044446D /* TGImageMediaAttachment.m in Sources */,
09053D7D20A5CCF10029652D /* TGPassportOCR.mm in Sources */,
@ -5034,7 +4906,6 @@
D01779151F20F4500044446D /* TGStaticBackdropAreaData.m in Sources */,
D07BC91C1F2A380D00ED97AA /* TGPaintRender.m in Sources */,
D07BCA101F2A9A2B00ED97AA /* TGMediaPickerLayoutMetrics.m in Sources */,
D07BCB251F2B646A00ED97AA /* TGDefaultPasscodeBackground.m in Sources */,
D0177A761F2202260044446D /* TGModernBackToolbarButton.m in Sources */,
D01777611F1F8FE60044446D /* TGBotInfo.m in Sources */,
D07BC8591F2A2DBD00ED97AA /* TGMenuSheetCollectionView.m in Sources */,
@ -5071,7 +4942,6 @@
09BD14EC20BD928100D95932 /* TGPhotoVideoEditor.m in Sources */,
D07BC7891F2A2B3700ED97AA /* TGPhotoEditorTintSwatchView.m in Sources */,
D07BC8C01F2A37EC00ED97AA /* TGPhotoPaintActionsView.m in Sources */,
D07BCB161F2B646A00ED97AA /* TGPasswordEntryView.m in Sources */,
D07BC9FC1F2A9A2B00ED97AA /* TGMediaPickerGalleryItem.m in Sources */,
D07BC8C81F2A37EC00ED97AA /* TGPhotoPaintFont.m in Sources */,
D07BC9FE1F2A9A2B00ED97AA /* TGMediaPickerGalleryModel.m in Sources */,
@ -5187,7 +5057,6 @@
D017784E1F1F961D0044446D /* TGForwardedMessageMediaAttachment.m in Sources */,
D07BCBD01F2B6F6300ED97AA /* NSDictionary+CBExtensions.m in Sources */,
D07BC7731F2A2B3700ED97AA /* TGPhotoEditorCurvesHistogramView.m in Sources */,
D07BCB3E1F2B65F100ED97AA /* TGWallpaperInfo.m in Sources */,
09053D7A20A5CCF10029652D /* fast-edge.cpp in Sources */,
D07BCA1C1F2A9A2B00ED97AA /* TGMediaPickerSelectionGestureRecognizer.m in Sources */,
D07BC9141F2A380D00ED97AA /* TGPaintNeonBrush.m in Sources */,

View file

@ -1,9 +1,22 @@
import CoreMedia
import Accelerate
import FFMpeg
import Accelerate
private let bufferCount = 32
private let deviceColorSpace: CGColorSpace = {
if #available(iOSApplicationExtension 9.3, iOS 9.3, *) {
if let colorSpace = CGColorSpace(name: CGColorSpace.displayP3) {
return colorSpace
} else {
return CGColorSpaceCreateDeviceRGB()
}
} else {
return CGColorSpaceCreateDeviceRGB()
}
}()
public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
private let codecContext: FFMpegAVCodecContext
@ -63,6 +76,17 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
return nil
}
public func render(frame: MediaTrackDecodableFrame) -> UIImage? {
let status = frame.packet.send(toDecoder: self.codecContext)
if status == 0 {
if self.codecContext.receive(into: self.videoFrame) {
return convertVideoFrameToImage(self.videoFrame)
}
}
return nil
}
public func takeRemainingFrame() -> MediaTrackFrame? {
if !self.delayedFrames.isEmpty {
var minFrameIndex = 0
@ -79,6 +103,53 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
}
}
private func convertVideoFrameToImage(_ frame: FFMpegAVFrame) -> UIImage? {
var info = vImage_YpCbCrToARGB()
var pixelRange: vImage_YpCbCrPixelRange
switch frame.colorRange {
case .full:
pixelRange = vImage_YpCbCrPixelRange(Yp_bias: 0, CbCr_bias: 128, YpRangeMax: 255, CbCrRangeMax: 255, YpMax: 255, YpMin: 0, CbCrMax: 255, CbCrMin: 0)
default:
pixelRange = vImage_YpCbCrPixelRange(Yp_bias: 16, CbCr_bias: 128, YpRangeMax: 235, CbCrRangeMax: 240, YpMax: 255, YpMin: 0, CbCrMax: 255, CbCrMin: 0)
}
var result = kvImageNoError
result = vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange, &info, kvImage420Yp8_Cb8_Cr8, kvImageARGB8888, 0)
if result != kvImageNoError {
return nil
}
var srcYp = vImage_Buffer(data: frame.data[0], height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width), rowBytes: Int(frame.lineSize[0]))
var srcCb = vImage_Buffer(data: frame.data[1], height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width / 2), rowBytes: Int(frame.lineSize[1]))
var srcCr = vImage_Buffer(data: frame.data[2], height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width / 2), rowBytes: Int(frame.lineSize[2]))
let argbBytesPerRow = (4 * Int(frame.width) + 15) & (~15)
let argbLength = argbBytesPerRow * Int(frame.height)
let argb = malloc(argbLength)!
guard let provider = CGDataProvider(dataInfo: argb, data: argb, size: argbLength, releaseData: { bytes, _, _ in
free(bytes)
}) else {
return nil
}
var dst = vImage_Buffer(data: argb, height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width), rowBytes: argbBytesPerRow)
var permuteMap: [UInt8] = [3, 2, 1, 0]
result = vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(&srcYp, &srcCb, &srcCr, &dst, &info, &permuteMap, 0x00, 0)
if result != kvImageNoError {
return nil
}
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue)
guard let image = CGImage(width: Int(frame.width), height: Int(frame.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: argbBytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent) else {
return nil
}
return UIImage(cgImage: image, scale: 1.0, orientation: .up)
}
private func convertVideoFrame(_ frame: FFMpegAVFrame, pts: CMTime, dts: CMTime, duration: CMTime) -> MediaTrackFrame? {
if frame.data[0] == nil {
return nil
@ -100,9 +171,6 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
ioSurfaceProperties["IOSurfaceIsGlobal"] = true as NSNumber
var options: [String: Any] = [kCVPixelBufferBytesPerRowAlignmentKey as String: frame.lineSize[0] as NSNumber]
/*if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
options[kCVPixelBufferOpenGLESTextureCacheCompatibilityKey as String] = true as NSNumber
}*/
options[kCVPixelBufferIOSurfacePropertiesKey as String] = ioSurfaceProperties
CVPixelBufferCreate(kCFAllocatorDefault,

View file

@ -0,0 +1,131 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import FFMpeg
private final class FramePreviewContext {
let source: UniversalSoftwareVideoSource
init(source: UniversalSoftwareVideoSource) {
self.source = source
}
}
private func initializedPreviewContext(queue: Queue, postbox: Postbox, fileReference: FileMediaReference) -> Signal<QueueLocalObject<FramePreviewContext>, NoError> {
return Signal { subscriber in
let source = UniversalSoftwareVideoSource(mediaBox: postbox.mediaBox, fileReference: fileReference)
let readyDisposable = (source.ready
|> filter { $0 }).start(next: { _ in
subscriber.putNext(QueueLocalObject(queue: queue, generate: {
return FramePreviewContext(source: source)
}))
})
return ActionDisposable {
readyDisposable.dispose()
}
}
}
private final class MediaPlayerFramePreviewImpl {
private let queue: Queue
private let context: Promise<QueueLocalObject<FramePreviewContext>>
private let currentFrameDisposable = MetaDisposable()
private var currentFrameTimestamp: Double?
private var nextFrameTimestamp: Double?
fileprivate let framePipe = ValuePipe<UIImage>()
init(queue: Queue, postbox: Postbox, fileReference: FileMediaReference) {
self.queue = queue
self.context = Promise()
self.context.set(initializedPreviewContext(queue: queue, postbox: postbox, fileReference: fileReference))
}
deinit {
assert(self.queue.isCurrent())
self.currentFrameDisposable.dispose()
}
func generateFrame(at timestamp: Double) {
if self.currentFrameTimestamp != nil {
self.nextFrameTimestamp = timestamp
return
}
self.currentFrameTimestamp = timestamp
let queue = self.queue
let takeDisposable = MetaDisposable()
let disposable = (self.context.get()
|> take(1)).start(next: { [weak self] context in
queue.async {
guard context.queue === queue else {
return
}
context.with { context in
let disposable = context.source.takeFrame(at: timestamp).start(next: { image in
guard let strongSelf = self else {
return
}
if let image = image {
strongSelf.framePipe.putNext(image)
}
strongSelf.currentFrameTimestamp = nil
if let nextFrameTimestamp = strongSelf.nextFrameTimestamp {
strongSelf.nextFrameTimestamp = nil
strongSelf.generateFrame(at: nextFrameTimestamp)
}
})
takeDisposable.set(disposable)
}
}
})
self.currentFrameDisposable.set(ActionDisposable {
takeDisposable.dispose()
disposable.dispose()
})
}
func cancelPendingFrames() {
self.nextFrameTimestamp = nil
self.currentFrameTimestamp = nil
self.currentFrameDisposable.set(nil)
}
}
public final class MediaPlayerFramePreview {
private let queue: Queue
private let impl: QueueLocalObject<MediaPlayerFramePreviewImpl>
public var generatedFrames: Signal<UIImage?, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.framePipe.signal().start(next: { image in
subscriber.putNext(image)
}))
}
return disposable
}
}
public init(postbox: Postbox, fileReference: FileMediaReference) {
let queue = Queue()
self.queue = queue
self.impl = QueueLocalObject(queue: queue, generate: {
return MediaPlayerFramePreviewImpl(queue: queue, postbox: postbox, fileReference: fileReference)
})
}
public func generateFrame(at timestamp: Double) {
self.impl.with { impl in
impl.generateFrame(at: timestamp)
}
}
public func cancelPendingFrames() {
self.impl.with { impl in
impl.cancelPendingFrames()
}
}
}

View file

@ -188,6 +188,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
public var playbackStatusUpdated: ((MediaPlayerPlaybackStatus?) -> Void)?
public var playerStatusUpdated: ((MediaPlayerStatus?) -> Void)?
public var seek: ((Double) -> Void)?
public var update: ((Double?, CGFloat) -> Void)?
private let _scrubbingTimestamp = Promise<Double?>(nil)
public var scrubbingTimestamp: Signal<Double?, NoError> {
@ -378,6 +379,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
strongSelf.scrubbingBeginTimestamp = statusValue.timestamp
strongSelf.scrubbingTimestampValue = statusValue.timestamp
strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue))
strongSelf.update?(strongSelf.scrubbingTimestampValue, CGFloat(statusValue.timestamp / statusValue.duration))
strongSelf.updateProgressAnimations()
}
}
@ -385,8 +387,10 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
handleNodeContainer.updateScrubbing = { [weak self] addedFraction in
if let strongSelf = self {
if let statusValue = strongSelf.statusValue, let scrubbingBeginTimestamp = strongSelf.scrubbingBeginTimestamp, Double(0.0).isLess(than: statusValue.duration) {
strongSelf.scrubbingTimestampValue = max(0.0, min(statusValue.duration, scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction)))
let timestampValue = max(0.0, min(statusValue.duration, scrubbingBeginTimestamp + statusValue.duration * Double(addedFraction)))
strongSelf.scrubbingTimestampValue = timestampValue
strongSelf._scrubbingTimestamp.set(.single(strongSelf.scrubbingTimestampValue))
strongSelf.update?(timestampValue, CGFloat(timestampValue / statusValue.duration))
strongSelf.updateProgressAnimations()
}
}
@ -408,6 +412,7 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
}
strongSelf.seek?(scrubbingTimestampValue)
}
strongSelf.update?(nil, 0.0)
strongSelf.updateProgressAnimations()
}
}
@ -691,30 +696,19 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
var hitBounds = self.bounds
let hitTestSlop = self.hitTestSlop
hitBounds.origin.x += hitTestSlop.left
hitBounds.origin.y += hitTestSlop.top
hitBounds.size.width += -hitTestSlop.left - hitTestSlop.right
hitBounds.size.height += -hitTestSlop.top - hitTestSlop.bottom
if hitBounds.contains(point) {
switch self.contentNodes {
case let .standard(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled {
return handleNodeContainer.view
} else {
return nil
}
case let .custom(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled {
return handleNodeContainer.view
} else {
return nil
}
switch self.contentNodes {
case let .standard(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled {
return handleNodeContainer.view
} else {
return nil
}
case let .custom(node):
if let handleNodeContainer = node.handleNodeContainer, handleNodeContainer.isUserInteractionEnabled {
return handleNodeContainer.view
} else {
return nil
}
} else {
return nil
}
}
}

View file

@ -3,7 +3,6 @@ import UIKit
import CoreMedia
import SwiftSignalKit
import FFMpeg
import UniversalMediaPlayer
private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer<UInt8>?, bufferSize: Int32) -> Int32 {
let context = Unmanaged<SoftwareVideoSource>.fromOpaque(userData!).takeUnretainedValue()
@ -46,7 +45,7 @@ private final class SoftwareVideoStream {
}
}
final class SoftwareVideoSource {
public final class SoftwareVideoSource {
private var readingError = false
private var videoStream: SoftwareVideoStream?
private var avIoContext: FFMpegAVIOContext?
@ -55,7 +54,7 @@ final class SoftwareVideoSource {
fileprivate let fd: Int32?
fileprivate let size: Int32
init(path: String) {
public init(path: String) {
let _ = FFMpegMediaFrameSourceContextHelpers.registerFFMpegGlobals
var s = stat()
@ -102,10 +101,10 @@ final class SoftwareVideoSource {
let codecId = avFormatContext.codecId(atStreamIndex: streamIndex)
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(1, 40000))
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(value: 1, timescale: 40000))
let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase)
let duration = CMTimeMake(avFormatContext.duration(atStreamIndex: streamIndex), timebase.timescale)
let duration = CMTimeMake(value: avFormatContext.duration(atStreamIndex: streamIndex), timescale: timebase.timescale)
let metrics = avFormatContext.metricsForStream(at: streamIndex)
@ -154,14 +153,14 @@ final class SoftwareVideoSource {
if let videoStream = videoStream, Int(packet.streamIndex) == videoStream.index {
let packetPts = packet.pts
let pts = CMTimeMake(packetPts, videoStream.timebase.timescale)
let dts = CMTimeMake(packet.dts, videoStream.timebase.timescale)
let pts = CMTimeMake(value: packetPts, timescale: videoStream.timebase.timescale)
let dts = CMTimeMake(value: packet.dts, timescale: videoStream.timebase.timescale)
let duration: CMTime
let frameDuration = packet.duration
if frameDuration != 0 {
duration = CMTimeMake(frameDuration * videoStream.timebase.value, videoStream.timebase.timescale)
duration = CMTimeMake(value: frameDuration * videoStream.timebase.value, timescale: videoStream.timebase.timescale)
} else {
duration = videoStream.fps
}
@ -193,7 +192,7 @@ final class SoftwareVideoSource {
return (frames.first, endOfStream)
}
func readFrame(maxPts: CMTime?) -> (MediaTrackFrame?, CGFloat, CGFloat, Bool) {
public func readFrame(maxPts: CMTime?) -> (MediaTrackFrame?, CGFloat, CGFloat, Bool) {
if let videoStream = self.videoStream {
let (decodableFrame, loop) = self.readDecodableFrame()
if let decodableFrame = decodableFrame {
@ -209,4 +208,28 @@ final class SoftwareVideoSource {
return (nil, 0.0, 1.0, false)
}
}
public func readImage() -> (UIImage?, CGFloat, CGFloat, Bool) {
if let videoStream = self.videoStream {
for _ in 0 ..< 10 {
let (decodableFrame, loop) = self.readDecodableFrame()
if let decodableFrame = decodableFrame {
if let renderedFrame = videoStream.decoder.render(frame: decodableFrame) {
return (renderedFrame, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), loop)
}
}
}
return (nil, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), true)
} else {
return (nil, 0.0, 1.0, false)
}
}
public func seek(timestamp: Double) {
if let stream = self.videoStream, let avFormatContext = self.avFormatContext {
let pts = CMTimeMakeWithSeconds(timestamp, preferredTimescale: stream.timebase.timescale)
avFormatContext.seekFrame(forStreamIndex: Int32(stream.index), pts: pts.value)
stream.decoder.reset()
}
}
}

View file

@ -0,0 +1,368 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import FFMpeg
private func readPacketCallback(userData: UnsafeMutableRawPointer?, buffer: UnsafeMutablePointer<UInt8>?, bufferSize: Int32) -> Int32 {
let context = Unmanaged<UniversalSoftwareVideoSourceImpl>.fromOpaque(userData!).takeUnretainedValue()
let data: Signal<Data, NoError>
let resourceSize: Int = context.size
let readCount = min(resourceSize - context.readingOffset, Int(bufferSize))
let requestRange: Range<Int> = context.readingOffset ..< (context.readingOffset + readCount)
let semaphore = DispatchSemaphore(value: 0)
data = context.mediaBox.resourceData(context.fileReference.media.resource, size: context.size, in: requestRange, mode: .complete)
var fetchedData: Data?
let disposable = data.start(next: { data in
if data.count == readCount {
fetchedData = data
semaphore.signal()
}
})
let cancelDisposable = context.cancelRead.start(next: { value in
if value {
semaphore.signal()
}
})
semaphore.wait()
disposable.dispose()
cancelDisposable.dispose()
if let fetchedData = fetchedData {
fetchedData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
memcpy(buffer, bytes, fetchedData.count)
}
let fetchedCount = Int32(fetchedData.count)
context.readingOffset += Int(fetchedCount)
return fetchedCount
} else {
return 0
}
}
private func seekCallback(userData: UnsafeMutableRawPointer?, offset: Int64, whence: Int32) -> Int64 {
let context = Unmanaged<UniversalSoftwareVideoSourceImpl>.fromOpaque(userData!).takeUnretainedValue()
if (whence & FFMPEG_AVSEEK_SIZE) != 0 {
return Int64(context.size)
} else {
context.readingOffset = Int(offset)
return offset
}
}
private final class SoftwareVideoStream {
let index: Int
let fps: CMTime
let timebase: CMTime
let duration: CMTime
let decoder: FFMpegMediaVideoFrameDecoder
let rotationAngle: Double
let aspect: Double
init(index: Int, fps: CMTime, timebase: CMTime, duration: CMTime, decoder: FFMpegMediaVideoFrameDecoder, rotationAngle: Double, aspect: Double) {
self.index = index
self.fps = fps
self.timebase = timebase
self.duration = duration
self.decoder = decoder
self.rotationAngle = rotationAngle
self.aspect = aspect
}
}
private final class UniversalSoftwareVideoSourceImpl {
fileprivate let mediaBox: MediaBox
fileprivate let fileReference: FileMediaReference
fileprivate let size: Int
fileprivate let state: ValuePromise<UniversalSoftwareVideoSourceState>
fileprivate var avIoContext: FFMpegAVIOContext!
fileprivate var avFormatContext: FFMpegAVFormatContext!
fileprivate var videoStream: SoftwareVideoStream!
fileprivate var readingOffset: Int = 0
fileprivate var cancelRead: Signal<Bool, NoError>
init?(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>) {
guard let size = fileReference.media.size else {
return nil
}
self.mediaBox = mediaBox
self.fileReference = fileReference
self.size = size
self.state = state
state.set(.initializing)
self.cancelRead = cancelInitialization
let ioBufferSize = 64 * 1024
guard let avIoContext = FFMpegAVIOContext(bufferSize: Int32(ioBufferSize), opaqueContext: Unmanaged.passUnretained(self).toOpaque(), readPacket: readPacketCallback, seek: seekCallback) else {
return nil
}
self.avIoContext = avIoContext
let avFormatContext = FFMpegAVFormatContext()
avFormatContext.setIO(avIoContext)
if !avFormatContext.openInput() {
return nil
}
if !avFormatContext.findStreamInfo() {
return nil
}
self.avFormatContext = avFormatContext
var videoStream: SoftwareVideoStream?
for streamIndexNumber in avFormatContext.streamIndices(for: FFMpegAVFormatStreamTypeVideo) {
let streamIndex = streamIndexNumber.int32Value
if avFormatContext.isAttachedPic(atStreamIndex: streamIndex) {
continue
}
let codecId = avFormatContext.codecId(atStreamIndex: streamIndex)
let fpsAndTimebase = avFormatContext.fpsAndTimebase(forStreamIndex: streamIndex, defaultTimeBase: CMTimeMake(value: 1, timescale: 40000))
let (fps, timebase) = (fpsAndTimebase.fps, fpsAndTimebase.timebase)
let duration = CMTimeMake(value: avFormatContext.duration(atStreamIndex: streamIndex), timescale: timebase.timescale)
let metrics = avFormatContext.metricsForStream(at: streamIndex)
let rotationAngle: Double = metrics.rotationAngle
let aspect = Double(metrics.width) / Double(metrics.height)
if let codec = FFMpegAVCodec.find(forId: codecId) {
let codecContext = FFMpegAVCodecContext(codec: codec)
if avFormatContext.codecParams(atStreamIndex: streamIndex, to: codecContext) {
if codecContext.open() {
videoStream = SoftwareVideoStream(index: Int(streamIndex), fps: fps, timebase: timebase, duration: duration, decoder: FFMpegMediaVideoFrameDecoder(codecContext: codecContext), rotationAngle: rotationAngle, aspect: aspect)
break
}
}
}
}
if let videoStream = videoStream {
self.videoStream = videoStream
} else {
return nil
}
state.set(.ready)
}
private func readPacketInternal() -> FFMpegPacket? {
guard let avFormatContext = self.avFormatContext else {
return nil
}
let packet = FFMpegPacket()
if avFormatContext.readFrame(into: packet) {
return packet
} else {
return nil
}
}
func readDecodableFrame() -> (MediaTrackDecodableFrame?, Bool) {
var frames: [MediaTrackDecodableFrame] = []
var endOfStream = false
while frames.isEmpty {
if let packet = self.readPacketInternal() {
if let videoStream = videoStream, Int(packet.streamIndex) == videoStream.index {
let packetPts = packet.pts
let pts = CMTimeMake(value: packetPts, timescale: videoStream.timebase.timescale)
let dts = CMTimeMake(value: packet.dts, timescale: videoStream.timebase.timescale)
let duration: CMTime
let frameDuration = packet.duration
if frameDuration != 0 {
duration = CMTimeMake(value: frameDuration * videoStream.timebase.value, timescale: videoStream.timebase.timescale)
} else {
duration = videoStream.fps
}
let frame = MediaTrackDecodableFrame(type: .video, packet: packet, pts: pts, dts: dts, duration: duration)
frames.append(frame)
}
} else {
if endOfStream {
break
} else {
if let avFormatContext = self.avFormatContext, let videoStream = self.videoStream {
endOfStream = true
avFormatContext.seekFrame(forStreamIndex: Int32(videoStream.index), pts: 0)
} else {
endOfStream = true
break
}
}
}
}
if endOfStream {
if let videoStream = self.videoStream {
videoStream.decoder.reset()
}
}
return (frames.first, endOfStream)
}
func readImage() -> (UIImage?, CGFloat, CGFloat, Bool) {
if let videoStream = self.videoStream {
for _ in 0 ..< 10 {
let (decodableFrame, loop) = self.readDecodableFrame()
if let decodableFrame = decodableFrame {
if let renderedFrame = videoStream.decoder.render(frame: decodableFrame) {
return (renderedFrame, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), loop)
}
}
}
return (nil, CGFloat(videoStream.rotationAngle), CGFloat(videoStream.aspect), true)
} else {
return (nil, 0.0, 1.0, false)
}
}
public func seek(timestamp: Double) {
if let stream = self.videoStream, let avFormatContext = self.avFormatContext {
let pts = CMTimeMakeWithSeconds(timestamp, preferredTimescale: stream.timebase.timescale)
avFormatContext.seekFrame(forStreamIndex: Int32(stream.index), pts: pts.value)
stream.decoder.reset()
}
}
}
private enum UniversalSoftwareVideoSourceState {
case initializing
case failed
case ready
case generatingFrame
}
private final class UniversalSoftwareVideoSourceThreadParams: NSObject {
let mediaBox: MediaBox
let fileReference: FileMediaReference
let state: ValuePromise<UniversalSoftwareVideoSourceState>
let cancelInitialization: Signal<Bool, NoError>
init(mediaBox: MediaBox, fileReference: FileMediaReference, state: ValuePromise<UniversalSoftwareVideoSourceState>, cancelInitialization: Signal<Bool, NoError>) {
self.mediaBox = mediaBox
self.fileReference = fileReference
self.state = state
self.cancelInitialization = cancelInitialization
}
}
private final class UniversalSoftwareVideoSourceTakeFrameParams: NSObject {
let timestamp: Double
let completion: (UIImage?) -> Void
let cancel: Signal<Bool, NoError>
init(timestamp: Double, completion: @escaping (UIImage?) -> Void, cancel: Signal<Bool, NoError>) {
self.timestamp = timestamp
self.completion = completion
self.cancel = cancel
}
}
private final class UniversalSoftwareVideoSourceThread: NSObject {
@objc static func entryPoint(_ params: UniversalSoftwareVideoSourceThreadParams) {
let runLoop = RunLoop.current
let timer = Timer(fireAt: .distantFuture, interval: 0.0, target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.none), userInfo: nil, repeats: false)
runLoop.add(timer, forMode: .common)
let source = UniversalSoftwareVideoSourceImpl(mediaBox: params.mediaBox, fileReference: params.fileReference, state: params.state, cancelInitialization: params.cancelInitialization)
Thread.current.threadDictionary["source"] = source
while true {
runLoop.run(mode: .default, before: .distantFuture)
if Thread.current.threadDictionary["UniversalSoftwareVideoSourceThread_stop"] != nil {
break
}
}
Thread.current.threadDictionary.removeObject(forKey: "source")
}
@objc static func none() {
}
@objc static func stop() {
Thread.current.threadDictionary["UniversalSoftwareVideoSourceThread_stop"] = "true"
}
@objc static func takeFrame(_ params: UniversalSoftwareVideoSourceTakeFrameParams) {
guard let source = Thread.current.threadDictionary["source"] as? UniversalSoftwareVideoSourceImpl else {
params.completion(nil)
return
}
source.cancelRead = params.cancel
source.state.set(.generatingFrame)
let startTime = CFAbsoluteTimeGetCurrent()
source.seek(timestamp: params.timestamp)
let image = source.readImage().0
params.completion(image)
source.state.set(.ready)
print("take frame: \(CFAbsoluteTimeGetCurrent() - startTime) s")
}
}
final class UniversalSoftwareVideoSource {
private let thread: Thread
private let stateValue: ValuePromise<UniversalSoftwareVideoSourceState> = ValuePromise(.initializing, ignoreRepeated: true)
private let cancelInitialization: ValuePromise<Bool> = ValuePromise(false)
var ready: Signal<Bool, NoError> {
return self.stateValue.get()
|> map { value -> Bool in
switch value {
case .ready:
return true
default:
return false
}
}
}
init(mediaBox: MediaBox, fileReference: FileMediaReference) {
self.thread = Thread(target: UniversalSoftwareVideoSourceThread.self, selector: #selector(UniversalSoftwareVideoSourceThread.entryPoint(_:)), object: UniversalSoftwareVideoSourceThreadParams(mediaBox: mediaBox, fileReference: fileReference, state: self.stateValue, cancelInitialization: self.cancelInitialization.get()))
self.thread.name = "UniversalSoftwareVideoSource"
self.thread.start()
}
deinit {
UniversalSoftwareVideoSourceThread.self.perform(#selector(UniversalSoftwareVideoSourceThread.stop), on: self.thread, with: nil, waitUntilDone: false)
self.cancelInitialization.set(true)
}
public func takeFrame(at timestamp: Double) -> Signal<UIImage?, NoError> {
return Signal { subscriber in
let cancel = ValuePromise<Bool>(false)
UniversalSoftwareVideoSourceThread.self.perform(#selector(UniversalSoftwareVideoSourceThread.takeFrame(_:)), on: self.thread, with: UniversalSoftwareVideoSourceTakeFrameParams(timestamp: timestamp, completion: { image in
subscriber.putNext(image)
subscriber.putCompletion()
}, cancel: cancel.get()), waitUntilDone: false)
return ActionDisposable {
cancel.set(true)
}
}
}
}

View file

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
D03B054022D8866A0000BE1A /* MediaPlayerFramePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B053F22D8866A0000BE1A /* MediaPlayerFramePreview.swift */; };
D03B054222D888A00000BE1A /* SoftwareVideoSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B054122D888A00000BE1A /* SoftwareVideoSource.swift */; };
D0750C6E22B28E6600BE5F6E /* RingBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = D0750C6B22B28E6500BE5F6E /* RingBuffer.m */; };
D0750C6F22B28E6600BE5F6E /* RingByteBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0750C6C22B28E6600BE5F6E /* RingByteBuffer.swift */; };
D0750C7022B28E6600BE5F6E /* RingBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = D0750C6D22B28E6600BE5F6E /* RingBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -37,9 +39,12 @@
D0AE325B22B286A70058D3BC /* MediaTrackDecodableFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE324922B286A70058D3BC /* MediaTrackDecodableFrame.swift */; };
D0AE325C22B286A70058D3BC /* MediaTrackFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE324A22B286A70058D3BC /* MediaTrackFrame.swift */; };
D0AE325E22B286C30058D3BC /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0AE325D22B286C30058D3BC /* AVFoundation.framework */; };
D0E8B10C22D8B7E800C82570 /* UniversalSoftwareVideoSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8B10B22D8B7E800C82570 /* UniversalSoftwareVideoSource.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
D03B053F22D8866A0000BE1A /* MediaPlayerFramePreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerFramePreview.swift; sourceTree = "<group>"; };
D03B054122D888A00000BE1A /* SoftwareVideoSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SoftwareVideoSource.swift; sourceTree = "<group>"; };
D0750C6B22B28E6500BE5F6E /* RingBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RingBuffer.m; sourceTree = "<group>"; };
D0750C6C22B28E6600BE5F6E /* RingByteBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RingByteBuffer.swift; sourceTree = "<group>"; };
D0750C6D22B28E6600BE5F6E /* RingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RingBuffer.h; sourceTree = "<group>"; };
@ -72,6 +77,7 @@
D0AE324922B286A70058D3BC /* MediaTrackDecodableFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackDecodableFrame.swift; sourceTree = "<group>"; };
D0AE324A22B286A70058D3BC /* MediaTrackFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTrackFrame.swift; sourceTree = "<group>"; };
D0AE325D22B286C30058D3BC /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
D0E8B10B22D8B7E800C82570 /* UniversalSoftwareVideoSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalSoftwareVideoSource.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -135,7 +141,10 @@
D0AE323E22B286A60058D3BC /* MediaTrackFrameBuffer.swift */,
D0AE324222B286A60058D3BC /* MediaTrackFrameDecoder.swift */,
D0AE323C22B286A50058D3BC /* VideoPlayerProxy.swift */,
D03B054122D888A00000BE1A /* SoftwareVideoSource.swift */,
D03B053F22D8866A0000BE1A /* MediaPlayerFramePreview.swift */,
D0AE322222B285F70058D3BC /* UniversalMediaPlayer.h */,
D0E8B10B22D8B7E800C82570 /* UniversalSoftwareVideoSource.swift */,
);
path = Sources;
sourceTree = "<group>";
@ -241,6 +250,7 @@
D0AE325A22B286A70058D3BC /* FFMpegMediaVideoFrameDecoder.swift in Sources */,
D0750C6E22B28E6600BE5F6E /* RingBuffer.m in Sources */,
D0AE325422B286A70058D3BC /* MediaTrackFrameDecoder.swift in Sources */,
D0E8B10C22D8B7E800C82570 /* UniversalSoftwareVideoSource.swift in Sources */,
D0AE325322B286A70058D3BC /* MediaPlayerNode.swift in Sources */,
D0AE325122B286A70058D3BC /* MediaPlayer.swift in Sources */,
D0AE325722B286A70058D3BC /* MediaPlaybackData.swift in Sources */,
@ -251,11 +261,13 @@
D0AE325222B286A70058D3BC /* FFMpegMediaFrameSourceContext.swift in Sources */,
D0AE324E22B286A70058D3BC /* VideoPlayerProxy.swift in Sources */,
D0AE325022B286A70058D3BC /* MediaTrackFrameBuffer.swift in Sources */,
D03B054022D8866A0000BE1A /* MediaPlayerFramePreview.swift in Sources */,
D0AE325C22B286A70058D3BC /* MediaTrackFrame.swift in Sources */,
D0AE324F22B286A70058D3BC /* MediaPlayerTimeTextNode.swift in Sources */,
D0750C6F22B28E6600BE5F6E /* RingByteBuffer.swift in Sources */,
D0AE325822B286A70058D3BC /* FFMpegMediaPassthroughVideoFrameDecoder.swift in Sources */,
D0AE325B22B286A70058D3BC /* MediaTrackDecodableFrame.swift in Sources */,
D03B054222D888A00000BE1A /* SoftwareVideoSource.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -79,7 +79,7 @@ static NSData *base64_decode(NSString *str) {
NSMutableArray *signals = [[NSMutableArray alloc] init];
for (NSString *host in hosts) {
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16", host, isTesting ? @"tapv2.stel.com" : @"apv2.stel.com"]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
MTSignal *signal = [[[MTHttpRequestOperation dataForHttpUrl:[NSURL URLWithString:[NSString stringWithFormat:@"https://%@/resolve?name=%@&type=16", host, isTesting ? @"tapv3.stel.com" : @"apv3.stel.com"]] headers:headers] mapToSignal:^MTSignal *(NSData *data) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([dict respondsToSelector:@selector(objectForKey:)]) {
NSArray *answer = dict[@"Answer"];

View file

@ -70,7 +70,8 @@
bool extendedPadding = false;
if (secret != nil) {
if ([MTSocksProxySettings secretSupportsExtendedPadding:secret]) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
}

View file

@ -1,7 +1,36 @@
#import <Foundation/Foundation.h>
@interface MTProxySecret : NSObject <NSCoding>
@property (nonatomic, strong, readonly) NSData * _Nonnull secret;
+ (MTProxySecret * _Nullable)parse:(NSString * _Nonnull)string;
+ (MTProxySecret * _Nullable)parseData:(NSData * _Nonnull)data;
- (NSData * _Nonnull)serialize;
- (NSString * _Nonnull)serializeToString;
@end
@interface MTProxySecretType0 : MTProxySecret
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret;
@end
@interface MTProxySecretType1 : MTProxySecret
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret;
@end
@interface MTProxySecretType2 : MTProxySecret
@property (nonatomic, strong, readonly) NSString * _Nonnull domain;
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret domain:(NSString * _Nonnull)domain;
@end
@interface MTSocksProxySettings : NSObject
@property (nonatomic, strong, readonly) NSString *ip;
@ -12,8 +41,6 @@
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret;
+ (bool)secretSupportsExtendedPadding:(NSData *)data;
@end
@interface MTNetworkSettings : NSObject

View file

@ -149,6 +149,284 @@ typedef enum {
} UIDeviceFamily;
static NSData * _Nullable parseHexString(NSString * _Nonnull hex) {
if ([hex length] % 2 != 0) {
return nil;
}
char buf[3];
buf[2] = '\0';
uint8_t *bytes = (uint8_t *)malloc(hex.length / 2);
uint8_t *bp = bytes;
for (CFIndex i = 0; i < [hex length]; i += 2) {
buf[0] = [hex characterAtIndex:i];
buf[1] = [hex characterAtIndex:i+1];
char *b2 = NULL;
*bp++ = strtol(buf, &b2, 16);
if (b2 != buf + 2) {
return nil;
}
}
return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES];
}
static NSString * _Nonnull dataToHexString(NSData * _Nonnull data) {
const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
if (dataBuffer == NULL) {
return @"";
}
NSUInteger dataLength = [data length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < (int)dataLength; ++i) {
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
}
return hexString;
}
static NSData *base64_decode(NSString *str) {
if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]];
#pragma clang diagnostic pop
}
}
@implementation MTProxySecret
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super init];
if (self != nil) {
_secret = secret;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self != nil) {
_secret = [aDecoder decodeObjectForKey:@"secret"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_secret forKey:@"secret"];
}
+ (MTProxySecret * _Nullable)parse:(NSString * _Nonnull)string {
NSData *hexData = parseHexString(string);
if (hexData == nil) {
NSString *finalString = @"";
finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]];
while (finalString.length % 4 != 0) {
finalString = [finalString stringByAppendingString:@"="];
}
hexData = base64_decode(finalString);
}
if (hexData != nil) {
return [self parseData:hexData];
} else {
return nil;
}
}
+ (MTProxySecret * _Nullable)parseData:(NSData * _Nonnull)data {
if (data == nil || data.length < 16) {
return nil;
}
uint8_t firstByte = 0;
[data getBytes:&firstByte length:1];
if (data.length == 16) {
return [[MTProxySecretType0 alloc] initWithSecret:data];
} else if (data.length == 17) {
if (firstByte == 0xdd) {
return [[MTProxySecretType1 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)]];
} else {
return nil;
}
} else if (data.length >= 18 && firstByte == 0xee) {
NSString *domain = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(1 + 16, data.length - (1 + 16))] encoding:NSUTF8StringEncoding];
if (domain == nil) {
return nil;
}
return [[MTProxySecretType2 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)] domain:domain];
} else {
return nil;
}
}
- (NSData * _Nonnull)serialize {
assert(false);
return nil;
}
- (NSString * _Nonnull)serializeToString {
assert(false);
return nil;
}
- (NSString *)description {
return dataToHexString([self serialize]);
}
@end
@implementation MTProxySecretType0
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super initWithSecret:secret];
if (self != nil) {
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
}
- (NSData * _Nonnull)serialize {
return self.secret;
}
- (NSString * _Nonnull)serializeToString {
return dataToHexString(self.serialize);
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType0 class]]) {
return false;
}
MTProxySecretType0 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
return true;
}
@end
@implementation MTProxySecretType1
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret {
self = [super initWithSecret:secret];
if (self != nil) {
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
}
- (NSData * _Nonnull)serialize {
NSMutableData *data = [[NSMutableData alloc] init];
uint8_t marker = 0xdd;
[data appendBytes:&marker length:1];
[data appendData:self.secret];
return data;
}
- (NSString * _Nonnull)serializeToString {
return dataToHexString(self.serialize);
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType1 class]]) {
return false;
}
MTProxySecretType1 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
return true;
}
@end
@implementation MTProxySecretType2
- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret domain:(NSString * _Nonnull)domain {
self = [super initWithSecret:secret];
if (self != nil) {
_domain = domain;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
_domain = [aDecoder decodeObjectForKey:@"domain"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_domain forKey:@"domain"];
}
- (NSData * _Nonnull)serialize {
NSMutableData *data = [[NSMutableData alloc] init];
uint8_t marker = 0xee;
[data appendBytes:&marker length:1];
[data appendData:self.secret];
[data appendData:[_domain dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
- (NSString * _Nonnull)serializeToString {
NSData *data = [self serialize];
if ([data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [[data base64EncodedStringWithOptions:kNilOptions] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [self.serialize base64Encoding];
#pragma clang diagnostic pop
}
}
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[MTProxySecretType2 class]]) {
return false;
}
MTProxySecretType2 *other = object;
if (![self.secret isEqual:other.secret]) {
return false;
}
if (![self.domain isEqual:other.domain]) {
return false;
}
return true;
}
@end
@implementation MTSocksProxySettings
- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret {
@ -187,16 +465,7 @@ typedef enum {
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, _password, _secret];
}
+ (bool)secretSupportsExtendedPadding:(NSData *)data {
if (data.length == 17) {
uint8_t first = 0;
[data getBytes:&first length:1];
return (first == 0xdd);
}
return false;
return [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, _password, [_secret description]];
}
@end

View file

@ -653,33 +653,9 @@
}];
}
- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo
{
[[MTContext contextQueue] dispatchOnQueue:^
{
#warning implement and reenable
- (void)scheduleSessionCleanupForAuthKeyId:(int64_t)authKeyId sessionInfo:(MTSessionInfo *)sessionInfo {
[[MTContext contextQueue] dispatchOnQueue:^{
return;
if (authKeyId == 0 || sessionInfo == nil)
return;
NSMutableArray *sessionIds = _cleanupSessionIdsByAuthKeyId[@(authKeyId)];
if (sessionIds == nil)
{
sessionIds = [[NSMutableArray alloc] init];
_cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
}
else if (![sessionIds respondsToSelector:@selector(setObject:forKey:)])
{
sessionIds = [[NSMutableArray alloc] initWithArray:sessionIds];
_cleanupSessionIdsByAuthKeyId[@(authKeyId)] = sessionIds;
}
[sessionIds addObject:@(sessionInfo.sessionId)];
[_currentSessionInfos addObject:sessionInfo];
[_keychain setObject:_cleanupSessionIdsByAuthKeyId forKey:@"cleanupSessionIdsByAuthKeyId" group:@"cleanup"];
}];
}
@ -1351,9 +1327,7 @@
[_datacenterAuthInfoById enumerateKeysAndObjectsUsingBlock:^(NSNumber *nDatacenterId, MTDatacenterAuthInfo *authInfo, __unused BOOL *stop)
{
if ([authInfo authSaltForMessageId:saltsRequiredAtLeastUntilMessageId == 0])
{
#warning TODO
if ([authInfo authSaltForMessageId:saltsRequiredAtLeastUntilMessageId == 0]) {
}
}];
}];

View file

@ -889,15 +889,17 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
return;
}
//bool debugResetTransport = false;
bool extendedPadding = false;
if (transport.proxySettings != nil && transport.proxySettings.secret.length != 0) {
if ([MTSocksProxySettings secretSupportsExtendedPadding:transport.proxySettings.secret]) {
if (transport.proxySettings != nil && transport.proxySettings.secret != nil) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:transport.proxySettings.secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
} else if (scheme.address.secret != nil) {
MTProxySecret *parsedSecret = [MTProxySecret parseData:scheme.address.secret];
if ([parsedSecret isKindOfClass:[MTProxySecretType1 class]] || [parsedSecret isKindOfClass:[MTProxySecretType2 class]]) {
extendedPadding = true;
}
} else if ([MTSocksProxySettings secretSupportsExtendedPadding:scheme.address.secret]) {
extendedPadding = true;
}
if ([self canAskForTransactions])
@ -1900,14 +1902,14 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
bool stop = false;
int64_t reqMsgId = 0;
if (true)
{
/*if (true)
{*/
while (!stop && reqMsgId == 0)
{
int32_t signature = [messageIs readInt32:&stop];
[self findReqMsgId:messageIs signature:signature reqMsgId:&reqMsgId failed:&stop];
}
}
/*}
else
{
int32_t signature = [messageIs readInt32];
@ -1927,7 +1929,7 @@ static const NSUInteger MTMaxUnacknowledgedMessageCount = 64;
reqMsgId = [messageIs readInt64];
}
}
}
}*/
if (reqMsgId != 0)
completion(token, @(reqMsgId));
@ -2370,8 +2372,6 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
return nil;
}
#warning check message id
NSMutableArray *messages = [[NSMutableArray alloc] init];
NSTimeInterval timestamp = embeddedMessageId / 4294967296.0;

View file

@ -8,6 +8,8 @@
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonHMac.h>
#import "MTInternalId.h"
@ -116,7 +118,13 @@ typedef enum {
MTTcpSocksReceiveBindAddrDomainNameLength,
MTTcpSocksReceiveBindAddrDomainName,
MTTcpSocksReceiveBindAddrPort,
MTTcpSocksReceiveAuthResponse
MTTcpSocksReceiveAuthResponse,
MTTcpSocksReceiveHelloResponse,
MTTcpSocksReceiveHelloResponse1,
MTTcpSocksReceiveHelloResponse2,
MTTcpSocksReceivePassthrough,
MTTcpSocksReceiveComplexLength,
MTTcpSocksReceiveComplexPacketPart
} MTTcpReadTags;
static const NSTimeInterval MTMinTcpResponseTimeout = 12.0;
@ -128,6 +136,49 @@ struct ctr_state {
unsigned char ecount[16];
};
@interface MTTcpSendData : NSObject
@property (nonatomic, strong, readonly) NSArray<NSData *> *dataSet;
@property (nonatomic, copy, readonly) void (^completion)(bool success);
@property (nonatomic, readonly) bool requestQuickAck;
@property (nonatomic, readonly) bool expectDataInResponse;
@end
@implementation MTTcpSendData
- (instancetype)initWithDataSet:(NSArray *)dataSet completion:(void (^)(bool success))completion requestQuickAck:(bool)requestQuickAck expectDataInResponse:(bool)expectDataInResponse {
self = [super init];
if (self != nil) {
_dataSet = dataSet;
_completion = [completion copy];
_requestQuickAck = requestQuickAck;
_expectDataInResponse = expectDataInResponse;
}
return self;
}
@end
@interface MTTcpReceiveData : NSObject
@property (nonatomic, readonly) int tag;
@property (nonatomic, readonly) int length;
@end
@implementation MTTcpReceiveData
- (instancetype)initWithTag:(int)tag length:(int)length {
self = [super init];
if (self != nil) {
_tag = tag;
_length = length;
}
return self;
}
@end
@interface MTTcpConnection () <GCDAsyncSocketDelegate>
{
@ -151,6 +202,7 @@ struct ctr_state {
NSData *_firstPacketControlByte;
bool _addedControlHeader;
bool _addedHelloHeader;
MTAesCtr *_outgoingAesCtr;
MTAesCtr *_incomingAesCtr;
@ -164,9 +216,16 @@ struct ctr_state {
NSString *_mtpIp;
int32_t _mtpPort;
NSData *_mtpSecret;
MTProxySecret *_mtpSecret;
NSData *_helloRandom;
NSData *_currentHelloResponse;
MTMetaDisposable *_resolveDisposable;
bool _readyToSendData;
NSMutableArray<MTTcpSendData *> *_pendingDataQueue;
NSMutableData *_receivedDataBuffer;
MTTcpReceiveData *_pendingReceiveData;
}
@property (nonatomic) int64_t packetHeadDecodeToken;
@ -207,11 +266,16 @@ struct ctr_state {
_firstPacketControlByte = [context.apiEnvironment tcpPayloadPrefix];
}
if (_scheme.address.secret != nil) {
_mtpIp = _scheme.address.ip;
_mtpPort = _scheme.address.port;
_mtpSecret = [MTProxySecret parseData:_scheme.address.secret];
}
if (context.apiEnvironment.socksProxySettings != nil) {
if (context.apiEnvironment.socksProxySettings.secret != nil) {
_mtpIp = context.apiEnvironment.socksProxySettings.ip;
_mtpPort = context.apiEnvironment.socksProxySettings.port;
_mtpSecret = context.apiEnvironment.socksProxySettings.secret;
_mtpSecret = [MTProxySecret parseData:context.apiEnvironment.socksProxySettings.secret];
} else {
_socksIp = context.apiEnvironment.socksProxySettings.ip;
_socksPort = context.apiEnvironment.socksProxySettings.port;
@ -221,11 +285,9 @@ struct ctr_state {
}
if (_mtpSecret != nil) {
if ([MTSocksProxySettings secretSupportsExtendedPadding:_mtpSecret]) {
if ([_mtpSecret isKindOfClass:[MTProxySecretType1 class]] || [_mtpSecret isKindOfClass:[MTProxySecretType2 class]]) {
_useIntermediateFormat = true;
}
} else if ([MTSocksProxySettings secretSupportsExtendedPadding:_scheme.address.secret]) {
_useIntermediateFormat = true;
}
_resolveDisposable = [[MTMetaDisposable alloc] init];
@ -243,6 +305,9 @@ struct ctr_state {
_datacenterTag = (int32_t)datacenterId;
}
}
_pendingDataQueue = [[NSMutableArray alloc] init];
_receivedDataBuffer = [[NSMutableData alloc] init];
}
return self;
}
@ -357,8 +422,6 @@ struct ctr_state {
}
} else if (strongSelf->_mtpIp != nil) {
MTLog(@"[MTTcpConnection#%x connecting to %@:%d via mtp://%@:%d:%@]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_mtpIp, (int)strongSelf->_mtpPort, strongSelf->_mtpSecret);
} else if (strongSelf->_scheme.address.secret != nil) {
MTLog(@"[MTTcpConnection#%x connecting to %@:%d with secret %@]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port, strongSelf->_scheme.address.secret);
} else {
MTLog(@"[MTTcpConnection#%x connecting to %@:%d]", (int)strongSelf, strongSelf->_scheme.address.ip, (int)strongSelf->_scheme.address.port);
}
@ -368,10 +431,157 @@ struct ctr_state {
if (![strongSelf->_socket connectToHost:connectionData.ip onPort:connectionData.port viaInterface:strongSelf->_interface withTimeout:12 error:&error] || error != nil) {
[strongSelf closeAndNotifyWithError:true];
} else if (strongSelf->_socksIp == nil) {
if (strongSelf->_useIntermediateFormat) {
[strongSelf->_socket readDataToLength:4 withTimeout:-1 tag:MTTcpReadTagPacketFullLength];
if (strongSelf->_mtpIp != nil && [strongSelf->_mtpSecret isKindOfClass:[MTProxySecretType2 class]]) {
MTProxySecretType2 *secret = (MTProxySecretType2 *)(strongSelf->_mtpSecret);
int greaseCount = 8;
NSMutableData *greaseData = [[NSMutableData alloc] initWithLength:greaseCount];
uint8_t *greaseBytes = (uint8_t *)greaseData.mutableBytes;
int result;
result = SecRandomCopyBytes(nil, greaseData.length, greaseData.mutableBytes);
assert(result == errSecSuccess);
for (int i = 0; i < greaseData.length; i++) {
uint8_t c = greaseBytes[i];
c = (c & 0xf0) | 0x0a;
greaseBytes[i] = c;
}
for (int i = 1; i < greaseData.length; i += 2) {
if (greaseBytes[i] == greaseBytes[i - 1]) {
greaseBytes[i] &= 0x10;
}
}
NSMutableData *helloData = [[NSMutableData alloc] init];
uint8_t s1[11] = { 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03 };
[helloData appendBytes:s1 length:11];
for (int i = 0; i < 32; i++) {
uint8_t zero = 0;
[helloData appendBytes:&zero length:1];
}
uint8_t s2[1] = { 0x20 };
[helloData appendBytes:s2 length:1];
uint8_t r1[32];
result = SecRandomCopyBytes(nil, 32, r1);
assert(result == errSecSuccess);
[helloData appendBytes:r1 length:32];
uint8_t s3[2] = { 0x00, 0x22 };
[helloData appendBytes:s3 length:2];
[helloData appendBytes:&greaseBytes[0] length:1];
[helloData appendBytes:&greaseBytes[0] length:1];
uint8_t s0[36] = { 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x01, 0x91 };
[helloData appendBytes:s0 length:36];
[helloData appendBytes:&greaseBytes[2] length:1];
[helloData appendBytes:&greaseBytes[2] length:1];
uint8_t s4[4] = { 0x00, 0x00, 0x00, 0x00 };
[helloData appendBytes:s4 length:4];
uint8_t stackZ[2] = { 0x00, 0x00 };
int stack1 = (int)helloData.length;
[helloData appendBytes:stackZ length:2];
int stack2 = (int)helloData.length;
[helloData appendBytes:stackZ length:2];
uint8_t s5[1] = { 0x00 };
[helloData appendBytes:s5 length:1];
int stack3 = (int)helloData.length;
[helloData appendBytes:stackZ length:2];
NSString *d1 = secret.domain;
[helloData appendData:[d1 dataUsingEncoding:NSUTF8StringEncoding]];
int16_t stack3Value = (int16_t)(helloData.length - stack3 - 2);
stack3Value = OSSwapInt16(stack3Value);
memcpy(((uint8_t *)helloData.mutableBytes) + stack3, &stack3Value, 2);
int16_t stack2Value = (int16_t)(helloData.length - stack2 - 2);
stack2Value = OSSwapInt16(stack2Value);
memcpy(((uint8_t *)helloData.mutableBytes) + stack2, &stack2Value, 2);
int16_t stack1Value = (int16_t)(helloData.length - stack1 - 2);
stack1Value = OSSwapInt16(stack1Value);
memcpy(((uint8_t *)helloData.mutableBytes) + stack1, &stack1Value, 2);
uint8_t s6[15] = { 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08 };
[helloData appendBytes:s6 length:15];
[helloData appendBytes:&greaseBytes[4] length:1];
[helloData appendBytes:&greaseBytes[4] length:1];
uint8_t s7[77] = { 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x29 };
[helloData appendBytes:s7 length:77];
[helloData appendBytes:&greaseBytes[4] length:1];
[helloData appendBytes:&greaseBytes[4] length:1];
uint8_t s8[7] = { 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x20 };
[helloData appendBytes:s8 length:7];
uint8_t r2[32];
result = SecRandomCopyBytes(nil, 32, r2);
assert(result == errSecSuccess);
[helloData appendBytes:r2 length:32];
uint8_t s9[11] = { 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x2b, 0x00, 0x0b, 0x0a };
[helloData appendBytes:s9 length:11];
[helloData appendBytes:&greaseBytes[6] length:1];
[helloData appendBytes:&greaseBytes[6] length:1];
uint8_t s10[15] = { 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x1b, 0x00, 0x03, 0x02, 0x00, 0x02 };
[helloData appendBytes:s10 length:15];
[helloData appendBytes:&greaseBytes[3] length:1];
[helloData appendBytes:&greaseBytes[3] length:1];
uint8_t s11[5] = { 0x00, 0x01, 0x00, 0x00, 0x15 };
[helloData appendBytes:s11 length:5];
int stack4 = (int)helloData.length;
[helloData appendBytes:stackZ length:2];
while (helloData.length < 517) {
uint8_t zero = 0;
[helloData appendBytes:&zero length:1];
}
int16_t stack4Value = (int16_t)(helloData.length - stack4 - 2);
stack4Value = OSSwapInt16(stack4Value);
memcpy(((uint8_t *)helloData.mutableBytes) + stack4, &stack4Value, 2);
NSData *effectiveSecret = strongSelf->_mtpSecret.secret;
uint8_t cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, effectiveSecret.bytes, effectiveSecret.length, helloData.bytes, helloData.length, cHMAC);
int32_t timestamp = (int32_t)[[NSDate date] timeIntervalSince1970];
uint8_t *timestampValue = (uint8_t *)&timestamp;
for (int i = 0; i < 4; i++) {
cHMAC[CC_SHA256_DIGEST_LENGTH - 4 + i] ^= timestampValue[i];
}
_helloRandom = [[NSData alloc] initWithBytes:cHMAC length:32];
memcpy(((uint8_t *)helloData.mutableBytes) + 11, cHMAC, 32);
[strongSelf->_socket writeData:helloData withTimeout:-1 tag:0];
[strongSelf->_socket readDataToLength:5 withTimeout:-1 tag:MTTcpSocksReceiveHelloResponse];
} else {
[strongSelf->_socket readDataToLength:1 withTimeout:-1 tag:MTTcpReadTagPacketShortLength];
strongSelf->_readyToSendData = true;
[strongSelf sendDataIfNeeded];
if (strongSelf->_useIntermediateFormat) {
[strongSelf requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[strongSelf requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
}
} else {
struct socks5_ident_req req;
@ -422,25 +632,18 @@ struct ctr_state {
}];
}
- (void)sendDatas:(NSArray *)datas completion:(void (^)(bool success))completion requestQuickAck:(bool)requestQuickAck expectDataInResponse:(bool)expectDataInResponse
{
if (datas.count == 0)
{
completion(false);
- (void)sendDataIfNeeded {
while (_pendingDataQueue.count != 0) {
MTTcpSendData *dataToSend = _pendingDataQueue[0];
[_pendingDataQueue removeObjectAtIndex:0];
return;
}
[[MTTcpConnection tcpQueue] dispatchOnQueue:^
{
if (!_closed)
{
if (_socket != nil)
{
if (!_closed) {
if (_socket != nil) {
NSUInteger completeDataLength = 0;
for (NSData *data in datas)
{
NSMutableData *completeData = [[NSMutableData alloc] init];
for (NSData *data in dataToSend.dataSet) {
NSMutableData *packetData = [[NSMutableData alloc] initWithCapacity:data.length + 8];
uint8_t padding[16];
@ -455,25 +658,24 @@ struct ctr_state {
}
length += (int32_t)paddingSize;
if (requestQuickAck) {
if (dataToSend.requestQuickAck) {
length |= 0x80000000;
}
[packetData appendBytes:&length length:4];
} else {
int32_t quarterLength = (int32_t)(data.length / 4);
if (quarterLength <= 0x7e)
{
if (quarterLength <= 0x7e) {
uint8_t quarterLengthMarker = (uint8_t)quarterLength;
if (requestQuickAck)
if (dataToSend.requestQuickAck) {
quarterLengthMarker |= 0x80;
}
[packetData appendBytes:&quarterLengthMarker length:1];
}
else
{
} else {
uint8_t quarterLengthMarker = 0x7f;
if (requestQuickAck)
if (dataToSend.requestQuickAck) {
quarterLengthMarker |= 0x80;
}
[packetData appendBytes:&quarterLengthMarker length:1];
[packetData appendBytes:((uint8_t *)&quarterLength) length:3];
}
@ -513,12 +715,10 @@ struct ctr_state {
NSData *incomingAesKey = [[NSData alloc] initWithBytes:controlBytesReversed + 8 length:32];
NSData *incomingAesIv = [[NSData alloc] initWithBytes:controlBytesReversed + 8 + 32 length:16];
NSData *effectiveSecret = nil;
if (_mtpSecret != nil) {
effectiveSecret = _mtpSecret;
} else if (_scheme.address.secret != nil) {
effectiveSecret = _scheme.address.secret;
effectiveSecret = _mtpSecret.secret;
}
if (effectiveSecret.length != 16 && effectiveSecret.length != 17) {
effectiveSecret = nil;
@ -544,7 +744,7 @@ struct ctr_state {
}
NSData *incomingAesKeyHash = MTSha256(incomingAesKeyData);
incomingAesKey = [incomingAesKeyHash subdataWithRange:NSMakeRange(0, 32)];
}
}
_outgoingAesCtr = [[MTAesCtr alloc] initWithKey:aesKey.bytes keyLength:32 iv:aesIv.bytes decrypt:false];
_incomingAesCtr = [[MTAesCtr alloc] initWithKey:incomingAesKey.bytes keyLength:32 iv:incomingAesIv.bytes decrypt:false];
@ -558,43 +758,84 @@ struct ctr_state {
[_outgoingAesCtr encryptIn:packetData.bytes out:outData.mutableBytes + 64 len:packetData.length];
[_socket writeData:outData withTimeout:-1 tag:0];
[completeData appendData:outData];
} else {
NSMutableData *encryptedData = [[NSMutableData alloc] initWithLength:packetData.length];
[_outgoingAesCtr encryptIn:packetData.bytes out:encryptedData.mutableBytes len:packetData.length];
[_socket writeData:encryptedData withTimeout:-1 tag:0];
[completeData appendData:encryptedData];
}
if ([_mtpSecret isKindOfClass:[MTProxySecretType2 class]]) {
NSMutableData *partitionedCompleteData = [[NSMutableData alloc] init];
if (!_addedHelloHeader) {
_addedHelloHeader = true;
uint8_t helloHeader[6] = { 0x14, 0x03, 0x03, 0x00, 0x01, 0x01 };
[partitionedCompleteData appendData:[[NSData alloc] initWithBytes:helloHeader length:6]];
}
NSUInteger limit = 2878;
NSUInteger offset = 0;
while (offset < completeData.length) {
NSUInteger partLength = MIN(limit, completeData.length - offset);
uint8_t packetHeader[5] = { 0x17, 0x03, 0x03, 0x00, 0x00 };
int16_t lengthValue = (int16_t)partLength;
lengthValue = OSSwapInt16(lengthValue);
memcpy(&packetHeader[3], &lengthValue, 2);
[partitionedCompleteData appendData:[[NSData alloc] initWithBytes:packetHeader length:5]];
[partitionedCompleteData appendData:[completeData subdataWithRange:NSMakeRange(offset, partLength)]];
offset += partLength;
}
[_socket writeData:partitionedCompleteData withTimeout:-1 tag:0];
} else {
[_socket writeData:completeData withTimeout:-1 tag:0];
}
}
if (expectDataInResponse && _responseTimeoutTimer == nil)
{
if (dataToSend.expectDataInResponse && _responseTimeoutTimer == nil) {
__weak MTTcpConnection *weakSelf = self;
_responseTimeoutTimer = [[MTTimer alloc] initWithTimeout:MTMinTcpResponseTimeout + completeDataLength / (12.0 * 1024) repeat:false completion:^
{
_responseTimeoutTimer = [[MTTimer alloc] initWithTimeout:MTMinTcpResponseTimeout + completeDataLength / (12.0 * 1024) repeat:false completion:^{
__strong MTTcpConnection *strongSelf = weakSelf;
[strongSelf responseTimeout];
} queue:[MTTcpConnection tcpQueue].nativeQueue];
[_responseTimeoutTimer start];
}
if (completion)
completion(true);
}
else
{
if (dataToSend.completion) {
dataToSend.completion(true);
}
} else {
if (MTLogEnabled()) {
MTLog(@"***** %s: can't send data: connection is not opened", __PRETTY_FUNCTION__);
}
if (completion)
completion(false);
if (dataToSend.completion) {
dataToSend.completion(false);
}
}
} else {
if (dataToSend.completion) {
dataToSend.completion(false);
}
}
else
{
if (completion)
completion(false);
}
}
- (void)sendDatas:(NSArray *)datas completion:(void (^)(bool success))completion requestQuickAck:(bool)requestQuickAck expectDataInResponse:(bool)expectDataInResponse
{
if (datas.count == 0)
{
completion(false);
return;
}
[[MTTcpConnection tcpQueue] dispatchOnQueue:^{
[_pendingDataQueue addObject:[[MTTcpSendData alloc] initWithDataSet:datas completion:completion requestQuickAck:requestQuickAck expectDataInResponse:expectDataInResponse]];
if (_readyToSendData) {
[self sendDataIfNeeded];
}
}];
}
@ -801,10 +1042,12 @@ struct ctr_state {
if ([delegate respondsToSelector:@selector(tcpConnectionOpened:)])
[delegate tcpConnectionOpened:self];
_readyToSendData = true;
[self sendDataIfNeeded];
if (_useIntermediateFormat) {
[_socket readDataToLength:4 withTimeout:-1 tag:MTTcpReadTagPacketFullLength];
[self requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[_socket readDataToLength:1 withTimeout:-1 tag:MTTcpReadTagPacketShortLength];
[self requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
return;
@ -825,15 +1068,198 @@ struct ctr_state {
[self requestSocksConnection];
return;
} else if (tag == MTTcpSocksReceiveHelloResponse) {
if (rawData.length != 5) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response length", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
NSData *header = [rawData subdataWithRange:NSMakeRange(0, 3)];
uint8_t expectedHeader[3] = { 0x16, 0x03, 0x03 };
if (![[[NSData alloc] initWithBytes:expectedHeader length:3] isEqualToData:header]) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response header", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
int16_t nextLength = 0;
[rawData getBytes:&nextLength range:NSMakeRange(3, 2)];
nextLength = OSSwapInt16(nextLength);
if (nextLength < 0 || nextLength > 10 * 1024) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response header length marker", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
_currentHelloResponse = [NSData dataWithData:rawData];
[_socket readDataToLength:((int)nextLength) + 9 + 2 withTimeout:-1 tag:MTTcpSocksReceiveHelloResponse1];
return;
} else if (tag == MTTcpSocksReceiveHelloResponse1) {
uint8_t expectedResponsePart[9] = { 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x17, 0x03, 0x03 };
NSData *responsePart = [rawData subdataWithRange:NSMakeRange(rawData.length - 9 - 2, 9)];
if (![[[NSData alloc] initWithBytes:expectedResponsePart length:9] isEqualToData:responsePart]) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response1 part", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
int16_t nextLength = 0;
[rawData getBytes:&nextLength range:NSMakeRange(rawData.length - 2, 2)];
nextLength = OSSwapInt16(nextLength);
if (nextLength < 0 || nextLength > 10 * 1024) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response header length marker", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
NSMutableData *currentHelloResponse = [[NSMutableData alloc] init];
[currentHelloResponse appendData:_currentHelloResponse];
[currentHelloResponse appendData:rawData];
_currentHelloResponse = currentHelloResponse;
[_socket readDataToLength:((int)nextLength) withTimeout:-1 tag:MTTcpSocksReceiveHelloResponse2];
return;
} else if (tag == MTTcpSocksReceiveHelloResponse2) {
NSMutableData *currentHelloResponse = [[NSMutableData alloc] init];
[currentHelloResponse appendData:_currentHelloResponse];
[currentHelloResponse appendData:rawData];
if (currentHelloResponse.length < 11 + 32) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response total length", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
NSData *currentHelloResponseRandom = [currentHelloResponse subdataWithRange:NSMakeRange(11, 32)];
memset(((uint8_t *)currentHelloResponse.mutableBytes) + 11, 0, 32);
NSMutableData *checkData = [[NSMutableData alloc] init];
[checkData appendData:_helloRandom];
[checkData appendData:currentHelloResponse];
NSData *effectiveSecret = _mtpSecret.secret;
uint8_t cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, effectiveSecret.bytes, effectiveSecret.length, checkData.bytes, checkData.length, cHMAC);
if (![[[NSData alloc] initWithBytes:cHMAC length:CC_SHA256_DIGEST_LENGTH] isEqualToData:currentHelloResponseRandom]) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid hello response random", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
_readyToSendData = true;
[self sendDataIfNeeded];
if (_useIntermediateFormat) {
[self requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[self requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
[_socket readDataToLength:5 withTimeout:-1 tag:MTTcpSocksReceiveComplexLength];
return;
} else if (tag == MTTcpSocksReceiveComplexLength) {
if (rawData.length != 5) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid complex header length", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
NSData *header = [rawData subdataWithRange:NSMakeRange(0, 3)];
uint8_t expectedHeader[3] = { 0x17, 0x03, 0x03 };
if (![[[NSData alloc] initWithBytes:expectedHeader length:3] isEqualToData:header]) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid complex header", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
int16_t nextLength = 0;
[rawData getBytes:&nextLength range:NSMakeRange(3, 2)];
nextLength = OSSwapInt16(nextLength);
if (nextLength < 0) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid complex header length marker", __PRETTY_FUNCTION__);
}
[self closeAndNotifyWithError:true];
return;
}
[_socket readDataToLength:(int)nextLength withTimeout:-1 tag:MTTcpSocksReceiveComplexPacketPart];
return;
} else if (tag == MTTcpSocksReceiveComplexPacketPart) {
[self addReadData:rawData];
[_socket readDataToLength:5 withTimeout:-1 tag:MTTcpSocksReceiveComplexLength];
return;
} else {
[self addReadData:rawData];
}
}
- (void)requestReadDataWithLength:(int)length tag:(int)tag {
assert(_pendingReceiveData == nil);
_pendingReceiveData = [[MTTcpReceiveData alloc] initWithTag:tag length:length];
if (![_mtpSecret isKindOfClass:[MTProxySecretType2 class]]) {
[_socket readDataToLength:length withTimeout:-1 tag:MTTcpSocksReceivePassthrough];
}
if (_receivedDataBuffer.length >= _pendingReceiveData.length) {
NSData *rawData = [_receivedDataBuffer subdataWithRange:NSMakeRange(0, _pendingReceiveData.length)];
[_receivedDataBuffer replaceBytesInRange:NSMakeRange(0, _pendingReceiveData.length) withBytes:nil length:0];
int tag = _pendingReceiveData.tag;
_pendingReceiveData = nil;
[self processReceivedData:rawData tag:tag];
}
}
- (void)addReadData:(NSData *)data {
if (_pendingReceiveData != nil && _pendingReceiveData.length == data.length) {
int tag = _pendingReceiveData.tag;
_pendingReceiveData = nil;
[self processReceivedData:data tag:tag];
} else {
[_receivedDataBuffer appendData:data];
if (_pendingReceiveData != nil) {
if (_receivedDataBuffer.length >= _pendingReceiveData.length) {
NSData *rawData = [_receivedDataBuffer subdataWithRange:NSMakeRange(0, _pendingReceiveData.length)];
[_receivedDataBuffer replaceBytesInRange:NSMakeRange(0, _pendingReceiveData.length) withBytes:nil length:0];
int tag = _pendingReceiveData.tag;
_pendingReceiveData = nil;
[self processReceivedData:rawData tag:tag];
}
}
}
}
- (void)processReceivedData:(NSData *)rawData tag:(int)tag {
NSMutableData *decryptedData = [[NSMutableData alloc] initWithLength:rawData.length];
[_incomingAesCtr encryptIn:rawData.bytes out:decryptedData.mutableBytes len:rawData.length];
NSData *data = decryptedData;
if (tag == MTTcpReadTagPacketShortLength)
{
if (tag == MTTcpReadTagPacketShortLength) {
#ifdef DEBUG
NSAssert(data.length == 1, @"data length should be equal to 1");
#endif
@ -841,38 +1267,29 @@ struct ctr_state {
uint8_t quarterLengthMarker = 0;
[data getBytes:&quarterLengthMarker length:1];
if ((quarterLengthMarker & 0x80) == 0x80)
{
if ((quarterLengthMarker & 0x80) == 0x80) {
_quickAckByte = quarterLengthMarker;
[_socket readDataToLength:3 withTimeout:-1 tag:MTTcpReadTagQuickAck];
}
else
{
if (quarterLengthMarker >= 0x01 && quarterLengthMarker <= 0x7e)
{
[self requestReadDataWithLength:3 tag:MTTcpReadTagQuickAck];
} else {
if (quarterLengthMarker >= 0x01 && quarterLengthMarker <= 0x7e) {
NSUInteger packetBodyLength = ((NSUInteger)quarterLengthMarker) * 4;
if (packetBodyLength >= MTTcpProgressCalculationThreshold)
{
if (packetBodyLength >= MTTcpProgressCalculationThreshold) {
_packetRestLength = packetBodyLength - 128;
_packetRestReceivedLength = 0;
[_socket readDataToLength:128 withTimeout:-1 tag:MTTcpReadTagPacketHead];
[self requestReadDataWithLength:128 tag:MTTcpReadTagPacketHead];
} else {
[self requestReadDataWithLength:(int)packetBodyLength tag:MTTcpReadTagPacketBody];
}
else
[_socket readDataToLength:packetBodyLength withTimeout:-1 tag:MTTcpReadTagPacketBody];
}
else if (quarterLengthMarker == 0x7f)
[_socket readDataToLength:3 withTimeout:-1 tag:MTTcpReadTagPacketLongLength];
else
{
} else if (quarterLengthMarker == 0x7f) {
[self requestReadDataWithLength:3 tag:MTTcpReadTagPacketLongLength];
} else {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid quarter length marker (%" PRIu8 ")", __PRETTY_FUNCTION__, quarterLengthMarker);
}
[self closeAndNotifyWithError:true];
}
}
}
else if (tag == MTTcpReadTagPacketLongLength)
{
} else if (tag == MTTcpReadTagPacketLongLength) {
#ifdef DEBUG
NSAssert(data.length == 3, @"data length should be equal to 3");
#endif
@ -880,24 +1297,20 @@ struct ctr_state {
uint32_t quarterLength = 0;
[data getBytes:(((uint8_t *)&quarterLength)) length:3];
if (quarterLength <= 0 || quarterLength > (4 * 1024 * 1024) / 4)
{
if (quarterLength <= 0 || quarterLength > (4 * 1024 * 1024) / 4) {
if (MTLogEnabled()) {
MTLog(@"***** %s: invalid quarter length (%" PRIu32 ")", __PRETTY_FUNCTION__, quarterLength);
}
[self closeAndNotifyWithError:true];
}
else
{
} else {
NSUInteger packetBodyLength = quarterLength * 4;
if (packetBodyLength >= MTTcpProgressCalculationThreshold)
{
if (packetBodyLength >= MTTcpProgressCalculationThreshold) {
_packetRestLength = packetBodyLength - 128;
_packetRestReceivedLength = 0;
[_socket readDataToLength:128 withTimeout:-1 tag:MTTcpReadTagPacketHead];
[self requestReadDataWithLength:128 tag:MTTcpReadTagPacketHead];
} else {
[self requestReadDataWithLength:(int)packetBodyLength tag:MTTcpReadTagPacketBody];
}
else
[_socket readDataToLength:packetBodyLength withTimeout:-1 tag:MTTcpReadTagPacketBody];
}
} else if (tag == MTTcpReadTagPacketFullLength) {
#ifdef DEBUG
@ -917,9 +1330,9 @@ struct ctr_state {
[delegate tcpConnectionReceivedQuickAck:self quickAck:ackId];
if (_useIntermediateFormat) {
[_socket readDataToLength:4 withTimeout:-1 tag:MTTcpReadTagPacketFullLength];
[self requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[_socket readDataToLength:1 withTimeout:-1 tag:MTTcpReadTagPacketShortLength];
[self requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
} else {
if (length > 16 * 1024 * 1024) {
@ -933,15 +1346,13 @@ struct ctr_state {
if (packetBodyLength >= MTTcpProgressCalculationThreshold) {
_packetRestLength = packetBodyLength - 128;
_packetRestReceivedLength = 0;
[_socket readDataToLength:128 withTimeout:-1 tag:MTTcpReadTagPacketHead];
[self requestReadDataWithLength:128 tag:MTTcpReadTagPacketHead];
} else {
[_socket readDataToLength:packetBodyLength withTimeout:-1 tag:MTTcpReadTagPacketBody];
[self requestReadDataWithLength:(int)packetBodyLength tag:MTTcpReadTagPacketBody];
}
}
}
}
else if (tag == MTTcpReadTagPacketHead)
{
} else if (tag == MTTcpReadTagPacketHead) {
_packetHead = data;
static int64_t nextToken = 0;
@ -949,11 +1360,9 @@ struct ctr_state {
nextToken++;
id<MTTcpConnectionDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(tcpConnectionDecodePacketProgressToken:data:token:completion:)])
{
if ([delegate respondsToSelector:@selector(tcpConnectionDecodePacketProgressToken:data:token:completion:)]) {
__weak MTTcpConnection *weakSelf = self;
[delegate tcpConnectionDecodePacketProgressToken:self data:data token:_packetHeadDecodeToken completion:^(int64_t token, id packetProgressToken)
{
[delegate tcpConnectionDecodePacketProgressToken:self data:data token:_packetHeadDecodeToken completion:^(int64_t token, id packetProgressToken) {
[[MTTcpConnection tcpQueue] dispatchOnQueue:^{
__strong MTTcpConnection *strongSelf = weakSelf;
if (strongSelf != nil && token == strongSelf.packetHeadDecodeToken)
@ -962,10 +1371,8 @@ struct ctr_state {
}];
}
[_socket readDataToLength:_packetRestLength withTimeout:-1 tag:MTTcpReadTagPacketBody];
}
else if (tag == MTTcpReadTagPacketBody)
{
[self requestReadDataWithLength:(int)_packetRestLength tag:MTTcpReadTagPacketBody];
} else if (tag == MTTcpReadTagPacketBody) {
[_responseTimeoutTimer invalidate];
_responseTimeoutTimer = nil;
@ -973,8 +1380,7 @@ struct ctr_state {
_packetProgressToken = nil;
NSData *packetData = data;
if (_packetHead != nil)
{
if (_packetHead != nil) {
NSMutableData *combinedData = [[NSMutableData alloc] initWithCapacity:_packetHead.length + data.length];
[combinedData appendData:_packetHead];
[combinedData appendData:data];
@ -1022,13 +1428,11 @@ struct ctr_state {
}
if (_useIntermediateFormat) {
[_socket readDataToLength:4 withTimeout:-1 tag:MTTcpReadTagPacketFullLength];
[self requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[_socket readDataToLength:1 withTimeout:-1 tag:MTTcpReadTagPacketShortLength];
[self requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
}
else if (tag == MTTcpReadTagQuickAck)
{
} else if (tag == MTTcpReadTagQuickAck) {
#ifdef DEBUG
NSAssert(data.length == 3, @"data length should be equal to 3");
#endif
@ -1044,9 +1448,9 @@ struct ctr_state {
[delegate tcpConnectionReceivedQuickAck:self quickAck:ackId];
if (_useIntermediateFormat) {
[_socket readDataToLength:4 withTimeout:-1 tag:MTTcpReadTagPacketFullLength];
[self requestReadDataWithLength:4 tag:MTTcpReadTagPacketFullLength];
} else {
[_socket readDataToLength:1 withTimeout:-1 tag:MTTcpReadTagPacketShortLength];
[self requestReadDataWithLength:1 tag:MTTcpReadTagPacketShortLength];
}
}
}

View file

@ -55,7 +55,10 @@ static NSData *base64_decode(NSString *str) {
NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
return data;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]];
#pragma clang diagnostic pop
}
}
@ -292,7 +295,7 @@ static NSData *base64_decode(NSString *str) {
&outlen
);
if (status != 0) {
NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
NSLog(@"SecKeyEncrypt fail. Error Code: %d", (int)status);
ret = nil;
break;
}else{
@ -348,7 +351,7 @@ static NSData *base64_decode(NSString *str) {
&outlen
);
if (status != 0) {
NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
NSLog(@"SecKeyEncrypt fail. Error Code: %d", (int)status);
ret = nil;
break;
}else{

View file

@ -2492,6 +2492,7 @@
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -2769,6 +2770,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -3016,6 +3018,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -3263,6 +3266,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -3671,6 +3675,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -3897,6 +3902,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -4116,6 +4122,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -4334,6 +4341,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -4552,6 +4560,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -4792,6 +4801,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -5129,6 +5139,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -5222,6 +5233,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -5273,6 +5285,7 @@
"$(PROJECT_DIR)/thirdparty",
);
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -5521,6 +5534,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/openssl";
INFOPLIST_FILE = MtProtoKitDynamic/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";

View file

@ -84,9 +84,6 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
return self;
}
- (void)dealloc {
}
- (NSHTTPURLResponse *)response {
return (NSHTTPURLResponse *)[super response];
}
@ -176,8 +173,8 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
}];
}
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
- (void)setCompletionBlockWithSuccess:(void (^)(NSOperation *operation, id responseObject))success
failure:(void (^)(NSOperation *operation, NSError *error))failure
{
self.completionBlock = ^ {
if ([self isCancelled]) {

View file

@ -187,6 +187,8 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
if (_outputStream) {
[_outputStream close];
}
[super dealloc];
}
@ -414,8 +416,8 @@ didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
if ([challenge previousFailureCount] == 0) {
NSURLCredential *credential = nil;
NSString *username = (__bridge_transfer NSString *)CFURLCopyUserName((CFURLRef)[self.request URL]);
NSString *password = (__bridge_transfer NSString *)CFURLCopyPassword((CFURLRef)[self.request URL]);
NSString *username = CFBridgingRelease(CFURLCopyUserName((CFURLRef)[self.request URL]));
NSString *password = CFBridgingRelease(CFURLCopyPassword((CFURLRef)[self.request URL]));
if (username && password) {
credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceNone];

View file

@ -446,8 +446,6 @@ typedef enum GCDAsyncSocketError GCDAsyncSocketError;
- (BOOL)isDisconnected;
- (BOOL)isConnected;
- (void)getIsConnectedAndDisconnected:(void (^)(bool isConnected, bool isDisconnected))completion;
/**
* Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
* The host will be an IP address.

View file

@ -33,6 +33,9 @@
#import "MTNetworkUsageCalculationInfo.h"
#import "MTNetworkUsageManager.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
// For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC
@ -205,7 +208,6 @@ enum GCDAsyncSocketConfig
// Disconnect
- (void)closeWithError:(NSError *)error;
- (void)close;
- (void)maybeClose;
// Errors
@ -1050,7 +1052,7 @@ enum GCDAsyncSocketConfig
- (void)dealloc
{
LogInfo(@"%@ - %@ (start)", THIS_METHOD, self);
if (dispatch_get_current_queue() == socketQueue)
{
[self closeWithError:nil];
@ -6785,11 +6787,14 @@ static OSStatus SSLWriteFunction(SSLConnectionRef connection, const void *data,
// We can't run the run loop unless it has an associated input source or a timer.
// So we'll just create a timer that will never fire - unless the server runs for decades.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
target:self
selector:@selector(doNothingAtAll:)
userInfo:nil
repeats:YES];
#pragma clang diagnostic pop
[[NSRunLoop currentRunLoop] run];
@ -7395,3 +7400,5 @@ static void CFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType ty
}
@end
#pragma clang diagnostic pop

View file

@ -116,6 +116,7 @@
D010E17A22C237CF009324D4 /* vbrush.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D010E06B22C237CE009324D4 /* vbrush.cpp */; };
D010E17F22C23C42009324D4 /* LottieInstance.mm in Sources */ = {isa = PBXBuildFile; fileRef = D010E17E22C23C42009324D4 /* LottieInstance.mm */; };
D010E18122C23D57009324D4 /* LottieInstance.h in Headers */ = {isa = PBXBuildFile; fileRef = D010E18022C23D57009324D4 /* LottieInstance.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05B16B022CC29F100B85A94 /* pixman-arma64-neon-asm.h in Headers */ = {isa = PBXBuildFile; fileRef = D05B16AE22CC29F100B85A94 /* pixman-arma64-neon-asm.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -232,6 +233,8 @@
D010E06B22C237CE009324D4 /* vbrush.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vbrush.cpp; sourceTree = "<group>"; };
D010E17E22C23C42009324D4 /* LottieInstance.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LottieInstance.mm; sourceTree = "<group>"; };
D010E18022C23D57009324D4 /* LottieInstance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LottieInstance.h; sourceTree = "<group>"; };
D05B16AD22CC29F100B85A94 /* pixman-arma64-neon-asm.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "pixman-arma64-neon-asm.S"; sourceTree = "<group>"; };
D05B16AE22CC29F100B85A94 /* pixman-arma64-neon-asm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "pixman-arma64-neon-asm.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -453,6 +456,8 @@
D010E03122C237CE009324D4 /* pixman */ = {
isa = PBXGroup;
children = (
D05B16AE22CC29F100B85A94 /* pixman-arma64-neon-asm.h */,
D05B16AD22CC29F100B85A94 /* pixman-arma64-neon-asm.S */,
D010E03322C237CE009324D4 /* pixman-arm-neon-asm.h */,
D010E03422C237CE009324D4 /* vregion.h */,
D010E03522C237CE009324D4 /* pixman-arm-neon-asm.S */,
@ -518,6 +523,7 @@
D010E12D22C237CE009324D4 /* rapidjson.h in Headers */,
D010E11E22C237CE009324D4 /* strtod.h in Headers */,
D010E14A22C237CF009324D4 /* config.h in Headers */,
D05B16B022CC29F100B85A94 /* pixman-arma64-neon-asm.h in Headers */,
D010E17422C237CF009324D4 /* vstackallocator.h in Headers */,
D010E10C22C237CE009324D4 /* lottieparser.h in Headers */,
D010E15A22C237CF009324D4 /* vpathmesure.h in Headers */,

View file

@ -20,6 +20,17 @@
_frameCount = (int32_t)_animation->totalFrame();
_frameRate = (int32_t)_animation->frameRate();
size_t width = 0;
size_t height = 0;
_animation->size(width, height);
if (width != height || (width != 100 && width != 512)) {
return nil;
}
if ((_frameRate > 30 && _frameRate != 60) || _animation->duration() > 4.5) {
return nil;
}
}
return self;
}

View file

@ -423,6 +423,8 @@ public:
* @internal
*/
~Animation();
bool isTgs() const;
private:
void setValue(Color_Type, Property, const std::string &, Color);

View file

@ -47,7 +47,8 @@ public:
Surface render(size_t frameNo, const Surface &surface);
std::future<Surface> renderAsync(size_t frameNo, Surface &&surface);
const LOTLayerNode * renderTree(size_t frameNo, const VSize &size);
bool isTgs() const { return mModel->isTgs(); }
const LayerInfoList &layerInfoList() const
{
return mModel->layerInfoList();
@ -113,7 +114,7 @@ void AnimationImpl::init(const std::shared_ptr<LOTModel> &model)
mRenderInProgress = false;
}
#if false //def LOTTIE_THREAD_SUPPORT
#ifdef LOTTIE_THREAD_SUPPORT
#include <thread>
#include "vtaskqueue.h"
@ -266,6 +267,11 @@ std::unique_ptr<Animation> Animation::loadFromFile(const std::string &path)
return nullptr;
}
bool Animation::isTgs() const
{
return d->isTgs();
}
void Animation::size(size_t &width, size_t &height) const
{
VSize sz = d->size();

Some files were not shown because too many files have changed in this diff Show more