Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.9 KiB |
|
|
@ -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.";
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.4 KiB |
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#import <LegacyComponents/TGWallpaperInfo.h>
|
||||
|
||||
@interface TGCustomImageWallpaperInfo : TGWallpaperInfo
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image;
|
||||
|
||||
@end
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#import <LegacyComponents/TGPasscodeBackground.h>
|
||||
|
||||
@interface TGDefaultPasscodeBackground : NSObject <TGPasscodeBackground>
|
||||
|
||||
@end
|
||||
|
|
@ -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
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#import "TGPasscodeBackground.h"
|
||||
|
||||
@interface TGImageBasedPasscodeBackground : NSObject <TGPasscodeBackground>
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image size:(CGSize)size;
|
||||
|
||||
@end
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
131
submodules/MediaPlayer/Sources/MediaPlayerFramePreview.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]) {
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *)×tamp;
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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]) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,6 +423,8 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
~Animation();
|
||||
|
||||
bool isTgs() const;
|
||||
|
||||
private:
|
||||
void setValue(Color_Type, Property, const std::string &, Color);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||