diff --git a/LegacyComponents.xcodeproj/project.pbxproj b/LegacyComponents.xcodeproj/project.pbxproj index f917c32734..942957428b 100644 --- a/LegacyComponents.xcodeproj/project.pbxproj +++ b/LegacyComponents.xcodeproj/project.pbxproj @@ -426,6 +426,48 @@ D0177B231F2641B10044446D /* PGCameraVolumeButtonHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = D0177B131F2641B10044446D /* PGCameraVolumeButtonHandler.m */; }; D0177B2F1F26430D0044446D /* TGCameraPreviewView.h in Headers */ = {isa = PBXBuildFile; fileRef = D0177B2D1F26430D0044446D /* TGCameraPreviewView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0177B301F26430D0044446D /* TGCameraPreviewView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0177B2E1F26430D0044446D /* TGCameraPreviewView.m */; }; + D02660561F34A7F8000E2DC5 /* TGLocationAnnotation.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660341F34A7F8000E2DC5 /* TGLocationAnnotation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660571F34A7F8000E2DC5 /* TGLocationAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660351F34A7F8000E2DC5 /* TGLocationAnnotation.m */; }; + D02660581F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660361F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.h */; }; + D02660591F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660371F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.m */; }; + D026605A1F34A7F8000E2DC5 /* TGLocationMapModeControl.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660381F34A7F8000E2DC5 /* TGLocationMapModeControl.h */; }; + D026605B1F34A7F8000E2DC5 /* TGLocationMapModeControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660391F34A7F8000E2DC5 /* TGLocationMapModeControl.m */; }; + D026605C1F34A7F8000E2DC5 /* TGLocationMapView.h in Headers */ = {isa = PBXBuildFile; fileRef = D026603A1F34A7F8000E2DC5 /* TGLocationMapView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D026605D1F34A7F8000E2DC5 /* TGLocationMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = D026603B1F34A7F8000E2DC5 /* TGLocationMapView.m */; }; + D026605E1F34A7F8000E2DC5 /* TGLocationPickerController.h in Headers */ = {isa = PBXBuildFile; fileRef = D026603C1F34A7F8000E2DC5 /* TGLocationPickerController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D026605F1F34A7F8000E2DC5 /* TGLocationPickerController.m in Sources */ = {isa = PBXBuildFile; fileRef = D026603D1F34A7F8000E2DC5 /* TGLocationPickerController.m */; }; + D02660601F34A7F8000E2DC5 /* TGLocationPinAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = D026603E1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.h */; }; + D02660611F34A7F8000E2DC5 /* TGLocationPinAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = D026603F1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.m */; }; + D02660621F34A7F8000E2DC5 /* TGLocationPinView.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660401F34A7F8000E2DC5 /* TGLocationPinView.h */; }; + D02660631F34A7F8000E2DC5 /* TGLocationPinView.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660411F34A7F8000E2DC5 /* TGLocationPinView.m */; }; + D02660641F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660421F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.h */; }; + D02660651F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660431F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.m */; }; + D02660661F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660441F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.h */; }; + D02660671F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660451F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.m */; }; + D02660681F34A7F8000E2DC5 /* TGLocationSignals.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660461F34A7F8000E2DC5 /* TGLocationSignals.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660691F34A7F8000E2DC5 /* TGLocationSignals.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660471F34A7F8000E2DC5 /* TGLocationSignals.m */; }; + D026606C1F34A7F8000E2DC5 /* TGLocationTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = D026604A1F34A7F8000E2DC5 /* TGLocationTitleView.h */; }; + D026606D1F34A7F8000E2DC5 /* TGLocationTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = D026604B1F34A7F8000E2DC5 /* TGLocationTitleView.m */; }; + D026606E1F34A7F8000E2DC5 /* TGLocationTrackingButton.h in Headers */ = {isa = PBXBuildFile; fileRef = D026604C1F34A7F8000E2DC5 /* TGLocationTrackingButton.h */; }; + D026606F1F34A7F8000E2DC5 /* TGLocationTrackingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D026604D1F34A7F8000E2DC5 /* TGLocationTrackingButton.m */; }; + D02660701F34A7F8000E2DC5 /* TGLocationUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D026604E1F34A7F8000E2DC5 /* TGLocationUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660711F34A7F8000E2DC5 /* TGLocationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D026604F1F34A7F8000E2DC5 /* TGLocationUtils.m */; }; + D02660721F34A7F8000E2DC5 /* TGLocationVenueCell.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660501F34A7F8000E2DC5 /* TGLocationVenueCell.h */; }; + D02660731F34A7F8000E2DC5 /* TGLocationVenueCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660511F34A7F8000E2DC5 /* TGLocationVenueCell.m */; }; + D02660761F34A7F8000E2DC5 /* TGLocationViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660541F34A7F8000E2DC5 /* TGLocationViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660771F34A7F8000E2DC5 /* TGLocationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660551F34A7F8000E2DC5 /* TGLocationViewController.m */; }; + D026607A1F34A961000E2DC5 /* TGLocationVenue.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660781F34A961000E2DC5 /* TGLocationVenue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D026607B1F34A961000E2DC5 /* TGLocationVenue.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660791F34A961000E2DC5 /* TGLocationVenue.m */; }; + D026607E1F34A97B000E2DC5 /* TGPinAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = D026607C1F34A97B000E2DC5 /* TGPinAnnotationView.h */; }; + D026607F1F34A97B000E2DC5 /* TGPinAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = D026607D1F34A97B000E2DC5 /* TGPinAnnotationView.m */; }; + D02660821F34B986000E2DC5 /* TGListsTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660801F34B986000E2DC5 /* TGListsTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660831F34B986000E2DC5 /* TGListsTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660811F34B986000E2DC5 /* TGListsTableView.m */; }; + D02660861F34B9B1000E2DC5 /* TGSearchBar.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660841F34B9B1000E2DC5 /* TGSearchBar.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D02660871F34B9B1000E2DC5 /* TGSearchBar.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660851F34B9B1000E2DC5 /* TGSearchBar.m */; }; + D026608A1F34B9F9000E2DC5 /* TGSearchDisplayMixin.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D026608B1F34B9F9000E2DC5 /* TGSearchDisplayMixin.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660891F34B9F9000E2DC5 /* TGSearchDisplayMixin.m */; }; + D026608E1F34BA71000E2DC5 /* TGPickPinAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = D026608C1F34BA71000E2DC5 /* TGPickPinAnnotationView.h */; }; + D026608F1F34BA71000E2DC5 /* TGPickPinAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = D026608D1F34BA71000E2DC5 /* TGPickPinAnnotationView.m */; }; 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, ); }; }; @@ -1487,6 +1529,48 @@ D0177B131F2641B10044446D /* PGCameraVolumeButtonHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGCameraVolumeButtonHandler.m; sourceTree = ""; }; D0177B2D1F26430D0044446D /* TGCameraPreviewView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCameraPreviewView.h; sourceTree = ""; }; D0177B2E1F26430D0044446D /* TGCameraPreviewView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGCameraPreviewView.m; sourceTree = ""; }; + D02660341F34A7F8000E2DC5 /* TGLocationAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationAnnotation.h; sourceTree = ""; }; + D02660351F34A7F8000E2DC5 /* TGLocationAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationAnnotation.m; sourceTree = ""; }; + D02660361F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationCurrentLocationCell.h; sourceTree = ""; }; + D02660371F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationCurrentLocationCell.m; sourceTree = ""; }; + D02660381F34A7F8000E2DC5 /* TGLocationMapModeControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationMapModeControl.h; sourceTree = ""; }; + D02660391F34A7F8000E2DC5 /* TGLocationMapModeControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationMapModeControl.m; sourceTree = ""; }; + D026603A1F34A7F8000E2DC5 /* TGLocationMapView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationMapView.h; sourceTree = ""; }; + D026603B1F34A7F8000E2DC5 /* TGLocationMapView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationMapView.m; sourceTree = ""; }; + D026603C1F34A7F8000E2DC5 /* TGLocationPickerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationPickerController.h; sourceTree = ""; }; + D026603D1F34A7F8000E2DC5 /* TGLocationPickerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationPickerController.m; sourceTree = ""; }; + D026603E1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationPinAnnotationView.h; sourceTree = ""; }; + D026603F1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationPinAnnotationView.m; sourceTree = ""; }; + D02660401F34A7F8000E2DC5 /* TGLocationPinView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationPinView.h; sourceTree = ""; }; + D02660411F34A7F8000E2DC5 /* TGLocationPinView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationPinView.m; sourceTree = ""; }; + D02660421F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationReverseGeocodeResult.h; sourceTree = ""; }; + D02660431F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationReverseGeocodeResult.m; sourceTree = ""; }; + D02660441F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationSectionHeaderCell.h; sourceTree = ""; }; + D02660451F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationSectionHeaderCell.m; sourceTree = ""; }; + D02660461F34A7F8000E2DC5 /* TGLocationSignals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationSignals.h; sourceTree = ""; }; + D02660471F34A7F8000E2DC5 /* TGLocationSignals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationSignals.m; sourceTree = ""; }; + D026604A1F34A7F8000E2DC5 /* TGLocationTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationTitleView.h; sourceTree = ""; }; + D026604B1F34A7F8000E2DC5 /* TGLocationTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationTitleView.m; sourceTree = ""; }; + D026604C1F34A7F8000E2DC5 /* TGLocationTrackingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationTrackingButton.h; sourceTree = ""; }; + D026604D1F34A7F8000E2DC5 /* TGLocationTrackingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationTrackingButton.m; sourceTree = ""; }; + D026604E1F34A7F8000E2DC5 /* TGLocationUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationUtils.h; sourceTree = ""; }; + D026604F1F34A7F8000E2DC5 /* TGLocationUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationUtils.m; sourceTree = ""; }; + D02660501F34A7F8000E2DC5 /* TGLocationVenueCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationVenueCell.h; sourceTree = ""; }; + D02660511F34A7F8000E2DC5 /* TGLocationVenueCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationVenueCell.m; sourceTree = ""; }; + D02660541F34A7F8000E2DC5 /* TGLocationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationViewController.h; sourceTree = ""; }; + D02660551F34A7F8000E2DC5 /* TGLocationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationViewController.m; sourceTree = ""; }; + D02660781F34A961000E2DC5 /* TGLocationVenue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationVenue.h; sourceTree = ""; }; + D02660791F34A961000E2DC5 /* TGLocationVenue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationVenue.m; sourceTree = ""; }; + D026607C1F34A97B000E2DC5 /* TGPinAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPinAnnotationView.h; sourceTree = ""; }; + D026607D1F34A97B000E2DC5 /* TGPinAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPinAnnotationView.m; sourceTree = ""; }; + D02660801F34B986000E2DC5 /* TGListsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGListsTableView.h; sourceTree = ""; }; + D02660811F34B986000E2DC5 /* TGListsTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGListsTableView.m; sourceTree = ""; }; + D02660841F34B9B1000E2DC5 /* TGSearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGSearchBar.h; sourceTree = ""; }; + D02660851F34B9B1000E2DC5 /* TGSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGSearchBar.m; sourceTree = ""; }; + D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGSearchDisplayMixin.h; sourceTree = ""; }; + D02660891F34B9F9000E2DC5 /* TGSearchDisplayMixin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGSearchDisplayMixin.m; sourceTree = ""; }; + D026608C1F34BA71000E2DC5 /* TGPickPinAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPickPinAnnotationView.h; sourceTree = ""; }; + D026608D1F34BA71000E2DC5 /* TGPickPinAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPickPinAnnotationView.m; sourceTree = ""; }; D07BC6C91F2A18B700ED97AA /* TGCameraMainPhoneView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCameraMainPhoneView.h; sourceTree = ""; }; D07BC6CA1F2A18B700ED97AA /* TGCameraMainPhoneView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGCameraMainPhoneView.m; sourceTree = ""; }; D07BC6CB1F2A18B700ED97AA /* TGCameraMainTabletView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGCameraMainTabletView.h; sourceTree = ""; }; @@ -1698,8 +1782,6 @@ D07BC87A1F2A365000ED97AA /* TGProgressSpinnerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGProgressSpinnerView.m; sourceTree = ""; }; D07BC87B1F2A365000ED97AA /* TGProgressWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGProgressWindow.h; sourceTree = ""; }; D07BC87C1F2A365000ED97AA /* TGProgressWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGProgressWindow.m; sourceTree = ""; }; - D07BC8811F2A367500ED97AA /* TGActivityIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGActivityIndicatorView.h; sourceTree = ""; }; - D07BC8821F2A367500ED97AA /* TGActivityIndicatorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGActivityIndicatorView.m; sourceTree = ""; }; D07BC8851F2A375800ED97AA /* TGPhotoCropAreaView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPhotoCropAreaView.h; sourceTree = ""; }; D07BC8861F2A375800ED97AA /* TGPhotoCropAreaView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPhotoCropAreaView.m; sourceTree = ""; }; D07BC8871F2A375800ED97AA /* TGPhotoCropControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPhotoCropControl.h; sourceTree = ""; }; @@ -2192,6 +2274,7 @@ D07BCB031F2B63D700ED97AA /* Passcode */, D07BCB2A1F2B65C400ED97AA /* Wallpapers */, D07BCB3F1F2B69D400ED97AA /* Embed Video */, + D02660331F34A7DA000E2DC5 /* Location */, D017772A1F1F8F100044446D /* LegacyComponents.h */, D017772B1F1F8F100044446D /* Info.plist */, ); @@ -2460,8 +2543,6 @@ D07BC7121F2A269400ED97AA /* TGImageView.m */, D07BC7251F2A2A5300ED97AA /* UICollectionView+Utils.h */, D07BC7261F2A2A5300ED97AA /* UICollectionView+Utils.m */, - D07BC8811F2A367500ED97AA /* TGActivityIndicatorView.h */, - D07BC8821F2A367500ED97AA /* TGActivityIndicatorView.m */, D07BC93D1F2A3DB900ED97AA /* TGMessageImageViewOverlayView.h */, D07BC93E1F2A3DB900ED97AA /* TGMessageImageViewOverlayView.m */, D07BC95A1F2A3EF000ED97AA /* TGLetteredAvatarView.h */, @@ -2480,6 +2561,12 @@ D07BCAA91F2B44C100ED97AA /* TGModernBarButton.m */, D07BCC031F2B82D100ED97AA /* TGModernConversationTitleActivityIndicator.h */, D07BCC041F2B82D100ED97AA /* TGModernConversationTitleActivityIndicator.m */, + D02660801F34B986000E2DC5 /* TGListsTableView.h */, + D02660811F34B986000E2DC5 /* TGListsTableView.m */, + D02660841F34B9B1000E2DC5 /* TGSearchBar.h */, + D02660851F34B9B1000E2DC5 /* TGSearchBar.m */, + D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */, + D02660891F34B9F9000E2DC5 /* TGSearchDisplayMixin.m */, ); name = "Basic UI Components"; sourceTree = ""; @@ -3062,6 +3149,49 @@ name = Camera; sourceTree = ""; }; + D02660331F34A7DA000E2DC5 /* Location */ = { + isa = PBXGroup; + children = ( + D026607C1F34A97B000E2DC5 /* TGPinAnnotationView.h */, + D026607D1F34A97B000E2DC5 /* TGPinAnnotationView.m */, + D02660781F34A961000E2DC5 /* TGLocationVenue.h */, + D02660791F34A961000E2DC5 /* TGLocationVenue.m */, + D02660341F34A7F8000E2DC5 /* TGLocationAnnotation.h */, + D02660351F34A7F8000E2DC5 /* TGLocationAnnotation.m */, + D02660361F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.h */, + D02660371F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.m */, + D02660381F34A7F8000E2DC5 /* TGLocationMapModeControl.h */, + D02660391F34A7F8000E2DC5 /* TGLocationMapModeControl.m */, + D026603A1F34A7F8000E2DC5 /* TGLocationMapView.h */, + D026603B1F34A7F8000E2DC5 /* TGLocationMapView.m */, + D026603C1F34A7F8000E2DC5 /* TGLocationPickerController.h */, + D026603D1F34A7F8000E2DC5 /* TGLocationPickerController.m */, + D026603E1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.h */, + D026603F1F34A7F8000E2DC5 /* TGLocationPinAnnotationView.m */, + D02660401F34A7F8000E2DC5 /* TGLocationPinView.h */, + D02660411F34A7F8000E2DC5 /* TGLocationPinView.m */, + D02660421F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.h */, + D02660431F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.m */, + D02660441F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.h */, + D02660451F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.m */, + D02660461F34A7F8000E2DC5 /* TGLocationSignals.h */, + D02660471F34A7F8000E2DC5 /* TGLocationSignals.m */, + D026604A1F34A7F8000E2DC5 /* TGLocationTitleView.h */, + D026604B1F34A7F8000E2DC5 /* TGLocationTitleView.m */, + D026604C1F34A7F8000E2DC5 /* TGLocationTrackingButton.h */, + D026604D1F34A7F8000E2DC5 /* TGLocationTrackingButton.m */, + D026604E1F34A7F8000E2DC5 /* TGLocationUtils.h */, + D026604F1F34A7F8000E2DC5 /* TGLocationUtils.m */, + D02660501F34A7F8000E2DC5 /* TGLocationVenueCell.h */, + D02660511F34A7F8000E2DC5 /* TGLocationVenueCell.m */, + D02660541F34A7F8000E2DC5 /* TGLocationViewController.h */, + D02660551F34A7F8000E2DC5 /* TGLocationViewController.m */, + D026608C1F34BA71000E2DC5 /* TGPickPinAnnotationView.h */, + D026608D1F34BA71000E2DC5 /* TGPickPinAnnotationView.m */, + ); + name = Location; + sourceTree = ""; + }; D07BC7911F2A2B7000ED97AA /* GPUImage */ = { isa = PBXGroup; children = ( @@ -3552,6 +3682,7 @@ D07BC9541F2A3EA900ED97AA /* TGModernConversationMentionsAssociatedPanel.h in Headers */, D01778FB1F20CF6B0044446D /* TGBackdropView.h in Headers */, D01779F51F2139980044446D /* POPBasicAnimationInternal.h in Headers */, + D026605A1F34A7F8000E2DC5 /* TGLocationMapModeControl.h in Headers */, D07BC8651F2A2F1300ED97AA /* TGMediaPickerCaptionInputPanel.h in Headers */, D07BC9191F2A380D00ED97AA /* TGPaintRadialBrush.h in Headers */, D07BC7131F2A269400ED97AA /* TGImageView.h in Headers */, @@ -3613,6 +3744,7 @@ D01779F21F2139980044446D /* POPAnimatorPrivate.h in Headers */, D07BC71F1F2A29E400ED97AA /* TGPhotoToolbarView.h in Headers */, D07BC7231F2A29E400ED97AA /* TGPhotoToolsController.h in Headers */, + D026607A1F34A961000E2DC5 /* TGLocationVenue.h in Headers */, D01777621F1F8FE60044446D /* TGLocalization.h in Headers */, D01778E71F20CAE60044446D /* TGNavigationBar.h in Headers */, D0177A1F1F21403E0044446D /* LegacyComponentsContext.h in Headers */, @@ -3632,6 +3764,7 @@ D01779141F20F4500044446D /* TGStaticBackdropAreaData.h in Headers */, D01778171F1F961D0044446D /* TGMessageEntityEmail.h in Headers */, D01779E21F2139980044446D /* POPAnimation.h in Headers */, + D02660721F34A7F8000E2DC5 /* TGLocationVenueCell.h in Headers */, D01777F41F1F961D0044446D /* TGTextCheckingResult.h in Headers */, D01778231F1F961D0044446D /* TGMessageEntityTextUrl.h in Headers */, D0177A9B1F22204A0044446D /* TGModernGalleryEmbeddedStickersHeaderView.h in Headers */, @@ -3700,9 +3833,11 @@ D07BC9721F2A467D00ED97AA /* TGPhotoTextSettingsView.h in Headers */, D07BC8A71F2A37A500ED97AA /* TGPhotoAvatarCropView.h in Headers */, D07BCBCD1F2B6F6300ED97AA /* CBVideoPlayer.h in Headers */, + D026607E1F34A97B000E2DC5 /* TGPinAnnotationView.h in Headers */, D07BCBB91F2B6F6300ED97AA /* CBCoubPlayer.h in Headers */, D07BCAB21F2B460B00ED97AA /* TGGifConverter.h in Headers */, D01778011F1F961D0044446D /* TGMessageViewCountContentProperty.h in Headers */, + D02660681F34A7F8000E2DC5 /* TGLocationSignals.h in Headers */, D07BCA9C1F2B443700ED97AA /* TGMediaAssetsUtils.h in Headers */, D0177A6E1F2201F00044446D /* TGModernGalleryDefaultFooterAccessoryView.h in Headers */, D017777A1F1F927A0044446D /* NSObject+TGLock.h in Headers */, @@ -3735,6 +3870,7 @@ D07BC9F11F2A9A2B00ED97AA /* TGMediaPickerCell.h in Headers */, D07BC8371F2A2D0C00ED97AA /* TGSecretTimerPickerItemView.h in Headers */, D07BC8D31F2A37EC00ED97AA /* TGPhotoPaintTextEntity.h in Headers */, + D02660761F34A7F8000E2DC5 /* TGLocationViewController.h in Headers */, D07BC86F1F2A2F5300ED97AA /* HPTextViewInternal.h in Headers */, D017794B1F20FFF60044446D /* TGMediaAssetMomentList.h in Headers */, D07BCA4B1F2A9DAB00ED97AA /* TGModernAnimatedImagePlayer.h in Headers */, @@ -3763,6 +3899,7 @@ D07BC9FD1F2A9A2B00ED97AA /* TGMediaPickerGalleryModel.h in Headers */, D017792F1F20FFAC0044446D /* TGMediaAssetsLegacyLibrary.h in Headers */, D07BC87D1F2A365000ED97AA /* TGProgressSpinnerView.h in Headers */, + D026608A1F34B9F9000E2DC5 /* TGSearchDisplayMixin.h in Headers */, D017790E1F20F4370044446D /* UIImage+TG.h in Headers */, D07BC89F1F2A375800ED97AA /* TGPhotoCropView.h in Headers */, D07BC7A11F2A2B8900ED97AA /* GLProgram.h in Headers */, @@ -3821,6 +3958,7 @@ D01779581F21031F0044446D /* TGPhotoEditorUtils.h in Headers */, D01778571F1F961D0044446D /* TGLocationMediaAttachment.h in Headers */, D07BCAC81F2B4E6200ED97AA /* TGAttachmentCameraView.h in Headers */, + D02660561F34A7F8000E2DC5 /* TGLocationAnnotation.h in Headers */, D07BC9521F2A3EA900ED97AA /* TGModernConversationHashtagsAssociatedPanel.h in Headers */, D07BCA151F2A9A2B00ED97AA /* TGMediaPickerPhotoStripCell.h in Headers */, D07BCB6E1F2B6A5600ED97AA /* TGEmbedVimeoPlayerView.h in Headers */, @@ -3850,11 +3988,13 @@ D07BC90B1F2A380D00ED97AA /* TGPaintFaceDetector.h in Headers */, D07BCA481F2A9CE300ED97AA /* TGModernMediaListSelectableItem.h in Headers */, D07BC9F31F2A9A2B00ED97AA /* TGMediaPickerController.h in Headers */, + D026608E1F34BA71000E2DC5 /* TGPickPinAnnotationView.h in Headers */, D07BC8161F2A2C0B00ED97AA /* PGPhotoGaussianBlurFilter.h in Headers */, D017783B1F1F961D0044446D /* TGStickerPackReference.h in Headers */, D01779DF1F2139980044446D /* POPAction.h in Headers */, D017791E1F20F7090044446D /* UIDevice+PlatformInfo.h in Headers */, D07BC9991F2A489C00ED97AA /* TGStickerPack.h in Headers */, + D02660641F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.h in Headers */, D07BC7A61F2A2B8900ED97AA /* GPUImageFilter.h in Headers */, D07BC9681F2A3F5C00ED97AA /* TGCache.h in Headers */, D0177AF31F23DF6D0044446D /* TGImageManagerTask.h in Headers */, @@ -3865,20 +4005,26 @@ D0177A561F21F7F40044446D /* TGDoubleTapGestureRecognizer.h in Headers */, D07BC90D1F2A380D00ED97AA /* TGPainting.h in Headers */, D017796D1F2103DB0044446D /* TGPhotoPaintStickerEntity.h in Headers */, + D02660861F34B9B1000E2DC5 /* TGSearchBar.h in Headers */, D07BC8021F2A2C0B00ED97AA /* PGHighlightsTool.h in Headers */, + D026605C1F34A7F8000E2DC5 /* TGLocationMapView.h in Headers */, D07BC95C1F2A3EF000ED97AA /* TGLetteredAvatarView.h in Headers */, + D026606C1F34A7F8000E2DC5 /* TGLocationTitleView.h in Headers */, D01777571F1F8FE60044446D /* PSKeyValueDecoder.h in Headers */, D07BCBA61F2B6F6300ED97AA /* AVAsset+CBExtension.h in Headers */, + D026605E1F34A7F8000E2DC5 /* TGLocationPickerController.h in Headers */, D017780D1F1F961D0044446D /* TGBotContextResultAttachment.h in Headers */, D07BCA3B1F2A9BE600ED97AA /* TGModernGalleryVideoContentView.h in Headers */, D07BC8C11F2A37EC00ED97AA /* TGPhotoPaintColorPicker.h in Headers */, D0177B2F1F26430D0044446D /* TGCameraPreviewView.h in Headers */, D01779E71F2139980044446D /* POPAnimationExtras.h in Headers */, D01778071F1F961D0044446D /* TGInvoiceMediaAttachment.h in Headers */, + D02660621F34A7F8000E2DC5 /* TGLocationPinView.h in Headers */, D07BC8581F2A2DBD00ED97AA /* TGMenuSheetCollectionView.h in Headers */, D07BCA301F2A9AE700ED97AA /* TGModernGallerySelectableItem.h in Headers */, D07BC9211F2A380D00ED97AA /* TGPaintSlice.h in Headers */, D07BC8971F2A375800ED97AA /* TGPhotoCropController.h in Headers */, + D02660821F34B986000E2DC5 /* TGListsTableView.h in Headers */, D01778421F1F961D0044446D /* TGDocumentAttributeVideo.h in Headers */, D07BC9501F2A3EA900ED97AA /* TGMentionPanelCell.h in Headers */, D01779AA1F210A2C0044446D /* TGMediaAssetImageSignals.h in Headers */, @@ -3891,10 +4037,12 @@ D0177A971F221DB60044446D /* TGModernGalleryContainerView.h in Headers */, D017790A1F20F3F90044446D /* TGImageBlur.h in Headers */, D07BC8931F2A375800ED97AA /* TGPhotoCropAreaView.h in Headers */, + D02660581F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.h in Headers */, D07BC85E1F2A2DBD00ED97AA /* TGMenuSheetTitleItemView.h in Headers */, D017799C1F2108670044446D /* TGMemoryImageCache.h in Headers */, D07BC80C1F2A2C0B00ED97AA /* PGPhotoEnhanceLUTGenerator.h in Headers */, D07BCAAA1F2B44C100ED97AA /* TGModernBarButton.h in Headers */, + D026606E1F34A7F8000E2DC5 /* TGLocationTrackingButton.h in Headers */, D01777821F1F93250044446D /* TGImageInfo.h in Headers */, D01779ED1F2139980044446D /* POPAnimationTracer.h in Headers */, D0177A931F221BB10044446D /* TGModernGalleryView.h in Headers */, @@ -3913,6 +4061,7 @@ D07BC8FF1F2A380D00ED97AA /* TGPaintBrush.h in Headers */, D01777871F1F93550044446D /* TGMessage.h in Headers */, D01777591F1F8FE60044446D /* PSKeyValueEncoder.h in Headers */, + D02660601F34A7F8000E2DC5 /* TGLocationPinAnnotationView.h in Headers */, D0177A3C1F21F2E50044446D /* TGTimerTarget.h in Headers */, D017798E1F2108130044446D /* PSLMDBKeyValueStore.h in Headers */, D017784B1F1F961D0044446D /* TGUnsupportedMediaAttachment.h in Headers */, @@ -4026,6 +4175,7 @@ D07BC8201F2A2C0B00ED97AA /* PGSaturationTool.h in Headers */, D07BCA6D1F2B3CE700ED97AA /* TGCameraFocusCrosshairsControl.h in Headers */, 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 */, @@ -4043,6 +4193,7 @@ D01778291F1F961D0044446D /* TGBotReplyMarkup.h in Headers */, D07BC6F31F2A19A700ED97AA /* TGCameraModeControl.h in Headers */, D07BCB5E1F2B6A5600ED97AA /* TGEmbedPIPButton.h in Headers */, + D02660661F34A7F8000E2DC5 /* TGLocationSectionHeaderCell.h in Headers */, D0177B161F2641B10044446D /* PGCameraCaptureSession.h in Headers */, D01777FB1F1F961D0044446D /* TGDatabaseMessageDraft.h in Headers */, ); @@ -4118,6 +4269,7 @@ files = ( D017798F1F2108130044446D /* PSLMDBKeyValueStore.m in Sources */, D07BC8291F2A2C0B00ED97AA /* PGVignetteTool.m in Sources */, + D02660831F34B986000E2DC5 /* TGListsTableView.m in Sources */, D0177A081F2139980044446D /* POPSpringAnimation.mm in Sources */, D07BCBA91F2B6F6300ED97AA /* CBAssetDownloadManager.m in Sources */, D07BCBBE1F2B6F6300ED97AA /* CBCoubVideoSource.m in Sources */, @@ -4161,6 +4313,7 @@ D07BC9691F2A3F5C00ED97AA /* TGCache.m in Sources */, D07BCBF81F2B72DC00ED97AA /* STKQueueEntry.m in Sources */, D07BC81F1F2A2C0B00ED97AA /* PGPhotoSharpenPass.m in Sources */, + D02660871F34B9B1000E2DC5 /* TGSearchBar.m in Sources */, D07BC9591F2A3EBF00ED97AA /* TGModernConversationAssociatedInputPanel.m in Sources */, D01778561F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m in Sources */, D07BCBB41F2B6F6300ED97AA /* CBCoubDownloadOperation.m in Sources */, @@ -4169,10 +4322,12 @@ D01778081F1F961D0044446D /* TGInvoiceMediaAttachment.m in Sources */, D07BC76D1F2A2B3700ED97AA /* TGPhotoEditorBlurTypeButton.m in Sources */, D07BC9921F2A480800ED97AA /* TGStickerKeyboardTabCell.m in Sources */, + D026606D1F34A7F8000E2DC5 /* TGLocationTitleView.m in Sources */, D01779541F2100520044446D /* TGMediaEditingContext.m in Sources */, D07BC6F41F2A19A700ED97AA /* TGCameraModeControl.m in Sources */, D017782C1F1F961D0044446D /* TGBotReplyMarkupButton.m in Sources */, D07BCAB81F2B4DE200ED97AA /* TGAttachmentCarouselItemView.m in Sources */, + D026605B1F34A7F8000E2DC5 /* TGLocationMapModeControl.m in Sources */, D01777771F1F92570044446D /* RMPhoneFormat.m in Sources */, D07BCA221F2A9A5300ED97AA /* TGCheckButtonView.m in Sources */, D017781E1F1F961D0044446D /* TGMessageEntityMention.m in Sources */, @@ -4194,6 +4349,7 @@ D01779931F2108130044446D /* PSLMDBKeyValueCursor.m in Sources */, D07BCAE41F2B502F00ED97AA /* TGImagePickerController.mm in Sources */, D07BC7791F2A2B3700ED97AA /* TGPhotoEditorHUDView.m in Sources */, + D02660771F34A7F8000E2DC5 /* TGLocationViewController.m in Sources */, D07BC8171F2A2C0B00ED97AA /* PGPhotoGaussianBlurFilter.m in Sources */, D01778BB1F2009880044446D /* Freedom.mm in Sources */, D01778061F1F961D0044446D /* TGWebDocument.m in Sources */, @@ -4220,6 +4376,7 @@ D07BC6D41F2A18B700ED97AA /* TGCameraMainView.m in Sources */, D07BC8071F2A2C0B00ED97AA /* PGPhotoCustomFilterPass.m in Sources */, D01779421F20FFF60044446D /* TGMediaAsset.m in Sources */, + D02660611F34A7F8000E2DC5 /* TGLocationPinAnnotationView.m in Sources */, D07BC76B1F2A2B3700ED97AA /* TGPhotoEditorBlurToolView.m in Sources */, D0177AA41F2222990044446D /* TGKeyCommandController.m in Sources */, D07BCA061F2A9A2B00ED97AA /* TGMediaPickerGalleryVideoItem.m in Sources */, @@ -4229,6 +4386,7 @@ D01779481F20FFF60044446D /* TGMediaAssetGroup.m in Sources */, D01779171F20F4500044446D /* TGStaticBackdropImageData.m in Sources */, D07BCBDC1F2B72BD00ED97AA /* STKAudioPlayer.m in Sources */, + D02660691F34A7F8000E2DC5 /* TGLocationSignals.m in Sources */, D07BC9241F2A380D00ED97AA /* TGPaintState.m in Sources */, D01778221F1F961D0044446D /* TGMessageEntityPre.m in Sources */, D07BC7421F2A2AC500ED97AA /* TGPhotoEditorButton.m in Sources */, @@ -4240,6 +4398,7 @@ D01779FF1F2139980044446D /* POPGeometry.mm in Sources */, D07BC8CC1F2A37EC00ED97AA /* TGPhotoPaintSelectionContainerView.m in Sources */, D07BC80F1F2A2C0B00ED97AA /* PGPhotoEnhancePass.m in Sources */, + D026606F1F34A7F8000E2DC5 /* TGLocationTrackingButton.m in Sources */, D0177A031F2139980044446D /* POPMath.mm in Sources */, D0177A681F21FB9B0044446D /* TGModernGalleryItemView.m in Sources */, D07BC6F01F2A19A700ED97AA /* TGCameraFlipButton.m in Sources */, @@ -4295,10 +4454,12 @@ D07BC8611F2A2DBD00ED97AA /* TGMenuSheetView.m in Sources */, D017796E1F2103DB0044446D /* TGPhotoPaintStickerEntity.m in Sources */, D07BC9F61F2A9A2B00ED97AA /* TGMediaPickerGalleryGifItem.m in Sources */, + D02660711F34A7F8000E2DC5 /* TGLocationUtils.m in Sources */, D07BC7101F2A25AE00ED97AA /* TGCameraPhotoPreviewController.m in Sources */, D07BC89C1F2A375800ED97AA /* TGPhotoCropRotationView.m in Sources */, D01779F91F2139980044446D /* POPCustomAnimation.mm in Sources */, D01778EE1F20CAE60044446D /* TGOverlayController.m in Sources */, + D02660571F34A7F8000E2DC5 /* TGLocationAnnotation.m in Sources */, D01779EC1F2139980044446D /* POPAnimationRuntime.mm in Sources */, D01777881F1F93550044446D /* TGMessage.mm in Sources */, D07BC9961F2A481C00ED97AA /* TGStickerKeyboardTabSettingsCell.m in Sources */, @@ -4323,6 +4484,7 @@ D01778321F1F961D0044446D /* TGInstantPage.m in Sources */, D017776B1F1F909E0044446D /* LegacyComponentsGlobals.m in Sources */, D07BCB651F2B6A5600ED97AA /* TGEmbedPlayerScrubber.m in Sources */, + D026608F1F34BA71000E2DC5 /* TGPickPinAnnotationView.m in Sources */, D01778301F1F961D0044446D /* TGReplyMarkupAttachment.m in Sources */, D07BC97A1F2A471000ED97AA /* TGStickerKeyboardTabPanel.m in Sources */, D07BC78D1F2A2B3700ED97AA /* TGPhotoEditorToolButtonsView.m in Sources */, @@ -4337,6 +4499,7 @@ D0177AF81F23DFC70044446D /* TGDataResource.m in Sources */, 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 */, @@ -4366,6 +4529,7 @@ D07BCA891F2B443700ED97AA /* TGMediaAssetsController.m in Sources */, D07BCB731F2B6A5600ED97AA /* TGEmbedVKPlayerView.m in Sources */, D07BCA0E1F2A9A2B00ED97AA /* TGMediaPickerGalleryVideoTrimView.m in Sources */, + D026608B1F34B9F9000E2DC5 /* TGSearchDisplayMixin.m in Sources */, D07BCA501F2A9DDD00ED97AA /* FLAnimatedImage.m in Sources */, D07BC9881F2A472900ED97AA /* TGPhotoStickersSectionHeader.m in Sources */, D0177ADE1F23D9B80044446D /* SGraphObjectNode.m in Sources */, @@ -4381,6 +4545,7 @@ D07BC8771F2A2F7B00ED97AA /* TGWeakDelegate.m in Sources */, D01779761F2104320044446D /* TGVideoEditAdjustments.m in Sources */, D0177A721F2201F00044446D /* TGModernGalleryDefaultInterfaceView.m in Sources */, + D02660731F34A7F8000E2DC5 /* TGLocationVenueCell.m in Sources */, D07BCAFC1F2B517900ED97AA /* TGLegacyMediaPickerTipView.m in Sources */, D07BC7381F2A2A7D00ED97AA /* PGPhotoEditorPicture.m in Sources */, D07BCBB21F2B6F6300ED97AA /* CBCoubAuthorVO.m in Sources */, @@ -4398,7 +4563,9 @@ D07BCBF21F2B72DC00ED97AA /* STKDataSourceWrapper.m in Sources */, D017783D1F1F961D0044446D /* TGDocumentAttributeFilename.m in Sources */, D017796C1F2103DB0044446D /* TGPhotoPaintEntity.m in Sources */, + D026607F1F34A97B000E2DC5 /* TGPinAnnotationView.m in Sources */, D07BCBF41F2B72DC00ED97AA /* STKHTTPDataSource.m in Sources */, + D026605D1F34A7F8000E2DC5 /* TGLocationMapView.m in Sources */, D07BCABC1F2B4E2600ED97AA /* TGTransitionLayout.m in Sources */, D07BCB791F2B6DB900ED97AA /* TGEmbedCoubPlayerView.m in Sources */, D07BCB231F2B646A00ED97AA /* TGTextField.m in Sources */, @@ -4424,6 +4591,7 @@ D0177A491F21F7010044446D /* TGModernGalleryVideoView.m in Sources */, 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 */, @@ -4441,6 +4609,7 @@ D07BC83A1F2A2D0C00ED97AA /* TGSecretTimerValueController.m in Sources */, D07BC9611F2A3F0A00ED97AA /* TGGradientLabel.m in Sources */, D07BC9101F2A380D00ED97AA /* TGPaintingWrapperView.m in Sources */, + D02660591F34A7F8000E2DC5 /* TGLocationCurrentLocationCell.m in Sources */, D07BC82B1F2A2C0B00ED97AA /* PGWarmthTool.m in Sources */, D01778F81F20CDAC0044446D /* TGHacks.m in Sources */, D07BC86C1F2A2F3800ED97AA /* HPGrowingTextView.m in Sources */, @@ -4549,6 +4718,7 @@ D07BCA9F1F2B443700ED97AA /* TGMediaAssetsVideoCell.m in Sources */, D07BC9AF1F2A4A5100ED97AA /* TGItemMenuSheetPreviewView.m in Sources */, D0177B231F2641B10044446D /* PGCameraVolumeButtonHandler.m in Sources */, + D02660651F34A7F8000E2DC5 /* TGLocationReverseGeocodeResult.m in Sources */, D07BCADC1F2B4F2800ED97AA /* TGOverlayFormsheetWindow.m in Sources */, D07BC73E1F2A2A7D00ED97AA /* PGPhotoEditorView.m in Sources */, D07BCBF01F2B72DC00ED97AA /* STKDataSource.m in Sources */, @@ -4600,6 +4770,7 @@ D07BC9B31F2A4B6600ED97AA /* TGStickerAssociation.m in Sources */, D07BC76F1F2A2B3700ED97AA /* TGPhotoEditorBlurView.m in Sources */, D07BCAEE1F2B507600ED97AA /* TGAttachmentVideoCell.m in Sources */, + D026605F1F34A7F8000E2DC5 /* TGLocationPickerController.m in Sources */, D07BCAC71F2B4E6200ED97AA /* TGAttachmentCameraCell.m in Sources */, D0177A3D1F21F2E50044446D /* TGTimerTarget.m in Sources */, D017784E1F1F961D0044446D /* TGForwardedMessageMediaAttachment.m in Sources */, @@ -4614,6 +4785,7 @@ D07BCA911F2B443700ED97AA /* TGMediaAssetsMomentsController.m in Sources */, D07BCBC41F2B6F6300ED97AA /* CBJSONCoubMapper.m in Sources */, D07BC7871F2A2B3700ED97AA /* TGPhotoEditorTabController.m in Sources */, + D02660631F34A7F8000E2DC5 /* TGLocationPinView.m in Sources */, D07BCA121F2A9A2B00ED97AA /* TGMediaPickerModernGalleryMixin.m in Sources */, D07BC8421F2A2D7900ED97AA /* TGPhotoCaptionController.m in Sources */, D07BC93C1F2A3C1F00ED97AA /* TGPhotoQualityController.m in Sources */, diff --git a/LegacyComponents/ASActor.m b/LegacyComponents/ASActor.m index f2bc25f583..4ee9ad48fa 100644 --- a/LegacyComponents/ASActor.m +++ b/LegacyComponents/ASActor.m @@ -22,7 +22,7 @@ static NSMutableDictionary *registeredRequestBuilders() NSString *genericPath = [requestBuilderClass genericPath]; if (genericPath == nil || genericPath.length == 0) { - TGLog(@"Error: ASActor::registerActorClass: genericPath is nil"); + TGLegacyLog(@"Error: ASActor::registerActorClass: genericPath is nil"); return; } @@ -42,7 +42,7 @@ static NSMutableDictionary *registeredRequestBuilders() + (NSString *)genericPath { - TGLog(@"Error: ASActor::genericPath: no default implementation provided"); + TGLegacyLog(@"Error: ASActor::genericPath: no default implementation provided"); return nil; } @@ -73,7 +73,7 @@ static int instanceCount = 0; /*#if TARGET_IPHONE_SIMULATOR instanceCount++; - TGLog(@"%d actors (++)", instanceCount); + TGLegacyLog(@"%d actors (++)", instanceCount); #endif*/ } return self; @@ -84,7 +84,7 @@ static int instanceCount = 0; /*#if TARGET_IPHONE_SIMULATOR instanceCount--; - TGLog(@"%d actors (--)", instanceCount); + TGLegacyLog(@"%d actors (--)", instanceCount); #endif*/ } @@ -94,7 +94,7 @@ static int instanceCount = 0; - (void)execute:(NSDictionary *)__unused options { - TGLog(@"Error: ASActor::execute: no default implementation provided"); + TGLegacyLog(@"Error: ASActor::execute: no default implementation provided"); } - (void)cancel diff --git a/LegacyComponents/ActionStage.mm b/LegacyComponents/ActionStage.mm index 78eda8d6f6..2827a5da98 100644 --- a/LegacyComponents/ActionStage.mm +++ b/LegacyComponents/ActionStage.mm @@ -123,7 +123,7 @@ ActionStage *ActionStageInstance() #ifdef DEBUG CFAbsoluteTime executionTime = (CFAbsoluteTimeGetCurrent() - startTime); if (executionTime > 0.1) - TGLog(@"***** Dispatch from %s:%d took %f s", function, line, executionTime); + TGLegacyLog(@"***** Dispatch from %s:%d took %f s", function, line, executionTime); #endif } else @@ -137,7 +137,7 @@ ActionStage *ActionStageInstance() CFAbsoluteTime executionTime = (CFAbsoluteTimeGetCurrent() - startTime); if (executionTime > 0.1) - TGLog(@"***** Dispatch from %s:%d took %f s", function, line, executionTime); + TGLegacyLog(@"***** Dispatch from %s:%d took %f s", function, line, executionTime); }); #else dispatch_async([self globalStageDispatchQueue], block); @@ -162,25 +162,25 @@ ActionStage *ActionStageInstance() { [self dispatchOnStageQueue:^ { - TGLog(@"===== SGraph State ====="); - TGLog(@"%d live node watchers", _liveNodeWatchers.count); + TGLegacyLog(@"===== SGraph State ====="); + TGLegacyLog(@"%d live node watchers", _liveNodeWatchers.count); [_liveNodeWatchers enumerateKeysAndObjectsUsingBlock:^(NSString *path, NSArray *watchers, __unused BOOL *stop) { - TGLog(@" %@", path); + TGLegacyLog(@" %@", path); for (ASHandle *handle in watchers) { id watcher = handle.delegate; if (watcher != nil) { - TGLog(@" %@", [watcher description]); + TGLegacyLog(@" %@", [watcher description]); } } }]; - TGLog(@"%d requests", _activeRequests.count); + TGLegacyLog(@"%d requests", _activeRequests.count); [_activeRequests enumerateKeysAndObjectsUsingBlock:^(NSString *path, __unused id obj, __unused BOOL *stop) { - TGLog(@" %@", path); + TGLegacyLog(@" %@", path); }]; - TGLog(@"========================"); + TGLegacyLog(@"========================"); }]; } @@ -220,7 +220,7 @@ ActionStage *ActionStageInstance() { if (![key isKindOfClass:StringClass]) { - TGLog(@"Warning: optionsHash: key is not a string"); + TGLegacyLog(@"Warning: optionsHash: key is not a string"); continue; } @@ -286,7 +286,7 @@ ActionStage *ActionStageInstance() { if (![actionHandle hasDelegate]) { - TGLog(@"Error: %s:%d: actionHandle.delegate is nil", __PRETTY_FUNCTION__, __LINE__); + TGLegacyLog(@"Error: %s:%d: actionHandle.delegate is nil", __PRETTY_FUNCTION__, __LINE__); return; } @@ -306,7 +306,7 @@ ActionStage *ActionStageInstance() requestInfo = [cancelRequestInfo objectForKey:@"requestInfo"]; [activeRequests setObject:requestInfo forKey:path]; [cancelTimers removeObjectForKey:path]; - TGLog(@"Resuming request to \"%@\"", path); + TGLegacyLog(@"Resuming request to \"%@\"", path); } if (requestInfo == nil) @@ -346,7 +346,7 @@ ActionStage *ActionStageInstance() if ([requestQueue count] > 1) { executeNow = false; - TGLog(@"Adding request %@ to request queue \"%@\"", requestBuilder, requestBuilder.requestQueueName); + TGLegacyLog(@"Adding request %@ to request queue \"%@\"", requestBuilder, requestBuilder.requestQueueName); if (flags & TGActorRequestChangePriority) { @@ -355,7 +355,7 @@ ActionStage *ActionStageInstance() [requestQueue removeLastObject]; [requestQueue insertObject:requestBuilder atIndex:1]; - TGLog(@"(Inserted actor with high priority (next in queue)"); + TGLegacyLog(@"(Inserted actor with high priority (next in queue)"); } } } @@ -369,7 +369,7 @@ ActionStage *ActionStageInstance() } else { - TGLog(@"Error: request builder not found for \"%@\"", path); + TGLegacyLog(@"Error: request builder not found for \"%@\"", path); } } else @@ -377,12 +377,12 @@ ActionStage *ActionStageInstance() NSMutableArray *watchers = [requestInfo objectForKey:@"watchers"]; if (![watchers containsObject:actionHandle]) { - TGLog(@"Joining watcher to the watchers of \"%@\"", path); + TGLegacyLog(@"Joining watcher to the watchers of \"%@\"", path); [watchers addObject:actionHandle]; } else { - TGLog(@"Continue to watch for actor \"%@\"", path); + TGLegacyLog(@"Continue to watch for actor \"%@\"", path); } ASActor *actor = [requestInfo objectForKey:@"requestBuilder"]; @@ -431,7 +431,7 @@ ActionStage *ActionStageInstance() [requestQueue removeObjectAtIndex:index]; [requestQueue insertObject:actor atIndex:1]; - TGLog(@"Changed actor %@ priority (next in %@)", path, actor.requestQueueName); + TGLegacyLog(@"Changed actor %@ priority (next in %@)", path, actor.requestQueueName); } } } @@ -474,7 +474,7 @@ ActionStage *ActionStageInstance() { if (![self isCurrentQueueStageQueue]) { - TGLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); + TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); return nil; } @@ -515,7 +515,7 @@ ActionStage *ActionStageInstance() { if (![self isCurrentQueueStageQueue]) { - TGLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); + TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); return nil; } @@ -552,7 +552,7 @@ ActionStage *ActionStageInstance() { if (![self isCurrentQueueStageQueue]) { - TGLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); + TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); return nil; } @@ -586,7 +586,7 @@ ActionStage *ActionStageInstance() { if (![self isCurrentQueueStageQueue]) { - TGLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); + TGLegacyLog(@"%s should be called from graph queue", __PRETTY_FUNCTION__); return nil; } @@ -636,7 +636,7 @@ ActionStage *ActionStageInstance() ASHandle *actionHandle = watcher.actionHandle; if (actionHandle == nil) { - TGLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); + TGLegacyLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); return; } @@ -659,7 +659,7 @@ ActionStage *ActionStageInstance() ASHandle *actionHandle = watcher.actionHandle; if (actionHandle == nil) { - TGLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); + TGLegacyLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); return; } @@ -685,7 +685,7 @@ ActionStage *ActionStageInstance() ASHandle *actionHandle = watcher.actionHandle; if (actionHandle == nil) { - TGLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); + TGLegacyLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); return; } @@ -708,7 +708,7 @@ ActionStage *ActionStageInstance() ASHandle *actionHandle = watcher.actionHandle; if (actionHandle == nil) { - TGLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); + TGLegacyLog(@"***** Warning: actionHandle is nil in %s:%d", __PRETTY_FUNCTION__, __LINE__); return; } @@ -729,12 +729,12 @@ ActionStage *ActionStageInstance() { NSMutableArray *requestQueue = [_requestQueues objectForKey:requestBuilder.requestQueueName == nil ? name : requestBuilder.requestQueueName]; if (requestQueue == nil) - TGLog(@"Warning: requestQueue is nil"); + TGLegacyLog(@"Warning: requestQueue is nil"); else { if (requestQueue.count == 0) { - TGLog(@"***** Warning ***** request queue \"%@\" is empty.", requestBuilder.requestQueueName); + TGLegacyLog(@"***** Warning ***** request queue \"%@\" is empty.", requestBuilder.requestQueueName); } else { @@ -756,7 +756,7 @@ ActionStage *ActionStageInstance() } else { - //TGLog(@"Request queue %@ finished", requestBuilder.requestQueueName); + //TGLegacyLog(@"Request queue %@ finished", requestBuilder.requestQueueName); [_requestQueues removeObjectForKey:requestBuilder.requestQueueName]; } } @@ -768,7 +768,7 @@ ActionStage *ActionStageInstance() } else { - TGLog(@"***** Warning ***** request queue \"%@\" doesn't contain request to %@", requestBuilder.requestQueueName, requestBuilder.path); + TGLegacyLog(@"***** Warning ***** request queue \"%@\" doesn't contain request to %@", requestBuilder.requestQueueName, requestBuilder.path); } } } @@ -785,7 +785,7 @@ ActionStage *ActionStageInstance() ASHandle *watcherGraphHandle = actionHandle; if (watcherGraphHandle == nil) { - TGLog(@"***** Warning: graph handle is nil in removeWatcher"); + TGLegacyLog(@"***** Warning: graph handle is nil in removeWatcher"); return; } @@ -889,7 +889,7 @@ ActionStage *ActionStageInstance() { if (watcherGraphHandle == nil) { - TGLog(@"***** Warning: graph handle is nil in removeWatcher:fromPath"); + TGLegacyLog(@"***** Warning: graph handle is nil in removeWatcher:fromPath"); return; } @@ -914,7 +914,7 @@ ActionStage *ActionStageInstance() if (removeWatchersFromPath.size() > 1) { - TGLog(@"Cancelled %ld requests at once", removeWatchersFromPath.size()); + TGLegacyLog(@"Cancelled %ld requests at once", removeWatchersFromPath.size()); } for (std::vector >::iterator it = removeWatchersFromPath.begin(); it != removeWatchersFromPath.end(); it++) @@ -1051,7 +1051,7 @@ ActionStage *ActionStageInstance() [actionWatchers removeAllObjects]; if (requestBuilder == nil) - TGLog(@"***** Warning ***** requestBuilder is nil"); + TGLegacyLog(@"***** Warning ***** requestBuilder is nil"); else if (requestBuilder.requestQueueName != nil) { [self removeRequestFromQueueAndProceedIfFirst:requestBuilder.requestQueueName fromRequestBuilder:requestBuilder]; @@ -1117,7 +1117,7 @@ ActionStage *ActionStageInstance() [actionWatchers removeAllObjects]; if (requestBuilder == nil) - TGLog(@"***** Warning ***** requestBuilder is nil"); + TGLegacyLog(@"***** Warning ***** requestBuilder is nil"); else if (requestBuilder.requestQueueName != nil) { [self removeRequestFromQueueAndProceedIfFirst:requestBuilder.requestQueueName fromRequestBuilder:requestBuilder]; @@ -1181,13 +1181,13 @@ ActionStage *ActionStageInstance() [activeRequests removeObjectForKey:path]; [requestBuilder cancel]; - TGLog(@"Cancelled request to \"%@\"", path); + TGLegacyLog(@"Cancelled request to \"%@\"", path); if (requestBuilder.requestQueueName != nil) [self removeRequestFromQueueAndProceedIfFirst:requestBuilder.requestQueueName fromRequestBuilder:requestBuilder]; } else { - TGLog(@"Will cancel request to \"%@\" in %f s", path, cancelTimeout); + TGLegacyLog(@"Will cancel request to \"%@\" in %f s", path, cancelTimeout); NSDictionary *cancelDict = [NSDictionary dictionaryWithObjectsAndKeys:path, @"path", [NSNumber numberWithInt:0], @"type", nil]; STimer *timer = [[STimer alloc] initWithTimeout:cancelTimeout repeat:false completion:^ { @@ -1204,7 +1204,7 @@ ActionStage *ActionStageInstance() } else if (cancelRequestInfo == nil) { - TGLog(@"Warning: cannot cancel request to \"%@\": no active request found", path); + TGLegacyLog(@"Warning: cannot cancel request to \"%@\": no active request found", path); } } @@ -1219,19 +1219,19 @@ ActionStage *ActionStageInstance() NSMutableDictionary *cancelRequestInfo = [cancelTimers objectForKey:path]; if (cancelRequestInfo == nil) { - TGLog(@"Warning: cancelNodeRequestTimerEvent: \"%@\": no cancel info found", path); + TGLegacyLog(@"Warning: cancelNodeRequestTimerEvent: \"%@\": no cancel info found", path); return; } NSDictionary *requestInfo = [cancelRequestInfo objectForKey:@"requestInfo"]; ASActor *requestBuilder = [requestInfo objectForKey:@"requestBuilder"]; if (requestBuilder == nil) { - TGLog(@"Warning: active request builder for \"%@\" not fond, cannot cancel request", path); + TGLegacyLog(@"Warning: active request builder for \"%@\" not fond, cannot cancel request", path); } else { [requestBuilder cancel]; - TGLog(@"Cancelled request to \"%@\"", path); + TGLegacyLog(@"Cancelled request to \"%@\"", path); if (requestBuilder.requestQueueName != nil) [self removeRequestFromQueueAndProceedIfFirst:requestBuilder.requestQueueName fromRequestBuilder:requestBuilder]; } diff --git a/LegacyComponents/FreedomUIKit.m b/LegacyComponents/FreedomUIKit.m index a3139466e8..bf25711750 100644 --- a/LegacyComponents/FreedomUIKit.m +++ b/LegacyComponents/FreedomUIKit.m @@ -249,7 +249,7 @@ void freedomUIKit_decorated4_1(id self, SEL _cmd, id arg1) if (nativeImpl != NULL) nativeImpl(self, _cmd, arg1); - TGLog(@"invoke %@", NSStringFromSelector(_cmd)); + TGLegacyLog(@"invoke %@", NSStringFromSelector(_cmd)); } @@ -265,7 +265,7 @@ void freedomUIKit_decorated4_2(id self, SEL _cmd) if (nativeImpl != NULL) nativeImpl(self, _cmd); - TGLog(@"invoke %@", NSStringFromSelector(_cmd)); + TGLegacyLog(@"invoke %@", NSStringFromSelector(_cmd)); } void freedomUIKitInit4() diff --git a/LegacyComponents/HPTextViewInternal.m b/LegacyComponents/HPTextViewInternal.m index 390ae63f52..c8eb8da2cb 100644 --- a/LegacyComponents/HPTextViewInternal.m +++ b/LegacyComponents/HPTextViewInternal.m @@ -229,7 +229,7 @@ return false; } - //TGLog(@"canPerformAction %@", NSStringFromSelector(action)); + //TGLegacyLog(@"canPerformAction %@", NSStringFromSelector(action)); return [super canPerformAction:action withSender:sender]; } diff --git a/LegacyComponents/LegacyComponents.h b/LegacyComponents/LegacyComponents.h index 70ff6a714c..300712924f 100644 --- a/LegacyComponents/LegacyComponents.h +++ b/LegacyComponents/LegacyComponents.h @@ -281,3 +281,8 @@ FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[]; #import #import + +#import +#import +#import +#import diff --git a/LegacyComponents/LegacyComponentsGlobals.h b/LegacyComponents/LegacyComponentsGlobals.h index 6143acd30a..319d7dc846 100644 --- a/LegacyComponents/LegacyComponentsGlobals.h +++ b/LegacyComponents/LegacyComponentsGlobals.h @@ -88,3 +88,12 @@ typedef enum { @end +#ifdef __cplusplus +extern "C" { +#endif +UIImage *TGComponentsImageNamed(NSString *name); +NSString *TGComponentsPathForResource(NSString *name, NSString *type); +#ifdef __cplusplus +} +#endif + diff --git a/LegacyComponents/LegacyComponentsInternal.h b/LegacyComponents/LegacyComponentsInternal.h index b69a834ef7..0fac5b8aa4 100644 --- a/LegacyComponents/LegacyComponentsInternal.h +++ b/LegacyComponents/LegacyComponentsInternal.h @@ -6,11 +6,11 @@ extern "C" { #endif -TGLocalization *effectiveLocalization(); +TGLocalization *legacyEffectiveLocalization(); NSString *TGLocalized(NSString *s); bool TGObjectCompare(id obj1, id obj2); bool TGStringCompare(NSString *s1, NSString *s2); -void TGLog(NSString *format, ...); +void TGLegacyLog(NSString *format, ...); int iosMajorVersion(); int iosMinorVersion(); @@ -22,16 +22,13 @@ void TGDispatchAfter(double delay, dispatch_queue_t queue, dispatch_block_t bloc int deviceMemorySize(); int cpuCoreCount(); -UIImage *TGComponentsImageNamed(NSString *name); -NSString *TGComponentsPathForResource(NSString *name, NSString *type); - #define UIColorRGB(rgb) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:1.0f]) #define UIColorRGBA(rgb,a) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:a]) -#define TGRestrictedToMainThread {if(![[NSThread currentThread] isMainThread]) TGLog(@"***** Warning: main thread-bound operation is running in background! *****");} +#define TGRestrictedToMainThread {if(![[NSThread currentThread] isMainThread]) TGLegacyLog(@"***** Warning: main thread-bound operation is running in background! *****");} #define TG_TIMESTAMP_DEFINE(s) CFAbsoluteTime tg_timestamp_##s = CFAbsoluteTimeGetCurrent(); int tg_timestamp_line_##s = __LINE__; -#define TG_TIMESTAMP_MEASURE(s) { CFAbsoluteTime tg_timestamp_current_time = CFAbsoluteTimeGetCurrent(); TGLog(@"%s %d-%d: %f ms", #s, tg_timestamp_line_##s, __LINE__, (tg_timestamp_current_time - tg_timestamp_##s) * 1000.0); tg_timestamp_##s = tg_timestamp_current_time; tg_timestamp_line_##s = __LINE__; } +#define TG_TIMESTAMP_MEASURE(s) { CFAbsoluteTime tg_timestamp_current_time = CFAbsoluteTimeGetCurrent(); TGLegacyLog(@"%s %d-%d: %f ms", #s, tg_timestamp_line_##s, __LINE__, (tg_timestamp_current_time - tg_timestamp_##s) * 1000.0); tg_timestamp_##s = tg_timestamp_current_time; tg_timestamp_line_##s = __LINE__; } #ifdef __LP64__ # define CGFloor floor diff --git a/LegacyComponents/LegacyComponentsInternal.m b/LegacyComponents/LegacyComponentsInternal.m index 58c12b85e2..0848e6afca 100644 --- a/LegacyComponents/LegacyComponentsInternal.m +++ b/LegacyComponents/LegacyComponentsInternal.m @@ -6,12 +6,12 @@ #import -TGLocalization *effectiveLocalization() { +TGLocalization *legacyEffectiveLocalization() { return [[LegacyComponentsGlobals provider] effectiveLocalization]; } NSString *TGLocalized(NSString *s) { - return [effectiveLocalization() get:s]; + return [legacyEffectiveLocalization() get:s]; } bool TGObjectCompare(id obj1, id obj2) { @@ -31,7 +31,7 @@ bool TGStringCompare(NSString *s1, NSString *s2) { return s1 == nil || [s1 isEqualToString:s2]; } -void TGLog(NSString *format, ...) +void TGLegacyLog(NSString *format, ...) { va_list L; va_start(L, format); diff --git a/LegacyComponents/NSInputStream+TL.m b/LegacyComponents/NSInputStream+TL.m index 199313828b..df5388730f 100644 --- a/LegacyComponents/NSInputStream+TL.m +++ b/LegacyComponents/NSInputStream+TL.m @@ -28,7 +28,7 @@ static inline int roundUpInput(int numToRound, int multiple) if ([self read:(uint8_t *)&value maxLength:4] != 4) { - TGLog(@"***** Couldn't read int32"); + TGLegacyLog(@"***** Couldn't read int32"); @throw [[NSException alloc] initWithName:@"NSInputStream+TLException" reason:@"readInt32 end of stream" userInfo:@{}]; } @@ -69,7 +69,7 @@ static inline int roundUpInput(int numToRound, int multiple) if ([self read:(uint8_t *)&value maxLength:8] != 8) { - TGLog(@"***** Couldn't read int64"); + TGLegacyLog(@"***** Couldn't read int64"); } #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -108,7 +108,7 @@ static inline int roundUpInput(int numToRound, int multiple) if ([self read:(uint8_t *)&value maxLength:8] != 8) { - TGLog(@"***** Couldn't read double"); + TGLegacyLog(@"***** Couldn't read double"); } #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -147,7 +147,7 @@ static inline int roundUpInput(int numToRound, int multiple) int readLen = (int)[self read:bytes maxLength:length]; if (readLen != length) { - TGLog(@"***** Couldn't read %d bytes", length); + TGLegacyLog(@"***** Couldn't read %d bytes", length); } NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; return data; @@ -173,7 +173,7 @@ static inline int roundUpInput(int numToRound, int multiple) int readLen = (int)[self read:bytes maxLength:length]; if (readLen != length) { - TGLog(@"***** Couldn't read %d bytes", length); + TGLegacyLog(@"***** Couldn't read %d bytes", length); } NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; return data; @@ -229,7 +229,7 @@ static inline int roundUpInput(int numToRound, int multiple) int readLen = (int)[self read:bytes maxLength:length]; if (readLen != length) { - TGLog(@"***** Couldn't read %d bytes", length); + TGLegacyLog(@"***** Couldn't read %d bytes", length); } string = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; @@ -331,7 +331,7 @@ static inline int roundUpInput(int numToRound, int multiple) int readLen = (int)[self read:bytes maxLength:length]; if (readLen != length) { - TGLog(@"***** Couldn't read %d bytes", length); + TGLegacyLog(@"***** Couldn't read %d bytes", length); } NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true]; diff --git a/LegacyComponents/PGCamera.m b/LegacyComponents/PGCamera.m index bec009f46b..c878470b1d 100644 --- a/LegacyComponents/PGCamera.m +++ b/LegacyComponents/PGCamera.m @@ -78,7 +78,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; - (void)dealloc { - TGLog(@"Camera: dealloc"); + TGLegacyLog(@"Camera: dealloc"); [_deviceAngleSampler stopMeasuring]; [self _unsubscribeFromCameraChanges]; @@ -109,7 +109,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; - (void)handleRuntimeError:(NSNotification *)notification { - TGLog(@"ERROR: Camera runtime error: %@", notification.userInfo[AVCaptureSessionErrorKey]); + TGLegacyLog(@"ERROR: Camera runtime error: %@", notification.userInfo[AVCaptureSessionErrorKey]); __weak PGCamera *weakSelf = self; TGDispatchAfter(1.5f, [PGCamera cameraQueue]._dispatch_queue, ^ @@ -140,7 +140,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; return; AVCaptureSessionInterruptionReason reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; - TGLog(@"WARNING: Camera was interrupted with reason %d", reason); + TGLegacyLog(@"WARNING: Camera was interrupted with reason %d", reason); if (self.captureInterrupted != nil) self.captureInterrupted(reason); } @@ -221,7 +221,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; _capturing = true; - TGLog(@"Camera: start capture"); + TGLegacyLog(@"Camera: start capture"); #if !TARGET_IPHONE_SIMULATOR [self.captureSession startRunning]; #endif @@ -248,7 +248,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; if (!pause) _invalidated = true; - TGLog(@"Camera: stop capture"); + TGLegacyLog(@"Camera: stop capture"); [[PGCamera cameraQueue] dispatch:^ { @@ -258,7 +258,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; [self.captureSession resetFlashMode]; - TGLog(@"Camera: stop capture invalidated"); + TGLegacyLog(@"Camera: stop capture invalidated"); TGCameraPreviewView *previewView = _previewView; if (previewView != nil) [previewView invalidate]; @@ -273,7 +273,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; #endif } - TGLog(@"Camera: stop running"); + TGLegacyLog(@"Camera: stop running"); #if !TARGET_IPHONE_SIMULATOR [self.captureSession stopRunning]; #endif diff --git a/LegacyComponents/PGCameraCaptureSession.m b/LegacyComponents/PGCameraCaptureSession.m index 90e38c94c8..34994add74 100644 --- a/LegacyComponents/PGCameraCaptureSession.m +++ b/LegacyComponents/PGCameraCaptureSession.m @@ -99,12 +99,12 @@ const NSInteger PGCameraFrameRate = 30; if (_videoInput != nil && [self canAddInput:_videoInput]) [self addInput:_videoInput]; else - TGLog(@"ERROR: camera can't add video input"); + TGLegacyLog(@"ERROR: camera can't add video input"); } else { _videoInput = nil; - TGLog(@"ERROR: camera can't create video device"); + TGLegacyLog(@"ERROR: camera can't create video device"); } if (_currentMode == PGCameraModePhoto || _currentMode == PGCameraModeSquare) @@ -132,7 +132,7 @@ const NSInteger PGCameraFrameRate = 30; else { _imageOutput = nil; - TGLog(@"ERROR: camera can't add still image output"); + TGLegacyLog(@"ERROR: camera can't add still image output"); } AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init]; @@ -149,7 +149,7 @@ const NSInteger PGCameraFrameRate = 30; else { _videoOutput = nil; - TGLog(@"ERROR: camera can't add video output"); + TGLegacyLog(@"ERROR: camera can't add video output"); } self.currentFlashMode = PGCameraFlashModeOff; @@ -712,7 +712,7 @@ const NSInteger PGCameraFrameRate = 30; [device unlockForConfiguration]; if (error != nil) - TGLog(@"ERROR: failed to reconfigure camera: %@", error); + TGLegacyLog(@"ERROR: failed to reconfigure camera: %@", error); } - (void)setFrameRate:(NSInteger)frameRate forDevice:(AVCaptureDevice *)videoDevice @@ -881,7 +881,7 @@ static UIImageOrientation TGSnapshotOrientationForVideoOrientation(bool mirrored if (!CMSampleBufferDataIsReady(sampleBuffer)) { - TGLog(@"WARNING: camera sample buffer data is not ready, skipping"); + TGLegacyLog(@"WARNING: camera sample buffer data is not ready, skipping"); return; } diff --git a/LegacyComponents/PGCameraMovieWriter.m b/LegacyComponents/PGCameraMovieWriter.m index 6f1eec4f28..f56adab50f 100644 --- a/LegacyComponents/PGCameraMovieWriter.m +++ b/LegacyComponents/PGCameraMovieWriter.m @@ -72,7 +72,7 @@ if (_assetWriter == nil && error != nil) { - TGLog(@"ERROR: camera movie writer failed to initialize: %@", error); + TGLegacyLog(@"ERROR: camera movie writer failed to initialize: %@", error); return; } @@ -86,7 +86,7 @@ } else { - TGLog(@"ERROR: camera movie writer failed to add video input"); + TGLegacyLog(@"ERROR: camera movie writer failed to add video input"); return; } @@ -100,7 +100,7 @@ } else { - TGLog(@"ERROR: camera movie writer failed to add audio input"); + TGLegacyLog(@"ERROR: camera movie writer failed to add audio input"); return; } } @@ -126,7 +126,7 @@ { if (self.finishedWithMovieAtURL != nil) self.finishedWithMovieAtURL(nil, CGAffineTransformIdentity, CGSizeZero, 0.0, false); - TGLog(@"ERROR: camera movie writer failed to write movie: %@", _assetWriter.error); + TGLegacyLog(@"ERROR: camera movie writer failed to write movie: %@", _assetWriter.error); _assetWriter = nil; }); @@ -168,7 +168,7 @@ { if (strongSelf.finishedWithMovieAtURL != nil) strongSelf.finishedWithMovieAtURL(strongSelf->_assetWriter.outputURL, CGAffineTransformIdentity, CGSizeZero, 0.0, false); - TGLog(@"ERROR: camera movie writer failed to write movie: %@", strongSelf->_assetWriter.error); + TGLegacyLog(@"ERROR: camera movie writer failed to write movie: %@", strongSelf->_assetWriter.error); } strongSelf->_assetWriter = nil; @@ -191,10 +191,10 @@ if (_assetWriter.status > AVAssetWriterStatusCompleted) { - TGLog(@"WARNING: camera movie writer status is %d", _assetWriter.status); + TGLegacyLog(@"WARNING: camera movie writer status is %d", _assetWriter.status); if (_assetWriter.status == AVAssetWriterStatusFailed) { - TGLog(@"ERROR: camera movie writer error: %@", _assetWriter.error); + TGLegacyLog(@"ERROR: camera movie writer error: %@", _assetWriter.error); _isRecording = false; if (self.finishedWithMovieAtURL != nil) @@ -224,7 +224,7 @@ if (success) _lastVideoTimeStamp = timestamp; else - TGLog(@"ERROR: camera movie writer failed to append pixel buffer"); + TGLegacyLog(@"ERROR: camera movie writer failed to append pixel buffer"); if (_audioOutputSettings != nil && _stopIminent && CMTimeCompare(_lastVideoTimeStamp, _lastAudioTimeStamp) != -1) { [self _finishWithCompletion]; @@ -261,7 +261,7 @@ if (success) _lastAudioTimeStamp = timestamp; else - TGLog(@"ERROR: camera movie writer failed to append audio buffer"); + TGLegacyLog(@"ERROR: camera movie writer failed to append audio buffer"); } } diff --git a/LegacyComponents/PSLMDBKeyValueCursor.m b/LegacyComponents/PSLMDBKeyValueCursor.m index 69f7658441..acb42e2250 100644 --- a/LegacyComponents/PSLMDBKeyValueCursor.m +++ b/LegacyComponents/PSLMDBKeyValueCursor.m @@ -58,7 +58,7 @@ } else { - TGLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); return false; } @@ -93,7 +93,7 @@ else { if (rc != MDB_NOTFOUND) - TGLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); return false; } @@ -128,7 +128,7 @@ else { if (rc != MDB_NOTFOUND) - TGLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueReader mdb_cursor_get error %d]", rc); return false; } diff --git a/LegacyComponents/PSLMDBKeyValueReaderWriter.m b/LegacyComponents/PSLMDBKeyValueReaderWriter.m index 488f1d228d..0d3462fcf6 100644 --- a/LegacyComponents/PSLMDBKeyValueReaderWriter.m +++ b/LegacyComponents/PSLMDBKeyValueReaderWriter.m @@ -55,7 +55,7 @@ else { if (rc != MDB_NOTFOUND) - TGLog(@"[PSLMDBKeyValueReader mdb_get error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueReader mdb_get error %d]", rc); return false; } @@ -79,7 +79,7 @@ rc = mdb_put(_txn, _dbi, &mdbKey, &mdbData, 0); if (rc != MDB_SUCCESS) - TGLog(@"[PSLMDBKeyValueWriter mdb_put error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueWriter mdb_put error %d]", rc); } - (void)readWithCursor:(void (^)(PSLMDBKeyValueCursor *))readWithCursorBlock @@ -97,7 +97,7 @@ mdb_cursor_close(cursor); } else - TGLog(@"[PSLMDBKeyValueWriter mdb_cursor_open error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueWriter mdb_cursor_open error %d]", rc); } - (bool)readValueBetweenLowerBoundKey:(PSConstData *)lowerBoundKey upperBoundKey:(PSConstData *)upperBoundKey selectKey:(PSKeyValueReaderSelectKey)selectKey selectedKey:(PSConstData *)selectedKey selectedValue:(PSConstData *)selectedValue @@ -245,7 +245,7 @@ rc = mdb_del(_txn, _dbi, &mdbKey, NULL); if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) - TGLog(@"[PSLMDBKeyValueWriter mdb_del error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueWriter mdb_del error %d]", rc); return rc == MDB_SUCCESS; } diff --git a/LegacyComponents/PSLMDBKeyValueStore.m b/LegacyComponents/PSLMDBKeyValueStore.m index c6cbf52ec6..36b0d6041b 100644 --- a/LegacyComponents/PSLMDBKeyValueStore.m +++ b/LegacyComponents/PSLMDBKeyValueStore.m @@ -91,7 +91,7 @@ rc = mdb_reader_check(_env, &removedReaders); if (removedReaders != 0) - TGLog(@"[PSLMDBKeyValueStore removed %d stale readers]", removedReaders); + TGLegacyLog(@"[PSLMDBKeyValueStore removed %d stale readers]", removedReaders); _table = [self _createTableWithName:@"main"]; @@ -113,7 +113,7 @@ rc = mdb_env_sync(_env, 1); if (rc != MDB_SUCCESS) - TGLog(@"[PSLMDBKeyValueStore sync: mdb_env_sync error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore sync: mdb_env_sync error %d]", rc); } - (void)panic @@ -133,11 +133,11 @@ rc = mdb_txn_begin(_env, NULL, 0, &txn); if (rc != MDB_SUCCESS) { - TGLog(@"[PSLMDBKeyValueStore transaction begin failed %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore transaction begin failed %d]", rc); if (rc == MDB_PANIC) { - TGLog(@"[PSLMDBKeyValueStore critical error received]"); + TGLegacyLog(@"[PSLMDBKeyValueStore critical error received]"); [self panic]; } @@ -150,7 +150,7 @@ { mdb_txn_abort(txn); - TGLog(@"[PSLMDBKeyValueStore mdb_dbi_open failed %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore mdb_dbi_open failed %d]", rc); } else { @@ -178,11 +178,11 @@ rc = mdb_txn_begin(_env, NULL, MDB_RDONLY, &txn); if (rc != MDB_SUCCESS) { - TGLog(@"[PSLMDBKeyValueStore mdb_txn_begin failed %d", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore mdb_txn_begin failed %d", rc); if (rc == MDB_PANIC) { - TGLog(@"[PSLMDBKeyValueStore critical error received]"); + TGLegacyLog(@"[PSLMDBKeyValueStore critical error received]"); [self panic]; } @@ -194,7 +194,7 @@ rc = mdb_txn_commit(txn); if (rc != MDB_SUCCESS) - TGLog(@"[PSLMDBKeyValueStore mdb_txn_commit error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore mdb_txn_commit error %d]", rc); } } } @@ -213,11 +213,11 @@ rc = mdb_txn_begin(_env, NULL, 0, &txn); if (rc != MDB_SUCCESS) { - TGLog(@"[PSLMDBKeyValueStore mdb_txn_begin failed %d", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore mdb_txn_begin failed %d", rc); if (rc == MDB_PANIC) { - TGLog(@"[PSLMDBKeyValueStore critical error received]"); + TGLegacyLog(@"[PSLMDBKeyValueStore critical error received]"); [self panic]; } @@ -229,7 +229,7 @@ rc = mdb_txn_commit(txn); if (rc != MDB_SUCCESS) - TGLog(@"[PSLMDBKeyValueStore mdb_txn_commit error %d]", rc); + TGLegacyLog(@"[PSLMDBKeyValueStore mdb_txn_commit error %d]", rc); } } } diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutAccessory@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutAccessory@2x.png new file mode 100644 index 0000000000..636dbe1100 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutAccessory@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow@2x.png new file mode 100644 index 0000000000..710487e0db Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow_Highlighted@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow_Highlighted@2x.png new file mode 100644 index 0000000000..ea5c91b128 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutArrow_Highlighted@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground@2x.png new file mode 100644 index 0000000000..d30a7daf77 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground_Highlighted@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground_Highlighted@2x.png new file mode 100644 index 0000000000..72ed8be57d Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutBackground_Highlighted@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground@2x.png new file mode 100644 index 0000000000..1812b32a09 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground_Highlighted@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground_Highlighted@2x.png new file mode 100644 index 0000000000..2697187254 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingBackground_Highlighted@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingIcon@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingIcon@2x.png new file mode 100644 index 0000000000..6eb9fa6052 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/CalloutDrivingIcon@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/FoursquareAttribution@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/FoursquareAttribution@2x.png new file mode 100644 index 0000000000..6b2650f7a3 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/FoursquareAttribution@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon.png new file mode 100644 index 0000000000..e89cd9a9d2 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@2x.png new file mode 100644 index 0000000000..4011081a50 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@3x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@3x.png new file mode 100644 index 0000000000..c18d240631 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationCurrentIcon@3x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationGenericIcon@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationGenericIcon@2x.png new file mode 100644 index 0000000000..1012166beb Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationGenericIcon@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo.png new file mode 100644 index 0000000000..5b1496cd6b Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@2x.png new file mode 100644 index 0000000000..716cb05675 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@3x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@3x.png new file mode 100644 index 0000000000..eb7805e81c Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo@3x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active.png new file mode 100644 index 0000000000..d8470628c4 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@2x.png new file mode 100644 index 0000000000..36fdadba9d Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@3x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@3x.png new file mode 100644 index 0000000000..f33d19f9be Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationInfo_Active@3x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPin@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPin@2x.png new file mode 100644 index 0000000000..709d3b08f9 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPin@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon.png new file mode 100644 index 0000000000..f9f82eb054 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@2x.png new file mode 100644 index 0000000000..479018d03f Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@3x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@3x.png new file mode 100644 index 0000000000..a9c7628e35 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinIcon@3x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinPoint@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinPoint@2x.png new file mode 100644 index 0000000000..7584de8b0f Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinPoint@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinShadow@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinShadow@2x.png new file mode 100644 index 0000000000..3bc04a4782 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/LocationPinShadow@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingHeading@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingHeading@2x.png new file mode 100644 index 0000000000..6ac33deeb2 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingHeading@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocation@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocation@2x.png new file mode 100644 index 0000000000..0999132115 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocation@2x.png differ diff --git a/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocationOff@2x.png b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocationOff@2x.png new file mode 100644 index 0000000000..a32fdbfb28 Binary files /dev/null and b/LegacyComponents/Resources/LegacyComponentsResources.bundle/TrackingLocationOff@2x.png differ diff --git a/LegacyComponents/TGCache.m b/LegacyComponents/TGCache.m index 3f119f3883..0b6d74fa29 100644 --- a/LegacyComponents/TGCache.m +++ b/LegacyComponents/TGCache.m @@ -236,7 +236,7 @@ static NSFileManager *cacheFileManager = nil; if (self.memoryCacheSize < 0) self.memoryCacheSize = 0; [_memoryCache removeObjectForKey:key]; - //TGLog(@"evict %@", key); + //TGLegacyLog(@"evict %@", key); } __block int currentCacheSize = 0; @@ -247,7 +247,7 @@ static NSFileManager *cacheFileManager = nil; self.memoryCacheSize = currentCacheSize; - //TGLog(@"TGCache: freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - self.memoryCacheSize) / 1024), (int)(self.memoryCacheSize / 1024)); + //TGLegacyLog(@"TGCache: freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - self.memoryCacheSize) / 1024), (int)(self.memoryCacheSize / 1024)); } }; if ([NSThread isMainThread]) @@ -305,7 +305,7 @@ static NSFileManager *cacheFileManager = nil; _dataMemoryCacheSize = 0; [_dataMemoryCache removeObjectForKey:key]; } - //TGLog(@"TGCache (compressed): freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - _dataMemoryCacheSize) / 1024), (int)(_dataMemoryCacheSize / 1024)); + //TGLegacyLog(@"TGCache (compressed): freed %d kbytes (cache size: %d kbytes)", (int)((sizeBefore - _dataMemoryCacheSize) / 1024), (int)(_dataMemoryCacheSize / 1024)); } } if (!reentrant) @@ -348,7 +348,7 @@ static NSFileManager *cacheFileManager = nil; { #ifdef DEBUG if (data != nil) - TGLog(@"cache image %d bytes with url %@", (int)data.length, url); + TGLegacyLog(@"cache image %d bytes with url %@", (int)data.length, url); #endif if (image != nil && (availability & TGCacheMemory)) @@ -417,7 +417,7 @@ static NSFileManager *cacheFileManager = nil; for (NSDictionary *dict in _temporaryCachedImagesSources) { UIImage *image = [dict objectForKey:url]; - //TGLog(@"From temp cache %@", url); + //TGLegacyLog(@"From temp cache %@", url); if (image != nil) { blockImage = image; @@ -600,9 +600,9 @@ static NSFileManager *cacheFileManager = nil; _thumbnailCacheSize += size; } - //TGLog(@"Cache thumbnail: %@", url); + //TGLegacyLog(@"Cache thumbnail: %@", url); - //TGLog(@"_thumbnailCacheSize = %d", _thumbnailCacheSize); + //TGLegacyLog(@"_thumbnailCacheSize = %d", _thumbnailCacheSize); if (_thumbnailCacheSize >= _thumbnailMemoryLimit + _thumbnailEvictionInterval) [self freeThumbnailCache:_thumbnailMemoryLimit]; @@ -628,7 +628,7 @@ static NSFileManager *cacheFileManager = nil; } else { - //TGLog(@"Thumbnail not found: %@", url); + //TGLegacyLog(@"Thumbnail not found: %@", url); } }; if ([NSThread isMainThread]) @@ -693,7 +693,7 @@ static NSFileManager *cacheFileManager = nil; - (void)changeCacheItemUrl:(NSString *)oldUrl newUrl:(NSString *)newUrl { - //TGLog(@"TGCache: rename \"%@\" -> \"%@\"", oldUrl, newUrl); + //TGLegacyLog(@"TGCache: rename \"%@\" -> \"%@\"", oldUrl, newUrl); dispatch_block_t block = ^ { TGCacheRecord *record = [_memoryCache objectForKey:oldUrl]; @@ -713,13 +713,13 @@ static NSFileManager *cacheFileManager = nil; NSError *error = nil; [cacheFileManager moveItemAtPath:[_diskCachePath stringByAppendingPathComponent:md5String(oldUrl)] toPath:[_diskCachePath stringByAppendingPathComponent:md5String(newUrl)] error:&error]; if (error != nil) - TGLog(@"Failed to move: %@", error); + TGLegacyLog(@"Failed to move: %@", error); }); } - (void)moveToCache:(NSString *)fileUrl cacheUrl:(NSString *)cacheUrl { - TGLog(@"TGCache: move \"%@\" -> \"%@\"", fileUrl, cacheUrl); + TGLegacyLog(@"TGCache: move \"%@\" -> \"%@\"", fileUrl, cacheUrl); dispatch_block_t block = ^ { TGCacheRecord *record = [_memoryCache objectForKey:fileUrl]; @@ -739,7 +739,7 @@ static NSFileManager *cacheFileManager = nil; NSError *error = nil; [cacheFileManager moveItemAtPath:fileUrl toPath:[_diskCachePath stringByAppendingPathComponent:md5String(cacheUrl)] error:&error]; if (error != nil) - TGLog(@"Failed to move: %@", error); + TGLegacyLog(@"Failed to move: %@", error); }); } @@ -781,7 +781,7 @@ static NSFileManager *cacheFileManager = nil; failedCount++; } - TGLog(@"TGCache: removed %d files (%d failed)", removedCount, failedCount); + TGLegacyLog(@"TGCache: removed %d files (%d failed)", removedCount, failedCount); }); } } @@ -812,7 +812,7 @@ static NSFileManager *cacheFileManager = nil; { [self cachedImage:url availability:TGCacheDisk]; } - TGLog(@"Cache restored in %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); + TGLegacyLog(@"Cache restored in %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); } @end diff --git a/LegacyComponents/TGConversation.m b/LegacyComponents/TGConversation.m index ccfc9b6a77..791dfc583d 100644 --- a/LegacyComponents/TGConversation.m +++ b/LegacyComponents/TGConversation.m @@ -64,7 +64,7 @@ if (version != 1 && version != 2 && version != 3) { - TGLog(@"***** Invalid encrypted data version"); + TGLegacyLog(@"***** Invalid encrypted data version"); return nil; } @@ -265,7 +265,7 @@ { if (ptr + 4 > length) { - TGLog(@"***** Invalid participants data"); + TGLegacyLog(@"***** Invalid participants data"); return nil; } @@ -275,7 +275,7 @@ if (ptr + 4 > length) { - TGLog(@"***** Invalid participants data"); + TGLegacyLog(@"***** Invalid participants data"); return nil; } int inviter = 0; @@ -284,7 +284,7 @@ if (ptr + 4 > length) { - TGLog(@"***** Invalid participants data"); + TGLegacyLog(@"***** Invalid participants data"); return nil; } int date = 0; @@ -310,7 +310,7 @@ { if (ptr + 8 > length) { - TGLog(@"***** Invalid participants data"); + TGLegacyLog(@"***** Invalid participants data"); return nil; } @@ -334,7 +334,7 @@ { if (ptr + 8 > length) { - TGLog(@"***** Invalid participants data"); + TGLegacyLog(@"***** Invalid participants data"); return nil; } diff --git a/LegacyComponents/TGDateUtils.mm b/LegacyComponents/TGDateUtils.mm index 56d485b8a8..a53ea67901 100644 --- a/LegacyComponents/TGDateUtils.mm +++ b/LegacyComponents/TGDateUtils.mm @@ -517,11 +517,11 @@ static inline NSString *dialogTimeFormat() return TGLocalized(@"LastSeen.JustNow"); else if (minutesDiff < 60) { - return [effectiveLocalization() getPluralized:@"LastSeen.MinutesAgo" count:(int32_t)minutesDiff]; + return [legacyEffectiveLocalization() getPluralized:@"LastSeen.MinutesAgo" count:(int32_t)minutesDiff]; } else { - return [effectiveLocalization() getPluralized:@"LastSeen.HoursAgo" count:(int32_t)hoursDiff]; + return [legacyEffectiveLocalization() getPluralized:@"LastSeen.HoursAgo" count:(int32_t)hoursDiff]; } } else if (dayDiff == 0 || dayDiff == -1) diff --git a/LegacyComponents/TGDocumentMediaAttachment.m b/LegacyComponents/TGDocumentMediaAttachment.m index d6b21a3624..9a1b0abae5 100644 --- a/LegacyComponents/TGDocumentMediaAttachment.m +++ b/LegacyComponents/TGDocumentMediaAttachment.m @@ -175,7 +175,7 @@ [is read:&version maxLength:sizeof(version)]; if (version != 1 && version != 2 && version != 3 && version != 4 && version != 5 && version != 6) { - TGLog(@"***** Document serialized version mismatch"); + TGLegacyLog(@"***** Document serialized version mismatch"); return nil; } diff --git a/LegacyComponents/TGEmbedCoubPlayerView.m b/LegacyComponents/TGEmbedCoubPlayerView.m index 1f56bd2e8d..4561e89ba2 100644 --- a/LegacyComponents/TGEmbedCoubPlayerView.m +++ b/LegacyComponents/TGEmbedCoubPlayerView.m @@ -161,7 +161,7 @@ NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[CoubEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[CoubEmbedPlayer]: Received error rendering template: %@", error); return nil; } @@ -289,7 +289,7 @@ - (void)playerDidFail:(CBCoubPlayer *)__unused player error:(NSError *)error { - TGLog(@"[CoubPlayer] ERROR: %@", error.localizedDescription); + TGLegacyLog(@"[CoubPlayer] ERROR: %@", error.localizedDescription); } - (void)playerDidStop:(CBCoubPlayer *)__unused player diff --git a/LegacyComponents/TGEmbedInstagramPlayerView.m b/LegacyComponents/TGEmbedInstagramPlayerView.m index 22bc77a484..20d4c341d2 100644 --- a/LegacyComponents/TGEmbedInstagramPlayerView.m +++ b/LegacyComponents/TGEmbedInstagramPlayerView.m @@ -91,7 +91,7 @@ NSString *const TGInstagramPlayerCallbackOnPlayback = @"onPlayback"; NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[InstagramEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[InstagramEmbedPlayer]: Received error rendering template: %@", error); return nil; } diff --git a/LegacyComponents/TGEmbedPlayerView.m b/LegacyComponents/TGEmbedPlayerView.m index a270f9d342..bb0f958a44 100644 --- a/LegacyComponents/TGEmbedPlayerView.m +++ b/LegacyComponents/TGEmbedPlayerView.m @@ -757,7 +757,7 @@ NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[DefaultEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[DefaultEmbedPlayer]: Received error rendering template: %@", error); return nil; } @@ -781,7 +781,7 @@ NSString *path = TGComponentsPathForResource(@"DefaultPlayerInject", @"js"); NSString *scriptText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) - TGLog(@"[DefaultEmbedPlayer]: Received error loading inject script: %@", error); + TGLegacyLog(@"[DefaultEmbedPlayer]: Received error loading inject script: %@", error); WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptText injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false]; [contentController addUserScript:script]; diff --git a/LegacyComponents/TGEmbedVimeoPlayerView.m b/LegacyComponents/TGEmbedVimeoPlayerView.m index 8d0ea459c3..f16b18eebc 100644 --- a/LegacyComponents/TGEmbedVimeoPlayerView.m +++ b/LegacyComponents/TGEmbedVimeoPlayerView.m @@ -153,7 +153,7 @@ NSString *const TGVimeoPlayerCallbackOnState = @"onState"; NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[VimeoEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[VimeoEmbedPlayer]: Received error rendering template: %@", error); return nil; } @@ -173,7 +173,7 @@ NSString *const TGVimeoPlayerCallbackOnState = @"onState"; NSString *path = TGComponentsPathForResource(@"VimeoPlayerInject", @"js"); NSString *scriptText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) - TGLog(@"[VimeoEmbedPlayer]: Received error loading inject script: %@", error); + TGLegacyLog(@"[VimeoEmbedPlayer]: Received error loading inject script: %@", error); WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptText injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false]; [contentController addUserScript:script]; diff --git a/LegacyComponents/TGEmbedVinePlayerView.m b/LegacyComponents/TGEmbedVinePlayerView.m index 7581fed87b..9e70127c86 100644 --- a/LegacyComponents/TGEmbedVinePlayerView.m +++ b/LegacyComponents/TGEmbedVinePlayerView.m @@ -180,7 +180,7 @@ NSString *const TGVinePlayerCallbackOnPlayback = @"onPlayback"; NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[VineEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[VineEmbedPlayer]: Received error rendering template: %@", error); return nil; } @@ -199,7 +199,7 @@ NSString *const TGVinePlayerCallbackOnPlayback = @"onPlayback"; NSString *path = TGComponentsPathForResource(@"VinePlayerInject", @"js"); NSString *scriptText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) - TGLog(@"[VineEmbedPlayer]: Received error loading inject script: %@", error); + TGLegacyLog(@"[VineEmbedPlayer]: Received error loading inject script: %@", error); WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptText injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false]; [contentController addUserScript:script]; diff --git a/LegacyComponents/TGEmbedYoutubePlayerView.m b/LegacyComponents/TGEmbedYoutubePlayerView.m index 3bdfc5ae86..dae16864ee 100644 --- a/LegacyComponents/TGEmbedYoutubePlayerView.m +++ b/LegacyComponents/TGEmbedYoutubePlayerView.m @@ -256,7 +256,7 @@ const NSInteger TGYTPlayerStateBufferingCode = 3; NSString *embedHTMLTemplate = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) { - TGLog(@"[YTEmbedPlayer]: Received error rendering template: %@", error); + TGLegacyLog(@"[YTEmbedPlayer]: Received error rendering template: %@", error); return nil; } @@ -286,7 +286,7 @@ const NSInteger TGYTPlayerStateBufferingCode = 3; NSString *path = TGComponentsPathForResource(@"YoutubePlayerInject", @"js"); NSString *scriptText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; if (error != nil) - TGLog(@"[YTEmbedPlayer]: Received error loading inject script: %@", error); + TGLegacyLog(@"[YTEmbedPlayer]: Received error loading inject script: %@", error); WKUserScript *script = [[WKUserScript alloc] initWithSource:scriptText injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false]; [contentController addUserScript:script]; diff --git a/LegacyComponents/TGGifConverter.m b/LegacyComponents/TGGifConverter.m index 81a2314137..4566e27a5e 100644 --- a/LegacyComponents/TGGifConverter.m +++ b/LegacyComponents/TGGifConverter.m @@ -170,7 +170,7 @@ const CGFloat TGGifConverterMaximumSide = 720.0f; if (![adaptor appendPixelBuffer:pxBuffer withPresentationTime:time]) { - TGLog(@"Could not save pixel buffer!: %@", videoWriter.error); + TGLegacyLog(@"Could not save pixel buffer!: %@", videoWriter.error); CFRelease(properties); CGImageRelease(imgRef); CVBufferRelease(pxBuffer); diff --git a/LegacyComponents/TGHacks.m b/LegacyComponents/TGHacks.m index cdad88c928..626ba9fb53 100644 --- a/LegacyComponents/TGHacks.m +++ b/LegacyComponents/TGHacks.m @@ -381,7 +381,7 @@ static UIView *findStatusBarView() NSMethodSignature *signature = [statusBarClass instanceMethodSignatureForSelector:selector]; if (signature == nil) { - TGLog(@"***** Method not found"); + TGLegacyLog(@"***** Method not found"); return 20.0f; } @@ -397,7 +397,7 @@ static UIView *findStatusBarView() NSMethodSignature *signature2 = [statusBarClass methodSignatureForSelector:selector2]; if (signature2 == nil) { - TGLog(@"***** Method not found"); + TGLegacyLog(@"***** Method not found"); return 20.0f; } NSInvocation *inv2 = [NSInvocation invocationWithMethodSignature:signature2]; @@ -458,7 +458,7 @@ static bool keyboardHidden = true; SEL selector = NSSelectorFromString(TGEncodeText(@"tj{fGpsJoufsgbdfPsjfoubujpo;", -1)); NSMethodSignature *signature = [keyboardClass methodSignatureForSelector:selector]; if (signature == nil) - TGLog(@"***** Method not found"); + TGLegacyLog(@"***** Method not found"); else { invocation = [NSInvocation invocationWithMethodSignature:signature]; diff --git a/LegacyComponents/TGImageBlur.m b/LegacyComponents/TGImageBlur.m index 44e71e3477..8b1b9637f6 100644 --- a/LegacyComponents/TGImageBlur.m +++ b/LegacyComponents/TGImageBlur.m @@ -330,7 +330,7 @@ static void computeImageVariance(uint8_t *memory, int width, int height, int str n1 += floatHistogram[i]; } - //TGLog(@"histogram: [%f %f %f %f %f %f %f %f %f %f]", floatHistogram[0], floatHistogram[1], floatHistogram[2], floatHistogram[3], floatHistogram[4], floatHistogram[5], floatHistogram[6], floatHistogram[7], floatHistogram[8], floatHistogram[9]); + //TGLegacyLog(@"histogram: [%f %f %f %f %f %f %f %f %f %f]", floatHistogram[0], floatHistogram[1], floatHistogram[2], floatHistogram[3], floatHistogram[4], floatHistogram[5], floatHistogram[6], floatHistogram[7], floatHistogram[8], floatHistogram[9]); if (outLuminance != NULL) *outLuminance = n0 < n1 ? 0.95f : 0.5f; diff --git a/LegacyComponents/TGImageManager.m b/LegacyComponents/TGImageManager.m index c5efdb1d61..0d0ea6d3d6 100644 --- a/LegacyComponents/TGImageManager.m +++ b/LegacyComponents/TGImageManager.m @@ -248,7 +248,7 @@ static UIImage *forceImageDecoding(UIImage *image) } else { - TGLog(@"[TGImageManager#%p Data source not found for URI: %@]", self, uri); + TGLegacyLog(@"[TGImageManager#%p Data source not found for URI: %@]", self, uri); if (completion) completion(nil); diff --git a/LegacyComponents/TGImagePickerController.mm b/LegacyComponents/TGImagePickerController.mm index ec9c5db5ad..960b67538b 100644 --- a/LegacyComponents/TGImagePickerController.mm +++ b/LegacyComponents/TGImagePickerController.mm @@ -49,7 +49,7 @@ void sharedAssetsLibraryRetain() if (sharedLibrary == nil) { - TGLog(@"Preloading shared assets library"); + TGLegacyLog(@"Preloading shared assets library"); sharedLibraryRetainCount = 1; sharedLibrary = [[ALAssetsLibrary alloc] init]; @@ -68,7 +68,7 @@ void sharedAssetsLibraryRetain() } } failureBlock:^(__unused NSError *error) { - TGLog(@"assets access error"); + TGLegacyLog(@"assets access error"); }]; } else @@ -95,7 +95,7 @@ void sharedAssetsLibraryRelease() { sharedLibraryReleaseTimer = nil; - TGLog(@"Destroyed shared assets library"); + TGLegacyLog(@"Destroyed shared assets library"); sharedLibrary = nil; } nativeQueue:assetsProcessingQueue()]; [sharedLibraryReleaseTimer start]; diff --git a/LegacyComponents/TGImageUtils.mm b/LegacyComponents/TGImageUtils.mm index 8948eba240..5401aa09d8 100644 --- a/LegacyComponents/TGImageUtils.mm +++ b/LegacyComponents/TGImageUtils.mm @@ -224,7 +224,7 @@ UIImage *TGScaleAndBlurImage(NSData *data, __unused CGSize size, __autoreleasing if (blurredData != NULL) *blurredData = UIImageJPEGRepresentation(returnImage, 0.6f); - TGLog(@"Blur time: %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); + TGLegacyLog(@"Blur time: %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); return returnImage; } diff --git a/LegacyComponents/TGImageView.m b/LegacyComponents/TGImageView.m index 9a83a050bd..ea31da5d0b 100644 --- a/LegacyComponents/TGImageView.m +++ b/LegacyComponents/TGImageView.m @@ -99,7 +99,7 @@ NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous"; if (strongSelf != nil && strongSelf->_version == version) [strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(CACurrentMediaTime() - loadStartTime)]; else - TGLog(@"[TGImageView _commitImage version mismatch]"); + TGLegacyLog(@"[TGImageView _commitImage version mismatch]"); }); } completion:^(UIImage *image) { @@ -113,7 +113,7 @@ NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous"; [strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(CACurrentMediaTime() - loadStartTime)]; } else - TGLog(@"[TGImageView _commitImage version mismatch]"); + TGLegacyLog(@"[TGImageView _commitImage version mismatch]"); }); }]; @@ -161,7 +161,7 @@ NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous"; if (strongSelf != nil && strongSelf->_version == version) [strongSelf _commitImage:partialImage partial:true loadTime:(NSTimeInterval)(CACurrentMediaTime() - loadStartTime)]; else - TGLog(@"[TGImageView _commitImage version mismatch]"); + TGLegacyLog(@"[TGImageView _commitImage version mismatch]"); }); } completion:^(UIImage *image) { @@ -175,7 +175,7 @@ NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous"; [strongSelf _commitImage:image partial:false loadTime:(NSTimeInterval)(CACurrentMediaTime() - loadStartTime)]; } else - TGLog(@"[TGImageView _commitImage version mismatch]"); + TGLegacyLog(@"[TGImageView _commitImage version mismatch]"); }); }]; } @@ -319,7 +319,7 @@ NSString *TGImageViewOptionSynchronous = @"TGImageViewOptionSynchronous"; }); } error:^(id error) { - TGLog(@"TGImageView signal error: %@", error); + TGLegacyLog(@"TGImageView signal error: %@", error); } completed:^ { }]]; diff --git a/LegacyComponents/TGLegacyCameraController.m b/LegacyComponents/TGLegacyCameraController.m index c74e6ff087..20090dfd0b 100644 --- a/LegacyComponents/TGLegacyCameraController.m +++ b/LegacyComponents/TGLegacyCameraController.m @@ -301,7 +301,7 @@ if (_storeCapturedAssets && [referenceUrl absoluteString].length != 0) { assetHash = [[NSString alloc] initWithFormat:@"%@", [referenceUrl absoluteString]]; - TGLog(@"Video hash: %@", assetHash); + TGLegacyLog(@"Video hash: %@", assetHash); } bool deleteFile = true; @@ -363,7 +363,7 @@ break; case AVAssetExportSessionStatusCompleted: { - TGLog(@"Export mp4 completed"); + TGLegacyLog(@"Export mp4 completed"); endProcessing = true; success = true; @@ -466,7 +466,7 @@ { [[NSFileManager defaultManager] removeItemAtPath:videoPath error:nil]; if (error != nil) - TGLog(@"Video saving error: %@", error); + TGLegacyLog(@"Video saving error: %@", error); } - (void)actionStageActionRequested:(NSString *)action options:(id)options diff --git a/LegacyComponents/TGListsTableView.h b/LegacyComponents/TGListsTableView.h new file mode 100644 index 0000000000..055aa629b6 --- /dev/null +++ b/LegacyComponents/TGListsTableView.h @@ -0,0 +1,11 @@ +#import + +@interface TGListsTableView : UITableView + +@property (nonatomic, assign) bool blockContentOffset; + +@property (nonatomic, copy) void (^onHitTest)(CGPoint); + +- (void)adjustBehaviour; + +@end diff --git a/LegacyComponents/TGListsTableView.m b/LegacyComponents/TGListsTableView.m new file mode 100644 index 0000000000..46cc95e7bc --- /dev/null +++ b/LegacyComponents/TGListsTableView.m @@ -0,0 +1,125 @@ +#import "TGListsTableView.h" + +#import "LegacyComponentsInternal.h" +#import "Freedom.h" + +#import "TGSearchBar.h" + +#import + +@interface TGListsTableView () +{ + UIView *_whiteFooterView; + bool _hackHeaderSize; +} + +@end + +@implementation TGListsTableView + +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style +{ + self = [super initWithFrame:frame style:style]; + if (self != nil) + { + if (iosMajorVersion() < 7) + { + self.backgroundView = [[UIView alloc] init]; + self.backgroundView.backgroundColor = [UIColor whiteColor]; + } + else + { + _whiteFooterView = [[UIView alloc] init]; + _whiteFooterView.backgroundColor = [UIColor whiteColor]; + [self insertSubview:_whiteFooterView atIndex:0]; + } + } + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGRect bounds = self.bounds; + + if (_whiteFooterView != nil) + _whiteFooterView.frame = CGRectMake(0.0f, MAX(0.0f, bounds.origin.y), bounds.size.width, bounds.size.height); + else + self.backgroundView.frame = CGRectMake(0.0f, MAX(0.0f, bounds.origin.y), bounds.size.width, bounds.size.height); + + if (_hackHeaderSize) + { + UIView *tableHeaderView = self.tableHeaderView; + if (tableHeaderView != nil) + { + CGSize size = self.frame.size; + + CGRect frame = tableHeaderView.frame; + if (frame.size.width < size.width) + { + frame.size.width = size.width; + tableHeaderView.frame = frame; + } + } + } + + UIView *tableHeaderView = self.tableHeaderView; + if (tableHeaderView != nil && [tableHeaderView respondsToSelector:@selector(updateClipping:)]) + { + [(TGSearchBar *)tableHeaderView updateClipping:bounds.origin.y + self.contentInset.top]; + } +} + +- (void)didAddSubview:(UIView *)subview +{ + [super didAddSubview:subview]; + + if (iosMajorVersion() >= 7) + { + static Class indexClass = Nil; + static ptrdiff_t backgroundColorPtr = -1; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + indexClass = freedomClass(0xd93a1ed6U); + if (indexClass != Nil) + backgroundColorPtr = freedomIvarOffset(indexClass, 0xca7e3046U); + }); + + if ([subview isKindOfClass:indexClass] && backgroundColorPtr >= 0) + { + __strong UIColor **backgroundColor = (__strong UIColor **)(void *)(((uint8_t *)(__bridge void *)subview) + backgroundColorPtr); + *backgroundColor = [UIColor clearColor]; + } + } +} + +- (void)hackHeaderSize +{ + _hackHeaderSize = true; +} + +- (void)adjustBehaviour +{ + //FreedomBitfield tableFlagsOffset = freedomIvarBitOffset([UITableView class], 0x3fa93ecU, 0xe3ca73b1U); + //if (tableFlagsOffset.offset != -1 && tableFlagsOffset.bit != -1) + // freedomSetBitfield((__bridge void *)self, tableFlagsOffset, 1); +} + +- (void)setContentOffset:(CGPoint)contentOffset +{ + if (_blockContentOffset) + return; + + [super setContentOffset:contentOffset]; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + if (_onHitTest) { + _onHitTest(point); + } + return [super hitTest:point withEvent:event]; +} + +@end diff --git a/LegacyComponents/TGLocationAnnotation.h b/LegacyComponents/TGLocationAnnotation.h new file mode 100644 index 0000000000..f944a307b6 --- /dev/null +++ b/LegacyComponents/TGLocationAnnotation.h @@ -0,0 +1,12 @@ +#import + +@interface TGLocationAnnotation : NSObject + +@property (nonatomic, assign) CLLocationCoordinate2D coordinate; +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *subtitle; +@property (nonatomic, strong) NSDictionary *userInfo; + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title; + +@end diff --git a/LegacyComponents/TGLocationAnnotation.m b/LegacyComponents/TGLocationAnnotation.m new file mode 100644 index 0000000000..8b21b74ac3 --- /dev/null +++ b/LegacyComponents/TGLocationAnnotation.m @@ -0,0 +1,22 @@ +#import "TGLocationAnnotation.h" + +@implementation TGLocationAnnotation + +- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title +{ + self = [super init]; + if (self != nil) + { + _coordinate = coordinate; + self.title = title; + self.subtitle = nil; + } + return self; +} + +- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate +{ + _coordinate = newCoordinate; +} + +@end diff --git a/LegacyComponents/TGLocationCurrentLocationCell.h b/LegacyComponents/TGLocationCurrentLocationCell.h new file mode 100644 index 0000000000..c9ef475b5c --- /dev/null +++ b/LegacyComponents/TGLocationCurrentLocationCell.h @@ -0,0 +1,12 @@ +#import +#import + +@interface TGLocationCurrentLocationCell : UITableViewCell + +- (void)configureForCurrentLocationWithAccuracy:(CLLocationAccuracy)accuracy; +- (void)configureForCustomLocationWithAddress:(NSString *)address; + +@end + +extern NSString *const TGLocationCurrentLocationCellKind; +extern const CGFloat TGLocationCurrentLocationCellHeight; diff --git a/LegacyComponents/TGLocationCurrentLocationCell.m b/LegacyComponents/TGLocationCurrentLocationCell.m new file mode 100644 index 0000000000..d9c15fa55a --- /dev/null +++ b/LegacyComponents/TGLocationCurrentLocationCell.m @@ -0,0 +1,160 @@ +#import "TGLocationCurrentLocationCell.h" + +#import "LegacyComponentsInternal.h" +#import "TGColor.h" +#import "TGImageUtils.h" +#import "TGFont.h" + +#import "TGLocationUtils.h" + +NSString *const TGLocationCurrentLocationCellKind = @"TGLocationCurrentLocationCellKind"; +const CGFloat TGLocationCurrentLocationCellHeight = 57; + +@interface TGLocationCurrentLocationCell () +{ + UIImageView *_iconView; + UILabel *_titleLabel; + UILabel *_subtitleLabel; + + bool _isCurrentLocation; +} +@end + +@implementation TGLocationCurrentLocationCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + self.selectedBackgroundView = [[UIView alloc] init]; + self.selectedBackgroundView.backgroundColor = TGSelectionColor(); + + _iconView = [[UIImageView alloc] initWithFrame:CGRectMake(14 + TGRetinaPixel, 8 + TGRetinaPixel, 40, 40)]; + _iconView.image = TGComponentsImageNamed(@"LocationCurrentIcon.png"); + [self.contentView addSubview:_iconView]; + + _titleLabel = [[UILabel alloc] init]; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.font = TGBoldSystemFontOfSize(TGIsRetina() ? 16.5f : 16.0f); + _titleLabel.text = TGLocalized(@"Map.SendMyCurrentLocation"); + _titleLabel.textColor = TGAccentColor(); + [self.contentView addSubview:_titleLabel]; + + _subtitleLabel = [[UILabel alloc] init]; + _subtitleLabel.backgroundColor = [UIColor clearColor]; + _subtitleLabel.font = TGSystemFontOfSize(13); + _subtitleLabel.text = TGLocalized(@"Map.Locating"); + _subtitleLabel.textColor = UIColorRGB(0xa6a6a6); + [self.contentView addSubview:_subtitleLabel]; + + _isCurrentLocation = true; + } + return self; +} + +- (void)configureForCurrentLocationWithAccuracy:(CLLocationAccuracy)accuracy +{ + if (!_isCurrentLocation) + { + [UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^ + { + _titleLabel.text = TGLocalized(@"Map.SendMyCurrentLocation"); + _iconView.image = TGComponentsImageNamed(@"LocationCurrentIcon.png"); + + if (accuracy > DBL_EPSILON) + { + NSString *accuracyString = [TGLocationUtils stringFromAccuracy:(NSInteger)accuracy]; + _subtitleLabel.text = [NSString stringWithFormat:TGLocalized(@"Map.AccurateTo"), accuracyString]; + + _iconView.alpha = 1.0f; + _titleLabel.alpha = 1.0f; + _subtitleLabel.alpha = 1.0f; + } + else + { + _subtitleLabel.text = TGLocalized(@"Map.Locating"); + + _iconView.alpha = 0.5f; + _titleLabel.alpha = 0.5f; + _subtitleLabel.alpha = 0.5f; + } + } completion:nil]; + + _isCurrentLocation = true; + } + else + { + if (accuracy > DBL_EPSILON) + { + NSString *accuracyString = [TGLocationUtils stringFromAccuracy:(NSInteger)accuracy]; + _subtitleLabel.text = [NSString stringWithFormat:TGLocalized(@"Map.AccurateTo"), accuracyString]; + + [UIView animateWithDuration:0.2f animations:^ + { + _iconView.alpha = 1.0f; + _titleLabel.alpha = 1.0f; + _subtitleLabel.alpha = 1.0f; + }]; + } + else + { + _subtitleLabel.text = TGLocalized(@"Map.Locating"); + + _iconView.alpha = 0.5f; + _titleLabel.alpha = 0.5f; + _subtitleLabel.alpha = 0.5f; + } + } +} + +- (void)configureForCustomLocationWithAddress:(NSString *)address +{ + if (_isCurrentLocation) + { + [UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^ + { + _titleLabel.text = TGLocalized(@"Map.SendThisLocation"); + _iconView.image = TGComponentsImageNamed(@"LocationPinIcon.png"); + _subtitleLabel.text = [self _subtitleForAddress:address]; + + _iconView.alpha = 1.0f; + _titleLabel.alpha = 1.0f; + _subtitleLabel.alpha = 1.0f; + } completion:nil]; + + _isCurrentLocation = false; + } + else + { + [UIView transitionWithView:self duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:^ + { + _subtitleLabel.text = [self _subtitleForAddress:address]; + } completion:nil]; + } +} + +- (NSString *)_subtitleForAddress:(NSString *)address +{ + if (address != nil && address.length == 0) + { + return TGLocalized(@"Map.Unknown"); + } + else if (address == nil) + { + return TGLocalized(@"Map.Locating"); + } + + return address; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGFloat padding = 65.0f; + _titleLabel.frame = CGRectMake(padding, 9, self.frame.size.width - padding - 14, 20); + _subtitleLabel.frame = CGRectMake(padding, 29 + TGRetinaPixel, self.frame.size.width - padding - 14, 20); +} + +@end diff --git a/LegacyComponents/TGLocationMapModeControl.h b/LegacyComponents/TGLocationMapModeControl.h new file mode 100644 index 0000000000..52d228e358 --- /dev/null +++ b/LegacyComponents/TGLocationMapModeControl.h @@ -0,0 +1,5 @@ +#import + +@interface TGLocationMapModeControl : UISegmentedControl + +@end diff --git a/LegacyComponents/TGLocationMapModeControl.m b/LegacyComponents/TGLocationMapModeControl.m new file mode 100644 index 0000000000..24e5499617 --- /dev/null +++ b/LegacyComponents/TGLocationMapModeControl.m @@ -0,0 +1,27 @@ +#import "TGLocationMapModeControl.h" + +#import "LegacyComponentsInternal.h" +#import "TGFont.h" +#import "TGColor.h" + +@implementation TGLocationMapModeControl + +- (instancetype)init +{ + self = [super initWithItems:@[TGLocalized(@"Map.Map"), TGLocalized(@"Map.Satellite"), TGLocalized(@"Map.Hybrid")]]; + if (self != nil) + { + [self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlBackground.png") forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + [self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected barMetrics:UIBarMetricsDefault]; + [self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected | UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; + [self setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlHighlighted.png") forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; + UIImage *dividerImage = TGComponentsImageNamed(@"ModernSegmentedControlDivider.png"); + [self setDividerImage:dividerImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + + [self setTitleTextAttributes:@{UITextAttributeTextColor: TGAccentColor(), UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateNormal]; + [self setTitleTextAttributes:@{UITextAttributeTextColor: [UIColor whiteColor], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateSelected]; + } + return self; +} + +@end diff --git a/LegacyComponents/TGLocationMapView.h b/LegacyComponents/TGLocationMapView.h new file mode 100644 index 0000000000..491452c4df --- /dev/null +++ b/LegacyComponents/TGLocationMapView.h @@ -0,0 +1,11 @@ +#import + +@interface TGLocationMapView : MKMapView + +@property (nonatomic, copy) void(^singleTap)(void); + +@property (nonatomic, assign) bool longPressAsTapEnabled; +@property (nonatomic, assign) bool tapEnabled; +@property (nonatomic, assign) bool manipulationEnabled; + +@end diff --git a/LegacyComponents/TGLocationMapView.m b/LegacyComponents/TGLocationMapView.m new file mode 100644 index 0000000000..8cc35de6fa --- /dev/null +++ b/LegacyComponents/TGLocationMapView.m @@ -0,0 +1,79 @@ +#import "TGLocationMapView.h" + +@interface TGLocationMapView () +{ + UITapGestureRecognizer *_tapGestureRecognizer; + UILongPressGestureRecognizer *_longPressGestureRecognizer; +} +@end + +@implementation TGLocationMapView + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + _manipulationEnabled = true; + + _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tg_handleTap:)]; + _tapGestureRecognizer.numberOfTapsRequired = 1; + _tapGestureRecognizer.numberOfTouchesRequired = 1; + [self addGestureRecognizer:_tapGestureRecognizer]; + + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(tg_handleLongPress:)]; + _longPressGestureRecognizer.enabled = false; + _longPressGestureRecognizer.minimumPressDuration = 0.2f; + [self addGestureRecognizer:_longPressGestureRecognizer]; + } + return self; +} + +- (bool)tapEnabled +{ + return _tapGestureRecognizer.enabled; +} + +- (void)setTapEnabled:(bool)enabled +{ + _tapGestureRecognizer.enabled = enabled; +} + +- (bool)longPressAsTapEnabled +{ + return _longPressGestureRecognizer.enabled; +} + +- (void)setLongPressAsTapEnabled:(bool)enabled +{ + _longPressGestureRecognizer.enabled = enabled; +} + +- (void)tg_handleTap:(UITapGestureRecognizer *)__unused gestureRecognizer +{ + if (self.singleTap != nil) + self.singleTap(); +} + +- (void)tg_handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer +{ + if (gestureRecognizer.state == UIGestureRecognizerStateBegan) + { + if (self.singleTap != nil) + self.singleTap(); + } +} + +- (void)setManipulationEnabled:(bool)enabled +{ + _manipulationEnabled = enabled; + + self.scrollEnabled = enabled; + self.zoomEnabled = enabled; + if ([self respondsToSelector:@selector(setRotateEnabled:)]) + self.rotateEnabled = enabled; + if ([self respondsToSelector:@selector(setPitchEnabled:)]) + self.pitchEnabled = enabled; +} + +@end diff --git a/LegacyComponents/TGLocationPickerController.h b/LegacyComponents/TGLocationPickerController.h new file mode 100644 index 0000000000..51b3a2bdfe --- /dev/null +++ b/LegacyComponents/TGLocationPickerController.h @@ -0,0 +1,18 @@ +#import + +#import + +@class TGVenueAttachment; + +typedef enum { + TGLocationPickerControllerDefaultIntent, + TGLocationPickerControllerCustomLocationIntent +} TGLocationPickerControllerIntent; + +@interface TGLocationPickerController : TGViewController + +@property (nonatomic, copy) void (^locationPicked)(CLLocationCoordinate2D coordinate, TGVenueAttachment *venue); + +- (instancetype)initWithIntent:(TGLocationPickerControllerIntent)intent; + +@end diff --git a/LegacyComponents/TGLocationPickerController.m b/LegacyComponents/TGLocationPickerController.m new file mode 100644 index 0000000000..b150474991 --- /dev/null +++ b/LegacyComponents/TGLocationPickerController.m @@ -0,0 +1,1500 @@ +#import "TGLocationPickerController.h" + +#import "LegacyComponentsInternal.h" +#import "TGColor.h" + +#import + +#import "TGLocationUtils.h" + +#import "TGLocationSignals.h" + +#import "TGListsTableView.h" +#import "TGSearchBar.h" +#import "TGSearchDisplayMixin.h" +#import +#import +#import + +#import "TGLocationAnnotation.h" +#import "TGLocationReverseGeocodeResult.h" + +#import "TGLocationMapView.h" +#import "TGLocationPinView.h" +#import "TGPickPinAnnotationView.h" + +#import "TGLocationVenue.h" + +#import "TGLocationVenueCell.h" +#import "TGLocationCurrentLocationCell.h" +#import "TGLocationSectionHeaderCell.h" +#import "TGLocationTrackingButton.h" +#import "TGLocationMapModeControl.h" + +const CGFloat TGLocationPickerMapClipHeight = 1600.0f; +const CGFloat TGLocationPickerMapWidescreenHeight = 342.0f; +const CGFloat TGLocationPickerMapHeight = 265.0f; +const CGFloat TGLocationPickerMapInset = 280.0f; +const MKCoordinateSpan TGLocationDefaultSpan = { 0.008, 0.008 }; +const CGPoint TGLocationPickerPinOffset = { 0.0f, 33.0f }; + +const TGLocationPlacesService TGLocationPickerPlacesProvider = TGLocationPlacesServiceFoursquare; + +@interface TGLocationPair : NSObject + +@property (nonatomic, readonly) CLLocation *location; +@property (nonatomic, readonly, getter=isCurrent) bool current; +@property (nonatomic, assign) bool onlyLocationUpdate; + +@end + +@implementation TGLocationPair + ++ (TGLocationPair *)pairWithLocation:(CLLocation *)location isCurrent:(bool)isCurrent onlyLocationUpdate:(bool)onlyLocationUpdate +{ + TGLocationPair *pair = [[TGLocationPair alloc] init]; + pair->_location = location; + pair->_current = isCurrent; + pair->_onlyLocationUpdate = onlyLocationUpdate; + return pair; +} + +@end + +@interface TGLocationPickerController () +{ + TGLocationPickerControllerIntent _intent; + + CLLocationManager *_locationManager; + bool _locationServicesDisabled; + + bool _nearbyVenuesLoadFailed; + NSArray *_nearbyVenues; + NSArray *_searchResults; + NSString *_searchResultsQuery; + + TGLocationAnnotation *_annotation; + TGLocationAnnotation *_customAnnotation; + + CLLocation *_currentUserLocation; + CLLocation *_startLocation; + CLLocation *_venuesFetchLocation; + SMetaDisposable *_locationUpdateDisposable; + void (^_userLocationObserver)(CLLocation *location); + + SMetaDisposable *_nearbyVenuesDisposable; + SMetaDisposable *_searchDisposable; + SMetaDisposable *_reverseGeocodeDisposable; + + UIView *_pickerPinWrapper; + TGLocationPinView *_pickerPinView; + TGPickPinAnnotationView *_pickerAnnotationView; + + NSValue *_fullScreenMapSpan; + + UIView *_mapClipView; + UIView *_mapViewWrapper; + TGLocationMapView *_mapView; + bool _mapInFullScreenMode; + bool _pinMovedFromUserLocation; + bool _updatePinAnnotation; + + CGFloat _tableViewTopInset; + UITableView *_nearbyVenuesTableView; + + UIView *_searchBarOverlay; + UIBarButtonItem *_searchButtonItem; + UIView *_searchReferenceView; + UIView *_searchBarWrapper; + TGSearchBar *_searchBar; + TGSearchDisplayMixin *_searchMixin; + + UIActivityIndicatorView *_activityIndicator; + UILabel *_messageLabel; + + UIView *_toolbarWrapperView; + UIView *_toolbarView; + TGModernButton *_userLocationButton; + TGModernButton *_showPlacesButton; + UIImageView *_mapModeControlMask; + TGLocationMapModeControl *_mapModeControl; + TGModernButton *_mapModeButton; + + UIImageView *_attributionView; +} +@end + +@implementation TGLocationPickerController + +- (instancetype)init +{ + return [self initWithIntent:TGLocationPickerControllerDefaultIntent]; +} + +- (instancetype)initWithIntent:(TGLocationPickerControllerIntent)intent +{ + self = [super init]; + if (self != nil) + { + _intent = intent; + _locationManager = [[CLLocationManager alloc] init]; + + _locationUpdateDisposable = [[SMetaDisposable alloc] init]; + _nearbyVenuesDisposable = [[SMetaDisposable alloc] init]; + + self.title = TGLocalized(@"Map.ChooseLocationTitle"); + [self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelButtonPressed)]]; + + _searchButtonItem = [self controllerRightBarButtonItem]; + _searchButtonItem.enabled = false; + [self setRightBarButtonItem:_searchButtonItem]; + + [TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager]; + } + return self; +} + +- (void)dealloc +{ + [_locationUpdateDisposable dispose]; + [_nearbyVenuesDisposable dispose]; + [_searchDisposable dispose]; + + _mapView.delegate = nil; + _searchBar.delegate = nil; + _searchMixin.delegate = nil; + + _nearbyVenuesTableView.dataSource = nil; + _nearbyVenuesTableView.delegate = nil; +} + +- (void)loadView +{ + [super loadView]; + + self.view.backgroundColor = [UIColor whiteColor]; + + _nearbyVenuesTableView = [[UITableView alloc] initWithFrame:self.view.bounds]; + _nearbyVenuesTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _nearbyVenuesTableView.dataSource = self; + _nearbyVenuesTableView.delegate = self; + _nearbyVenuesTableView.separatorStyle = UITableViewCellSeparatorStyleNone; + [self.view addSubview:_nearbyVenuesTableView]; + + //if ([_nearbyVenuesTableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) + // _nearbyVenuesTableView.cellLayoutMarginsFollowReadableWidth = false; + + if (TGLocationPickerPlacesProvider == TGLocationPlacesServiceFoursquare) + { + _attributionView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, _nearbyVenuesTableView.frame.size.width, 55)]; + _attributionView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _attributionView.contentMode = UIViewContentModeCenter; + _attributionView.hidden = true; + _attributionView.image = TGComponentsImageNamed(@"FoursquareAttribution.png"); + _nearbyVenuesTableView.tableFooterView = _attributionView; + } + + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityIndicator.userInteractionEnabled = false; + [_nearbyVenuesTableView addSubview:_activityIndicator]; + [_activityIndicator startAnimating]; + + _messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)]; + _messageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _messageLabel.backgroundColor = [UIColor clearColor]; + _messageLabel.font = TGSystemFontOfSize(16); + _messageLabel.hidden = true; + _messageLabel.textAlignment = NSTextAlignmentCenter; + _messageLabel.textColor = UIColorRGB(0x8e8e93); + _messageLabel.userInteractionEnabled = false; + [_nearbyVenuesTableView addSubview:_messageLabel]; + + CGFloat mapHeight = [TGLocationPickerController mapHeight]; + + CGFloat stripeThickness = TGScreenPixel; + _tableViewTopInset = mapHeight + stripeThickness; + + _mapClipView = [[UIView alloc] initWithFrame:CGRectMake(0, -TGLocationPickerMapClipHeight, self.view.frame.size.width, TGLocationPickerMapClipHeight)]; + _mapClipView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _mapClipView.clipsToBounds = true; + [_nearbyVenuesTableView addSubview:_mapClipView]; + + _mapViewWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, TGLocationPickerMapClipHeight - mapHeight - stripeThickness, self.view.frame.size.width, mapHeight + stripeThickness)]; + _mapViewWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_mapClipView addSubview:_mapViewWrapper]; + + __weak TGLocationPickerController *weakSelf = self; + + _mapView = [[TGLocationMapView alloc] initWithFrame:CGRectMake(0, -TGLocationPickerMapInset, self.view.frame.size.width, mapHeight + 2 * TGLocationPickerMapInset)]; + _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _mapView.delegate = self; + _mapView.showsUserLocation = true; + _mapView.tapEnabled = true; + _mapView.longPressAsTapEnabled = true; + _mapView.singleTap = ^ + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + [strongSelf switchToFullscreenMapAnimated:true]; + }; + [_mapViewWrapper addSubview:_mapView]; + + CGFloat pinWrapperWidth = self.view.frame.size.width; + _pickerPinWrapper = [[TGLocationPinWrapperView alloc] initWithFrame:CGRectMake((_mapViewWrapper.frame.size.width - pinWrapperWidth) / 2, (_mapViewWrapper.frame.size.height - pinWrapperWidth) / 2, pinWrapperWidth, pinWrapperWidth)]; + _pickerPinWrapper.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; + _pickerPinWrapper.hidden = true; + [_mapViewWrapper addSubview:_pickerPinWrapper]; + + _pickerPinView = [[TGLocationPinView alloc] init]; + _pickerPinView.frame = CGRectMake((_pickerPinWrapper.frame.size.width - _pickerPinView.frame.size.width) / 2, (_pickerPinWrapper.frame.size.height - _pickerPinView.frame.size.height) / 2 - 15, _pickerPinView.frame.size.width, _pickerPinView.frame.size.height); + [_pickerPinWrapper addSubview:_pickerPinView]; + + _pickerAnnotationView = [[TGPickPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:nil]; + _pickerAnnotationView.calloutPressed = ^ + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf != nil) + [strongSelf _sendLocation]; + }; + _pickerAnnotationView.hidden = true; + + UIView *stripeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, _mapViewWrapper.frame.size.height - stripeThickness, _mapViewWrapper.frame.size.width, stripeThickness)]; + stripeView.backgroundColor = TGSeparatorColor(); + stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + [_mapViewWrapper addSubview:stripeView]; + + _searchBarOverlay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.navigationController.view.frame.size.width, 64)]; + _searchBarOverlay.alpha = 0.0f; + _searchBarOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _searchBarOverlay.backgroundColor = UIColorRGB(0xf7f7f7); + _searchBarOverlay.userInteractionEnabled = false; + [self.navigationController.view addSubview:_searchBarOverlay]; + + _searchBarWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, -64, self.navigationController.view.frame.size.width, 64)]; + _searchBarWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _searchBarWrapper.backgroundColor = [UIColor whiteColor]; + _searchBarWrapper.hidden = true; + [self.navigationController.view addSubview:_searchBarWrapper]; + + _searchBar = [[TGSearchBar alloc] initWithFrame:CGRectMake(0.0f, 20, _searchBarWrapper.frame.size.width, [TGSearchBar searchBarBaseHeight]) style:TGSearchBarStyleHeader]; + _searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _searchBar.customBackgroundView.image = nil; + _searchBar.customActiveBackgroundView.image = nil; + _searchBar.delegate = self; + [_searchBar setShowsCancelButton:true animated:false]; + [_searchBar setAlwaysExtended:true]; + _searchBar.placeholder = TGLocalized(@"Map.Search"); + [_searchBar sizeToFit]; + _searchBar.delayActivity = false; + [_searchBarWrapper addSubview:_searchBar]; + + _searchMixin = [[TGSearchDisplayMixin alloc] init]; + _searchMixin.searchBar = _searchBar; + _searchMixin.alwaysShowsCancelButton = true; + _searchMixin.delegate = self; + + _toolbarWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 44.0f, self.view.frame.size.width, 44.0f)]; + _toolbarWrapperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + _toolbarWrapperView.userInteractionEnabled = false; + [self.view addSubview:_toolbarWrapperView]; + + _toolbarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, _toolbarWrapperView.frame.size.height, _toolbarWrapperView.frame.size.width, 44.0f)]; + _toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _toolbarView.backgroundColor = UIColorRGB(0xf7f7f7); + [_toolbarWrapperView addSubview:_toolbarView]; + stripeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _toolbarView.frame.size.width, stripeThickness)]; + stripeView.backgroundColor = UIColorRGB(0xb2b2b2); + stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_toolbarView addSubview:stripeView]; + + _userLocationButton = [[TGModernButton alloc] initWithFrame:CGRectMake(4, 2, 44, 44)]; + _userLocationButton.adjustsImageWhenHighlighted = false; + _userLocationButton.contentMode = UIViewContentModeCenter; + _userLocationButton.exclusiveTouch = true; + _userLocationButton.enabled = false; + [_userLocationButton setImage:TGComponentsImageNamed(@"TrackingLocation.png") forState:UIControlStateNormal]; + [_userLocationButton addTarget:self action:@selector(userLocationButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_toolbarView addSubview:_userLocationButton]; + + _showPlacesButton = [[TGModernButton alloc] initWithFrame:CGRectZero]; + _showPlacesButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + _showPlacesButton.exclusiveTouch = true; + _showPlacesButton.titleLabel.font = TGSystemFontOfSize(18); + [_showPlacesButton setTitle:TGLocalized(@"Map.ShowPlaces") forState:UIControlStateNormal]; + [_showPlacesButton setTitleColor:TGAccentColor()]; + [_showPlacesButton addTarget:self action:@selector(showPlacesButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_showPlacesButton sizeToFit]; + CGFloat showPlacesWidth = MAX(110, _showPlacesButton.frame.size.width); + _showPlacesButton.frame = CGRectMake((self.view.frame.size.width - showPlacesWidth) / 2, 0, showPlacesWidth, 44); + [_toolbarView addSubview:_showPlacesButton]; + + static UIImage *maskImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(55.0f, 43.0f), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGColorRef colors[3] = { + CGColorRetain(UIColorRGBA(0xf7f7f7, 0.0f).CGColor), + CGColorRetain(UIColorRGBA(0xf7f7f7, 1.0f).CGColor), + CGColorRetain(UIColorRGBA(0xf7f7f7, 1.0f).CGColor) + }; + + CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 3, NULL); + CGFloat locations[3] = {0.0f, 0.45f, 1.0f}; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations); + + CFRelease(colorsArray); + CFRelease(colors[0]); + CFRelease(colors[1]); + CFRelease(colors[2]); + + CGColorSpaceRelease(colorSpace); + + CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0f, 0.0f), CGPointMake(55.0f, 0.0f), 0); + + CFRelease(gradient); + + maskImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + }); + + _mapModeControl = [[TGLocationMapModeControl alloc] init]; + _mapModeControl.alpha = 0.0f; + _mapModeControl.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _mapModeControl.userInteractionEnabled = false; + _mapModeControl.frame = CGRectMake(_toolbarView.frame.size.width, (_toolbarView.frame.size.height - 29) / 2 + 0.5f, _toolbarView.frame.size.width - 55 - 7.5f, 29); + _mapModeControl.selectedSegmentIndex = MAX(0, MIN(2, (NSInteger)_mapView.mapType)); + [_mapModeControl addTarget:self action:@selector(mapModeControlValueChanged:) forControlEvents:UIControlEventValueChanged]; + [_toolbarView addSubview:_mapModeControl]; + + _mapModeControlMask = [[UIImageView alloc] initWithFrame:CGRectMake(_toolbarView.frame.size.width - 55, 1, 55, 43)]; + _mapModeControlMask.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + _mapModeControlMask.image = maskImage; + [_toolbarView addSubview:_mapModeControlMask]; + + _mapModeButton = [[TGModernButton alloc] initWithFrame:CGRectMake(_toolbarView.frame.size.width - 50, TGRetinaPixel, 44, 44)]; + _mapModeButton.adjustsImageWhenHighlighted = false; + _mapModeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + _mapModeButton.contentMode = UIViewContentModeCenter; + _mapModeButton.exclusiveTouch = true; + [_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo.png") forState:UIControlStateNormal]; + [_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected]; + [_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected | UIControlStateHighlighted]; + [_mapModeButton addTarget:self action:@selector(mapModeButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_toolbarView addSubview:_mapModeButton]; + + _searchReferenceView = [[UIView alloc] initWithFrame:self.view.bounds]; + _searchReferenceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _searchReferenceView.userInteractionEnabled = false; + [self.view addSubview:_searchReferenceView]; + + self.scrollViewsForAutomaticInsetsAdjustment = @[ _nearbyVenuesTableView ]; + + if (![self _updateControllerInset:false]) + [self controllerInsetUpdated:UIEdgeInsetsZero]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (_intent == TGLocationPickerControllerCustomLocationIntent) + { + [self switchToFullscreenMapAnimated:true]; + _showPlacesButton.hidden = true; + } + + __weak TGLocationPickerController *weakSelf = self; + [_locationUpdateDisposable setDisposable:[[self userLocationSignal] startWithNext:^(TGLocationPair *locationPair) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + [strongSelf setCurrentUserLocation:locationPair.location storeLocation:locationPair.isCurrent updateMapView:!locationPair.onlyLocationUpdate]; + + if (strongSelf->_intent != TGLocationPickerControllerCustomLocationIntent && locationPair.isCurrent && !locationPair.onlyLocationUpdate) + [strongSelf fetchNearbyVenuesWithLocation:locationPair.location]; + }]]; + + [self _layoutTableProgressViews]; +} + +- (void)reloadNearbyVenuesIfNeeded +{ + if (!_nearbyVenuesLoadFailed) + return; + + _nearbyVenuesLoadFailed = false; + [self fetchNearbyVenuesWithLocation:_currentUserLocation]; +} + +- (void)fetchNearbyVenuesWithLocation:(CLLocation *)location +{ + _venuesFetchLocation = location; + + _messageLabel.hidden = true; + + __weak TGLocationPickerController *weakSelf = self; + [_nearbyVenuesDisposable setDisposable:[[[TGLocationSignals searchNearbyPlacesWithQuery:nil coordinate:location.coordinate service:TGLocationPickerPlacesProvider] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *venues) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf != nil && venues != nil) + [strongSelf setNearbyVenues:venues]; + } error:^(__unused id error) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf->_nearbyVenuesLoadFailed = true; + + [strongSelf setIsLoading:false]; + + [strongSelf _layoutTableProgressViews]; + strongSelf->_messageLabel.hidden = false; + strongSelf->_messageLabel.text = TGLocalized(@"Map.LoadError"); + } completed:nil]]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + _searchBarWrapper.hidden = false; + + if (_intent != TGLocationPickerControllerCustomLocationIntent) + { + [[[LegacyComponentsGlobals provider] accessChecker] checkLocationAuthorizationStatusForIntent:TGLocationAccessIntentSend alertDismissComlpetion:^ + { + if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) + { + [self switchToFullscreenMapAnimated:true]; + _showPlacesButton.hidden = true; + } + }]; + } +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + _searchBarWrapper.hidden = true; +} + +#pragma mark - Actions + +- (void)cancelButtonPressed +{ + [self.presentingViewController dismissViewControllerAnimated:true completion:nil]; +} + +- (void)_sendLocation +{ + CLLocationCoordinate2D coordinate = _currentUserLocation.coordinate; + if (_mapInFullScreenMode) + coordinate = [self mapCenterCoordinateForPickerPin]; + + if (self.locationPicked != nil) + self.locationPicked(coordinate, nil); +} + +- (void)searchButtonPressed +{ + [self setSearchHidden:false animated:true]; + [_searchBar becomeFirstResponder]; +} + +- (void)showPlacesButtonPressed +{ + [self switchToVenuesTableView]; +} + +- (void)userLocationButtonPressed +{ + if (!_pinMovedFromUserLocation || _currentUserLocation == nil) + return; + + _pinMovedFromUserLocation = false; + + [self hidePickerAnnotationAnimated:true]; + [_pickerPinView setPinRaised:true animated:true completion:nil]; + + MKCoordinateSpan span = _fullScreenMapSpan != nil ? _fullScreenMapSpan.MKCoordinateSpanValue : TGLocationDefaultSpan; + [self _setMapCenterCoordinate:_mapView.userLocation.location.coordinate span:span offset:TGLocationPickerPinOffset animated:true]; +} + +- (void)mapModeButtonPressed +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissMapModeControl) object:nil]; + [self setMapModeControlHidden:_mapModeButton.selected withUserAction:true animated:true]; +} + +- (void)mapModeControlValueChanged:(TGLocationMapModeControl *)sender +{ + NSInteger mapMode = MAX(0, MIN(2, sender.selectedSegmentIndex)); + [_mapView setMapType:(MKMapType)mapMode]; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissMapModeControl) object:nil]; + if (_mapModeButton.isSelected) + [self performSelector:@selector(dismissMapModeControl) withObject:nil afterDelay:1.0f]; +} + +- (void)dismissMapModeControl +{ + [self setMapModeControlHidden:true withUserAction:false animated:true]; +} + +#pragma mark - Map View Delegate + +- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)__unused animated +{ + UIView *view = mapView.subviews.firstObject; + + for (UIGestureRecognizer *recognizer in view.gestureRecognizers) + { + if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateEnded) + { + if (_mapInFullScreenMode) + { + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pinPinView) object:nil]; + [self hidePickerAnnotationAnimated:true]; + } + else + { + [self switchToFullscreenMapAnimated:true]; + } + + [_pickerPinView setPinRaised:true animated:true completion:nil]; + + _pinMovedFromUserLocation = true; + _updatePinAnnotation = false; + + break; + } + } +} + +- (void)mapView:(MKMapView *)__unused mapView regionDidChangeAnimated:(BOOL)__unused animated +{ + if (_pickerPinView.isPinRaised) + { + NSTimeInterval delay = _pinMovedFromUserLocation ? 0.38 : 0.05; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pinPinView) object:nil]; + [self performSelector:@selector(pinPinView) withObject:nil afterDelay:delay]; + } + else if (_updatePinAnnotation) + { + [self pinPinView]; + } +} + +- (void)pinPinView +{ + __weak TGLocationPickerController *weakSelf = self; + [_pickerPinView setPinRaised:false animated:true completion:^ + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf != nil) + [strongSelf showPickerAnnotationAnimated:true]; + }]; +} + +- (void)mapView:(MKMapView *)__unused mapView didUpdateUserLocation:(MKUserLocation *)userLocation +{ + userLocation.title = @""; + + _locationServicesDisabled = false; + + if (_userLocationObserver != nil) + _userLocationObserver(userLocation.location); + else if (userLocation.location != nil) + _startLocation = userLocation.location; +} + +- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation +{ + if (annotation == mapView.userLocation) + return nil; + + MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:TGPickPinAnnotationKind]; + if (view == nil) + view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:TGPickPinAnnotationKind]; + else + view.annotation = annotation; + + if (_mapInFullScreenMode) + view.hidden = true; + + view.canShowCallout = false; + view.animatesDrop = false; + view.draggable = false; + + return view; +} + +- (void)updateAnnotationWithLocation:(CLLocation *)location +{ + if (_mapView.userLocation == nil || _mapView.userLocation.location == nil) + return; + + if (_annotation == nil) + { + _annotation = [[TGLocationAnnotation alloc] initWithCoordinate:_currentUserLocation.coordinate title:nil]; + [_mapView addAnnotation:_annotation]; + [_mapView selectAnnotation:_annotation animated:false]; + } + + _annotation.coordinate = location.coordinate; +} + +- (void)updatePickerAnnotation +{ + __weak TGLocationPickerController *weakSelf = self; + + CLLocationCoordinate2D coordinate = [self mapCenterCoordinateForPickerPin]; + _customAnnotation = [[TGLocationAnnotation alloc] initWithCoordinate:coordinate title:TGLocalized(@"Map.SendThisLocation")]; + [self _updatePickerAnnotationViewAnimated:false]; + + [_reverseGeocodeDisposable setDisposable:[[[TGLocationSignals reverseGeocodeCoordinate:coordinate] deliverOn:[SQueue mainQueue]] startWithNext:^(TGLocationReverseGeocodeResult *result) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + NSString *address = @""; + if (result != nil) + address = result.displayAddress; + + strongSelf->_customAnnotation.subtitle = address; + [strongSelf updateCurrentLocationCell]; + } error:^(__unused id error) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + strongSelf->_customAnnotation.subtitle = TGLocalized(@"Map.LocatingError"); + [strongSelf updateCurrentLocationCell]; + } completed:^ + { + + }]]; +} + +- (void)showPickerAnnotationAnimated:(bool)__unused animated +{ + [self updatePickerAnnotation]; + _customAnnotation.subtitle = nil; + + [self updateCurrentLocationCell]; +} + +- (void)hidePickerAnnotationAnimated:(bool)__unused animated +{ + _customAnnotation.subtitle = nil; + [self updateCurrentLocationCell]; +} + +- (void)_updatePickerAnnotationViewAnimated:(bool)animated +{ + TGPickPinAnnotationView *annotationView = _pickerAnnotationView; + annotationView.annotation = _customAnnotation; + [annotationView sizeToFit]; + [annotationView setNeedsLayout]; + + if (animated && annotationView.appeared) + { + [UIView animateWithDuration:0.2f animations:^ + { + [annotationView layoutIfNeeded]; + }]; + } +} + +- (CLLocationCoordinate2D)mapCenterCoordinateForPickerPin +{ + return [_mapView convertPoint:CGPointMake((_mapView.frame.size.width + TGLocationPickerPinOffset.x) / 2, (_mapView.frame.size.height + TGLocationPickerPinOffset.y) / 2) toCoordinateFromView:_mapView]; +} + +- (void)_setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate offset:(CGPoint)offset animated:(bool)animated +{ + [self _setMapCenterCoordinate:coordinate span:TGLocationDefaultSpan offset:offset animated:animated]; +} + +- (void)_setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate span:(MKCoordinateSpan)span offset:(CGPoint)offset animated:(bool)animated +{ + @try + { + MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, span); + if (!CGPointEqualToPoint(offset, CGPointZero)) + { + MKMapRect mapRect = [TGLocationUtils MKMapRectForCoordinateRegion:region]; + [_mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(offset.y, offset.x, 0, 0) animated:animated]; + } + else + { + [_mapView setRegion:region animated:animated]; + } + } + @catch (NSException *exception) + { + TGLegacyLog(@"ERROR: failed to set location picker map region with exception: %@", exception); + } +} + +#pragma mark - Signals + +- (SSignal *)userLocationSignal +{ + __weak TGLocationPickerController *weakSelf = self; + return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) + { + SMetaDisposable *disposable = [[SMetaDisposable alloc] init]; + + [disposable setDisposable:[[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf == nil) + return nil; + + if (strongSelf->_startLocation != nil) + { + [subscriber putNext:[TGLocationPair pairWithLocation:strongSelf->_startLocation isCurrent:true onlyLocationUpdate:false]]; + strongSelf->_startLocation = nil; + } + else + { + CLLocation *knownUserLocation = [TGLocationSignals lastKnownUserLocation]; + [subscriber putNext:[TGLocationPair pairWithLocation:knownUserLocation isCurrent:false onlyLocationUpdate:false]]; + } + + strongSelf->_userLocationObserver = ^(CLLocation *location) + { + [subscriber putNext:[TGLocationPair pairWithLocation:location isCurrent:true onlyLocationUpdate:false]]; + }; + + return nil; + }] map:^TGLocationPair *(TGLocationPair *locationPair) + { + if (!locationPair.isCurrent) + return locationPair; + + __strong TGLocationPickerController *strongSelf = weakSelf; + CLLocation *location = locationPair.location; + + if (strongSelf != nil && strongSelf->_venuesFetchLocation != nil) + { + CLLocation *currentLocation = strongSelf->_venuesFetchLocation; + if ([location distanceFromLocation:currentLocation] < 250) + { + if ((location.horizontalAccuracy < currentLocation.horizontalAccuracy || fabs(location.horizontalAccuracy - currentLocation.horizontalAccuracy) < 50)) + { + locationPair.onlyLocationUpdate = true; + } + } + } + + return locationPair; + }] startWithNext:^(TGLocationPair *locationPair) + { + [subscriber putNext:locationPair]; + }]]; + + return disposable; + }]; +} + +- (void)setCurrentUserLocation:(CLLocation *)userLocation storeLocation:(bool)storeLocation updateMapView:(bool)updateMapView +{ + if (userLocation == nil) + return; + + bool hadNoLocation = (_currentUserLocation == nil); + + if (_mapInFullScreenMode && hadNoLocation) + _pinMovedFromUserLocation = true; + + if (storeLocation) + { + [TGLocationSignals storeLastKnownUserLocation:userLocation]; + _currentUserLocation = userLocation; + _searchButtonItem.enabled = true; + _userLocationButton.enabled = true; + + if (updateMapView) + [self updateAnnotationWithLocation:_currentUserLocation]; + } + + [self updateCurrentLocationCell]; + + if (updateMapView) + { + if (!_mapInFullScreenMode) + { + [self _setMapCenterCoordinate:userLocation.coordinate offset:TGLocationPickerPinOffset animated:true]; + } + else if (_intent == TGLocationPickerControllerCustomLocationIntent && hadNoLocation) + { + _pinMovedFromUserLocation = false; + _updatePinAnnotation = true; + [self _setMapCenterCoordinate:userLocation.coordinate offset:TGLocationPickerPinOffset animated:true]; + } + } +} + +#pragma mark - Appearance + +- (void)switchToFullscreenMapAnimated:(bool)__unused animated +{ + _mapInFullScreenMode = true; + + _searchButtonItem.enabled = true; + + MKAnnotationView *annotationView = [_mapView viewForAnnotation:_annotation]; + annotationView.hidden = true; + _pickerPinWrapper.hidden = false; + + [self showPickerAnnotationAnimated:true]; + + _mapView.tapEnabled = false; + _mapView.longPressAsTapEnabled = false; + + [self setToolbarHidden:false animated:true]; + + _nearbyVenuesTableView.clipsToBounds = false; + _nearbyVenuesTableView.scrollEnabled = false; + [_mapViewWrapper.superview bringSubviewToFront:_mapViewWrapper]; + + void (^changeBlock)(void) = ^ + { + CGFloat toolbarHeight = _toolbarView.frame.size.height; + + _nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top); + _nearbyVenuesTableView.frame = CGRectMake(_nearbyVenuesTableView.frame.origin.x, self.view.frame.size.height - [TGLocationPickerController mapHeight] - TGLocationCurrentLocationCellHeight - self.controllerInset.top - toolbarHeight, _nearbyVenuesTableView.frame.size.width, _nearbyVenuesTableView.frame.size.height); + + _mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - self.view.frame.size.height + self.controllerInset.top + toolbarHeight + 20, _mapViewWrapper.frame.size.width, self.view.frame.size.height - TGLocationCurrentLocationCellHeight - self.controllerInset.top - 7); + _mapView.center = CGPointMake(_mapView.center.x, _mapViewWrapper.frame.size.height / 2); + }; + + void (^completionBlock)(BOOL) = ^(BOOL finished) + { + if (finished) + { + _nearbyVenuesTableView.clipsToBounds = true; + _mapView.manipulationEnabled = true; + + _mapViewWrapper.frame = [self.view convertRect:_mapViewWrapper.frame fromView:_mapViewWrapper.superview]; + [self.view insertSubview:_mapViewWrapper belowSubview:_toolbarWrapperView]; + _mapViewWrapper.clipsToBounds = true; + + if (_annotation != nil) + _fullScreenMapSpan = [NSValue valueWithMKCoordinateSpan:_mapView.region.span]; + } + }; + + if (animated) + { + if (iosMajorVersion() >= 7) + { + [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:0.5f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:completionBlock]; + } + else + { + [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:changeBlock completion:completionBlock]; + } + } + else + { + changeBlock(); + completionBlock(true); + } +} + +- (void)switchToVenuesTableView +{ + _mapInFullScreenMode = false; + + _searchButtonItem.enabled = (_currentUserLocation != nil); + + MKAnnotationView *annotationView = [_mapView viewForAnnotation:_annotation]; + annotationView.hidden = false; + _pickerPinWrapper.hidden = true; + + [self updateCurrentLocationCell]; + + _mapView.mapType = MKMapTypeStandard; + + [self controllerInsetUpdated:self.controllerInset]; + + [self setToolbarHidden:true animated:true]; + if (_annotation != nil) + [self _setMapCenterCoordinate:_annotation.coordinate offset:TGLocationPickerPinOffset animated:true]; + + _nearbyVenuesTableView.clipsToBounds = false; + + _mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - self.view.frame.size.height + self.controllerInset.top + _toolbarView.frame.size.height + 20, _mapViewWrapper.frame.size.width, self.view.frame.size.height - TGLocationCurrentLocationCellHeight - self.controllerInset.top - 7); + [_mapClipView addSubview:_mapViewWrapper]; + _mapViewWrapper.clipsToBounds = false; + + void (^animationBlock)(void) = ^ + { + _nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top); + _nearbyVenuesTableView.frame = CGRectMake(_nearbyVenuesTableView.frame.origin.x, 0, _nearbyVenuesTableView.frame.size.width, _nearbyVenuesTableView.frame.size.height); + + CGFloat stripeThickness = TGScreenPixel; + _mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - [TGLocationPickerController mapHeight] - stripeThickness, self.view.frame.size.width, [TGLocationPickerController mapHeight] + stripeThickness); + _mapView.frame = CGRectMake(_mapView.frame.origin.x, (_mapViewWrapper.frame.size.height - _mapView.frame.size.height) / 2, _mapView.frame.size.width, _mapView.frame.size.height); + }; + + void (^completionBlock)(BOOL) = ^(BOOL finished) + { + _mapModeControl.selectedSegmentIndex = 0; + if (finished) + { + _mapView.tapEnabled = true; + _mapView.longPressAsTapEnabled = true; + _nearbyVenuesTableView.clipsToBounds = true; + _nearbyVenuesTableView.scrollEnabled = true; + } + }; + + if (iosMajorVersion() >= 7) + { + [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.85f initialSpringVelocity:0.5f options:UIViewAnimationOptionCurveLinear animations:animationBlock completion:completionBlock]; + } + else + { + [UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:animationBlock completion:completionBlock]; + } + + [self reloadNearbyVenuesIfNeeded]; +} + +- (void)setToolbarHidden:(bool)hidden animated:(bool)animated +{ + _toolbarWrapperView.userInteractionEnabled = !hidden; + + void (^changeBlock)(void) = ^ + { + _toolbarView.frame = CGRectMake(_toolbarView.frame.origin.x, hidden ? _toolbarWrapperView.frame.size.height : 0, _toolbarView.frame.size.width, _toolbarView.frame.size.height); + }; + + if (animated) + [UIView animateWithDuration:0.3f delay:0.0f options:[TGViewController preferredAnimationCurve] << 16 animations:changeBlock completion:nil]; + else + changeBlock(); +} + +- (void)setMapModeControlHidden:(bool)hidden withUserAction:(bool)withUserAction animated:(bool)animated +{ + _userLocationButton.userInteractionEnabled = hidden; + _showPlacesButton.userInteractionEnabled = hidden; + _mapModeControl.userInteractionEnabled = !hidden; + _mapModeButton.userInteractionEnabled = true; + + if (!withUserAction && animated) + { + [UIView transitionWithView:_mapModeButton duration:0.25f options:UIViewAnimationOptionTransitionCrossDissolve animations:^ + { + _mapModeButton.selected = !hidden; + } completion:nil]; + } + else + { + _mapModeButton.selected = !hidden; + } + + if (animated) + { + [UIView animateWithDuration:0.3f delay:0.0f options:[TGViewController preferredAnimationCurve] << 16 animations:^ + { + _mapModeControl.frame = CGRectMake(hidden ? _toolbarView.frame.size.width : 8, _mapModeControl.frame.origin.y, _mapModeControl.frame.size.width, _mapModeControl.frame.size.height); + } completion:nil]; + + [UIView animateWithDuration:0.25f animations:^ + { + if (hidden) + { + _mapModeControl.alpha = 0.0f; + } + else + { + _userLocationButton.alpha = 0.0f; + _showPlacesButton.alpha = 0.0f; + } + }]; + + [UIView animateWithDuration:0.25f delay:0.05f options:kNilOptions animations:^ + { + if (hidden) + { + _userLocationButton.alpha = 1.0f; + _showPlacesButton.alpha = 1.0f; + } + else + { + _mapModeControl.alpha = 1.0f; + } + } completion:nil]; + } + else + { + _userLocationButton.alpha = hidden ? 1.0f : 0.0f; + _showPlacesButton.alpha = hidden ? 1.0f : 0.0f; + _mapModeControl.alpha = hidden ? 0.0f : 1.0f; + } +} + +- (UIBarButtonItem *)controllerRightBarButtonItem +{ + if (iosMajorVersion() < 7) + { + TGModernBarButton *searchButton = [[TGModernBarButton alloc] initWithImage:TGComponentsImageNamed(@"NavigationSearchIcon.png")]; + searchButton.portraitAdjustment = CGPointMake(-7, -5); + [searchButton addTarget:self action:@selector(searchButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + return [[UIBarButtonItem alloc] initWithCustomView:searchButton]; + } + + return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed)]; +} + +#pragma mark - Search + +- (void)setSearchHidden:(bool)hidden animated:(bool)animated +{ + void (^changeBlock)(void) = ^ + { + CGRect frame = _searchBarWrapper.frame; + if (hidden) + { + frame.origin.y = -64; + _searchBarOverlay.alpha = 0.0f; + } + else + { + frame.origin.y = 0; + if (self.navigationController.modalPresentationStyle == UIModalPresentationFormSheet) + frame.origin.y -= 20; + + _searchBarOverlay.alpha = 1.0f; + } + _searchBarWrapper.frame = frame; + }; + + if (animated) + [UIView animateWithDuration:0.2f animations:changeBlock]; + else + changeBlock(); +} + +- (void)searchBar:(TGSearchBar *)__unused searchBar willChangeHeight:(CGFloat)__unused newHeight +{ + +} + +- (void)searchMixin:(TGSearchDisplayMixin *)__unused searchMixin hasChangedSearchQuery:(NSString *)searchQuery withScope:(int)__unused scope +{ + if (searchQuery.length == 0) + { + [_searchDisposable setDisposable:nil]; + [_searchMixin reloadSearchResults]; + [_searchMixin setSearchResultsTableViewHidden:true]; + _searchBar.showActivity = false; + } + else + { + __weak TGLocationPickerController *weakSelf = self; + void (^changeActivityIndicatorState)(bool) = ^(bool active) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf != nil) + strongSelf->_searchBar.showActivity = active; + }; + + SSignal *searchSignal = [[SSignal complete] delay:0.65f onQueue:[SQueue mainQueue]]; + searchSignal = [searchSignal onCompletion:^ + { + changeActivityIndicatorState(true); + }]; + + CLLocationCoordinate2D coordinate = _mapInFullScreenMode ? _customAnnotation.coordinate : _currentUserLocation.coordinate; + searchSignal = [[searchSignal then:[TGLocationSignals searchNearbyPlacesWithQuery:searchQuery coordinate:coordinate service:TGLocationPickerPlacesProvider]] deliverOn:[SQueue mainQueue]]; + + if (_searchDisposable == nil) + _searchDisposable = [[SMetaDisposable alloc] init]; + + [_searchDisposable setDisposable:[[searchSignal onDispose:^ + { + changeActivityIndicatorState(false); + }] startWithNext:^(NSArray *results) + { + __strong TGLocationPickerController *strongSelf = weakSelf; + if (strongSelf != nil) + [strongSelf setSearchResults:results withSearchQuery:searchQuery]; + } error:^(__unused id error) + { + changeActivityIndicatorState(false); + } completed:^ + { + changeActivityIndicatorState(false); + }]]; + } +} + +- (void)searchMixinWillActivate:(bool)__unused animated +{ + if (_mapInFullScreenMode) + return; + + _nearbyVenuesTableView.scrollEnabled = false; + + [UIView animateWithDuration:0.2f animations:^ + { + _nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top); + [self _layoutTableProgressViews]; + }]; +} + +- (void)searchMixinWillDeactivate:(bool)animated +{ + [_searchDisposable setDisposable:nil]; + + [self setSearchHidden:true animated:animated]; + + if (_mapInFullScreenMode) + return; + + _nearbyVenuesTableView.scrollEnabled = true; + + [UIView animateWithDuration:0.2f animations:^ + { + _nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top); + [self _layoutTableProgressViews]; + }]; +} + +- (UITableView *)createTableViewForSearchMixin:(TGSearchDisplayMixin *)__unused searchMixin +{ + UITableView *tableView = [[UITableView alloc] init]; + tableView.delegate = self; + tableView.dataSource = self; + tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + tableView.tableFooterView = [[UIView alloc] init]; + + return tableView; +} + +- (UIView *)referenceViewForSearchResults +{ + return _searchReferenceView; +} + +#pragma mark - Scroll View Delegate + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + if (_searchMixin.isActive && scrollView == _searchMixin.searchResultsTableView) + { + [_searchBar resignFirstResponder]; + } + else if (scrollView == _nearbyVenuesTableView) + { + [self _layoutTableProgressViews]; + + CGFloat offset = scrollView.contentInset.top + scrollView.contentOffset.y; + CGFloat mapOffset = MIN(offset, [TGLocationPickerController mapHeight]); + _mapView.frame = CGRectMake(_mapView.frame.origin.x, -TGLocationPickerMapInset + mapOffset / 2, _mapView.frame.size.width, _mapView.frame.size.height); + } +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + if (_searchMixin.isActive && scrollView == _searchMixin.searchResultsTableView) + [_searchBar resignFirstResponder]; +} + +#pragma mark - Data + +- (void)updateCurrentLocationCell +{ + UITableViewCell *cell = [_nearbyVenuesTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; + if ([cell isKindOfClass:[TGLocationCurrentLocationCell class]]) + { + TGLocationCurrentLocationCell *locationCell = (TGLocationCurrentLocationCell *)cell; + + if (_mapInFullScreenMode) + [locationCell configureForCustomLocationWithAddress:_customAnnotation.subtitle]; + else + [locationCell configureForCurrentLocationWithAccuracy:_currentUserLocation.horizontalAccuracy]; + } + + [cell.superview bringSubviewToFront:cell]; +} + +- (void)setNearbyVenues:(NSArray *)nearbyVenues +{ + bool shouldFadeIn = (_nearbyVenues.count == 0); + + _nearbyVenues = nearbyVenues; + [_nearbyVenuesTableView reloadData]; + + [self setIsLoading:false]; + + _attributionView.hidden = (_nearbyVenues.count == 0); + + if (shouldFadeIn) + { + NSMutableArray *animatedCells = [[NSMutableArray alloc] init]; + + for (UIView *cell in _nearbyVenuesTableView.visibleCells) + { + if ([cell isKindOfClass:[TGLocationVenueCell class]]) + { + cell.alpha = 0.0f; + [animatedCells addObject:cell]; + } + } + + [UIView animateWithDuration:0.14f animations:^ + { + for (UIView *cell in animatedCells) + cell.alpha = 1.0f; + }]; + } +} + +- (void)setSearchResults:(NSArray *)results withSearchQuery:(NSString *)query +{ + _searchResults = results; + _searchResultsQuery = query; + + [_searchMixin reloadSearchResults]; + [_searchMixin setSearchResultsTableViewHidden:query.length == 0]; +} + +#pragma mark - Table View Data Source & Delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _nearbyVenuesTableView && indexPath.row == 0) + { + [self _sendLocation]; + } + else + { + TGLocationVenue *venue = nil; + if (tableView == _nearbyVenuesTableView) + { + venue = _nearbyVenues[indexPath.row - 2]; + } + else if (tableView == _searchMixin.searchResultsTableView) + { + venue = _searchResults[indexPath.row]; + [_searchBar resignFirstResponder]; + } + + if (self.locationPicked != nil) + self.locationPicked(venue.coordinate, [venue venueAttachment]); + } +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _nearbyVenuesTableView) + { + if (indexPath.row == 0) + return (_mapInFullScreenMode || _currentUserLocation != nil); + if (indexPath.row == 1) + return false; + } + + return true; +} + +- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _nearbyVenuesTableView && indexPath.row == 1) + return nil; + + return indexPath; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = nil; + + if (tableView == _nearbyVenuesTableView && indexPath.row == 0) + { + TGLocationCurrentLocationCell *locationCell = [tableView dequeueReusableCellWithIdentifier:TGLocationCurrentLocationCellKind]; + if (locationCell == nil) + locationCell = [[TGLocationCurrentLocationCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationCurrentLocationCellKind]; + + if (_mapInFullScreenMode) + [locationCell configureForCustomLocationWithAddress:_customAnnotation.subtitle]; + else + [locationCell configureForCurrentLocationWithAccuracy:_currentUserLocation.horizontalAccuracy]; + + cell = locationCell; + } + else if (tableView == _nearbyVenuesTableView && indexPath.row == 1) + { + TGLocationSectionHeaderCell *sectionCell = [tableView dequeueReusableCellWithIdentifier:TGLocationSectionHeaderKind]; + if (sectionCell == nil) + sectionCell = [[TGLocationSectionHeaderCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationSectionHeaderKind]; + + [sectionCell configureWithTitle:TGLocalized(@"Map.ChooseAPlace")]; + + cell = sectionCell; + } + else + { + TGLocationVenueCell *venueCell = [tableView dequeueReusableCellWithIdentifier:TGLocationVenueCellKind]; + if (venueCell == nil) + venueCell = [[TGLocationVenueCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationVenueCellKind]; + + TGLocationVenue *venue = nil; + if (tableView == _nearbyVenuesTableView) + venue = _nearbyVenues[indexPath.row - 2]; + else if (tableView == _searchMixin.searchResultsTableView) + venue = _searchResults[indexPath.row]; + + [venueCell configureWithVenue:venue]; + + cell = venueCell; + } + + return cell; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)__unused section +{ + if (tableView == _nearbyVenuesTableView) + return _nearbyVenues.count + 2; + else if (tableView == _searchMixin.searchResultsTableView) + return _searchResults.count; + + return 0; +} + +- (CGFloat)tableView:(UITableView *)__unused tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _nearbyVenuesTableView) + { + if (indexPath.row == 0) + return TGLocationCurrentLocationCellHeight; + else if (indexPath.row == 1) + return TGLocationSectionHeaderHeight; + } + + return TGLocationVenueCellHeight; +} + +- (CGFloat)tableView:(UITableView *)__unused tableView heightForFooterInSection:(NSInteger)__unused section +{ + return 0.001f; +} + +- (UIView *)tableView:(UITableView *)__unused tableView viewForFooterInSection:(NSInteger)__unused section +{ + return [[UIView alloc] init]; +} + +#pragma mark - + +- (void)setIsLoading:(bool)isLoading +{ + if (isLoading) + { + if (_nearbyVenues.count == 0) + [_activityIndicator startAnimating]; + } + else + { + [_activityIndicator stopAnimating]; + } +} + +#pragma mark - Layout + +- (BOOL)shouldAutorotate +{ + return false; +} + +- (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView previousInset:(UIEdgeInsets)previousInset +{ + if (_mapInFullScreenMode) + return; + + CGPoint contentOffset = scrollView.contentOffset; + + UIEdgeInsets finalInset = self.controllerInset; + finalInset.top += _tableViewTopInset; + + scrollView.contentInset = finalInset; + scrollView.scrollIndicatorInsets = self.controllerScrollInset; + + if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero)) + { + CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - finalInset.bottom); + + if (![self shouldAdjustScrollViewInsetsForInversedLayout]) + contentOffset.y += previousInset.top - finalInset.top; + + contentOffset.y = MAX(-finalInset.top, MIN(contentOffset.y, maxOffset)); + [scrollView setContentOffset:contentOffset animated:false]; + } + else if (contentOffset.y < finalInset.top) + { + contentOffset.y = -finalInset.top; + [scrollView setContentOffset:contentOffset animated:false]; + } +} + +- (void)controllerInsetUpdated:(UIEdgeInsets)previousInset +{ + if (_searchMixin != nil) + { + UIEdgeInsets inset = self.controllerInset; + inset.top -= 44; + [_searchMixin controllerInsetUpdated:inset]; + } + + [super controllerInsetUpdated:previousInset]; +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; + + if (_searchMixin != nil) + [_searchMixin controllerLayoutUpdated:[TGViewController screenSizeForInterfaceOrientation:toInterfaceOrientation]]; +} + +- (void)_layoutTableProgressViews +{ + _activityIndicator.center = CGPointMake(_nearbyVenuesTableView.frame.size.width / 2, (_nearbyVenuesTableView.frame.size.height - [TGLocationPickerController mapHeight] + 20) / 2 + (_nearbyVenuesTableView.contentInset.top + _nearbyVenuesTableView.contentOffset.y) / 2); + + _messageLabel.frame = CGRectMake(0, _activityIndicator.center.y - _messageLabel.frame.size.height, _messageLabel.frame.size.width, _messageLabel.frame.size.height); +} + ++ (CGFloat)mapHeight +{ + static dispatch_once_t onceToken; + static CGFloat mapHeight = 0; + dispatch_once(&onceToken, ^ + { + mapHeight = [TGViewController isWidescreen] ? TGLocationPickerMapWidescreenHeight : TGLocationPickerMapHeight; + }); + return mapHeight; +} + +@end diff --git a/LegacyComponents/TGLocationPinAnnotationView.h b/LegacyComponents/TGLocationPinAnnotationView.h new file mode 100644 index 0000000000..34dd9cbb78 --- /dev/null +++ b/LegacyComponents/TGLocationPinAnnotationView.h @@ -0,0 +1,11 @@ +#import "TGPinAnnotationView.h" + +@interface TGLocationPinAnnotationView : TGPinAnnotationView + +@property (nonatomic, copy) void(^getDirectionsPressed)(void); + +@end + +extern NSString * const TGLocationPinAnnotationKind; + +extern NSString * const TGLocationETAKey; \ No newline at end of file diff --git a/LegacyComponents/TGLocationPinAnnotationView.m b/LegacyComponents/TGLocationPinAnnotationView.m new file mode 100644 index 0000000000..a6c3ae2b4d --- /dev/null +++ b/LegacyComponents/TGLocationPinAnnotationView.m @@ -0,0 +1,208 @@ +#import "TGLocationPinAnnotationView.h" + +#import "LegacyComponentsInternal.h" +#import "TGFont.h" + +#import "TGLocationAnnotation.h" + +NSString *const TGLocationPinAnnotationKind = @"TGLocationPinAnnotation"; + +NSString *const TGLocationETAKey = @"eta"; + +@interface TGLocationPinAnnotationView () +{ + UIButton *_drivingButton; + UIImageView *_drivingIconView; + UILabel *_drivingEtaLabel; + + UIImageView *_accessoryView; +} +@end + +@implementation TGLocationPinAnnotationView + +- (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + self.exclusiveTouch = true; + + _titleLabel.font = TGSystemFontOfSize(15.5f); + _titleLabel.text = annotation.title; + _titleLabel.textColor = [UIColor blackColor]; + + _subtitleLabel.font = TGSystemFontOfSize(12.5f); + _subtitleLabel.textColor = UIColorRGB(0x2289e8); + + _drivingButton = [[UIButton alloc] init]; + _drivingButton.adjustsImageWhenHighlighted = false; + _drivingButton.exclusiveTouch = true; + [_drivingButton setBackgroundImage:[TGComponentsImageNamed(@"CalloutDrivingBackground.png") resizableImageWithCapInsets:UIEdgeInsetsMake(8, 8, 8, 1)] forState:UIControlStateNormal]; + [_drivingButton setBackgroundImage:[TGComponentsImageNamed(@"CalloutDrivingBackground_Highlighted.png") resizableImageWithCapInsets:UIEdgeInsetsMake(8, 8, 8, 1)] forState:UIControlStateHighlighted]; + [_drivingButton addTarget:self action:@selector(drivingButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_calloutWrapper addSubview:_drivingButton]; + + _drivingIconView = [[UIImageView alloc] initWithFrame:CGRectMake(11, 16, 22, 15)]; + _drivingIconView.image = TGComponentsImageNamed(@"CalloutDrivingIcon.png"); + [_drivingButton addSubview:_drivingIconView]; + + _drivingEtaLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 44, 15)]; + _drivingEtaLabel.backgroundColor = [UIColor clearColor]; + _drivingEtaLabel.numberOfLines = 1; + _drivingEtaLabel.minimumScaleFactor = 8.0f / 11.0f; + _drivingEtaLabel.textColor = [UIColor whiteColor]; + _drivingEtaLabel.textAlignment = NSTextAlignmentCenter; + _drivingEtaLabel.adjustsFontSizeToFitWidth = true; + [_drivingButton addSubview:_drivingEtaLabel]; + + _accessoryView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 9, 14)]; + _accessoryView.image = TGComponentsImageNamed(@"CalloutAccessory"); + [_calloutWrapper addSubview:_accessoryView]; + } + return self; +} + +- (void)setAnnotation:(id)annotation +{ + [super setAnnotation:annotation]; + + _titleLabel.text = annotation.title; + _subtitleLabel.text = annotation.subtitle; + + if ([annotation isKindOfClass:[TGLocationAnnotation class]]) + { + TGLocationAnnotation *locationAnnotation = (TGLocationAnnotation *)annotation; + + NSTimeInterval eta = [locationAnnotation.userInfo[TGLocationETAKey] doubleValue]; + [self setDrivingETA:eta]; + } +} + +#pragma mark - Actions + +- (void)drivingButtonPressed +{ + if (self.getDirectionsPressed != nil) + self.getDirectionsPressed(); +} + +#pragma mark - Properties + +- (void)setDrivingETA:(NSTimeInterval)drivingETA +{ + if (drivingETA > 0 && drivingETA < 60 * 60 * 10) + { + drivingETA = MAX(drivingETA, 60); + + NSInteger minutes = (NSInteger)(drivingETA / 60) % 60; + NSInteger hours = (NSInteger)(drivingETA / 3600.0f); + + NSString *string = nil; + + if (hours < 1) + { + string = [NSString stringWithFormat:TGLocalized(@"Map.ETAMinutes_any"), [NSString stringWithFormat:@"**%d**", (int)minutes]]; + } + else + { + if (hours == 1 && minutes == 0) + { + string = [NSString stringWithFormat:TGLocalized(@"Map.ETAHours_1"), @"**1**"]; + } + else + { + string = [NSString stringWithFormat:TGLocalized(@"Map.ETAHours_any"), [NSString stringWithFormat:@"**%d**:**%02d**", (int)hours, (int)minutes]]; + } + } + + NSMutableArray *boldRanges = [[NSMutableArray alloc] init]; + NSMutableString *cleanText = [[NSMutableString alloc] initWithString:string]; + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"**"]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"**"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + [boldRanges addObject:[NSValue valueWithRange:NSMakeRange(startRange.location, endRange.location - startRange.location)]]; + } + + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:cleanText]; + [attributedString addAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName: TGSystemFontOfSize(11)} range:NSMakeRange(0, attributedString.length)]; + + NSDictionary *boldAttributes = @{NSFontAttributeName: TGBoldSystemFontOfSize(11)}; + for (NSValue *range in boldRanges) + [attributedString addAttributes:boldAttributes range:[range rangeValue]]; + + _drivingEtaLabel.attributedText = attributedString; + } + else + { + _drivingEtaLabel.attributedText = nil; + } +} + +#pragma mark - Layout + +- (void)sizeToFit +{ + CGRect frame = _calloutWrapper.frame; + + CGSize titleLabelSize = [_titleLabel sizeThatFits:CGSizeMake(214, FLT_MAX)]; + CGSize subtitleLabelSize = [_subtitleLabel sizeThatFits:CGSizeMake(214, FLT_MAX)]; + + CGFloat labelsWidth = MAX(titleLabelSize.width, subtitleLabelSize.width) + 86; + + frame.size.width = MIN(300, MAX(labelsWidth, 194)); + frame.size.height = 46; + + _calloutWrapper.frame = frame; +} + +- (void)layoutSubviews +{ + _drivingButton.frame = CGRectMake(0.5f, 0.5f, 44, _calloutWrapper.frame.size.height - 1); + + CGFloat iconViewOriginY = (_drivingButton.frame.size.height - _drivingIconView.frame.size.height) / 2; + if (_drivingEtaLabel.attributedText.length > 0) + { + iconViewOriginY -= 4; + _drivingEtaLabel.alpha = 1.0f; + } + else + { + _drivingEtaLabel.alpha = 0.0f; + } + _drivingIconView.frame = CGRectMake((_drivingButton.frame.size.width - _drivingIconView.frame.size.width) / 2, iconViewOriginY, _drivingIconView.frame.size.width, _drivingIconView.frame.size.height); + _drivingEtaLabel.frame = CGRectMake(5, _drivingButton.frame.size.height / 2 + 4, 34, 15); + + CGFloat titleLabelOriginY = _calloutWrapper.frame.size.height / 2 - 10; + CGFloat subtitleLabelOriginY = _calloutWrapper.frame.size.height / 2 - 7; + if (_subtitleLabel.text.length > 0) + { + titleLabelOriginY = 5; + subtitleLabelOriginY = 25; + _subtitleLabel.alpha = 1.0f; + } + else + { + _subtitleLabel.alpha = 0.0f; + } + + _titleLabel.frame = CGRectMake(_drivingButton.frame.size.width + 12, titleLabelOriginY, _calloutWrapper.frame.size.width - 82, 19); + _subtitleLabel.frame = CGRectMake(_drivingButton.frame.size.width + 12, subtitleLabelOriginY, _calloutWrapper.frame.size.width - 82, 15); + + _accessoryView.frame = CGRectMake(_calloutWrapper.frame.size.width - _accessoryView.frame.size.width - 13, (_calloutWrapper.frame.size.height - _accessoryView.frame.size.height) / 2, _accessoryView.frame.size.width, _accessoryView.frame.size.height); + + [super layoutSubviews]; +} + +@end diff --git a/LegacyComponents/TGLocationPinView.h b/LegacyComponents/TGLocationPinView.h new file mode 100644 index 0000000000..968d919d17 --- /dev/null +++ b/LegacyComponents/TGLocationPinView.h @@ -0,0 +1,12 @@ +#import + +@interface TGLocationPinWrapperView : UIView + +@end + +@interface TGLocationPinView : UIView + +@property (nonatomic, assign, getter=isPinRaised) bool pinRaised; +- (void)setPinRaised:(bool)raised animated:(bool)animated completion:(void (^)(void))completion; + +@end diff --git a/LegacyComponents/TGLocationPinView.m b/LegacyComponents/TGLocationPinView.m new file mode 100644 index 0000000000..4d4b66dc22 --- /dev/null +++ b/LegacyComponents/TGLocationPinView.m @@ -0,0 +1,127 @@ +#import "TGLocationPinView.h" + +#import "LegacyComponentsInternal.h" + +const CGSize TGLocationPinSize = { 13.5f, 36 }; +const CGFloat TGLocationPinDamping = 2.0f; +const CGFloat TGLocationPinPinnedOrigin = 47; +const CGFloat TGLocationPinRaisedOrigin = 7; +const CGPoint TGLocationPinShadowPinnedOrigin = { 43, 47 }; +const CGPoint TGLocationPinShadowRaisedOrigin = { 87, -33 }; + +@implementation TGLocationPinWrapperView + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *view = [super hitTest:point withEvent:event]; + if (view == self) + return nil; + + return view; +} + +@end + +@interface TGLocationPinView () +{ + UIImageView *_pinView; + UIImageView *_pinPointView; + UIImageView *_shadowView; +} +@end + +@implementation TGLocationPinView + +- (instancetype)init +{ + self = [super initWithFrame:CGRectMake(0, 0, 100, 100)]; + if (self != nil) + { + self.userInteractionEnabled = false; + + _shadowView = [[UIImageView alloc] initWithFrame:CGRectMake(43, 47, 32, 39)]; + _shadowView.alpha = 0.9f; + _shadowView.image = TGComponentsImageNamed(@"LocationPinShadow.png"); + [self addSubview:_shadowView]; + + _pinPointView = [[UIImageView alloc] initWithFrame:CGRectMake(CGFloor(self.frame.size.width / 2 - 2), self.frame.size.height - 18.5f, 3.5f, 1.5f)]; + _pinPointView.image = TGComponentsImageNamed(@"LocationPinPoint.png"); + [self addSubview:_pinPointView]; + + _pinView = [[UIImageView alloc] initWithFrame:CGRectMake(CGFloor(self.frame.size.width / 2 - 7), 47, 13.5f, 36)]; + _pinView.image = TGComponentsImageNamed(@"LocationPin.png"); + [self addSubview:_pinView]; + } + return self; +} + +- (void)setPinRaised:(bool)pinRaised +{ + [self setPinRaised:pinRaised animated:false completion:nil]; +} + +- (void)setPinRaised:(bool)raised animated:(bool)animated completion:(void (^)(void))completion +{ + _pinRaised = raised; + + [_pinView.layer removeAllAnimations]; + [_shadowView.layer removeAllAnimations]; + + if (animated) + { + if (raised) + { + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinRaisedOrigin, TGLocationPinSize.width, TGLocationPinSize.height); + _shadowView.frame = CGRectMake(TGLocationPinShadowRaisedOrigin.x, TGLocationPinShadowRaisedOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height); + } completion:^(BOOL finished) + { + if (finished && completion != nil) + completion(); + }]; + } + else + { + [UIView animateWithDuration:0.2f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height); + _shadowView.frame = CGRectMake(TGLocationPinShadowPinnedOrigin.x, TGLocationPinShadowPinnedOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height); + } completion:^(BOOL finished) + { + if (finished) + { + [UIView animateWithDuration:0.1f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin + TGLocationPinDamping, TGLocationPinSize.width, TGLocationPinSize.height - TGLocationPinDamping); + } completion:^(BOOL finished) + { + if (finished) + { + [UIView animateWithDuration:0.1f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _pinView.frame = CGRectMake(_pinView.frame.origin.x, TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height); + } completion:^(BOOL finished) + { + if (finished && completion != nil) + completion(); + }]; + } + }]; + } + }]; + } + } + else + { + _pinView.frame = CGRectMake(_pinView.frame.origin.x, raised ? TGLocationPinRaisedOrigin : TGLocationPinPinnedOrigin, TGLocationPinSize.width, TGLocationPinSize.height); + + CGPoint shadowOrigin = raised ? TGLocationPinShadowRaisedOrigin : TGLocationPinShadowPinnedOrigin; + _shadowView.frame = CGRectMake(shadowOrigin.x, shadowOrigin.y, _shadowView.frame.size.width, _shadowView.frame.size.height); + + if (completion != nil) + completion(); + } +} + +@end diff --git a/LegacyComponents/TGLocationReverseGeocodeResult.h b/LegacyComponents/TGLocationReverseGeocodeResult.h new file mode 100644 index 0000000000..bc1b730f6f --- /dev/null +++ b/LegacyComponents/TGLocationReverseGeocodeResult.h @@ -0,0 +1,20 @@ +#import + +@interface TGLocationReverseGeocodeResult : NSObject + +@property (nonatomic, readonly) NSString *identifier; +@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; + +@property (nonatomic, readonly) NSString *displayAddress; + +@property (nonatomic, readonly) NSString *country; +@property (nonatomic, readonly) NSString *countryAbbr; +@property (nonatomic, readonly) NSString *state; +@property (nonatomic, readonly) NSString *stateAbbr; +@property (nonatomic, readonly) NSString *city; +@property (nonatomic, readonly) NSString *district; +@property (nonatomic, readonly) NSString *street; + ++ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithDictionary:(NSDictionary *)dictionary; + +@end diff --git a/LegacyComponents/TGLocationReverseGeocodeResult.m b/LegacyComponents/TGLocationReverseGeocodeResult.m new file mode 100644 index 0000000000..a528f0d826 --- /dev/null +++ b/LegacyComponents/TGLocationReverseGeocodeResult.m @@ -0,0 +1,59 @@ +#import "TGLocationReverseGeocodeResult.h" + +@implementation TGLocationReverseGeocodeResult + ++ (TGLocationReverseGeocodeResult *)reverseGeocodeResultWithDictionary:(NSDictionary *)dictionary +{ + TGLocationReverseGeocodeResult *result = [[TGLocationReverseGeocodeResult alloc] init]; + + for (NSDictionary *component in dictionary[@"address_components"]) + { + NSArray *types = component[@"types"]; + __unused NSString *shortName = component[@"short_name"]; + NSString *longName = component[@"long_name"]; + + if ([types containsObject:@"country"]) + { + result->_country = longName; + result->_countryAbbr = shortName; + } + else if ([types containsObject:@"administrative_area_level_1"]) + { + result->_state = longName; + result->_stateAbbr = shortName; + } + else if ([types containsObject:@"locality"]) + { + result->_city = longName; + } + else if ([types containsObject:@"sublocality"]) + { + result->_district = longName; + } + else if ([types containsObject:@"neighborhood"]) + { + if (result->_district.length == 0) + result->_district = longName; + } + else if ([types containsObject:@"route"]) + { + result->_street = longName; + } + } + + return result; +} + +- (NSString *)displayAddress +{ + if (self.street.length > 0) + return self.street; + else if (self.city.length > 0) + return self.city; + else if (self.country.length > 0) + return self.country; + + return nil; +} + +@end \ No newline at end of file diff --git a/LegacyComponents/TGLocationSectionHeaderCell.h b/LegacyComponents/TGLocationSectionHeaderCell.h new file mode 100644 index 0000000000..52eaea9297 --- /dev/null +++ b/LegacyComponents/TGLocationSectionHeaderCell.h @@ -0,0 +1,10 @@ +#import + +@interface TGLocationSectionHeaderCell : UITableViewCell + +- (void)configureWithTitle:(NSString *)title; + +@end + +extern NSString *const TGLocationSectionHeaderKind; +extern const CGFloat TGLocationSectionHeaderHeight; diff --git a/LegacyComponents/TGLocationSectionHeaderCell.m b/LegacyComponents/TGLocationSectionHeaderCell.m new file mode 100644 index 0000000000..5e6229f59c --- /dev/null +++ b/LegacyComponents/TGLocationSectionHeaderCell.m @@ -0,0 +1,47 @@ +#import "TGLocationSectionHeaderCell.h" + +#import "LegacyComponentsInternal.h" +#import "TGFont.h" + +NSString *const TGLocationSectionHeaderKind = @"TGLocationSectionHeaderKind"; +const CGFloat TGLocationSectionHeaderHeight = 28.5f; + +@interface TGLocationSectionHeaderCell () +{ + UILabel *_titleLabel; +} +@end + +@implementation TGLocationSectionHeaderCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + self.selectedBackgroundView = [[UIView alloc] init]; + self.contentView.backgroundColor = UIColorRGB(0xf7f7f7); + + _titleLabel = [[UILabel alloc] init]; + _titleLabel.backgroundColor = self.contentView.backgroundColor; + _titleLabel.font = TGMediumSystemFontOfSize(14); + _titleLabel.textColor = UIColorRGB(0x8e8e93); + [self addSubview:_titleLabel]; + } + return self; +} + +- (void)configureWithTitle:(NSString *)title +{ + _titleLabel.text = title; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGFloat padding = 14; + _titleLabel.frame = CGRectMake(padding, 0, self.frame.size.width - padding, self.frame.size.height - 2); +} + +@end diff --git a/LegacyComponents/TGLocationSignals.h b/LegacyComponents/TGLocationSignals.h new file mode 100644 index 0000000000..637b27aa57 --- /dev/null +++ b/LegacyComponents/TGLocationSignals.h @@ -0,0 +1,20 @@ +#import +#import + +typedef enum { + TGLocationPlacesServiceNone, + TGLocationPlacesServiceFoursquare, + TGLocationPlacesServiceGooglePlaces +} TGLocationPlacesService; + +@interface TGLocationSignals : NSObject + ++ (SSignal *)searchNearbyPlacesWithQuery:(NSString *)query coordinate:(CLLocationCoordinate2D)coordinate service:(TGLocationPlacesService)service; ++ (SSignal *)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate; + ++ (void)storeLastKnownUserLocation:(CLLocation *)location; ++ (CLLocation *)lastKnownUserLocation; + ++ (SSignal *)userLocation:(SVariable *)locationRequired; + +@end diff --git a/LegacyComponents/TGLocationSignals.m b/LegacyComponents/TGLocationSignals.m new file mode 100644 index 0000000000..bac2ee15f7 --- /dev/null +++ b/LegacyComponents/TGLocationSignals.m @@ -0,0 +1,285 @@ +#import "TGLocationSignals.h" + +#import "LegacyComponentsInternal.h" +#import "TGStringUtils.h" + +#import + +#import + +#import "TGLocationVenue.h" +#import "TGLocationReverseGeocodeResult.h" + +NSString *const TGLocationFoursquareSearchEndpointUrl = @"https://api.foursquare.com/v2/venues/search/"; +NSString *const TGLocationFoursquareClientId = @"BN3GWQF1OLMLKKQTFL0OADWD1X1WCDNISPPOT1EMMUYZTQV1"; +NSString *const TGLocationFoursquareClientSecret = @"WEEZHCKI040UVW2KWW5ZXFAZ0FMMHKQ4HQBWXVSX4WXWBWYN"; +NSString *const TGLocationFoursquareVersion = @"20150326"; +NSString *const TGLocationFoursquareVenuesCountLimit = @"25"; +NSString *const TGLocationFoursquareLocale = @"en"; + +NSString *const TGLocationGooglePlacesSearchEndpointUrl = @"https://maps.googleapis.com/maps/api/place/nearbysearch/json"; +NSString *const TGLocationGooglePlacesApiKey = @"AIzaSyBCTH4aAdvi0MgDGlGNmQAaFS8GTNBrfj4"; +NSString *const TGLocationGooglePlacesRadius = @"150"; +NSString *const TGLocationGooglePlacesLocale = @"en"; + +NSString *const TGLocationGoogleGeocodeLocale = @"en"; + +@interface TGLocationHelper : NSObject { + CLLocationManager *_locationManager; + void (^_locationDetermined)(CLLocation *); + bool _startedUpdating; +} + +@end + +@implementation TGLocationHelper + +- (instancetype)initWithLocationDetermined:(void (^)(CLLocation *))locationDetermined { + self = [super init]; + if (self != nil) { + _locationDetermined = [locationDetermined copy]; + + _locationManager = [[CLLocationManager alloc] init]; + _locationManager.delegate = self; + _locationManager.distanceFilter = kCLDistanceFilterNone; + _locationManager.desiredAccuracy = kCLLocationAccuracyBest; + + bool startUpdating = false; + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { + switch ([CLLocationManager authorizationStatus]) + { + case kCLAuthorizationStatusAuthorizedAlways: + case kCLAuthorizationStatusAuthorizedWhenInUse: + startUpdating = true; + default: + break; + } + } + + if (startUpdating) { + [self startUpdating]; + } + } + return self; +} + +- (void)dealloc { + [_locationManager stopUpdatingLocation]; +} + +- (void)startUpdating { + if (!_startedUpdating) { + _startedUpdating = true; + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { + [_locationManager requestWhenInUseAuthorization]; + } + [_locationManager startUpdatingLocation]; + } +} + +- (void)locationManager:(CLLocationManager *)__unused manager didUpdateLocations:(NSArray *)locations { + if (locations.count != 0) { + if (_locationDetermined) { + _locationDetermined([locations lastObject]); + } + } +} + +@end + +@implementation TGLocationSignals + ++ (SSignal *)reverseGeocodeCoordinate:(CLLocationCoordinate2D)coordinate +{ + NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"https://maps.googleapis.com/maps/api/geocode/json?latlng=%f,%f&sensor=true&language=%@", coordinate.latitude, coordinate.longitude, TGLocationGoogleGeocodeLocale]]; + + return [[[LegacyComponentsGlobals provider] jsonForHttpLocation:url.absoluteString] map:^id(id json) + { + if (![json respondsToSelector:@selector(objectForKey:)]) + return nil; + + NSArray *results = json[@"results"]; + if (![results respondsToSelector:@selector(objectAtIndex:)]) + return nil; + + if (![results.firstObject isKindOfClass:[NSDictionary class]]) + return nil; + + return [TGLocationReverseGeocodeResult reverseGeocodeResultWithDictionary:results.firstObject]; + }]; +} + ++ (SSignal *)searchNearbyPlacesWithQuery:(NSString *)query coordinate:(CLLocationCoordinate2D)coordinate service:(TGLocationPlacesService)service +{ + switch (service) + { + case TGLocationPlacesServiceGooglePlaces: + return [self _searchGooglePlacesWithQuery:query coordinate:coordinate]; + + default: + return [self _searchFoursquareVenuesWithQuery:query coordinate:coordinate]; + } +} + ++ (SSignal *)_searchFoursquareVenuesWithQuery:(NSString *)query coordinate:(CLLocationCoordinate2D)coordinate +{ + NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; + parameters[@"limit"] = TGLocationFoursquareVenuesCountLimit; + parameters[@"ll"] = [NSString stringWithFormat:@"%lf,%lf", coordinate.latitude, coordinate.longitude]; + if (query.length > 0) + parameters[@"query"] = query; + + NSString *url = [self _urlForService:TGLocationPlacesServiceFoursquare parameters:parameters]; + return [[[LegacyComponentsGlobals provider] jsonForHttpLocation:url] map:^id(id json) + { + if (![json respondsToSelector:@selector(objectForKey:)]) + return nil; + + NSArray *results = json[@"response"][@"venues"]; + if (![results respondsToSelector:@selector(objectAtIndex:)]) + return nil; + + NSMutableArray *venues = [[NSMutableArray alloc] init]; + for (NSDictionary *result in results) + { + TGLocationVenue *venue = [TGLocationVenue venueWithFoursquareDictionary:result]; + if (venue != nil) + [venues addObject:venue]; + } + + return venues; + }]; +} + ++ (SSignal *)_searchGooglePlacesWithQuery:(NSString *)query coordinate:(CLLocationCoordinate2D)coordinate +{ + NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init]; + parameters[@"location"] = [NSString stringWithFormat:@"%lf,%lf", coordinate.latitude, coordinate.longitude]; + if (query.length > 0) + parameters[@"name"] = query; + + NSString *url = [self _urlForService:TGLocationPlacesServiceGooglePlaces parameters:parameters]; + return [[[LegacyComponentsGlobals provider] jsonForHttpLocation:url] map:^id(id json) + { + if (![json respondsToSelector:@selector(objectForKey:)]) + return nil; + + NSArray *results = json[@"results"]; + if (![results respondsToSelector:@selector(objectAtIndex:)]) + return nil; + + NSMutableArray *venues = [[NSMutableArray alloc] init]; + for (NSDictionary *result in results) + { + TGLocationVenue *venue = [TGLocationVenue venueWithGooglePlacesDictionary:result]; + if (venue != nil) + [venues addObject:venue]; + } + + return venues; + }]; +} + + + ++ (NSString *)_urlForService:(TGLocationPlacesService)service parameters:(NSDictionary *)parameters +{ + if (service == TGLocationPlacesServiceNone) + return nil; + + NSMutableDictionary *finalParameters = [[self _defaultParametersForService:service] mutableCopy]; + [finalParameters addEntriesFromDictionary:parameters]; + + NSMutableString *queryString = [[NSMutableString alloc] init]; + [finalParameters enumerateKeysAndObjectsUsingBlock:^(id key, id obj, __unused BOOL *stop) { + if (queryString.length != 0) { + [queryString appendString:@"&"]; + } + [queryString appendString:[TGStringUtils stringByEscapingForURL:[NSString stringWithFormat:@"%@", key]]]; + [queryString appendString:@"="]; + [queryString appendString:[TGStringUtils stringByEscapingForURL:[NSString stringWithFormat:@"%@", obj]]]; + }]; + + NSString *urlString = [NSString stringWithFormat:@"%@?%@", [self _endpointUrlForService:service], queryString]; + + return urlString; +} + ++ (NSString *)_endpointUrlForService:(TGLocationPlacesService)service +{ + switch (service) + { + case TGLocationPlacesServiceGooglePlaces: + return TGLocationGooglePlacesSearchEndpointUrl; + + case TGLocationPlacesServiceFoursquare: + return TGLocationFoursquareSearchEndpointUrl; + + default: + return nil; + } +} + ++ (NSDictionary *)_defaultParametersForService:(TGLocationPlacesService)service +{ + switch (service) + { + case TGLocationPlacesServiceGooglePlaces: + return @ + { + @"key": TGLocationGooglePlacesApiKey, + @"language": TGLocationGooglePlacesLocale, + @"radius": TGLocationGooglePlacesRadius, + @"sensor": @"true" + }; + + case TGLocationPlacesServiceFoursquare: + return @ + { + @"v": TGLocationFoursquareVersion, + @"locale": TGLocationFoursquareLocale, + @"client_id": TGLocationFoursquareClientId, + @"client_secret" :TGLocationFoursquareClientSecret + }; + + default: + return nil; + } +} + +#pragma mark - + +static CLLocation *lastKnownUserLocation; + ++ (void)storeLastKnownUserLocation:(CLLocation *)location +{ + lastKnownUserLocation = location; +} + ++ (CLLocation *)lastKnownUserLocation +{ + NSTimeInterval locationAge = -[lastKnownUserLocation.timestamp timeIntervalSinceNow]; + if (locationAge > 600) + lastKnownUserLocation = nil; + + return lastKnownUserLocation; +} + ++ (SSignal *)userLocation:(SVariable *)locationRequired { + return [[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { + TGLocationHelper *helper = [[TGLocationHelper alloc] initWithLocationDetermined:^(CLLocation *location) { + [subscriber putNext:location]; + }]; + + id requiredDisposable = [[[[locationRequired signal] take:1] deliverOn:[SQueue mainQueue]] startWithNext:^(__unused id next) { + [helper startUpdating]; + }]; + + return [[SBlockDisposable alloc] initWithBlock:^{ + [helper description]; // keep reference + [requiredDisposable dispose]; + }]; + }] startOn:[SQueue mainQueue]]; +} + +@end diff --git a/LegacyComponents/TGLocationTitleView.h b/LegacyComponents/TGLocationTitleView.h new file mode 100644 index 0000000000..aa96ebd5fe --- /dev/null +++ b/LegacyComponents/TGLocationTitleView.h @@ -0,0 +1,12 @@ +#import + +@interface TGLocationTitleView : UIView + +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *address; + +@property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation; +@property (nonatomic, assign) CGFloat backButtonWidth; +@property (nonatomic, assign) CGFloat actionsButtonWidth; + +@end diff --git a/LegacyComponents/TGLocationTitleView.m b/LegacyComponents/TGLocationTitleView.m new file mode 100644 index 0000000000..d8198d51e8 --- /dev/null +++ b/LegacyComponents/TGLocationTitleView.m @@ -0,0 +1,135 @@ +#import "TGLocationTitleView.h" + +#import "LegacyComponentsInternal.h" +#import "TGFont.h" + +@interface TGLocationTitleView () +{ + UILabel *_titleLabel; + UILabel *_addressLabel; +} +@end + +@implementation TGLocationTitleView + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, frame.size.width, 20)]; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.font = TGBoldSystemFontOfSize(17); + _titleLabel.textColor = [UIColor blackColor]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:_titleLabel]; + + _addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 21, frame.size.width, 21)]; + _addressLabel.backgroundColor = [UIColor clearColor]; + _addressLabel.font = TGSystemFontOfSize(13.0f); + _addressLabel.textColor = UIColorRGB(0x787878); + _addressLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:_addressLabel]; + } + return self; +} + +- (NSString *)title +{ + return _titleLabel.text; +} + +- (void)setTitle:(NSString *)title +{ + _titleLabel.text = title; + [self setNeedsLayout]; +} + +- (NSString *)address +{ + return _addressLabel.text; +} + +- (void)setAddress:(NSString *)address +{ + _addressLabel.text = address; + [self setNeedsLayout]; +} + +- (UIView *)_findNavigationBar:(UIView *)view +{ + if (view.superview == nil) + return nil; + else if ([view.superview isKindOfClass:[UINavigationBar class]]) + return view.superview; + else + return [self _findNavigationBar:view.superview]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + [_titleLabel sizeToFit]; + [_addressLabel sizeToFit]; + + CGRect titleFrame = _titleLabel.frame; + titleFrame.size.width = CGCeil(titleFrame.size.width); + + CGRect addressFrame = _addressLabel.frame; + addressFrame.size.width = CGCeil(addressFrame.size.width); + + UIView *navigationBar = [self _findNavigationBar:self]; + CGRect offsetRect = [self.superview convertRect:self.frame toView:navigationBar]; + + UIEdgeInsets edges = UIEdgeInsetsMake(0, self.backButtonWidth, 0, navigationBar.frame.size.width - self.actionsButtonWidth); + + if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) + { + if (_addressLabel.text.length > 0) + titleFrame.origin.y = 5; + else + titleFrame.origin.y = 12; + + addressFrame.origin.y = 23.5f; + + titleFrame.origin.x = (navigationBar.frame.size.width - titleFrame.size.width) / 2; + if (titleFrame.origin.x < edges.left || CGRectGetMaxX(titleFrame) > edges.right) + { + titleFrame.origin.x = edges.left; + titleFrame.size.width = edges.right - edges.left; + } + + CGFloat titleCenter = CGRectGetMidX(titleFrame); + addressFrame.origin.x = titleCenter - addressFrame.size.width / 2; + if (addressFrame.origin.x < edges.left || CGRectGetMaxX(addressFrame) > edges.right) + { + addressFrame.origin.x = edges.left; + addressFrame.size.width = edges.right - edges.left; + } + } + else + { + titleFrame.origin.y = 10; + addressFrame.origin.y = 13; + + CGFloat jointWidth = titleFrame.size.width + addressFrame.size.width + 6; + CGFloat jointOrigin = (navigationBar.frame.size.width - jointWidth) / 2; + if (jointOrigin < edges.left || jointOrigin + jointWidth > edges.right) + { + jointOrigin = edges.left; + + CGFloat newJointWidth = edges.right - edges.left; + addressFrame.size.width -= (jointWidth - newJointWidth); + jointWidth = newJointWidth; + } + + titleFrame.origin.x = jointOrigin; + addressFrame.origin.x = jointOrigin + jointWidth - addressFrame.size.width; + } + + _titleLabel.frame = CGRectOffset(titleFrame, -offsetRect.origin.x, 0); + _addressLabel.frame = CGRectOffset(addressFrame, -offsetRect.origin.x, 0); +} + +@end diff --git a/LegacyComponents/TGLocationTrackingButton.h b/LegacyComponents/TGLocationTrackingButton.h new file mode 100644 index 0000000000..4e6618d6c5 --- /dev/null +++ b/LegacyComponents/TGLocationTrackingButton.h @@ -0,0 +1,21 @@ + +#import + +typedef enum { + TGLocationTrackingModeNone, + TGLocationTrackingModeFollow, + TGLocationTrackingModeFollowWithHeading +} TGLocationTrackingMode; + +@interface TGLocationTrackingButton : UIButton + +@property (nonatomic, assign) TGLocationTrackingMode trackingMode; +- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated; + +@property (nonatomic, assign, getter=isLocationAvailable) bool locationAvailable; +- (void)setLocationAvailable:(bool)available animated:(bool)animated; + ++ (TGLocationTrackingMode)locationTrackingModeWithUserTrackingMode:(MKUserTrackingMode)mode; ++ (MKUserTrackingMode)userTrackingModeWithLocationTrackingMode:(TGLocationTrackingMode)mode; + +@end diff --git a/LegacyComponents/TGLocationTrackingButton.m b/LegacyComponents/TGLocationTrackingButton.m new file mode 100644 index 0000000000..e823b61327 --- /dev/null +++ b/LegacyComponents/TGLocationTrackingButton.m @@ -0,0 +1,142 @@ +#import "TGLocationTrackingButton.h" + +#import "LegacyComponentsInternal.h" + +@interface TGLocationTrackingButton () +{ + UIImageView *_noneModeIconView; + UIImageView *_followModeIconView; + UIImageView *_followWithHeadingModeIconView; + UIActivityIndicatorView *_activityIndicator; +} +@end + +@implementation TGLocationTrackingButton + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + self.exclusiveTouch = true; + + _noneModeIconView = [[UIImageView alloc] initWithFrame:self.bounds]; + _noneModeIconView.contentMode = UIViewContentModeCenter; + _noneModeIconView.image = TGComponentsImageNamed(@"TrackingLocationOff.png"); + [self addSubview:_noneModeIconView]; + + _followModeIconView = [[UIImageView alloc] initWithFrame:self.bounds]; + _followModeIconView.contentMode = UIViewContentModeCenter; + _followModeIconView.image = TGComponentsImageNamed(@"TrackingLocation.png"); + [self addSubview:_followModeIconView]; + + _followWithHeadingModeIconView = [[UIImageView alloc] initWithFrame:CGRectOffset(self.bounds, 1, 0.5f)]; + _followWithHeadingModeIconView.contentMode = UIViewContentModeCenter; + _followWithHeadingModeIconView.image = TGComponentsImageNamed(@"TrackingHeading.png"); + [self addSubview:_followWithHeadingModeIconView]; + + _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityIndicator.userInteractionEnabled = false; + _activityIndicator.frame = CGRectOffset(_activityIndicator.frame, 0, 0); + _activityIndicator.alpha = 0.0f; + _activityIndicator.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + [self addSubview:_activityIndicator]; + + _locationAvailable = true; + [self setTrackingMode:TGLocationTrackingModeNone]; + } + return self; +} + +- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode +{ + [self setTrackingMode:trackingMode animated:false]; +} + +- (void)setTrackingMode:(TGLocationTrackingMode)trackingMode animated:(bool)animated +{ + _trackingMode = trackingMode; + + CGFloat noneModeAlpha = (trackingMode == TGLocationTrackingModeNone) ? 1.0f : 0.0f; + CGFloat followModeAlpha = (trackingMode == TGLocationTrackingModeFollow) ? 1.0f : 0.0f; + CGFloat followWithHeadingModeAlpha = (trackingMode == TGLocationTrackingModeFollowWithHeading) ? 1.0f : 0.0f; + + void (^changeBlock)(void) = ^ + { + _noneModeIconView.alpha = noneModeAlpha; + _followModeIconView.alpha = followModeAlpha; + _followWithHeadingModeIconView.alpha = followWithHeadingModeAlpha; + + if (followWithHeadingModeAlpha < FLT_EPSILON) + { + _noneModeIconView.transform = CGAffineTransformIdentity; + _followModeIconView.transform = CGAffineTransformIdentity; + _followWithHeadingModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + } + else + { + _noneModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + _followModeIconView.transform = CGAffineTransformMakeScale(0.1f, 0.1f); + _followWithHeadingModeIconView.transform = CGAffineTransformIdentity; + } + }; + + if (animated) + [UIView animateWithDuration:0.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:changeBlock completion:nil]; + else + changeBlock(); +} + +- (void)setIsLocationAvailable:(bool)available +{ + [self setLocationAvailable:available animated:false]; +} + +- (void)setLocationAvailable:(bool)available animated:(bool)animated +{ + if (available == _locationAvailable) + return; + + _locationAvailable = available; + + if (animated) + { + + } + else + { + + } +} + ++ (TGLocationTrackingMode)locationTrackingModeWithUserTrackingMode:(MKUserTrackingMode)mode +{ + switch (mode) + { + case MKUserTrackingModeFollow: + return TGLocationTrackingModeFollow; + + case MKUserTrackingModeFollowWithHeading: + return TGLocationTrackingModeFollowWithHeading; + + default: + return TGLocationTrackingModeNone; + } +} + ++ (MKUserTrackingMode)userTrackingModeWithLocationTrackingMode:(TGLocationTrackingMode)mode +{ + switch (mode) + { + case TGLocationTrackingModeFollow: + return MKUserTrackingModeFollow; + + case TGLocationTrackingModeFollowWithHeading: + return MKUserTrackingModeFollowWithHeading; + + default: + return MKUserTrackingModeNone; + } +} + +@end diff --git a/LegacyComponents/TGLocationUtils.h b/LegacyComponents/TGLocationUtils.h new file mode 100644 index 0000000000..38ccfbf7c3 --- /dev/null +++ b/LegacyComponents/TGLocationUtils.h @@ -0,0 +1,47 @@ +#import +#import + +@interface TGLocationUtils : NSObject + ++ (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region; + ++ (bool)requestWhenInUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager; + ++ (NSString *)stringFromDistance:(CLLocationDistance)distance; ++ (NSString *)stringFromAccuracy:(CLLocationAccuracy)accuracy; + +@end + +@interface TGLocationUtils (GoogleMaps) + ++ (CLLocationDegrees)adjustGMapLatitude:(CLLocationDegrees)latitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom; ++ (CLLocationDegrees)adjustGMapLongitude:(CLLocationDegrees)longitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom; ++ (CLLocationCoordinate2D)adjustGMapCoordinate:(CLLocationCoordinate2D)coordinate withPixelOffset:(CGPoint)offset zoom:(NSInteger)zoom; + +@end + +@interface TGLocationUtils (ThirdPartyAppLauncher) + ++ (void)openMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections locationName:(NSString *)locationName; + ++ (void)openGoogleMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections; ++ (bool)isGoogleMapsInstalled; + ++ (void)openGoogleWithPlaceId:(NSString *)placeId; + ++ (void)openFoursquareWithVenueId:(NSString *)venueId; ++ (bool)isFoursquareInstalled; + ++ (void)openHereMapsWithCoordinate:(CLLocationCoordinate2D)coordinate; ++ (bool)isHereMapsInstalled; + ++ (void)openYandexMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections; ++ (bool)isYandexMapsInstalled; + ++ (void)openDirectionsInYandexNavigatorWithCoordinate:(CLLocationCoordinate2D)coordinate; ++ (bool)isYandexNavigatorInstalled; + ++ (void)openWazeWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections; ++ (bool)isWazeInstalled; + +@end diff --git a/LegacyComponents/TGLocationUtils.m b/LegacyComponents/TGLocationUtils.m new file mode 100644 index 0000000000..067155a3ca --- /dev/null +++ b/LegacyComponents/TGLocationUtils.m @@ -0,0 +1,325 @@ +#import "TGLocationUtils.h" + +#import "LegacyComponentsInternal.h" + +#import + +@implementation TGLocationUtils + ++ (MKMapRect)MKMapRectForCoordinateRegion:(MKCoordinateRegion)region +{ + MKMapPoint a = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta / 2, + region.center.longitude - region.span.longitudeDelta / 2)); + MKMapPoint b = MKMapPointForCoordinate(CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta / 2, + region.center.longitude + region.span.longitudeDelta / 2)); + return MKMapRectMake(MIN(a.x,b.x), MIN(a.y,b.y), ABS(a.x-b.x), ABS(a.y-b.y)); +} + ++ (bool)requestWhenInUserLocationAuthorizationWithLocationManager:(CLLocationManager *)locationManager +{ + CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus]; + + if (authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted) + return false; + + if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) + { + [locationManager requestWhenInUseAuthorization]; + return true; + } + + return false; +} + ++ (NSString *)stringFromDistance:(CLLocationDistance)distance +{ + if (iosMajorVersion() >= 7) + return [[self sharedDistanceFormatter] stringFromDistance:distance]; + else + return [self _customStringFromDistance:distance]; +} + ++ (NSString *)stringFromAccuracy:(CLLocationAccuracy)accuracy +{ + if (iosMajorVersion() >= 7) + return [[self sharedAccuracyFormatter] stringFromDistance:accuracy]; + else + return [self _customStringFromDistance:accuracy]; +} + ++ (NSString *)_customStringFromDistance:(CLLocationDistance)distance +{ + static bool metricUnits = true; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + NSLocale *locale = [NSLocale currentLocale]; + metricUnits = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue]; + }); + + NSString *distanceString = nil; + + if (metricUnits) + { + if (distance >= 1000 * 1000) + distanceString = [[NSString alloc] initWithFormat:@"%.1fK km", distance / (1000.0 * 1000.0)]; + else if (distance > 1000) + distanceString = [[NSString alloc] initWithFormat:@"%.1f km", distance / 1000.0]; + else + distanceString = [[NSString alloc] initWithFormat:@"%d m", (int)distance]; + } + else + { + double feetDistance = distance / 0.3048; + + if (feetDistance >= 5280) + { + char buf[32]; + snprintf(buf, 32, "%.1f", feetDistance / 5280.0); + bool dot = false; + for (int i = 0; i < 32; i++) + { + char c = buf[i]; + if (c == '\0') + break; + else if (c < '0' || c > '9') + { + dot = true; + break; + } + } + distanceString = [[NSString alloc] initWithFormat:@"%s mile%s", buf, dot || feetDistance / 5280.0 > 1.0 ? "s" : ""]; + } + else + { + distanceString = [[NSString alloc] initWithFormat:@"%d %s", (int)feetDistance, (int)feetDistance != 1 ? "feet" : "foot"]; + } + } + + return distanceString; +} + ++ (MKDistanceFormatter *)sharedDistanceFormatter +{ + static dispatch_once_t once; + static MKDistanceFormatter *distanceFormatter; + dispatch_once(&once, ^ + { + distanceFormatter = [[MKDistanceFormatter alloc] init]; + + NSLocale *locale = [NSLocale currentLocale]; + if ([[locale objectForKey:NSLocaleUsesMetricSystem] boolValue]) + distanceFormatter.unitStyle = MKDistanceFormatterUnitStyleAbbreviated; + else + distanceFormatter.unitStyle = MKDistanceFormatterUnitStyleDefault; + }); + + return distanceFormatter; +} + ++ (MKDistanceFormatter *)sharedAccuracyFormatter +{ + static dispatch_once_t once; + static MKDistanceFormatter *accuracyFormatter; + dispatch_once(&once, ^ + { + accuracyFormatter = [[MKDistanceFormatter alloc] init]; + accuracyFormatter.unitStyle = MKDistanceFormatterUnitStyleFull; + }); + + return accuracyFormatter; +} + +@end + +const NSInteger TGGoogleMapsOffset = 268435456; +const CGFloat TGGoogleMapsRadius = TGGoogleMapsOffset / (CGFloat)M_PI; + +@implementation TGLocationUtils (GoogleMaps) + ++ (CLLocationCoordinate2D)adjustGMapCoordinate:(CLLocationCoordinate2D)coordinate withPixelOffset:(CGPoint)offset zoom:(NSInteger)zoom +{ + return CLLocationCoordinate2DMake([self adjustGMapLatitude:coordinate.latitude withPixelOffset:(NSInteger)offset.y zoom:zoom], [self adjustGMapLongitude:coordinate.longitude withPixelOffset:(NSInteger)offset.x zoom:zoom]); +} + ++ (CLLocationDegrees)adjustGMapLatitude:(CLLocationDegrees)latitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom +{ + return [self _yToLatitude:([self _latitudeToY:latitude] + (offset << (21 - zoom)))]; +} + ++ (CLLocationDegrees)adjustGMapLongitude:(CLLocationDegrees)longitude withPixelOffset:(NSInteger)offset zoom:(NSInteger)zoom +{ + return [self _xToLongitude:([self _longitudeToX:longitude] + (offset << (21 - zoom)))]; +} + ++ (NSInteger)_latitudeToY:(CLLocationDegrees)latitude +{ + return (NSInteger)round(TGGoogleMapsOffset - TGGoogleMapsRadius * log((1 + sin(latitude * M_PI / 180.0)) / (1 - sin(latitude * M_PI / 180.0))) / 2); +} + ++ (CLLocationDegrees)_yToLatitude:(NSInteger)y +{ + return (M_PI_2 - 2 * atan(exp((y - TGGoogleMapsOffset) / TGGoogleMapsRadius))) * 180.0 / M_PI; +} + ++ (NSInteger)_longitudeToX:(CLLocationDegrees)longitude +{ + return (NSInteger)round(TGGoogleMapsOffset + TGGoogleMapsRadius * longitude * M_PI / 180); +} + ++ (CLLocationDegrees)_xToLongitude:(NSInteger)x +{ + return (x - TGGoogleMapsOffset) / TGGoogleMapsRadius * 180.0 / M_PI; +} + +@end + +@implementation TGLocationUtils (ThirdPartyAppLauncher) + +#pragma mark Apple Maps + ++ (void)openMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections locationName:(NSString *)locationName +{ + MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate + addressDictionary:nil]; + MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark]; + [mapItem setName:locationName]; + + if (withDirections) + { + NSDictionary *options = @{ MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving }; + MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation]; + [MKMapItem openMapsWithItems:@[ currentLocationMapItem, mapItem ] + launchOptions:options]; + } + else + { + [mapItem openInMapsWithLaunchOptions:nil]; + } +} + +#pragma mark Google Maps + ++ (void)openGoogleMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections +{ + NSURL *url = nil; + NSString *coordinatePair = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude]; + + if (withDirections) + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?daddr=%@&directionsmode=driving&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair]]; + } + else + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"comgooglemaps-x-callback://?center=%@&q=%@&x-success=telegram://?resume=true&&x-source=Telegram", coordinatePair, coordinatePair]]; + } + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isGoogleMapsInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"comgooglemaps-x-callback://"]]; +} + + ++ (void)openGoogleWithPlaceId:(NSString *)placeId +{ + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://maps.google.com/maps/place?cid=%@", placeId]]; + [[LegacyComponentsGlobals provider] openURL:url]; +} +#pragma mark Foursquare + ++ (void)openFoursquareWithVenueId:(NSString *)venueId +{ + NSURL *url = nil; + + if ([self isFoursquareInstalled]) + url = [NSURL URLWithString:[NSString stringWithFormat:@"foursquare://venues/%@", venueId]]; + else + url = [NSURL URLWithString:[NSString stringWithFormat:@"https://foursquare.com/venue/%@", venueId]]; + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isFoursquareInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"foursquare://"]]; +} + +#pragma mark Here Maps + ++ (void)openHereMapsWithCoordinate:(CLLocationCoordinate2D)coordinate +{ + NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"here-location://%f,%f", coordinate.latitude, coordinate.longitude]]; + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isHereMapsInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"here-location://"]]; +} + +#pragma mark Yandex Maps + ++ (void)openYandexMapsWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections +{ + NSURL *url = nil; + + if (withDirections) + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]]; + } + else + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexmaps://maps.yandex.ru/?pt=%f,%f&z=16", coordinate.longitude, coordinate.latitude]]; + } + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isYandexMapsInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexmaps://"]]; +} + +#pragma mark Yandex Navigator + ++ (void)openDirectionsInYandexNavigatorWithCoordinate:(CLLocationCoordinate2D)coordinate +{ + NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"yandexnavi://build_route_on_map?lat_to=%f&lon_to=%f", coordinate.latitude, coordinate.longitude]]; + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isYandexNavigatorInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"yandexnavi://"]]; +} + +#pragma mark - Waze + ++ (void)openWazeWithCoordinate:(CLLocationCoordinate2D)coordinate withDirections:(bool)withDirections +{ + NSURL *url = nil; + + if (withDirections) + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f&navigate=yes", coordinate.latitude, coordinate.longitude]]; + } + else + { + url = [NSURL URLWithString:[[NSString alloc] initWithFormat:@"waze://?ll=%f,%f", coordinate.latitude, coordinate.longitude]]; + } + + [[LegacyComponentsGlobals provider] openURL:url]; +} + ++ (bool)isWazeInstalled +{ + return [[LegacyComponentsGlobals provider] canOpenURL:[NSURL URLWithString:@"waze://"]]; +} + +@end diff --git a/LegacyComponents/TGLocationVenue.h b/LegacyComponents/TGLocationVenue.h new file mode 100644 index 0000000000..0c3b1aff2c --- /dev/null +++ b/LegacyComponents/TGLocationVenue.h @@ -0,0 +1,31 @@ +#import + +@class TGVenueAttachment; + +@interface TGLocationVenue : NSObject + +@property (nonatomic, readonly) NSString *identifier; +@property (nonatomic, readonly) NSString *name; +@property (nonatomic, readonly) NSString *displayAddress; +@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property (nonatomic, readonly) NSString *categoryName; +@property (nonatomic, readonly) NSURL *categoryIconUrl; + +@property (nonatomic, readonly) NSString *provider; + +@property (readonly, nonatomic) NSString *country; +@property (readonly, nonatomic) NSString *state; +@property (readonly, nonatomic) NSString *city; +@property (readonly, nonatomic) NSString *address; +@property (readonly, nonatomic) NSString *crossStreet; +@property (readonly, nonatomic) NSString *street; + +- (TGVenueAttachment *)venueAttachment; + ++ (TGLocationVenue *)venueWithFoursquareDictionary:(NSDictionary *)dictionary; ++ (TGLocationVenue *)venueWithGooglePlacesDictionary:(NSDictionary *)dictionary; + +@end + +extern NSString *const TGLocationGooglePlacesVenueProvider; +extern NSString *const TGLocationFoursquareVenueProvider; diff --git a/LegacyComponents/TGLocationVenue.m b/LegacyComponents/TGLocationVenue.m new file mode 100644 index 0000000000..70bb5169ea --- /dev/null +++ b/LegacyComponents/TGLocationVenue.m @@ -0,0 +1,102 @@ +#import "TGLocationVenue.h" + +#import + +NSString *const TGLocationGooglePlacesVenueProvider = @"google"; +NSString *const TGLocationFoursquareVenueProvider = @"foursquare"; + +@interface TGLocationVenue () +{ + NSString *_displayAddress; +} +@end + +@implementation TGLocationVenue + ++ (TGLocationVenue *)venueWithFoursquareDictionary:(NSDictionary *)dictionary +{ + TGLocationVenue *venue = [[TGLocationVenue alloc] init]; + venue->_identifier = dictionary[@"id"]; + venue->_name = dictionary[@"name"]; + + NSDictionary *location = dictionary[@"location"]; + venue->_coordinate = CLLocationCoordinate2DMake([location[@"lat"] doubleValue], [location[@"lng"] doubleValue]); + + NSArray *categories = dictionary[@"categories"]; + if (categories.count > 0) + { + NSDictionary *category = categories.firstObject; + venue->_categoryName = category[@"name"]; + + NSDictionary *icon = category[@"icon"]; + if (icon != nil) + venue->_categoryIconUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@64%@", icon[@"prefix"], icon[@"suffix"]]]; + } + + + + venue->_country = location[@"country"]; + venue->_state = location[@"state"]; + venue->_city = location[@"city"]; + venue->_address = location[@"address"]; + venue->_crossStreet = location[@"crossStreet"]; + + venue->_provider = TGLocationFoursquareVenueProvider; + + return venue; +} + ++ (TGLocationVenue *)venueWithGooglePlacesDictionary:(NSDictionary *)dictionary +{ + TGLocationVenue *venue = [[TGLocationVenue alloc] init]; + venue->_identifier = dictionary[@"place_id"]; + venue->_name = dictionary[@"name"]; + + NSDictionary *location = dictionary[@"geometry"][@"location"]; + venue->_coordinate = CLLocationCoordinate2DMake([location[@"lat"] doubleValue], + [location[@"lng"] doubleValue]); + + NSArray *types = dictionary[@"types"]; + if (types.count > 0) + { + if ([types containsObject:@"political"]) + return nil; + + venue->_categoryName = types.firstObject; + } + + venue->_displayAddress = dictionary[@"vicinity"]; + + venue->_provider = TGLocationGooglePlacesVenueProvider; + + return venue; +} + +- (NSString *)displayAddress +{ + if (_displayAddress.length > 0) + return _displayAddress; + if (self.street.length > 0) + return self.street; + else if (self.city.length > 0) + return self.city; + else if (self.country.length > 0) + return self.country; + + return nil; +} + +- (NSString *)street +{ + if (self.address.length > 0) + return self.address; + else + return self.crossStreet; +} + +- (TGVenueAttachment *)venueAttachment +{ + return [[TGVenueAttachment alloc] initWithTitle:self.name address:self.displayAddress provider:self.provider venueId:self.identifier]; +} + +@end diff --git a/LegacyComponents/TGLocationVenueCell.h b/LegacyComponents/TGLocationVenueCell.h new file mode 100644 index 0000000000..6ec7440965 --- /dev/null +++ b/LegacyComponents/TGLocationVenueCell.h @@ -0,0 +1,12 @@ +#import + +@class TGLocationVenue; + +@interface TGLocationVenueCell : UITableViewCell + +- (void)configureWithVenue:(TGLocationVenue *)venue; + +@end + +extern NSString *const TGLocationVenueCellKind; +extern const CGFloat TGLocationVenueCellHeight; diff --git a/LegacyComponents/TGLocationVenueCell.m b/LegacyComponents/TGLocationVenueCell.m new file mode 100644 index 0000000000..dc9cb4ba0c --- /dev/null +++ b/LegacyComponents/TGLocationVenueCell.m @@ -0,0 +1,89 @@ +#import "TGLocationVenueCell.h" + +#import "LegacyComponentsInternal.h" +#import "TGColor.h" +#import "TGFont.h" +#import "TGStringUtils.h" +#import "TGImageUtils.h" + +#import "TGLocationVenue.h" + +#import + +NSString *const TGLocationVenueCellKind = @"TGLocationVenueCellKind"; +const CGFloat TGLocationVenueCellHeight = 48.5f; + +@interface TGLocationVenueCell () +{ + TGImageView *_iconView; + UILabel *_titleLabel; + UILabel *_addressLabel; + UIView *_separatorView; +} +@end + +@implementation TGLocationVenueCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + self.selectedBackgroundView = [[UIView alloc] init]; + self.selectedBackgroundView.backgroundColor = TGSelectionColor(); + + _iconView = [[TGImageView alloc] init]; + [self.contentView addSubview:_iconView]; + + _titleLabel = [[UILabel alloc] init]; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.font = TGSystemFontOfSize(TGIsRetina() ? 16.5f : 16.0f); + _titleLabel.textColor = [UIColor blackColor]; + [self.contentView addSubview:_titleLabel]; + + _addressLabel = [[UILabel alloc] init]; + _addressLabel.backgroundColor = [UIColor clearColor]; + _addressLabel.font = TGSystemFontOfSize(13); + _addressLabel.textColor = UIColorRGB(0xa6a6a6); + [self.contentView addSubview:_addressLabel]; + + _separatorView = [[UIView alloc] init]; + _separatorView.backgroundColor = TGSeparatorColor(); + [self addSubview:_separatorView]; + } + return self; +} + +- (void)prepareForReuse +{ + [_iconView reset]; +} + +- (void)configureWithVenue:(TGLocationVenue *)venue +{ + _titleLabel.text = venue.name; + _addressLabel.text = venue.displayAddress; + if (venue.categoryIconUrl != nil) + { + [_iconView loadUri:[NSString stringWithFormat:@"location-venue-icon://url=%@&width=%d&height=%d", [TGStringUtils stringByEscapingForURL:venue.categoryIconUrl.absoluteString], 40, 40] withOptions:nil]; + } + else + { + [_iconView loadUri:@"embedded://" withOptions:@{ TGImageViewOptionEmbeddedImage:TGComponentsImageNamed(@"LocationGenericIcon.png)] }]; + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGFloat padding = 65.0f; + CGFloat separatorThickness = TGScreenPixel; + + _iconView.frame = CGRectMake(14, 4, 40, 40); + _titleLabel.frame = CGRectMake(padding, 5, self.frame.size.width - padding - 14, 20); + _addressLabel.frame = CGRectMake(padding, 25, self.frame.size.width - padding - 14, 20); + _separatorView.frame = CGRectMake(padding, self.frame.size.height - separatorThickness, self.frame.size.width - padding, separatorThickness); +} + +@end diff --git a/LegacyComponents/TGLocationViewController.h b/LegacyComponents/TGLocationViewController.h new file mode 100644 index 0000000000..a771771e33 --- /dev/null +++ b/LegacyComponents/TGLocationViewController.h @@ -0,0 +1,22 @@ +#import +#import + +#import + +@class TGLocationMediaAttachment; +@class TGVenueAttachment; +@class TGMenuSheetController; + +@interface TGLocationViewController : TGViewController + +@property (nonatomic, assign) bool previewMode; + +@property (nonatomic, copy) bool (^presentShareMenu)(TGMenuSheetController *, CLLocationCoordinate2D); +@property (nonatomic, copy) bool (^presentOpenInMenu)(TGLocationViewController *, TGLocationMediaAttachment *, bool, void (^)(TGMenuSheetController *)); +@property (nonatomic, copy) void (^shareAction)(NSArray *peerIds, NSString *caption); +@property (nonatomic, copy) void (^calloutPressed)(void); + +- (instancetype)initWithContext:(id)context coordinate:(CLLocationCoordinate2D)coordinate venue:(TGVenueAttachment *)venue peer:(id)peer; +- (instancetype)initWithContext:(id)context locationAttachment:(TGLocationMediaAttachment *)locationAttachment peer:(id)peer; + +@end diff --git a/LegacyComponents/TGLocationViewController.m b/LegacyComponents/TGLocationViewController.m new file mode 100644 index 0000000000..3f32de00a4 --- /dev/null +++ b/LegacyComponents/TGLocationViewController.m @@ -0,0 +1,534 @@ +#import "TGLocationViewController.h" + +#import "LegacyComponentsInternal.h" + +#import "TGNavigationBar.h" +#import "TGUser.h" +#import "TGConversation.h" +#import "TGMessage.h" +#import "TGImageUtils.h" +#import "TGFont.h" + +#import + +#import "TGLocationUtils.h" + +#import "TGLocationVenue.h" +#import "TGLocationAnnotation.h" + +#import "TGLocationTitleView.h" +#import "TGLocationMapView.h" +#import "TGLocationTrackingButton.h" +#import "TGLocationMapModeControl.h" +#import "TGLocationPinAnnotationView.h" + +#import + +@interface TGLocationViewController () +{ + CLLocationManager *_locationManager; + + bool _locationServicesDisabled; + + CLLocation *_location; + TGVenueAttachment *_venue; + TGLocationMediaAttachment *_locationAttachment; + TGLocationAnnotation *_annotation; + + CLLocation *_lastDirectionsStartLocation; + MKDirections *_directions; + + TGLocationTitleView *_titleView; + TGLocationMapView *_mapView; + + UIBarButtonItem *_actionsBarItem; + UIView *_toolbarView; + TGLocationTrackingButton *_trackingButton; + TGLocationMapModeControl *_mapModeControl; + id _peer; + + TGNavigationBar *_previewNavigationBar; + + id _context; +} +@end + +@implementation TGLocationViewController + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + self.titleText = TGLocalized(@"Map.LocationTitle"); + + _locationManager = [[CLLocationManager alloc] init]; + } + return self; +} + +- (instancetype)initWithContext:(id)context coordinate:(CLLocationCoordinate2D)coordinate venue:(TGVenueAttachment *)venue peer:(id)peer +{ + self = [self init]; + if (self != nil) + { + _context = context; + _location = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude]; + _venue = venue; + _peer = peer; + NSString *title = @""; + if ([peer isKindOfClass:[TGUser class]]) { + title = ((TGUser *)peer).displayName; + } else if ([peer isKindOfClass:[TGConversation class]]) { + title = ((TGConversation *)peer).chatTitle; + } + _annotation = [[TGLocationAnnotation alloc] initWithCoordinate:coordinate title:title]; + } + return self; +} + +- (instancetype)initWithContext:(id)context locationAttachment:(TGLocationMediaAttachment *)locationAttachment peer:(id)peer +{ + self = [self initWithContext:context coordinate:CLLocationCoordinate2DMake(locationAttachment.latitude, locationAttachment.longitude) venue:locationAttachment.venue peer:peer]; + if (self != nil) + { + _locationAttachment = locationAttachment; + } + return self; +} + +- (void)dealloc +{ + _mapView.delegate = nil; +} + +- (void)loadView +{ + [super loadView]; + + self.view.backgroundColor = [UIColor whiteColor]; + + _mapView = [[TGLocationMapView alloc] initWithFrame:self.view.bounds]; + _mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _mapView.delegate = self; + _mapView.showsUserLocation = true; + _mapView.tapEnabled = false; + [self.view addSubview:_mapView]; + + _toolbarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, self.view.frame.size.height - 44.0f, self.view.frame.size.width, 44.0f)]; + _toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + _toolbarView.backgroundColor = UIColorRGBA(0xf7f7f7, 1.0f); + _toolbarView.hidden = self.previewMode; + UIView *stripeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _toolbarView.frame.size.width, TGScreenPixel)]; + stripeView.backgroundColor = UIColorRGB(0xb2b2b2); + stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [_toolbarView addSubview:stripeView]; + [self.view addSubview:_toolbarView]; + + _trackingButton = [[TGLocationTrackingButton alloc] initWithFrame:CGRectMake(4, 2, 44, 44)]; + [_trackingButton addTarget:self action:@selector(trackingModePressed) forControlEvents:UIControlEventTouchUpInside]; + [_toolbarView addSubview:_trackingButton]; + + _mapModeControl = [[TGLocationMapModeControl alloc] init]; + _mapModeControl.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _mapModeControl.frame = CGRectMake(55, (_toolbarView.frame.size.height - 29) / 2 + 0.5f, _toolbarView.frame.size.width - 55 - 7.5f, 29); + _mapModeControl.selectedSegmentIndex = MAX(0, MIN(2, (NSInteger)_mapView.mapType)); + [_mapModeControl addTarget:self action:@selector(mapModeControlValueChanged:) forControlEvents:UIControlEventValueChanged]; + [_toolbarView addSubview:_mapModeControl]; + + NSString *backButtonTitle = TGLocalized(@"Common.Back"); + if (TGIsPad()) + { + backButtonTitle = TGLocalized(@"Common.Done"); + [self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:backButtonTitle style:UIBarButtonItemStyleDone target:self action:@selector(dismissButtonPressed)]]; + } + + CGFloat actionsButtonWidth = 0.0f; + if (iosMajorVersion() >= 7) + { + _actionsBarItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionsButtonPressed)]; + [self setRightBarButtonItem:_actionsBarItem]; + actionsButtonWidth = 48.0f; + } + else + { + NSString *actionsButtonTitle = TGLocalized(@"Common.More"); + _actionsBarItem = [[UIBarButtonItem alloc] initWithTitle:actionsButtonTitle style:UIBarButtonItemStylePlain target:self action:@selector(actionsButtonPressed)]; + [self setRightBarButtonItem:_actionsBarItem]; + + actionsButtonWidth = 16.0f; + if ([actionsButtonTitle respondsToSelector:@selector(sizeWithAttributes:)]) + actionsButtonWidth += CGCeil([actionsButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(16.0f) }].width); + else + actionsButtonWidth += CGCeil([actionsButtonTitle sizeWithFont:TGSystemFontOfSize(16.0f)].width); + } + + if (_venue.title.length > 0) + { + CGFloat backButtonWidth = 27.0f + 8.0f; + if ([backButtonTitle respondsToSelector:@selector(sizeWithAttributes:)]) + backButtonWidth += CGCeil([backButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(16.0f) }].width); + else + backButtonWidth += CGCeil([backButtonTitle sizeWithFont:TGSystemFontOfSize(16.0f)].width); + + _titleView = [[TGLocationTitleView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)]; + _titleView.title = _venue.title; + _titleView.address = _venue.address; + _titleView.interfaceOrientation = [[LegacyComponentsGlobals provider] applicationStatusBarOrientation]; + _titleView.backButtonWidth = backButtonWidth; + _titleView.actionsButtonWidth = actionsButtonWidth; + [self setTitleView:_titleView]; + + if (self.previewMode) + { + _previewNavigationBar = [[TGNavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44.0f) barStyle:UIBarStyleDefault]; + [self.view addSubview:_previewNavigationBar]; + + [self setRightBarButtonItem:nil]; + [_previewNavigationBar setItems:@[ [self navigationItem] ]]; + } + } +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [_mapView addAnnotation:_annotation]; + [_mapView selectAnnotation:_annotation animated:false]; + + _mapView.region = MKCoordinateRegionMake(_location.coordinate, MKCoordinateSpanMake(0.008, 0.008)); + + [TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (self.previewMode && !animated) + { + UIView *contentView = [[_mapView subviews] firstObject]; + UIView *annotationContainer = nil; + for (NSUInteger i = 1; i < contentView.subviews.count; i++) + { + UIView *view = contentView.subviews[i]; + if ([NSStringFromClass(view.class) rangeOfString:@"AnnotationContainer"].location != NSNotFound) + { + annotationContainer = view; + break; + } + } + + for (UIView *view in annotationContainer.subviews) + view.frame = CGRectOffset(view.frame, 0, 48.5f); + } +} + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + + _titleView.interfaceOrientation = toInterfaceOrientation; +} + +#pragma mark - + +- (void)setPreviewMode:(bool)previewMode +{ + _previewMode = previewMode; + _toolbarView.hidden = previewMode; + + if (!previewMode) + { + [self setRightBarButtonItem:_actionsBarItem]; + [_previewNavigationBar removeFromSuperview]; + _previewNavigationBar = nil; + } +} + +#pragma mark - Actions + +- (void)dismissButtonPressed +{ + [self.presentingViewController dismissViewControllerAnimated:true completion:nil]; +} + +- (void)actionsButtonPressed +{ + TGLocationMediaAttachment *locationAttachment = _locationAttachment; + if (locationAttachment == nil) + return; + + CGRect (^sourceRect)(void) = ^CGRect + { + return CGRectZero; + }; + + + __weak TGLocationViewController *weakSelf = self; + + if (_presentOpenInMenu && _presentOpenInMenu(self, locationAttachment, false, ^(TGMenuSheetController *menuController) + { + __strong TGLocationViewController *strongSelf = weakSelf; + if (strongSelf != nil && strongSelf->_presentShareMenu) { + strongSelf->_presentShareMenu(menuController, strongSelf->_location.coordinate); + } + })) + { + } + else + { + TGMenuSheetController *controller = [[TGMenuSheetController alloc] initWithContext:_context dark:false]; + controller.dismissesByOutsideTap = true; + controller.hasSwipeGesture = true; + controller.narrowInLandscape = true; + controller.sourceRect = sourceRect; + controller.barButtonItem = self.navigationItem.rightBarButtonItem; + + NSMutableArray *itemViews = [[NSMutableArray alloc] init]; + + __weak TGMenuSheetController *weakController = controller; + TGMenuSheetButtonItemView *openItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Map.OpenInMaps") type:TGMenuSheetButtonTypeDefault action:^ + { + __strong TGLocationViewController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + __strong TGMenuSheetController *strongController = weakController; + if (strongController == nil) + return; + + [strongController dismissAnimated:true]; + [TGLocationUtils openMapsWithCoordinate:strongSelf->_location.coordinate withDirections:false locationName:strongSelf->_annotation.title]; + }]; + [itemViews addObject:openItem]; + + TGMenuSheetButtonItemView *shareItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Conversation.ContextMenuShare") type:TGMenuSheetButtonTypeDefault action:^ + { + __strong TGMenuSheetController *strongController = weakController; + if (strongController == nil) + return; + + __strong TGLocationViewController *strongSelf = weakSelf; + if (strongSelf != nil && strongSelf->_presentShareMenu) { + strongSelf->_presentShareMenu(strongController, strongSelf->_location.coordinate); + } + }]; + [itemViews addObject:shareItem]; + + TGMenuSheetButtonItemView *cancelItem = [[TGMenuSheetButtonItemView alloc] initWithTitle:TGLocalized(@"Common.Cancel") type:TGMenuSheetButtonTypeCancel action:^ + { + __strong TGMenuSheetController *strongController = weakController; + if (strongController == nil) + return; + + [strongController dismissAnimated:true manual:true]; + }]; + [itemViews addObject:cancelItem]; + + [controller setItemViews:itemViews]; + [controller presentInViewController:self sourceView:self.view animated:true]; + + } +} + +- (NSString *)_coordinateString +{ + NSInteger latSeconds = (NSInteger)(_location.coordinate.latitude * 3600); + NSInteger latDegrees = latSeconds / 3600; + latSeconds = labs(latSeconds % 3600); + NSInteger latMinutes = latSeconds / 60; + latSeconds %= 60; + + NSInteger longSeconds = (NSInteger)(_location.coordinate.longitude * 3600); + NSInteger longDegrees = longSeconds / 3600; + longSeconds = labs(longSeconds % 3600); + NSInteger longMinutes = longSeconds / 60; + longSeconds %= 60; + + NSString *result = [NSString stringWithFormat:@"%@%02ld° %02ld' %02ld\" %@%02ld° %02ld' %02ld\"", latDegrees >= 0 ? @"N" : @"S", labs(latDegrees), (long)latMinutes, (long)latSeconds, longDegrees >= 0 ? @"E" : @"W", labs(longDegrees), (long)longMinutes, (long)longSeconds]; + + return result; +} + +- (void)trackingModePressed +{ + if (![self _hasUserLocation]) + { + if (![TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager]) + { + if (_locationServicesDisabled) + [[[LegacyComponentsGlobals provider] accessChecker] checkLocationAuthorizationStatusForIntent:TGLocationAccessIntentTracking alertDismissComlpetion:nil]; + } + + [self updateLocationAvailability]; + return; + } + + TGLocationTrackingMode newMode = TGLocationTrackingModeNone; + + switch ([TGLocationTrackingButton locationTrackingModeWithUserTrackingMode:_mapView.userTrackingMode]) + { + case TGLocationTrackingModeFollow: + newMode = TGLocationTrackingModeFollowWithHeading; + break; + + case TGLocationTrackingModeFollowWithHeading: + newMode = TGLocationTrackingModeNone; + break; + + default: + newMode = TGLocationTrackingModeFollow; + break; + } + + [_mapView setUserTrackingMode:[TGLocationTrackingButton userTrackingModeWithLocationTrackingMode:newMode] animated:true]; + [_trackingButton setTrackingMode:newMode animated:true]; +} + +- (void)mapModeControlValueChanged:(TGLocationMapModeControl *)sender +{ + NSInteger mapMode = MAX(0, MIN(2, sender.selectedSegmentIndex)); + [_mapView setMapType:(MKMapType)mapMode]; +} + +- (void)getDirectionsPressed +{ + TGLocationMediaAttachment *locationAttachment = _locationAttachment; + if (locationAttachment == nil) + return; + + if (_presentOpenInMenu && _presentOpenInMenu(self, locationAttachment, true, nil)) + { + } + else + { + [TGLocationUtils openMapsWithCoordinate:_location.coordinate withDirections:true locationName:_annotation.title]; + } +} + +#pragma mark - Map View Delegate + +- (void)mapView:(MKMapView *)__unused mapView didUpdateUserLocation:(MKUserLocation *)userLocation +{ + userLocation.title = @""; + + _locationServicesDisabled = false; + + [self updateAnnotation]; + [self updateLocationAvailability]; +} + +- (void)mapView:(MKMapView *)__unused mapView didFailToLocateUserWithError:(NSError *)__unused error +{ + if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted) + { + _locationServicesDisabled = true; + [self updateLocationAvailability]; + } +} + +- (bool)_hasUserLocation +{ + return (_mapView.userLocation != nil && _mapView.userLocation.location != nil); +} + +- (void)updateLocationAvailability +{ + bool locationAvailable = [self _hasUserLocation] || _locationServicesDisabled; + [_trackingButton setLocationAvailable:locationAvailable animated:true]; +} + +- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation +{ + if (annotation == mapView.userLocation) + return nil; + + TGLocationPinAnnotationView *view = (TGLocationPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:TGLocationPinAnnotationKind]; + if (view == nil) + view = [[TGLocationPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:TGLocationPinAnnotationKind]; + else + view.annotation = annotation; + + view.selectable = false; + view.canShowCallout = false; + view.animatesDrop = false; + + __weak TGLocationViewController *weakSelf = self; + view.calloutPressed = self.calloutPressed; + view.getDirectionsPressed = ^ + { + __strong TGLocationViewController *strongSelf = weakSelf; + if (strongSelf != nil) + [strongSelf getDirectionsPressed]; + }; + + [view sizeToFit]; + [view setNeedsLayout]; + + return view; +} + +- (void)updateAnnotation +{ + if (_mapView.userLocation == nil || _mapView.userLocation.location == nil) + return; + + CLLocationDistance distanceToLocation = [_location distanceFromLocation:_mapView.userLocation.location]; + _annotation.subtitle = [NSString stringWithFormat:TGLocalized(@"Map.DistanceAway"), [TGLocationUtils stringFromDistance:distanceToLocation]]; + [self _updateAnnotationView]; + + [self _updateDirectionsETA]; +} + +- (void)_updateAnnotationView +{ + TGLocationPinAnnotationView *annotationView = (TGLocationPinAnnotationView *)[_mapView viewForAnnotation:_annotation]; + annotationView.annotation = _annotation; + [annotationView sizeToFit]; + [annotationView setNeedsLayout]; + + if (annotationView.appeared) + { + [UIView animateWithDuration:0.2f animations:^ + { + [annotationView layoutIfNeeded]; + }]; + } +} + +- (void)_updateDirectionsETA +{ + if (iosMajorVersion() < 7) + return; + + if (_lastDirectionsStartLocation == nil || [_mapView.userLocation.location distanceFromLocation:_lastDirectionsStartLocation] > 100) + { + if (_directions != nil) + [_directions cancel]; + + MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:_location.coordinate addressDictionary:nil]; + MKMapItem *destinationMapItem = [[MKMapItem alloc] initWithPlacemark:destinationPlacemark]; + + MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init]; + request.source = [MKMapItem mapItemForCurrentLocation]; + request.destination = destinationMapItem; + request.transportType = MKDirectionsTransportTypeAutomobile; + request.requestsAlternateRoutes = false; + + _directions = [[MKDirections alloc] initWithRequest:request]; + [_directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) + { + if (error != nil) + return; + + _annotation.userInfo = @{ TGLocationETAKey: @(response.expectedTravelTime) }; + [self _updateAnnotationView]; + }]; + + _lastDirectionsStartLocation = _mapView.userLocation.location; + } +} + +@end diff --git a/LegacyComponents/TGMediaAssetLegacyImageSignals.m b/LegacyComponents/TGMediaAssetLegacyImageSignals.m index ded6c56ad1..96d929c1cf 100644 --- a/LegacyComponents/TGMediaAssetLegacyImageSignals.m +++ b/LegacyComponents/TGMediaAssetLegacyImageSignals.m @@ -307,7 +307,7 @@ static size_t TGGetAssetBytesCallback(void *info, void *buffer, off_t position, size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:count error:&error]; if (countRead == 0 && error) - TGLog(@"error occured while reading an asset: %@", error); + TGLegacyLog(@"error occured while reading an asset: %@", error); return countRead; } diff --git a/LegacyComponents/TGMediaAssetModernImageSignals.m b/LegacyComponents/TGMediaAssetModernImageSignals.m index 9945ba42d3..9120a17ff3 100644 --- a/LegacyComponents/TGMediaAssetModernImageSignals.m +++ b/LegacyComponents/TGMediaAssetModernImageSignals.m @@ -128,7 +128,7 @@ bool degraded = [info[PHImageResultIsDegradedKey] boolValue]; if (result == nil && !networkAccessAllowed) { - TGLog(@"requestImageForAsset: error -1"); + TGLegacyLog(@"requestImageForAsset: error -1"); [subscriber putError:@true]; return; diff --git a/LegacyComponents/TGMediaAttachment.m b/LegacyComponents/TGMediaAttachment.m index 63f3634906..3bed1cf633 100644 --- a/LegacyComponents/TGMediaAttachment.m +++ b/LegacyComponents/TGMediaAttachment.m @@ -9,7 +9,7 @@ - (void)serialize:(NSMutableData *)__unused data { - TGLog(@"***** TGMediaAttachment: default implementation not provided"); + TGLegacyLog(@"***** TGMediaAttachment: default implementation not provided"); } @end diff --git a/LegacyComponents/TGMediaPickerGalleryModel.m b/LegacyComponents/TGMediaPickerGalleryModel.m index 689b646b0e..88f37f285f 100644 --- a/LegacyComponents/TGMediaPickerGalleryModel.m +++ b/LegacyComponents/TGMediaPickerGalleryModel.m @@ -352,7 +352,7 @@ { __strong TGMediaPickerGalleryModel *strongSelf = weakSelf; if (strongSelf == nil) { - TGLog(@"controller.didFinishEditing strongSelf == nil"); + TGLegacyLog(@"controller.didFinishEditing strongSelf == nil"); } #ifdef DEBUG diff --git a/LegacyComponents/TGMessage.mm b/LegacyComponents/TGMessage.mm index 17b053ca6c..fbd4cca500 100644 --- a/LegacyComponents/TGMessage.mm +++ b/LegacyComponents/TGMessage.mm @@ -1009,7 +1009,7 @@ typedef enum { std::unordered_map >::iterator it = mediaAttachmentParsers.find(type); if (it == mediaAttachmentParsers.end()) { - TGLog(@"***** Unknown media attachment type %d", type); + TGLegacyLog(@"***** Unknown media attachment type %d", type); return [NSArray array]; } diff --git a/LegacyComponents/TGModernCache.m b/LegacyComponents/TGModernCache.m index 4d7712085e..03ced482b4 100644 --- a/LegacyComponents/TGModernCache.m +++ b/LegacyComponents/TGModernCache.m @@ -105,7 +105,7 @@ typedef enum { if (currentSize != [self _getCurrentSize:readerWriter]) { - TGLog(@"current size mismatch"); + TGLegacyLog(@"current size mismatch"); } } @@ -163,14 +163,14 @@ typedef enum { [upperBound appendBytes:&afterKeyspace length:1]; PSData upperBoundKey = {.data = (void *)upperBound.bytes, .length = upperBound.length}; - TGLog(@"-----------------------------"); + TGLegacyLog(@"-----------------------------"); [readerWriter enumerateKeysAndValuesBetweenLowerBoundKey:&lowerBoundKey upperBoundKey:&upperBoundKey options:PSKeyValueReaderEnumerationUpperBoundExclusive withBlock:^(__unused PSConstData *key, PSConstData *value, __unused bool *stop) { NSData *originalKey = [[NSData alloc] initWithBytes:value->data + 4 length:value->length - 4]; NSString *filePath = [self _filePathForKey:originalKey]; - TGLog(@"%@", filePath); + TGLegacyLog(@"%@", filePath); }]; - TGLog(@"-----------------------------"); + TGLegacyLog(@"-----------------------------"); } - (void)_evictValuesOfTotalSize:(NSUInteger)totalSize removedSize:(NSUInteger *)removedSize readerWriter:(id)readerWriter @@ -197,7 +197,7 @@ typedef enum { { int32_t sortingValue = 0; memcpy(&sortingValue, key->data + 1 + 4, 4); - //TGLog(@"removing %d", sortingValue); + //TGLegacyLog(@"removing %d", sortingValue); } [keysToRemove addObject:[[NSData alloc] initWithBytes:key->data length:key->length]]; @@ -211,7 +211,7 @@ typedef enum { NSData *originalKey = [[NSData alloc] initWithBytes:value->data + 4 length:value->length - 4]; NSString *filePath = [self _filePathForKey:originalKey]; - //TGLog(@"remove %d %@", (int)size, filePath); + //TGLegacyLog(@"remove %d %@", (int)size, filePath); NSMutableData *primaryKeyData = [[NSMutableData alloc] init]; int8_t keyspace = TGModernCacheKeyspaceLastUsageByPath; @@ -303,7 +303,7 @@ typedef enum { { [_queue dispatch:^ { - //TGLog(@"store %d %@", (int)value.length, [self _filePathForKey:key]); + //TGLegacyLog(@"store %d %@", (int)value.length, [self _filePathForKey:key]); [_keyValueStore readWriteInTransaction:^(id readerWriter) { @@ -354,7 +354,7 @@ typedef enum { } else { - //TGLog(@"no data for %@", [self _filePathForKey:key]); + //TGLegacyLog(@"no data for %@", [self _filePathForKey:key]); } if (completion) diff --git a/LegacyComponents/TGModernGalleryController.m b/LegacyComponents/TGModernGalleryController.m index ab98edfeab..86ad25733e 100644 --- a/LegacyComponents/TGModernGalleryController.m +++ b/LegacyComponents/TGModernGalleryController.m @@ -728,7 +728,7 @@ static CGRect adjustFrameForOriginalSubframe(CGRect originalFrame, CGRect origin frame = CGRectApplyAffineTransform(frame, transform); } - //TGLog(@"%f: %@", rotation, currentView); + //TGLegacyLog(@"%f: %@", rotation, currentView); if ([currentView.superview isKindOfClass:[UIWindow class]]) sourceWindowRotation = rotation; diff --git a/LegacyComponents/TGModernGalleryZoomableScrollViewSwipeGestureRecognizer.m b/LegacyComponents/TGModernGalleryZoomableScrollViewSwipeGestureRecognizer.m index 3b723dd883..18853ecc81 100644 --- a/LegacyComponents/TGModernGalleryZoomableScrollViewSwipeGestureRecognizer.m +++ b/LegacyComponents/TGModernGalleryZoomableScrollViewSwipeGestureRecognizer.m @@ -87,7 +87,7 @@ - (void)touchesEnded:(NSSet *)__unused touches withEvent:(UIEvent *)__unused event { - //TGLog(@"touches ended"); + //TGLegacyLog(@"touches ended"); _swipeVelocity = [self velocityInView:self.view].y; diff --git a/LegacyComponents/TGPaintBuffers.m b/LegacyComponents/TGPaintBuffers.m index 5cb75b7d3c..087367928d 100644 --- a/LegacyComponents/TGPaintBuffers.m +++ b/LegacyComponents/TGPaintBuffers.m @@ -85,7 +85,7 @@ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - TGLog(@"Failed to create complete framebuffer %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + TGLegacyLog(@"Failed to create complete framebuffer %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); return false; } diff --git a/LegacyComponents/TGPaintShader.m b/LegacyComponents/TGPaintShader.m index cff09ecf54..99fa31d7e5 100644 --- a/LegacyComponents/TGPaintShader.m +++ b/LegacyComponents/TGPaintShader.m @@ -133,14 +133,14 @@ { GLchar *log = (GLchar *)malloc(logLength); glGetProgramInfoLog(program, logLength, &logLength, log); - TGLog(@"Program link log:\n%s", log); + TGLegacyLog(@"Program link log:\n%s", log); free(log); } #endif glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) - TGLog(@"Failed to link program %d", program); + TGLegacyLog(@"Failed to link program %d", program); return status; } diff --git a/LegacyComponents/TGPaintUtils.m b/LegacyComponents/TGPaintUtils.m index c19f48d34e..c512277404 100644 --- a/LegacyComponents/TGPaintUtils.m +++ b/LegacyComponents/TGPaintUtils.m @@ -37,7 +37,7 @@ void TGPaintHasGLError_(const char* file, int line) { break; } - TGLog(@"PAINT ERROR: glGetError %@ at %s:%d", message, file, line); + TGLegacyLog(@"PAINT ERROR: glGetError %@ at %s:%d", message, file, line); } } diff --git a/LegacyComponents/TGPainting.m b/LegacyComponents/TGPainting.m index e8c2ee1d6e..cce8fe48b3 100644 --- a/LegacyComponents/TGPainting.m +++ b/LegacyComponents/TGPainting.m @@ -429,7 +429,7 @@ if (status != GL_FRAMEBUFFER_COMPLETE) { - TGLog(@"ERROR: imageAndData: - Incomplete Framebuffer!"); + TGLegacyLog(@"ERROR: imageAndData: - Incomplete Framebuffer!"); TGPaintHasGLError(); return nil; } @@ -477,7 +477,7 @@ if (status != GL_FRAMEBUFFER_COMPLETE) { - TGLog(@"ERROR: imageAndData: - Incomplete Framebuffer!"); + TGLegacyLog(@"ERROR: imageAndData: - Incomplete Framebuffer!"); TGPaintHasGLError(); return nil; } diff --git a/LegacyComponents/TGPhotoAvatarCropController.m b/LegacyComponents/TGPhotoAvatarCropController.m index 5880e68cdb..afc6c8b966 100644 --- a/LegacyComponents/TGPhotoAvatarCropController.m +++ b/LegacyComponents/TGPhotoAvatarCropController.m @@ -280,7 +280,7 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f; { if (dispatch_semaphore_wait(_waitSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)))) { - TGLog(@"Photo crop on switching failed"); + TGLegacyLog(@"Photo crop on switching failed"); return; } diff --git a/LegacyComponents/TGPhotoCropController.m b/LegacyComponents/TGPhotoCropController.m index 9dbb9ff216..370c103c8c 100644 --- a/LegacyComponents/TGPhotoCropController.m +++ b/LegacyComponents/TGPhotoCropController.m @@ -335,7 +335,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; { if (dispatch_semaphore_wait(_waitSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)))) { - TGLog(@"Photo crop on switching failed"); + TGLegacyLog(@"Photo crop on switching failed"); return; } diff --git a/LegacyComponents/TGPhotoEditorController.m b/LegacyComponents/TGPhotoEditorController.m index 8e9ee230fc..7cf2df9b6f 100644 --- a/LegacyComponents/TGPhotoEditorController.m +++ b/LegacyComponents/TGPhotoEditorController.m @@ -666,7 +666,7 @@ self.didFinishEditing(editorValues, image, thumbnailImage, true); } error:^(__unused id error) { - TGLog(@"renderedImageSignal error"); + TGLegacyLog(@"renderedImageSignal error"); } completed:nil]; } } diff --git a/LegacyComponents/TGPhotoQualityController.m b/LegacyComponents/TGPhotoQualityController.m index c31d5acf1e..268c4eeebe 100644 --- a/LegacyComponents/TGPhotoQualityController.m +++ b/LegacyComponents/TGPhotoQualityController.m @@ -751,7 +751,7 @@ const NSTimeInterval TGPhotoQualityPreviewDuration = 15.0f; [strongSelf->_overlayView setProgress:progress cancelEnabled:false animated:true]; } } error:^(id error) { - TGLog(@"Video Quality Preview Error: %@", error); + TGLegacyLog(@"Video Quality Preview Error: %@", error); } completed:nil]]; } diff --git a/LegacyComponents/TGPickPinAnnotationView.h b/LegacyComponents/TGPickPinAnnotationView.h new file mode 100644 index 0000000000..1462e6c97e --- /dev/null +++ b/LegacyComponents/TGPickPinAnnotationView.h @@ -0,0 +1,9 @@ +#import "TGPinAnnotationView.h" + +@interface TGPickPinAnnotationView : TGPinAnnotationView + +- (void)setHidden:(bool)hidden animated:(bool)animated; + +@end + +extern NSString * const TGPickPinAnnotationKind; diff --git a/LegacyComponents/TGPickPinAnnotationView.m b/LegacyComponents/TGPickPinAnnotationView.m new file mode 100644 index 0000000000..c959b92e2e --- /dev/null +++ b/LegacyComponents/TGPickPinAnnotationView.m @@ -0,0 +1,92 @@ +#import "TGPickPinAnnotationView.h" + +#import "LegacyComponentsInternal.h" +#import "TGFont.h" +#import "TGColor.h" +#import "TGImageUtils.h" + +NSString * const TGPickPinAnnotationKind = @"TGPickPinAnnotationKind"; + +@implementation TGPickPinAnnotationView + +- (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + self.image = [[UIImage alloc] init]; + + _titleLabel.font = TGIsRetina() ? TGBoldSystemFontOfSize(16.5f) : TGBoldSystemFontOfSize(16); + _titleLabel.text = annotation.title; + _titleLabel.textColor = UIColorRGB(0x2385df); + + _subtitleLabel.font = TGSystemFontOfSize(12.5f); + _subtitleLabel.textColor = UIColorRGB(0xa6a6a6); + } + return self; +} + +- (void)setAnnotation:(id)annotation +{ + [super setAnnotation:annotation]; + + _titleLabel.text = annotation.title; + + if (annotation.subtitle.length == 0) + _subtitleLabel.text = TGLocalized(@"Map.Locating"); + else + _subtitleLabel.text = annotation.subtitle; +} + +- (void)setHidden:(BOOL)hidden +{ + [self setHidden:hidden animated:false]; +} + +- (void)setHidden:(bool)hidden animated:(bool)animated +{ + if (animated) + { + super.hidden = false; + [UIView animateWithDuration:0.2f animations:^ + { + self.alpha = hidden ? 0.0f : 1.0f; + } completion:^(BOOL finished) + { + if (finished) + super.hidden = hidden; + }]; + } + else + { + self.alpha = hidden ? 0.0f : 1.0f; + super.hidden = hidden; + } +} + +#pragma mark - Layout + +- (void)sizeToFit +{ + CGRect frame = _calloutWrapper.frame; + + CGFloat titleLabelWidth = CGCeil([_titleLabel sizeThatFits:CGSizeMake(214, FLT_MAX)].width); + CGFloat subtitleLabelWidth = CGCeil([_subtitleLabel sizeThatFits:CGSizeMake(214, FLT_MAX)].width); + + CGFloat labelsWidth = MAX(titleLabelWidth, subtitleLabelWidth) + 24; + + frame.size.width = MIN(300, MAX(labelsWidth, 175)); + frame.size.height = 46; + + _calloutWrapper.frame = frame; +} + +- (void)layoutSubviews +{ + _titleLabel.frame = CGRectMake(12, 5, _calloutWrapper.frame.size.width - 24, 19); + _subtitleLabel.frame = CGRectMake(12, 25, _calloutWrapper.frame.size.width - 24, 15); + + [super layoutSubviews]; +} + +@end diff --git a/LegacyComponents/TGPinAnnotationView.h b/LegacyComponents/TGPinAnnotationView.h new file mode 100644 index 0000000000..b338bea4f9 --- /dev/null +++ b/LegacyComponents/TGPinAnnotationView.h @@ -0,0 +1,19 @@ +#import + +@interface TGPinAnnotationView : MKPinAnnotationView +{ + UIButton *_calloutWrapper; + + UILabel *_titleLabel; + UILabel *_subtitleLabel; +} + +@property (nonatomic, copy) void(^calloutPressed)(void); + +@property (nonatomic, assign) NSString *title; +@property (nonatomic, assign) NSString *subtitle; +@property (nonatomic, assign) bool selectable; + +@property (nonatomic, readonly) bool appeared; + +@end diff --git a/LegacyComponents/TGPinAnnotationView.m b/LegacyComponents/TGPinAnnotationView.m new file mode 100644 index 0000000000..df45c37bd5 --- /dev/null +++ b/LegacyComponents/TGPinAnnotationView.m @@ -0,0 +1,128 @@ +#import "TGPinAnnotationView.h" + +#import "LegacyComponentsInternal.h" +#import "TGImageUtils.h" + +@interface TGPinAnnotationView () +{ + UIImageView *_arrowView; +} +@end + +@implementation TGPinAnnotationView + +- (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + _calloutWrapper = [[UIButton alloc] init]; + _calloutWrapper.adjustsImageWhenHighlighted = false; + _calloutWrapper.exclusiveTouch = true; + [_calloutWrapper setBackgroundImage:[TGComponentsImageNamed(@"CalloutBackground.png") resizableImageWithCapInsets:UIEdgeInsetsMake(8.5f, 8.5f, 8.5f, 8.5f)] forState:UIControlStateNormal]; + [_calloutWrapper setBackgroundImage:[TGComponentsImageNamed(@"CalloutBackground_Highlighted.png") resizableImageWithCapInsets:UIEdgeInsetsMake(8.5f, 8.5f, 8.5f, 8.5f)] forState:UIControlStateHighlighted]; + [_calloutWrapper addTarget:self action:@selector(calloutButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_calloutWrapper addTarget:self action:@selector(calloutButtonTouchedDown) forControlEvents:UIControlEventTouchDown]; + [_calloutWrapper addTarget:self action:@selector(calloutButtonTouchedUp) forControlEvents:UIControlEventTouchCancel]; + [_calloutWrapper addTarget:self action:@selector(calloutButtonTouchedUp) forControlEvents:UIControlEventTouchDragExit]; + [_calloutWrapper addTarget:self action:@selector(calloutButtonTouchedDown) forControlEvents:UIControlEventTouchDragEnter]; + [self addSubview:_calloutWrapper]; + + _arrowView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 13)]; + _arrowView.image = TGComponentsImageNamed(@"CalloutArrow.png"); + _arrowView.highlightedImage = TGComponentsImageNamed(@"CalloutArrow_Highlighted.png"); + [_calloutWrapper addSubview:_arrowView]; + + _titleLabel = [[UILabel alloc] init]; + _titleLabel.backgroundColor = [UIColor clearColor]; + [_calloutWrapper addSubview:_titleLabel]; + + _subtitleLabel = [[UILabel alloc] init]; + _subtitleLabel.backgroundColor = [UIColor clearColor]; + [_calloutWrapper addSubview:_subtitleLabel]; + + _calloutWrapper.layer.rasterizationScale = TGScreenScaling(); + _calloutWrapper.layer.shouldRasterize = true; + } + return self; +} + +- (void)calloutButtonPressed +{ + if (self.calloutPressed != nil) + self.calloutPressed(); + + [self calloutButtonTouchedUp]; +} + +- (void)calloutButtonTouchedUp +{ + _arrowView.highlighted = false; +} + +- (void)calloutButtonTouchedDown +{ + _arrowView.highlighted = true; +} + +#pragma mark - Properties + +- (NSString *)title +{ + return _titleLabel.text; +} + +- (void)setTitle:(NSString *)title +{ + _titleLabel.text = title; +} + +- (NSString *)subtitle +{ + return _subtitleLabel.text; +} + +- (void)setSubtitle:(NSString *)subtitle +{ + _subtitleLabel.text = subtitle; +} + +- (void)setSelected:(BOOL)selected +{ + if (self.selectable) + [super setSelected:selected]; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + if (self.selectable) + [super setSelected:selected animated:animated]; +} + +#pragma mark - Layout + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + bool pointInside = [super pointInside:point withEvent:event]; + + if (CGRectContainsPoint(_calloutWrapper.frame, point)) + pointInside = true; + + return pointInside; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + _arrowView.frame = CGRectMake((_calloutWrapper.frame.size.width - _arrowView.frame.size.width) / 2, _calloutWrapper.frame.size.height - 0.5f, _arrowView.frame.size.width, _arrowView.frame.size.height); + + CGRect calloutFrame = _calloutWrapper.frame; + calloutFrame.origin.x = CGFloor((self.frame.size.width - calloutFrame.size.width) / 2) - 8; + calloutFrame.origin.y = -calloutFrame.size.height - 12; + _calloutWrapper.frame = calloutFrame; + + _appeared = true; +} + +@end diff --git a/LegacyComponents/TGRemoteImageView.m b/LegacyComponents/TGRemoteImageView.m index 73cb7b5469..f1929fb3f9 100644 --- a/LegacyComponents/TGRemoteImageView.m +++ b/LegacyComponents/TGRemoteImageView.m @@ -503,7 +503,7 @@ static TGCache *sharedCache = nil; } /*else if (self.path != nil && ![path isEqualToString:self.path]) { - TGLog(@"Received wrong path: <<<%@>>> vs <<<%@>>>", self.path, path); + TGLegacyLog(@"Received wrong path: <<<%@>>> vs <<<%@>>>", self.path, path); }*/ }); } diff --git a/LegacyComponents/TGSearchBar.h b/LegacyComponents/TGSearchBar.h new file mode 100644 index 0000000000..55ef996eb4 --- /dev/null +++ b/LegacyComponents/TGSearchBar.h @@ -0,0 +1,66 @@ +#import + +@class TGSearchBar; + +typedef enum { + TGSearchBarStyleDefault = 0, + TGSearchBarStyleDark = 1, + TGSearchBarStyleLight = 2, + TGSearchBarStyleLightPlain = 3, + TGSearchBarStyleLightAlwaysPlain = 4, + TGSearchBarStyleHeader = 5, +} TGSearchBarStyle; + +@protocol TGSearchBarDelegate + +- (void)searchBar:(TGSearchBar *)searchBar willChangeHeight:(CGFloat)newHeight; + +@end + +@interface TGSearchBar : UIView + ++ (CGFloat)searchBarBaseHeight; ++ (CGFloat)searchBarScopeHeight; +- (CGFloat)baseHeight; + +@property (nonatomic, copy) void (^clearPrefix)(bool); + +@property (nonatomic, weak) id delegate; +@property (nonatomic) bool highContrast; + +@property (nonatomic, strong) UITextField *customTextField; +@property (nonatomic, readonly) UITextField *maybeCustomTextField; + +@property (nonatomic, strong) UIImageView *customBackgroundView; +@property (nonatomic, strong) UIImageView *customActiveBackgroundView; + +@property (nonatomic, strong) NSArray *customScopeButtonTitles; +@property (nonatomic) NSInteger selectedScopeButtonIndex; +@property (nonatomic) bool showsScopeBar; +@property (nonatomic) bool scopeBarCollapsed; + +@property (nonatomic) bool searchBarShouldShowScopeControl; +@property (nonatomic) bool alwaysExtended; +@property (nonatomic) bool hidesCancelButton; + +@property (nonatomic, strong) UIButton *customCancelButton; + +@property (nonatomic) TGSearchBarStyle style; +@property (nonatomic, strong) NSString *text; +@property (nonatomic, strong) NSString *placeholder; +@property (nonatomic, strong) NSAttributedString *prefixText; + +@property (nonatomic) bool showActivity; +@property (nonatomic) bool delayActivity; + +- (instancetype)initWithFrame:(CGRect)frame style:(TGSearchBarStyle)style; + +- (void)setShowsCancelButton:(bool)showsCancelButton animated:(bool)animated; + +- (void)setCustomScopeBarHidden:(bool)hidden; + +- (void)updateClipping:(CGFloat)clippedHeight; + +- (void)localizationUpdated; + +@end diff --git a/LegacyComponents/TGSearchBar.m b/LegacyComponents/TGSearchBar.m new file mode 100644 index 0000000000..73a732300f --- /dev/null +++ b/LegacyComponents/TGSearchBar.m @@ -0,0 +1,994 @@ +#import "TGSearchBar.h" + +#import "LegacyComponentsInternal.h" +#import "TGColor.h" + +#import +#import + +#import + +#import + +#import + +#import + +@interface TGSearchBar () +{ + CGFloat _cancelButtonWidth; +} + +@property (nonatomic, strong) UIView *wrappingClip; +@property (nonatomic, strong) UIView *wrappingView; + +@property (nonatomic, strong) UIImageView *textFieldBackground; + +@property (nonatomic, strong) UIImage *normalTextFieldBackgroundImage; +@property (nonatomic, strong) UIImage *activeTextFieldBackgroundImage; + +@property (nonatomic) bool showsCustomCancelButton; +@property (nonatomic, strong) UILabel *placeholderLabel; +@property (nonatomic, strong) UILabel *prefixLabel; +@property (nonatomic, strong) UIImageView *customSearchIcon; +@property (nonatomic, strong) UIActivityIndicatorView *customSearchActivityIndicator; +@property (nonatomic, strong) NSTimer *searchActivityTimer; +@property (nonatomic, strong) UIButton *customClearButton; + +@property (nonatomic, strong) UIView *customScopeButtonContainer; +@property (nonatomic, strong) UISegmentedControl *customSegmentedControl; +@property (nonatomic) int customCurrentScope; + +@end + +@implementation TGSearchBar + ++ (CGFloat)searchBarBaseHeight +{ + return 44.0f; +} + +- (CGFloat)baseHeight { + if (self.showsScopeBar) + return 44.0f; + return [self inputHeight] + 12.0f; +} + +- (CGFloat)inputContentOffset { + return _style == TGSearchBarStyleLightAlwaysPlain ? 5.0f : 0.0f; +} + +- (CGFloat)searchIconOffset { + return _style == TGSearchBarStyleLightAlwaysPlain ? TGRetinaPixel : 0.0f; +} + +- (CGFloat)inputHeight { + return _style == TGSearchBarStyleLightAlwaysPlain ? 40.0f : 28.0f; +} + ++ (CGFloat)searchBarScopeHeight +{ + return 44.0f; +} + +- (CGFloat)topPadding +{ + return -1.0f; +} + +- (id)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame style:TGSearchBarStyleLightPlain]; +} + +- (instancetype)initWithFrame:(CGRect)frame style:(TGSearchBarStyle)style +{ + self = [super initWithFrame:frame]; + if (self != nil) + { + _delayActivity = true; + + _wrappingClip = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -20.0f, frame.size.width, frame.size.height + 20.0f)]; + _wrappingClip.clipsToBounds = true; + [self addSubview:_wrappingClip]; + + _wrappingView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 20.0f, frame.size.width, frame.size.height)]; + [_wrappingClip addSubview:_wrappingView]; + + _style = style; + + NSString *backgroundFileName = nil; + NSString *backgroundActiveFileName = nil; + + UIImage *backgroundManualImage = nil; + UIImage *backgroundManualActiveImage = nil; + + if (_style == TGSearchBarStyleDefault) + { + backgroundFileName = @"SearchBarBackground.png"; + backgroundActiveFileName = @"SearchBarBackground_Active.png"; + } + else if (_style == TGSearchBarStyleDark) + { + backgroundFileName = @"SearchBarBackgroundDark.png"; + backgroundActiveFileName = @"SearchBarBackgroundDark.png"; + } + else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) + { + static UIImage *image = nil; + static UIImage *imagePlain = nil; + static UIImage *imagePlainForced = nil; + static UIImage *imageHeader = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 3.0f), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor); + CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 3.0f)); + imagePlain = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; + CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 3.0f)); + imagePlainForced = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; + + CGContextSetFillColorWithColor(context, UIColorRGB(0xc8c7cc).CGColor); + CGFloat separatorHeight = TGScreenPixel; + CGContextFillRect(context, CGRectMake(0.0f, 3.0f - separatorHeight, 1.0f, separatorHeight)); + + image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; + UIGraphicsEndImageContext(); + + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 3.0f), true, 0.0f); + context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, UIColorRGB(0xf7f7f7).CGColor); + CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 3.0f)); + + CGContextSetFillColorWithColor(context, UIColorRGB(0xc8c7cc).CGColor); + CGContextFillRect(context, CGRectMake(0.0f, 3.0f - separatorHeight, 1.0f, separatorHeight)); + + imageHeader = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; + + UIGraphicsEndImageContext(); + }); + + if (_style == TGSearchBarStyleHeader) + { + backgroundManualImage = imageHeader; + backgroundManualActiveImage = imageHeader; + } + else + { + backgroundManualImage = _style == TGSearchBarStyleLight ? image : imagePlain; + backgroundManualActiveImage = _style == TGSearchBarStyleLightAlwaysPlain ? imagePlainForced : image; + } + } + + UIImage *backgroundImage = nil; + if (backgroundManualImage != nil) + backgroundImage = backgroundManualImage; + else + backgroundImage = [[UIImage imageNamed:backgroundFileName] stretchableImageWithLeftCapWidth:1 topCapHeight:1]; + _customBackgroundView = [[UIImageView alloc] initWithFrame:self.bounds]; + _customBackgroundView.image = backgroundImage; + _customBackgroundView.userInteractionEnabled = true; + [_customBackgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapGesture:)]]; + [_wrappingView addSubview:_customBackgroundView]; + + UIImage *activeBackgroundImage = nil; + if (backgroundManualActiveImage != nil) + activeBackgroundImage = backgroundManualActiveImage; + else + activeBackgroundImage = [[UIImage imageNamed:backgroundActiveFileName] stretchableImageWithLeftCapWidth:1 topCapHeight:1]; + _customActiveBackgroundView = [[UIImageView alloc] initWithFrame:self.bounds]; + _customActiveBackgroundView.image = activeBackgroundImage; + _customActiveBackgroundView.alpha = 0.0f; + [_wrappingView addSubview:_customActiveBackgroundView]; + + _textFieldBackground = [[UIImageView alloc] initWithImage:self.normalTextFieldBackgroundImage]; + _textFieldBackground.userInteractionEnabled = false; + [_wrappingView addSubview:_textFieldBackground]; + + UIColor *placeholderColor = nil; + if (_style == TGSearchBarStyleDefault) + placeholderColor = UIColorRGB(0x8e8e93); + else if (_style == TGSearchBarStyleDark) + placeholderColor = [UIColor whiteColor]; + else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) + placeholderColor = UIColorRGB(0x8e8e93); + + _placeholderLabel = [[UILabel alloc] init]; + _placeholderLabel.textAlignment = NSTextAlignmentLeft; + _placeholderLabel.userInteractionEnabled = false; + _placeholderLabel.textColor = placeholderColor; + _placeholderLabel.backgroundColor = [UIColor clearColor]; + _placeholderLabel.font = TGSystemFontOfSize(style == TGSearchBarStyleLightAlwaysPlain ? 16.0f : 14.0f); + _placeholderLabel.text = TGLocalized(@"Common.Search"); + [_wrappingView addSubview:_placeholderLabel]; + + _prefixLabel = [[UILabel alloc] init]; + _prefixLabel.textAlignment = NSTextAlignmentLeft; + _prefixLabel.userInteractionEnabled = false; + _prefixLabel.textColor = placeholderColor; + _prefixLabel.backgroundColor = [UIColor clearColor]; + _prefixLabel.font = TGSystemFontOfSize(style == TGSearchBarStyleLightAlwaysPlain ? 16.0f : 14.0f); + [_wrappingView addSubview:_prefixLabel]; + + NSString *iconFileName = nil; + + if (_style == TGSearchBarStyleDefault) + iconFileName = @"SearchBarIcon.png"; + else if (_style == TGSearchBarStyleDark) + iconFileName = @"SearchBarIconDark.png"; + else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleHeader) + iconFileName = @"SearchBarIconLight.png"; + else if (_style == TGSearchBarStyleLightAlwaysPlain) + iconFileName = @"SearchBarIconLightLarge.png"; + + _customSearchIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:iconFileName]]; + _customSearchIcon.userInteractionEnabled = false; + [_wrappingView addSubview:_customSearchIcon]; + + _customSearchActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:4]; + _customSearchActivityIndicator.alpha = 0.4f; + _customSearchActivityIndicator.userInteractionEnabled = false; + _customSearchActivityIndicator.hidden = true; + [_wrappingView addSubview:_customSearchActivityIndicator]; + } + return self; +} + +- (void)setHighContrast:(bool)highContrast { + if (_highContrast != highContrast) { + _highContrast = highContrast; + _textFieldBackground.image = _showsCustomCancelButton ? self.activeTextFieldBackgroundImage : self.normalTextFieldBackgroundImage; + } +} + +- (void)setAlwaysExtended:(bool)alwaysExtended +{ + if (_alwaysExtended != alwaysExtended) + { + _alwaysExtended = alwaysExtended; + + [self layoutSubviews]; + } +} + +- (void)setFrame:(CGRect)frame +{ + [super setFrame:frame]; +} + +- (void)sizeToFit +{ + CGFloat requiredHeight = 0; + + if (_searchBarShouldShowScopeControl && ![self landscapeMode] && !_scopeBarCollapsed) + { + requiredHeight = [self baseHeight] + [TGSearchBar searchBarScopeHeight]; + } + else + { + requiredHeight = [self baseHeight]; + } + + if (_style == TGSearchBarStyleLight) + requiredHeight += 4.0f; + + CGRect frame = self.frame; + frame.size.height = requiredHeight; + self.frame = frame; +} + +- (BOOL)showsCancelButton +{ + return _showsCustomCancelButton; +} + +- (UIImage *)normalTextFieldBackgroundImage +{ + if (_highContrast) { + static UIImage *image = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + CGFloat diameter = 10.0f; + UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, UIColorRGB(0xe5e5e5).CGColor); + CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); + image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)]; + UIGraphicsEndImageContext(); + }); + return image; + } else if (_normalTextFieldBackgroundImage == nil) { + NSString *fileName = nil; + + if (_style == TGSearchBarStyleDefault) + fileName = @"SearchInputField.png"; + else if (_style == TGSearchBarStyleDark) + fileName = @"SearchInputFieldDark.png"; + else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain) + fileName = @"SearchInputFieldLight.png"; + else if (_style == TGSearchBarStyleLightAlwaysPlain) + { + static UIImage *image = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + CGFloat diameter = 10.0f; + UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, UIColorRGB(0xe8e8e8).CGColor); + CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); + image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)]; + UIGraphicsEndImageContext(); + }); + + _normalTextFieldBackgroundImage = image; + return _normalTextFieldBackgroundImage; + } + else if (_style == TGSearchBarStyleHeader) + { + static UIImage *headerImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + CGFloat diameter = 10.0f; + UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, UIColorRGB(0xe4e4e4).CGColor); + CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); + headerImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)]; + UIGraphicsEndImageContext(); + }); + + _normalTextFieldBackgroundImage = headerImage; + return _normalTextFieldBackgroundImage; + } + UIImage *rawImage = [UIImage imageNamed:fileName]; + _normalTextFieldBackgroundImage = [rawImage stretchableImageWithLeftCapWidth:(int)(rawImage.size.width / 2) topCapHeight:(int)(rawImage.size.height / 2)]; + } + + return _normalTextFieldBackgroundImage; +} + +- (UIImage *)activeTextFieldBackgroundImage +{ + if (_activeTextFieldBackgroundImage == nil) + { + NSString *fileName = nil; + + if (_style == TGSearchBarStyleDefault) + fileName = @"SearchInputField_Active.png"; + else if (_style == TGSearchBarStyleDark) + fileName = @"SearchInputFieldDark.png"; + else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain) + fileName = @"SearchInputFieldLight.png"; + else if (_style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) { + _activeTextFieldBackgroundImage = [self normalTextFieldBackgroundImage]; + return _activeTextFieldBackgroundImage; + } + + UIImage *rawImage = [UIImage imageNamed:fileName]; + _activeTextFieldBackgroundImage = [rawImage stretchableImageWithLeftCapWidth:(int)(rawImage.size.width / 2) topCapHeight:(int)(rawImage.size.height / 2)]; + } + + return _activeTextFieldBackgroundImage; +} + +- (UITextField *)maybeCustomTextField +{ + return _customTextField; +} + +- (UITextField *)customTextField +{ + if (_customTextField == nil) + { + CGRect frame = _textFieldBackground.frame; + frame.origin.y -= TGIsRetina() ? 0.0f : 0.0f; + frame.origin.x += 27; + frame.size.width -= 27 + 8 + 14; + _customTextField = [[TGTextField alloc] initWithFrame:frame]; + __weak TGSearchBar *weakSelf = self; + ((TGTextField *)_customTextField).deleteBackwardEmpty = ^{ + __strong TGSearchBar *strongSelf = weakSelf; + if (strongSelf != nil && strongSelf->_clearPrefix) { + strongSelf->_clearPrefix(false); + } + }; + _customTextField.font = _placeholderLabel.font; + if (iosMajorVersion() >= 7) + _customTextField.textAlignment = NSTextAlignmentNatural; + _customTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; + _customTextField.autocorrectionType = UITextAutocorrectionTypeNo; + + UIColor *textColor = nil; + UIImage *clearImage = nil; + + if (_style == TGSearchBarStyleDefault || _style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) + { + textColor = [UIColor blackColor]; + clearImage = [UIImage imageNamed:@"SearchBarClearIcon.png"]; + } + else if (_style == TGSearchBarStyleDark) + { + textColor = [UIColor whiteColor]; + clearImage = [UIImage imageNamed:@"SearchBarClearIconDark.png"]; + } + + _customTextField.textColor = textColor; + + _customTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; + _customTextField.returnKeyType = UIReturnKeySearch; + _customTextField.keyboardAppearance = _style == TGSearchBarStyleDark ? UIKeyboardAppearanceAlert : UIKeyboardAppearanceDefault; + _customTextField.delegate = self; + [_customTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; + + _customClearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, clearImage.size.width, clearImage.size.height)]; + [_customClearButton setBackgroundImage:clearImage forState:UIControlStateNormal]; + [_customClearButton addTarget:self action:@selector(customClearButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + _customClearButton.hidden = true; + + [_wrappingView addSubview:_customTextField]; + [_wrappingView addSubview:_customClearButton]; + + [self setNeedsLayout]; + } + + return _customTextField; +} + +- (UIButton *)customCancelButton +{ + if (_customCancelButton == nil) + { + _cancelButtonWidth = [TGLocalized(@"Common.Cancel") sizeWithFont:TGSystemFontOfSize(17.0f)].width + 11.0f; + + CGRect textFieldBackgroundFrame = _textFieldBackground.frame; + _customCancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(textFieldBackgroundFrame.origin.x + textFieldBackgroundFrame.size.width + 10, 0, _cancelButtonWidth, [self baseHeight])]; + [_customCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; + + UIColor *buttonColor = nil; + + if (_style == TGSearchBarStyleDefault || _style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) + buttonColor = TGAccentColor(); + else if (_style == TGSearchBarStyleDark) + buttonColor = [UIColor whiteColor]; + + [(TGModernButton *)_customCancelButton setTitleColor:buttonColor]; + _customCancelButton.titleLabel.font = TGSystemFontOfSize(17.0f); + _customCancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight; + _customCancelButton.hidden = true; + [_customCancelButton addTarget:self action:@selector(searchCancelButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [_wrappingView addSubview:_customCancelButton]; + } + + return _customCancelButton; +} + +- (void)setShowsCancelButton:(BOOL)showsCancelButton +{ + [self setShowsCancelButton:showsCancelButton animated:false]; +} + +- (void)setShowsCancelButton:(bool)showsCancelButton animated:(bool)animated +{ + if (_showsCustomCancelButton != showsCancelButton) + { + if (showsCancelButton) + { + [self customCancelButton]; + _customCancelButton.hidden = _hidesCancelButton; + + if (_customScopeButtonTitles.count > 1) + { + self.customScopeButtonContainer.hidden = false; + [_customSegmentedControl setSelectedSegmentIndex:0]; + } + } + else + { + [_customTextField setText:@""]; + [self updatePlaceholder:@""]; + } + + _textFieldBackground.image = showsCancelButton ? self.activeTextFieldBackgroundImage : self.normalTextFieldBackgroundImage; + + _showsCustomCancelButton = showsCancelButton; + + if (animated) + { + if (showsCancelButton) + _wrappingClip.clipsToBounds = false; + + [UIView animateWithDuration:0.2 animations:^ + { + if (!showsCancelButton) + { + _customTextField.alpha = 0.0f; + _customClearButton.alpha = 0.0f; + } + + if (_customScopeButtonTitles.count > 1) + { + [self setSearchBarShouldShowScopeControl:showsCancelButton]; + _customScopeButtonContainer.alpha = showsCancelButton ? 1.0f : 0.0f; + } + + [self layoutSubviews]; + + _customActiveBackgroundView.alpha = showsCancelButton ? 1.0f : 0.0f; + } completion:^(__unused BOOL finished) + { + //if (finished) + { + if (showsCancelButton) + { + _customTextField.alpha = 1.0f; + _customClearButton.alpha = 1.0f; + } + else + { + _customCancelButton.hidden = true; + _customScopeButtonContainer.hidden = true; + + _wrappingClip.clipsToBounds = true; + } + } + }]; + } + else + { + _wrappingClip.clipsToBounds = !showsCancelButton; + + if (_customScopeButtonTitles.count > 1) + { + [self setSearchBarShouldShowScopeControl:showsCancelButton]; + _customScopeButtonContainer.alpha = showsCancelButton ? 1.0f : 0.0f; + } + + _customTextField.alpha = showsCancelButton ? 1.0f : 0.0f; + _customClearButton.alpha = _customTextField.alpha; + _customActiveBackgroundView.alpha = showsCancelButton ? 1.0f : 0.0f; + _customCancelButton.hidden = !showsCancelButton; + _customScopeButtonContainer.hidden = !showsCancelButton; + + [self layoutSubviews]; + } + } +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGRect bounds = self.bounds; + + bool landscapeMode = [self landscapeMode]; + + CGRect clippingFrame = _wrappingClip.frame; + clippingFrame.size = CGSizeMake(bounds.size.width, bounds.size.height + 20.0f); + _wrappingClip.frame = clippingFrame; + + CGRect wrappingFrame = _wrappingView.frame; + wrappingFrame.size = bounds.size; + _wrappingView.frame = wrappingFrame; + + float retinaPixel = TGIsRetina() ? 0.5f : 0.0f; + const float scopeBarHorizontalWidth = 220; + + CGFloat rightPadding = _showsCustomCancelButton && !_hidesCancelButton ? ((_customScopeButtonContainer != nil && landscapeMode ? scopeBarHorizontalWidth : 0) + _cancelButtonWidth) : 0.0f; + + _customBackgroundView.frame = CGRectMake(0, ((_showsCustomCancelButton || _alwaysExtended) ? -20.0f : 0.0f), self.frame.size.width, self.frame.size.height + (_showsCustomCancelButton || _alwaysExtended ? 20.0f : 0.0f)); + _customActiveBackgroundView.frame = _customBackgroundView.frame; + + _textFieldBackground.frame = CGRectMake(8, 9 + [self topPadding], self.frame.size.width - 16 - rightPadding, [self inputHeight]); + + CGFloat prefixOffset = 0.0f; + { + [_prefixLabel sizeToFit]; + + CGRect frame = _textFieldBackground.frame; + //frame.origin.y -= retinaPixel; + frame.origin.y += 5.0f; + frame.origin.x += 27; + frame.size = _prefixLabel.bounds.size; + + frame.size.width = MIN(frame.size.width, CGFloor((bounds.size.width - rightPadding - 20.0f) / 2.0f)); + + _prefixLabel.frame = frame; + + prefixOffset = frame.size.width; + } + + CGSize placeholderSize = [_placeholderLabel.text sizeWithFont:_placeholderLabel.font]; + placeholderSize.width = MIN(placeholderSize.width, self.frame.size.width - rightPadding - 40.0f - prefixOffset); + + _customSearchIcon.frame = CGRectMake(_showsCustomCancelButton ? (_textFieldBackground.frame.origin.x + 8.0f) : ((CGFloor((self.frame.size.width - placeholderSize.width) / 2) + 10 + TGRetinaPixel) - 20), [self searchIconOffset] + [self inputContentOffset] + 16 + retinaPixel + [self topPadding], _customSearchIcon.frame.size.width, _customSearchIcon.frame.size.height); + + _customSearchActivityIndicator.frame = (CGRect){{CGFloor(_customSearchIcon.frame.origin.x + (_customSearchIcon.frame.size.width - _customSearchActivityIndicator.frame.size.width) / 2.0f), CGFloor(_customSearchIcon.frame.origin.y + (_customSearchIcon.frame.size.height - _customSearchActivityIndicator.frame.size.height) / 2.0f) + 1.0f + TGRetinaPixel}, _customSearchActivityIndicator.frame.size}; + + _placeholderLabel.frame = CGRectMake(_showsCustomCancelButton ? ((TGIsRTL() ? (CGRectGetMaxX(_textFieldBackground.frame) - placeholderSize.width - 32.0f) : 36) + prefixOffset) : (CGFloor((self.frame.size.width - placeholderSize.width) / 2) + 10 + TGRetinaPixel), [self inputContentOffset] + 14 + [self topPadding], placeholderSize.width, placeholderSize.height); + + if (_customTextField != nil) + { + CGRect frame = _textFieldBackground.frame; + frame.origin.y -= retinaPixel; + frame.origin.x += 27; + frame.size.width -= 27 + 8 + 24; + + frame.origin.x += prefixOffset; + frame.size.width -= prefixOffset; + + _customTextField.frame = frame; + + _customClearButton.frame = CGRectMake(CGRectGetMaxX(_textFieldBackground.frame) - 22, [self inputContentOffset] + 16 + [self topPadding], _customClearButton.frame.size.width, _customClearButton.frame.size.height); + } + + if (_customCancelButton != nil) + { + _customCancelButton.frame = CGRectMake(self.frame.size.width + (_showsCustomCancelButton ? (-_customCancelButton.frame.size.width - 9) : 9), [self topPadding] + 2.0f, _cancelButtonWidth, [self baseHeight]); + } + + if (_customScopeButtonContainer != nil) + { + if (_showsCustomCancelButton) + { + if (!landscapeMode) + _customScopeButtonContainer.frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f + [self topPadding], self.frame.size.width - 14.0f, 29.0f); + else + _customScopeButtonContainer.frame = CGRectMake(self.frame.size.width - scopeBarHorizontalWidth - _customCancelButton.frame.size.width, 5.0f + [self topPadding], scopeBarHorizontalWidth - 14.0f, 32.0f); + } + else + { + if (!landscapeMode) + _customScopeButtonContainer.frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f + [self topPadding], self.frame.size.width - 14.0f, 29.0f); + else + _customScopeButtonContainer.frame = CGRectMake(self.frame.size.width + 71.0f, 5.0f + [self topPadding], scopeBarHorizontalWidth - 14.0f, 29.0f); + } + } +} + +- (bool)landscapeMode +{ + static CGFloat landscapeScreenWidth = 0.0f; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + CGSize screenSize = TGScreenSize(); + landscapeScreenWidth = MAX(screenSize.width, screenSize.height); + }); + + return self.frame.size.width >= landscapeScreenWidth - FLT_EPSILON; +} + +- (void)setSearchBarShouldShowScopeControl:(bool)searchBarShouldShowScopeControl +{ + _searchBarShouldShowScopeControl = searchBarShouldShowScopeControl; + + if (_searchBarShouldShowScopeControl) + self.customScopeButtonContainer.hidden = false; + + CGFloat requiredHeight = 0; + + if (_searchBarShouldShowScopeControl && ![self landscapeMode]) + requiredHeight = [self baseHeight] + [TGSearchBar searchBarScopeHeight]; + else + requiredHeight = [self baseHeight]; + + if (ABS(requiredHeight - self.frame.size.height) > FLT_EPSILON) + { + id delegate = (id)self.delegate; + if ([delegate respondsToSelector:@selector(searchBar:willChangeHeight:)]) + [delegate searchBar:self willChangeHeight:requiredHeight]; + } +} + +- (void)setCustomScopeBarHidden:(bool)hidden +{ + self.customScopeButtonContainer.alpha = hidden ? 0.0f : 1.0f; + self.customScopeButtonContainer.userInteractionEnabled = !hidden; +} + +#pragma mark - + +- (void)tappedSearchBar:(id)__unused arg +{ +} + +- (BOOL)becomeFirstResponder +{ + if (![_customTextField isFirstResponder]) + { + bool shouldBeginEditing = true; + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) + shouldBeginEditing = [delegate searchBarShouldBeginEditing:(UISearchBar *)self]; + + if (shouldBeginEditing) + { + [self.customTextField becomeFirstResponder]; + + if ([delegate respondsToSelector:@selector(searchBarTextDidBeginEditing:)]) + [delegate searchBarTextDidBeginEditing:(UISearchBar *)self]; + + return true; + } + } + + return false; +} + +- (BOOL)resignFirstResponder +{ + return [_customTextField resignFirstResponder]; +} + +- (BOOL)canBecomeFirstResponder +{ + return _customTextField == nil || [_customTextField canBecomeFirstResponder]; +} + +- (BOOL)canResignFirstResponder +{ + return [_customTextField canResignFirstResponder]; +} + +- (BOOL)isFirstResponder { + return [_customTextField isFirstResponder]; +} + +#pragma mark - + +- (void)searchCancelButtonPressed +{ + id delegate = self.delegate; + + if ([delegate respondsToSelector:@selector(searchBarCancelButtonClicked:)]) + [delegate searchBarCancelButtonClicked:(UISearchBar *)self]; +} + +- (UIView *)customScopeButtonContainer +{ + if (_customScopeButtonContainer == nil) + { + CGRect frame = CGRectZero; + if (![self landscapeMode]) + frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f, self.frame.size.width - 14.0f, 29.0f); + else + frame = CGRectMake(0, 0, self.frame.size.width, 29.0f); + + _customScopeButtonContainer = [[UIView alloc] initWithFrame:frame]; + _customScopeButtonContainer.alpha = 0.0f; + [_wrappingView insertSubview:_customScopeButtonContainer aboveSubview:_customActiveBackgroundView]; + + _customSegmentedControl = [[UISegmentedControl alloc] initWithItems:self.customScopeButtonTitles]; + + [_customSegmentedControl setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlBackground.png") forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + [_customSegmentedControl setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected barMetrics:UIBarMetricsDefault]; + [_customSegmentedControl setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlSelected.png") forState:UIControlStateSelected | UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; + [_customSegmentedControl setBackgroundImage:TGComponentsImageNamed(@"ModernSegmentedControlHighlighted.png") forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; + UIImage *dividerImage = TGComponentsImageNamed(@"ModernSegmentedControlDivider.png"); + [_customSegmentedControl setDividerImage:dividerImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; + + [_customSegmentedControl setTitleTextAttributes:@{UITextAttributeTextColor: TGAccentColor(), UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateNormal]; + [_customSegmentedControl setTitleTextAttributes:@{UITextAttributeTextColor: [UIColor whiteColor], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateSelected]; + + _customSegmentedControl.frame = CGRectMake(0, _customScopeButtonContainer.frame.size.height - 29.0f, _customScopeButtonContainer.frame.size.width, 29.0f); + _customSegmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + + [_customSegmentedControl setSelectedSegmentIndex:0]; + [_customSegmentedControl addTarget:self action:@selector(segmentedControlChanged) forControlEvents:UIControlEventValueChanged]; + + [_customScopeButtonContainer addSubview:_customSegmentedControl]; + } + + return _customScopeButtonContainer; +} + +- (void)backgroundTapGesture:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) + { + if (![_customTextField isFirstResponder]) + { + bool shouldBeginEditing = true; + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) + shouldBeginEditing = [delegate searchBarShouldBeginEditing:(UISearchBar *)self]; + + if (shouldBeginEditing) + { + [self.customTextField becomeFirstResponder]; + + if ([delegate respondsToSelector:@selector(searchBarTextDidBeginEditing:)]) + [delegate searchBarTextDidBeginEditing:(UISearchBar *)self]; + } + } + } +} + +- (void)updatePlaceholder:(NSString *)text +{ + _placeholderLabel.hidden = text.length != 0; + _customClearButton.hidden = !_placeholderLabel.hidden && _prefixText.length == 0; +} + +- (void)textFieldDidChange:(UITextField *)textField +{ + if (textField == _customTextField) + { + NSString *text = textField.text; + + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBar:textDidChange:)]) + [delegate searchBar:(UISearchBar *)self textDidChange:text]; + + [self updatePlaceholder:text]; + } +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + if (textField == _customTextField) + { + if (textField.text.length != 0) + { + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBarSearchButtonClicked:)]) + [delegate searchBarSearchButtonClicked:(UISearchBar *)self]; + } + + [textField resignFirstResponder]; + + return false; + } + + return false; +} + +- (void)customClearButtonPressed +{ + if (_customTextField.text.length == 0) { + if (_prefixText.length != 0) { + if (_clearPrefix) { + _clearPrefix(true); + } + } + } else { + [_customTextField setText:@""]; + [self updatePlaceholder:@""]; + + [self becomeFirstResponder]; + + NSString *text = @""; + + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBar:textDidChange:)]) + [delegate searchBar:(UISearchBar *)self textDidChange:text]; + } +} + +- (NSInteger)selectedScopeButtonIndex +{ + return _customSegmentedControl.selectedSegmentIndex; +} + +- (void)setSelectedScopeButtonIndex:(NSInteger)selectedScopeButtonIndex +{ + [self.customSegmentedControl setSelectedSegmentIndex:selectedScopeButtonIndex]; +} + +- (void)setPlaceholder:(NSString *)placeholder +{ + _placeholder = placeholder; + _placeholderLabel.text = placeholder; + + [self setNeedsLayout]; +} + +- (NSString *)text +{ + return _customTextField.text; +} + +- (void)setText:(NSString *)text +{ + bool layout = _customTextField == nil; + self.customTextField.text = text; + if (layout) + [self setNeedsLayout]; + + [self textFieldDidChange:_customTextField]; +} + +- (void)segmentedControlChanged +{ + if (_showsCustomCancelButton) + { + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(searchBar:selectedScopeButtonIndexDidChange:)]) + [delegate searchBar:(UISearchBar *)self selectedScopeButtonIndexDidChange:_customSegmentedControl.selectedSegmentIndex]; + } +} + +- (void)updateClipping:(CGFloat)clippedHeight +{ + CGFloat offset = self.frame.size.height + MAX(0.0f, MIN(clippedHeight, self.frame.size.height)); + + CGRect frame = _wrappingClip.frame; + frame.origin.y = offset - frame.size.height + 20.0f; + _wrappingClip.frame = frame; + + CGRect wrapFrame = _wrappingView.frame; + wrapFrame.origin.y = -offset + wrapFrame.size.height; + _wrappingView.frame = wrapFrame; +} + +- (void)localizationUpdated +{ + _placeholderLabel.text = TGLocalized(@"Common.Search"); + + _cancelButtonWidth = [TGLocalized(@"Common.Cancel") sizeWithFont:TGSystemFontOfSize(17.0f)].width + 11.0f; + + CGRect textFieldBackgroundFrame = _textFieldBackground.frame; + _customCancelButton.frame = CGRectMake(textFieldBackgroundFrame.origin.x + textFieldBackgroundFrame.size.width + 10, 0, _cancelButtonWidth, [self baseHeight]); + [_customCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; + + [_customSegmentedControl removeAllSegments]; + int index = -1; + for (NSString *itemText in _customScopeButtonTitles) + { + index++; + [_customSegmentedControl insertSegmentWithTitle:itemText atIndex:(NSUInteger)index animated:false]; + } + + [self setNeedsLayout]; +} + +- (void)setShowActivity:(bool)showActivity +{ + if (_showActivity != showActivity) + { + [_searchActivityTimer invalidate]; + _searchActivityTimer = nil; + + _showActivity = showActivity; + + if (_delayActivity && showActivity) + { + _searchActivityTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(searchActivityTimerEvent) interval:0.2 repeat:false]; + } + else + [self searchActivityTimerEvent]; + } +} + +- (void)searchActivityTimerEvent +{ + _customSearchIcon.hidden = _showActivity; + if (_showActivity) + { + _customSearchActivityIndicator.hidden = false; + [_customSearchActivityIndicator startAnimating]; + } + else + { + _customSearchActivityIndicator.hidden = true; + [_customSearchActivityIndicator stopAnimating]; + } +} + +- (void)setPrefixText:(NSAttributedString *)prefixText { + _prefixText = prefixText; + _prefixLabel.attributedText = prefixText; + _customClearButton.hidden = !_placeholderLabel.hidden && _prefixText.length == 0; + + [self setNeedsLayout]; +} + +@end diff --git a/LegacyComponents/TGSearchDisplayMixin.h b/LegacyComponents/TGSearchDisplayMixin.h new file mode 100644 index 0000000000..18f042edf5 --- /dev/null +++ b/LegacyComponents/TGSearchDisplayMixin.h @@ -0,0 +1,46 @@ +#import + +@class TGSearchDisplayMixin; +@class TGSearchBar; + +@protocol TGSearchDisplayMixinDelegate + +@required + +- (UITableView *)createTableViewForSearchMixin:(TGSearchDisplayMixin *)searchMixin; +- (UIView *)referenceViewForSearchResults; +- (void)searchMixin:(TGSearchDisplayMixin *)searchMixin hasChangedSearchQuery:(NSString *)searchQuery withScope:(int)scope; + +@optional + +- (void)searchMixinWillActivate:(bool)animated; +- (void)searchMixinWillDeactivate:(bool)animated; + +@end + +@interface TGSearchDisplayMixin : NSObject + +@property (nonatomic, weak) id delegate; + +@property (nonatomic, strong) TGSearchBar *searchBar; +@property (nonatomic) bool isActive; +@property (nonatomic, strong) UITableView *searchResultsTableView; +@property (nonatomic) bool alwaysShowsCancelButton; + +@property (nonatomic) bool searchResultsTableViewHidden; + +@property (nonatomic) bool simpleLayout; + +- (void)setSearchResultsTableViewHidden:(bool)searchResultsTableViewHidden animated:(bool)animated; + +- (void)setIsActive:(bool)isActive animated:(bool)animated; + +- (void)controllerInsetUpdated:(UIEdgeInsets)controllerInset; +- (void)controllerLayoutUpdated:(CGSize)layoutSize; + +- (void)reloadSearchResults; +- (void)resignResponderIfAny; + +- (void)unload; + +@end diff --git a/LegacyComponents/TGSearchDisplayMixin.m b/LegacyComponents/TGSearchDisplayMixin.m new file mode 100644 index 0000000000..6555122b37 --- /dev/null +++ b/LegacyComponents/TGSearchDisplayMixin.m @@ -0,0 +1,451 @@ +#import "TGSearchDisplayMixin.h" + +#import "LegacyComponentsInternal.h" +#import "TGColor.h" +#import "TGHacks.h" + +#import "TGSearchBar.h" + +#import + +@interface TGSearchDisplayMixin () + +@property (nonatomic) UIEdgeInsets controllerInset; + +@property (nonatomic, strong) UIView *dimView; + +@property (nonatomic, strong) UIView *tableViewContainer; + +@end + +@implementation TGSearchDisplayMixin + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + + } + return self; +} + +- (void)dealloc +{ + [self unload]; +} + +- (void)unload +{ + [self _unloadTableView]; +} + +- (void)_unloadTableView +{ + [_searchResultsTableView removeFromSuperview]; + + _searchResultsTableView.delegate = nil; + _searchResultsTableView.dataSource = nil; + _searchResultsTableView = nil; +} + +- (void)setSearchBar:(UISearchBar *)searchBar +{ + if (_searchBar != nil) + _searchBar.delegate = nil; + + _searchBar = (TGSearchBar *)searchBar; + _searchBar.delegate = self; +} + +- (void)setIsActive:(bool)isActive +{ + [self setIsActive:isActive animated:true]; +} + +- (void)setIsActive:(bool)isActive animated:(bool)animated +{ + if (_isActive != isActive) + { + _isActive = isActive; + + if (isActive || !self.alwaysShowsCancelButton) + [_searchBar setShowsCancelButton:isActive animated:animated]; + + if (isActive) + { + id delegate = _delegate; + + UIView *referenceView = [delegate referenceViewForSearchResults]; + + [self setSearchResultsTableViewHidden:true]; + _searchResultsTableView.alpha = 1.0f; + + if (_dimView == nil) + { + _dimView = [[UIView alloc] init]; + _dimView.backgroundColor = UIColorRGBA(0x000000, 0.4f); + _dimView.alpha = 0.0f; + _dimView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + } + + if (_tableViewContainer == nil) + { + _tableViewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + _tableViewContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + UIView *tapView = [[UIView alloc] initWithFrame:_tableViewContainer.bounds]; + tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [tapView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimViewTapped:)]]; + [_tableViewContainer addSubview:tapView]; + } + + CGRect dimViewFrame = referenceView.bounds; + + if ([referenceView isKindOfClass:[UIScrollView class]]) + { + UIScrollView *referenceScrollView = (UIScrollView *)referenceView; + dimViewFrame.origin.y = -referenceScrollView.contentOffset.y + _searchBar.frame.size.height; + } + else + { + if (_simpleLayout) { + dimViewFrame.origin.y = _controllerInset.top; + } else { + CGRect searchBarReferenceFrame = [_searchBar convertRect:_searchBar.bounds toView:referenceView.superview]; + dimViewFrame.origin.y = searchBarReferenceFrame.origin.y + searchBarReferenceFrame.size.height; + } + } + + [[referenceView superview] insertSubview:_tableViewContainer aboveSubview:referenceView]; + + [[referenceView superview] insertSubview:_dimView aboveSubview:referenceView]; + + _dimView.frame = dimViewFrame; + + _dimView.layer.frame = dimViewFrame; + + CGRect tableViewContainerFrame = referenceView.frame; + if (_simpleLayout) { + tableViewContainerFrame.origin.y = _controllerInset.top; + } else { + tableViewContainerFrame.origin.y = _controllerInset.top + _searchBar.frame.size.height; + } + _tableViewContainer.frame = tableViewContainerFrame; + + _tableViewContainer.layer.frame = _tableViewContainer.frame; + + if (animated) + { + [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _dimView.alpha = 1.0f; + } completion:nil]; + } + else + { + _dimView.alpha = 1.0f; + } + + [self _updateSearchBarLayout:animated]; + + if ([delegate respondsToSelector:@selector(searchMixinWillActivate:)]) + [delegate searchMixinWillActivate:animated]; + } + else + { + [self _updateSearchBarLayout:animated]; + + [_searchBar resignFirstResponder]; + [_searchBar setText:@""]; + + if (animated) + { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(searchMixinWillDeactivate:)]) + [delegate searchMixinWillDeactivate:animated]; + + [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + _dimView.alpha = 0.0f; + _searchResultsTableView.alpha = 0.0f; + } completion:^(BOOL finished) + { + if (finished) + { + [_dimView removeFromSuperview]; + [_tableViewContainer removeFromSuperview]; + [_searchResultsTableView removeFromSuperview]; + + [self _unloadTableView]; + } + }]; + } + else + { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(searchMixinWillDeactivate:)]) + [delegate searchMixinWillDeactivate:animated]; + + _dimView.alpha = 0.0f; + + [_dimView removeFromSuperview]; + [_tableViewContainer removeFromSuperview]; + [_searchResultsTableView removeFromSuperview]; + + [self _unloadTableView]; + } + } + } +} + +- (void)_updateSearchBarLayout:(bool)animated +{ + CGFloat currentHeight = _searchBar.frame.size.height; + + if (((TGSearchBar *)_searchBar).customScopeButtonTitles.count > 1) + { + bool updateSize = false; + + updateSize = true; + + if (updateSize) + [_searchBar sizeToFit]; + } + + if (ABS(currentHeight - _searchBar.frame.size.height) > FLT_EPSILON) + { + if (animated) + { + [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ + { + [self searchBar:(TGSearchBar *)_searchBar willChangeHeight:_searchBar.frame.size.height]; + [_searchBar layoutSubviews]; + } completion:^(BOOL finished) + { + if (finished) + { + } + }]; + } + else + { + [self searchBar:(TGSearchBar *)_searchBar willChangeHeight:_searchBar.frame.size.height]; + [_searchBar layoutSubviews]; + } + } +} + +- (void)controllerInsetUpdated:(UIEdgeInsets)controllerInset +{ + _controllerInset = controllerInset; + + [self controllerLayoutUpdated:CGSizeZero]; +} + +- (void)controllerLayoutUpdated:(CGSize)__unused layoutSize +{ + [self _updateSearchBarLayout:false]; + + if (_dimView != nil && _dimView.superview != nil) + { + CGRect frame = _dimView.superview.bounds; + if (_simpleLayout) { + frame.origin.y = _controllerInset.top; + } else { + frame.origin.y = _controllerInset.top + _searchBar.frame.size.height; + } + _dimView.frame = frame; + } + + if (_tableViewContainer != nil && _tableViewContainer.superview != nil) + { + CGRect tableViewContainerFrame = _tableViewContainer.frame; + if (_simpleLayout) { + tableViewContainerFrame.origin.y = _controllerInset.top; + } else { + tableViewContainerFrame.origin.y = _controllerInset.top + _searchBar.frame.size.height; + } + _tableViewContainer.frame = tableViewContainerFrame; + } + + if (_searchResultsTableView != nil && _searchResultsTableView.superview != nil) + { + UIEdgeInsets tableInset = _controllerInset; + tableInset.bottom += tableInset.top + _searchBar.frame.size.height; + tableInset.top = 0; + _searchResultsTableView.contentInset = tableInset; + _searchResultsTableView.scrollIndicatorInsets = tableInset; + } + + if (_searchBar.showsScopeBar) + { + CATransition *transition = [CATransition animation]; + transition.duration = 0.2 * TGAnimationSpeedFactor(); + transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [_searchBar.layer addAnimation:transition forKey:@"content"]; + } +} + +- (void)searchBar:(TGSearchBar *)searchBar willChangeHeight:(CGFloat)newHeight +{ + if (searchBar == _searchBar) + { + id delegate = _delegate; + UIView *referenceView = [delegate referenceViewForSearchResults]; + + if ([referenceView isKindOfClass:[UITableView class]]) + { + static NSInvocation *invocation = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + SEL selector = NSSelectorFromString(TGEncodeText(@"`ubcmfIfbefsIfjhiuEjeDibohfUpIfjhiu;", -1)); + + NSMethodSignature *signature = [[UITableView class] instanceMethodSignatureForSelector:selector]; + if (signature == nil) + { + TGLegacyLog(@"***** Method not found"); + } + else + { + invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setSelector:selector]; + } + }); + + if (invocation != nil) + { + [invocation setTarget:referenceView]; + CGFloat height = newHeight; + [invocation setArgument:&height atIndex:2]; + [invocation invoke]; + + [invocation setTarget:nil]; + } + } + } +} + +- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar +{ + if (searchBar == (UISearchBar *)_searchBar) + { + [self setIsActive:true animated:true]; + } +} + +- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText +{ + if (searchBar == (UISearchBar *)_searchBar) + { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(searchMixin:hasChangedSearchQuery:withScope:)]) + { + [delegate searchMixin:self hasChangedSearchQuery:searchText withScope:(int)_searchBar.selectedScopeButtonIndex]; + } + + //if (searchText.length == 0) + // [self setSearchResultsTableViewHidden:false]; + } +} + +- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar +{ + if (searchBar == (UISearchBar *)_searchBar) + { + [self setIsActive:false animated:true]; + } +} + +- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar +{ + if (searchBar == (UISearchBar *)_searchBar) + { + [_searchBar resignFirstResponder]; + } +} + +- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope +{ + if (searchBar == (UISearchBar *)_searchBar) + { + id delegate = _delegate; + if ([delegate respondsToSelector:@selector(searchMixin:hasChangedSearchQuery:withScope:)]) + [delegate searchMixin:self hasChangedSearchQuery:_searchBar.text withScope:(int)selectedScope]; + } +} + +- (void)dimViewTapped:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) + { + if (_searchResultsTableView == nil || _searchResultsTableView.hidden) + [self setIsActive:false animated:true]; + } +} + +#pragma mark - + +- (bool)searchResultsTableViewHidden +{ + return _searchResultsTableView == nil || _searchResultsTableView.hidden; +} + +- (void)setSearchResultsTableViewHidden:(bool)searchResultsTableViewHidden +{ + [self setSearchResultsTableViewHidden:searchResultsTableViewHidden animated:false]; +} + +- (void)setSearchResultsTableViewHidden:(bool)searchResultsTableViewHidden animated:(bool)animated +{ + bool wasHidden = _searchResultsTableView.hidden; + + _searchResultsTableView.hidden = searchResultsTableViewHidden; + _dimView.hidden = !searchResultsTableViewHidden; + + if (animated && wasHidden && !searchResultsTableViewHidden) + { + _searchResultsTableView.alpha = 0.0f; + [UIView animateWithDuration:0.25 animations:^ + { + _searchResultsTableView.alpha = 1.0f; + }]; + } +} + +- (void)reloadSearchResults +{ + if (_searchResultsTableView == nil) + { + id delegate = _delegate; + + _searchResultsTableView = [delegate createTableViewForSearchMixin:self]; + _searchResultsTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + [self setSearchResultsTableViewHidden:true]; + } + + if (_searchResultsTableView.superview == nil) + { + _searchResultsTableView.frame = _tableViewContainer.bounds; + + UIEdgeInsets tableInset = _controllerInset; + tableInset.bottom += tableInset.top + _searchBar.frame.size.height; + tableInset.top = 0; + _searchResultsTableView.contentInset = tableInset; + _searchResultsTableView.scrollIndicatorInsets = tableInset; + + [_tableViewContainer addSubview:_searchResultsTableView]; + } + + [_searchResultsTableView reloadData]; +} + +- (void)resignResponderIfAny +{ + [_searchBar resignFirstResponder]; +} + +@end diff --git a/LegacyComponents/TGStringUtils.mm b/LegacyComponents/TGStringUtils.mm index 24b0487f2c..22b5fdb7a5 100644 --- a/LegacyComponents/TGStringUtils.mm +++ b/LegacyComponents/TGStringUtils.mm @@ -615,43 +615,43 @@ static bool isEmojiCharacter(NSString *singleChar) { int number = (int)seconds; - return [effectiveLocalization() getPluralized:@"MessageTimer.Seconds" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Seconds" count:(int32_t)number]; } else if (seconds < 60 * 60) { int number = (int)seconds / 60; - return [effectiveLocalization() getPluralized:@"MessageTimer.Minutes" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Minutes" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24) { int number = (int)seconds / (60 * 60); - return [effectiveLocalization() getPluralized:@"MessageTimer.Hours" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Hours" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24 * 7) { int number = (int)seconds / (60 * 60 * 24); - return [effectiveLocalization() getPluralized:@"MessageTimer.Days" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Days" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24 * 7 * 4) { int number = (int)seconds / (60 * 60 * 24 * 7); - return [effectiveLocalization() getPluralized:@"MessageTimer.Weeks" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Weeks" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24 * 365) { int number = MAX(1, (int)ceilf((int)(seconds / (60 * 60 * 24 * 29)))); - return [effectiveLocalization() getPluralized:@"MessageTimer.Months" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Months" count:(int32_t)number]; } else { int number = (int)seconds / (60 * 60 * 24 * 365); - return [effectiveLocalization() getPluralized:@"MessageTimer.Years" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.Years" count:(int32_t)number]; } return @""; @@ -663,31 +663,31 @@ static bool isEmojiCharacter(NSString *singleChar) { int number = (int)seconds; - return [effectiveLocalization() getPluralized:@"MessageTimer.ShortSeconds" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortSeconds" count:(int32_t)number]; } else if (seconds < 60 * 60) { int number = (int)seconds / 60; - return [effectiveLocalization() getPluralized:@"MessageTimer.ShortMinutes" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortMinutes" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24) { int number = (int)seconds / (60 * 60); - return [effectiveLocalization() getPluralized:@"MessageTimer.ShortHours" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortHours" count:(int32_t)number]; } else if (seconds < 60 * 60 * 24 * 7) { int number = (int)seconds / (60 * 60 * 24); - return [effectiveLocalization() getPluralized:@"MessageTimer.ShortDays" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortDays" count:(int32_t)number]; } else { int number = (int)seconds / (60 * 60 * 24 * 7); - return [effectiveLocalization() getPluralized:@"MessageTimer.ShortWeeks" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"MessageTimer.ShortWeeks" count:(int32_t)number]; } return @""; @@ -813,13 +813,13 @@ static bool isEmojiCharacter(NSString *singleChar) { int number = (int)seconds; - return [effectiveLocalization() getPluralized:@"Call.Seconds" count:number]; + return [legacyEffectiveLocalization() getPluralized:@"Call.Seconds" count:number]; } else { int number = (int)seconds / 60; - return [effectiveLocalization() getPluralized:@"Call.Minutes" count:number]; + return [legacyEffectiveLocalization() getPluralized:@"Call.Minutes" count:number]; } } @@ -829,13 +829,13 @@ static bool isEmojiCharacter(NSString *singleChar) { int number = (int)seconds; - return [effectiveLocalization() getPluralized:@"Call.ShortSeconds" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"Call.ShortSeconds" count:(int32_t)number]; } else { int number = (int)seconds / 60; - return [effectiveLocalization() getPluralized:@"Call.ShortMinutes" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"Call.ShortMinutes" count:(int32_t)number]; } } @@ -843,7 +843,7 @@ static bool isEmojiCharacter(NSString *singleChar) { NSUInteger number = userCount; - return [effectiveLocalization() getPluralized:@"UserCount" count:(int32_t)number]; + return [legacyEffectiveLocalization() getPluralized:@"UserCount" count:(int32_t)number]; } + (NSString *)stringForFileSize:(int64_t)size @@ -897,7 +897,7 @@ static bool isEmojiCharacter(NSString *singleChar) + (NSString *)integerValueFormat:(NSString *)prefix value:(NSInteger)value { - TGLocalization *localization = effectiveLocalization(); + TGLocalization *localization = legacyEffectiveLocalization(); NSString *form = @"any"; switch (TGPluralForm(localization.languageCodeHash, (int)value)) { case TGPluralFormZero: @@ -1239,9 +1239,9 @@ bool TGIsLocaleArabic() { if (substring.length != 0) length++; - //TGLog(@"substringRange %@, enclosingRange %@, length %d", NSStringFromRange(substringRange), NSStringFromRange(enclosingRange), length); + //TGLegacyLog(@"substringRange %@, enclosingRange %@, length %d", NSStringFromRange(substringRange), NSStringFromRange(enclosingRange), length); }]; - //TGLog(@"length %d", length); + //TGLegacyLog(@"length %d", length); return (int)length; } diff --git a/LegacyComponents/TGViewController.mm b/LegacyComponents/TGViewController.mm index 491d28d623..f091524a73 100644 --- a/LegacyComponents/TGViewController.mm +++ b/LegacyComponents/TGViewController.mm @@ -741,7 +741,7 @@ static id _defaultContext = nil; - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - //TGLog(@"Did rotate"); + //TGLegacyLog(@"Did rotate"); _viewControllerIsChangingInterfaceOrientation = false; _currentSizeChangeDuration = 0.0;