completion
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bt4-r9-Trl">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Nix Share Extension-->
|
||||
<scene sceneID="ceB-am-kn3">
|
||||
<objects>
|
||||
<viewController id="j1y-V4-xli" customClass="NixShareExtension" sceneMemberID="viewController">
|
||||
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-270" y="-1356"/>
|
||||
</scene>
|
||||
<!--Enter Captions-->
|
||||
<scene sceneID="heX-HD-CZY">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="ShareViewController" id="Dq8-gr-n5Y" customClass="ImageCaptionViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="q8x-aR-pqn"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="XDI-cm-ZCd"/>
|
||||
</layoutGuides>
|
||||
<view key="view" opaque="NO" contentMode="scaleToFill" id="Qzl-Cn-JM7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" indicatorStyle="black" keyboardDismissMode="onDrag" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="512" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="joK-Be-CpC" customClass="TPKeyboardAvoidingTableView">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="559"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ImageCaptionCell" id="vja-JL-Ztl" customClass="ImageCaptionTableViewCell">
|
||||
<rect key="frame" x="0.0" y="22" width="375" height="512"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vja-JL-Ztl" id="3tn-Gy-EEd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="512"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="R4h-8T-Gi8">
|
||||
<rect key="frame" x="15" y="11" width="345" height="421"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</imageView>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="bma-Ax-dpe">
|
||||
<rect key="frame" x="177.5" y="211.5" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rpd-pm-k7F">
|
||||
<rect key="frame" x="15" y="442" width="345" height="60"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="60" id="fcL-7s-kAz"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences" autocorrectionType="no" spellCheckingType="no" returnKeyType="done" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
|
||||
</textView>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rdb-lo-ZfB">
|
||||
<rect key="frame" x="15" y="442" width="345" height="60"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="0.40000000600000002" blue="0.40000000600000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="rpd-pm-k7F" secondAttribute="bottom" constant="-1" id="2Ql-GR-lk8"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rdb-lo-ZfB" secondAttribute="bottom" constant="10" id="4QO-xa-POV"/>
|
||||
<constraint firstItem="rdb-lo-ZfB" firstAttribute="top" secondItem="R4h-8T-Gi8" secondAttribute="bottom" constant="10" id="BPx-Gt-byP"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="top" secondItem="rdb-lo-ZfB" secondAttribute="top" id="Dey-Wg-cwm"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="centerX" secondItem="bma-Ax-dpe" secondAttribute="centerX" id="Nnu-Ys-5hH"/>
|
||||
<constraint firstItem="bma-Ax-dpe" firstAttribute="centerY" secondItem="R4h-8T-Gi8" secondAttribute="centerY" id="P1p-Hq-9eF"/>
|
||||
<constraint firstItem="bma-Ax-dpe" firstAttribute="centerX" secondItem="R4h-8T-Gi8" secondAttribute="centerX" id="Rwo-xa-Qgc"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="trailing" secondItem="rdb-lo-ZfB" secondAttribute="trailing" id="VyT-ce-sWV"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="bottom" secondItem="rdb-lo-ZfB" secondAttribute="bottom" id="X7G-rr-gQB"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="R4h-8T-Gi8" secondAttribute="trailing" id="XX7-sP-gxv"/>
|
||||
<constraint firstItem="bma-Ax-dpe" firstAttribute="width" secondItem="R4h-8T-Gi8" secondAttribute="width" id="gqM-47-c0u"/>
|
||||
<constraint firstItem="bma-Ax-dpe" firstAttribute="height" secondItem="R4h-8T-Gi8" secondAttribute="height" id="lEf-ql-t08"/>
|
||||
<constraint firstItem="R4h-8T-Gi8" firstAttribute="leading" secondItem="3tn-Gy-EEd" secondAttribute="leadingMargin" id="ngz-fi-GTY"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="leading" secondItem="3tn-Gy-EEd" secondAttribute="leadingMargin" id="rbG-oo-QSQ"/>
|
||||
<constraint firstItem="R4h-8T-Gi8" firstAttribute="top" secondItem="3tn-Gy-EEd" secondAttribute="topMargin" id="swP-Vn-wYj"/>
|
||||
<constraint firstItem="rpd-pm-k7F" firstAttribute="leading" secondItem="rdb-lo-ZfB" secondAttribute="leading" id="tQ0-11-BGs"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="gqM-47-c0u"/>
|
||||
<exclude reference="lEf-ql-t08"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="activityIndicatorView" destination="bma-Ax-dpe" id="IbH-ED-kPy"/>
|
||||
<outlet property="errorLabel" destination="rdb-lo-ZfB" id="gHD-OE-0du"/>
|
||||
<outlet property="textView" destination="rpd-pm-k7F" id="SC8-3d-0Pc"/>
|
||||
<outlet property="thumbnailView" destination="R4h-8T-Gi8" id="C90-1w-dC7"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="Dq8-gr-n5Y" id="MFh-6y-aKs"/>
|
||||
<outlet property="delegate" destination="Dq8-gr-n5Y" id="5oi-dp-Ihx"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="XDI-cm-ZCd" firstAttribute="top" secondItem="joK-Be-CpC" secondAttribute="bottom" id="UyZ-x6-OAm"/>
|
||||
<constraint firstItem="joK-Be-CpC" firstAttribute="leading" secondItem="Qzl-Cn-JM7" secondAttribute="leadingMargin" constant="-16" id="X6g-P8-qen"/>
|
||||
<constraint firstItem="joK-Be-CpC" firstAttribute="top" secondItem="Qzl-Cn-JM7" secondAttribute="topMargin" id="jeH-P8-QGE"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="joK-Be-CpC" secondAttribute="trailing" constant="-16" id="oSw-iK-jD1"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="ZfE-2l-wnm"/>
|
||||
</view>
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" title="Enter Captions" id="9gy-vt-gU0">
|
||||
<barButtonItem key="backBarButtonItem" title="Back" id="ARf-0c-syw"/>
|
||||
<barButtonItem key="leftBarButtonItem" title="Cancel" id="Oz8-8U-jBt">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="Dq8-gr-n5Y" id="GbE-Fc-j7T"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" title="Next" id="liG-JY-nUM">
|
||||
<connections>
|
||||
<segue destination="YSB-oQ-RzE" kind="show" identifier="pushToPlaylistsTableView" id="v3F-fd-hUM"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="tableView" destination="joK-Be-CpC" id="Bj7-i7-U70"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="UTD-fj-Qxk" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<navigationBar contentMode="scaleToFill" id="vkB-1h-kSh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</navigationBar>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="405.60000000000002" y="107.49625187406298"/>
|
||||
</scene>
|
||||
<!--Select Receivers-->
|
||||
<scene sceneID="m8D-cG-8bC">
|
||||
<objects>
|
||||
<tableViewController id="YSB-oQ-RzE" customClass="ReceiverTableViewController" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" allowsSelectionDuringEditing="YES" allowsMultipleSelection="YES" allowsMultipleSelectionDuringEditing="YES" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="Z7J-nI-d1p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" indentationWidth="10" reuseIdentifier="ReceiverCell" rowHeight="512" id="8rm-dO-heR">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="512"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8rm-dO-heR" id="EXg-Qx-0br">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="511.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="YSB-oQ-RzE" id="8vR-ju-e9W"/>
|
||||
<outlet property="delegate" destination="YSB-oQ-RzE" id="GA2-Ky-yOS"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" title="Select Receivers" id="zBq-p7-QR9">
|
||||
<barButtonItem key="rightBarButtonItem" title="Send" style="done" id="jty-bC-dNd">
|
||||
<connections>
|
||||
<action selector="send:" destination="YSB-oQ-RzE" id="QEa-aL-xJH"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="DSg-wk-dJR" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<navigationBar contentMode="scaleToFill" id="zuZ-xG-faf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</navigationBar>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1146" y="108"/>
|
||||
</scene>
|
||||
<!--Container View Controller-->
|
||||
<scene sceneID="lgx-fI-BVY">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="ContainerViewId" id="bt4-r9-Trl" customClass="ContainerViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="wB4-ov-Dgz"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="N2E-uY-WfY"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="cTm-za-nKK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="LXC-Xv-bwp"/>
|
||||
</view>
|
||||
<connections>
|
||||
<segue destination="fP3-PA-YK2" kind="presentation" identifier="InitialSegue" modalPresentationStyle="overFullScreen" id="Gkw-cW-zlX"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="qCF-9c-6mQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-269" y="-601"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="fnC-KA-I6D">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="NavigationController" id="fP3-PA-YK2" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" id="7bZ-Ai-tt6">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="Dq8-gr-n5Y" kind="relationship" relationship="rootViewController" id="wAw-ws-KMS"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="O5y-Cm-6pa" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-269" y="108"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// ContainerViewController.h
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 27/5/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <NixNetwork/NixUpload.h>
|
||||
#import <SDWebImage/SDImageCache.h>
|
||||
@interface ContainerViewController : UIViewController <NixUploadDelegate>
|
||||
@property (nonatomic, strong) NixAPI *api;
|
||||
@property (nonatomic, strong) NixUpload *uploadHandler;
|
||||
|
||||
@property (nonatomic, strong) NSArray *imageErrors;
|
||||
@property (nonatomic, strong) NSArray *imageUrls;
|
||||
@property (nonatomic, strong) NSArray *captions;
|
||||
@property (nonatomic, strong) NSArray *playlistIds;
|
||||
@property (nonatomic, strong) NSArray *friendUsernames;
|
||||
|
||||
@property (nonatomic, strong) NSArray *playlistData;
|
||||
@property (nonatomic, strong) NSArray *friendData;
|
||||
@property (nonatomic, strong) NSArray *selectedReceiverIndexPaths;
|
||||
|
||||
- (void)verifyServiceAvailable;
|
||||
- (void)verifyServiceAvailableWithCompletionHandler:(void (^)(BOOL))completionHandler;
|
||||
- (void)pingWithCompletionHandler:(void (^)(NSError *))completionHandler;
|
||||
- (void)send;
|
||||
- (void)cancel;
|
||||
@end
|
||||
@@ -0,0 +1,368 @@
|
||||
//
|
||||
// ContainerViewController.m
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 27/5/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "ContainerViewController.h"
|
||||
#import "ImageCaptionViewController.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
|
||||
@interface ContainerViewController ()
|
||||
@property (assign) ImageCaptionViewController *svc;
|
||||
- (BOOL)tokenExists;
|
||||
- (void)handleUploadError:(NSError *)error;
|
||||
@end
|
||||
|
||||
@implementation ContainerViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
[self performSegueWithIdentifier:@"InitialSegue" sender:nil];
|
||||
|
||||
self.api = [[NixAPI alloc] init];
|
||||
self.uploadHandler = [[NixUpload alloc] initWithAPI:self.api];
|
||||
self.uploadHandler.delegate = self;
|
||||
self.uploadHandler.uploadSessionIdentifier = @"com.creedon.Nixplay.uploadsession.appext";
|
||||
[self.uploadHandler createSession]; // rejoin the session
|
||||
[self.uploadHandler loadUploadState];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
// Get the new view controller using [segue destinationViewController].
|
||||
// Pass the selected object to the new view controller.
|
||||
self.svc = ((UINavigationController *)segue.destinationViewController)
|
||||
.childViewControllers[0];
|
||||
self.svc.containerViewController = self;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
- (void)verifyServiceAvailable {
|
||||
[self verifyServiceAvailableWithCompletionHandler:nil];
|
||||
}
|
||||
|
||||
- (void)verifyServiceAvailableWithCompletionHandler:(void (^)(BOOL))completionHandler {
|
||||
|
||||
[self.uploadHandler getCurrentTasks:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
|
||||
NSLog(@"uploadTasks: %@", uploadTasks);
|
||||
}];
|
||||
|
||||
|
||||
if (!self.api.apiURL.length) {
|
||||
|
||||
[self showAlertViewWithTitle:NSLocalizedString(@"app_never_launched_title", nil)
|
||||
message:NSLocalizedString(@"app_never_launched_message", nil)
|
||||
actionWithTitle:NSLocalizedString(@"close", nil)
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self cancel];
|
||||
}];
|
||||
completionHandler(NO);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self tokenExists]) {
|
||||
[self showAlertViewWithTitle:NSLocalizedString(@"no_account_title", nil)
|
||||
message:NSLocalizedString(@"no_account_message", nil)
|
||||
actionWithTitle:NSLocalizedString(@"close", nil)
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self cancel];
|
||||
}];
|
||||
completionHandler(NO);
|
||||
} else {
|
||||
[self pingWithCompletionHandler:^(NSError *error) {
|
||||
if (error) {
|
||||
if (error.code == 401) {
|
||||
|
||||
[self showAlertViewWithTitle:NSLocalizedString(@"signin_again_title", nil)
|
||||
message:NSLocalizedString(@"signin_again_message", nil)
|
||||
actionWithTitle:NSLocalizedString(@"close", nil)
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self cancel];
|
||||
}];
|
||||
} else {
|
||||
[self showAlertViewWithTitle:NSLocalizedString(@"server_connection_error_title", nil)
|
||||
message:NSLocalizedString(@"server_connection_error_message", nil)
|
||||
actionWithTitle:NSLocalizedString(@"close", nil)
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self cancel];
|
||||
}];
|
||||
}
|
||||
completionHandler(NO);
|
||||
} else {
|
||||
completionHandler(YES);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pingWithCompletionHandler:(void (^)(NSError *))completionHandler {
|
||||
[self.api ping:^(id data, NSURLResponse *response, NSError *error) {
|
||||
NSLog(@"pingError: %@", error);
|
||||
completionHandler(error);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)send {
|
||||
|
||||
NSMutableIndexSet *imageErrorIndexes = [NSMutableIndexSet indexSet];
|
||||
for (NSUInteger i = 0; i < self.imageErrors.count; i++) {
|
||||
NSError *error = self.imageErrors[i];
|
||||
if ([error isKindOfClass:[NSError class]]) {
|
||||
[imageErrorIndexes addIndex:i];
|
||||
}
|
||||
}
|
||||
|
||||
NSMutableArray *mutableImageUrls = [NSMutableArray arrayWithArray:self.imageUrls];
|
||||
NSMutableArray *mutableCaptions = [NSMutableArray arrayWithArray:self.captions];
|
||||
[mutableImageUrls removeObjectsAtIndexes:imageErrorIndexes];
|
||||
[mutableCaptions removeObjectsAtIndexes:imageErrorIndexes];
|
||||
|
||||
if (mutableImageUrls.count == 0) {
|
||||
NSError *error = [NSError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier]
|
||||
code:0
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"upload_error_no_files_selected", nil)
|
||||
}];
|
||||
[self handleUploadError:error];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//process image before upload
|
||||
//if user choose optimize photo size
|
||||
if( [self isOptimizeResolution ]){
|
||||
//optimize photo size
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
SDImageCache *imageCache = [SDImageCache sharedImageCache];
|
||||
//store images to /tmp
|
||||
NSString *defaultCachePath = NSTemporaryDirectory();
|
||||
|
||||
//nix opt. size
|
||||
float optimizedDimension = 1820.0f;
|
||||
NSNumber *maxWidth = [NSNumber numberWithFloat:optimizedDimension];
|
||||
|
||||
|
||||
for (NSUInteger i = 0; i < mutableImageUrls.count; i++) {
|
||||
@autoreleasepool {
|
||||
NSURL *imageUrl = mutableImageUrls[i];
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil);
|
||||
|
||||
// load the image at the desired size
|
||||
NSDictionary* options = @{
|
||||
(id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceThumbnailMaxPixelSize: maxWidth
|
||||
};
|
||||
|
||||
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
|
||||
|
||||
//80% jpg quality
|
||||
NSData * data = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 0.8f);
|
||||
|
||||
//retrieve cache path from Table view SDImageCache
|
||||
NSURL* cachePathURL = [[NSURL alloc] initWithString:[imageCache cachePathForKey:[imageUrl absoluteString]]] ;
|
||||
NSURL* newPathURL = [[NSURL alloc] initWithString:
|
||||
[NSString stringWithFormat:@"%@/%@.JPG",defaultCachePath, [cachePathURL lastPathComponent]]];
|
||||
|
||||
NSDictionary *metaoptions = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
|
||||
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)metaoptions);
|
||||
if (imageProperties) {
|
||||
NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
|
||||
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata];
|
||||
[mutableMetadata setValue:[NSNumber numberWithInt:1] forKey:@"Orientation"];
|
||||
[self writeData: [self writeMetadataIntoImageData:data metadata:mutableMetadata ] toPath: [newPathURL absoluteString]];
|
||||
|
||||
}
|
||||
else{
|
||||
//wirte to file
|
||||
[self writeData: data toPath: [newPathURL absoluteString]];
|
||||
}
|
||||
//replace imageURLs object
|
||||
[mutableImageUrls replaceObjectAtIndex:i withObject:[newPathURL absoluteString] ];
|
||||
}
|
||||
}
|
||||
|
||||
[self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy]
|
||||
captions:[mutableCaptions copy]
|
||||
playlists:self.playlistIds
|
||||
friends:self.friendUsernames
|
||||
camera:NO];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
else{
|
||||
[mutableImageUrls enumerateObjectsUsingBlock:^(NSURL* url, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[mutableImageUrls replaceObjectAtIndex:idx withObject:[url absoluteString]];
|
||||
}];
|
||||
[self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy]
|
||||
captions:[mutableCaptions copy]
|
||||
playlists:self.playlistIds
|
||||
friends:self.friendUsernames
|
||||
camera:NO];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)cancel {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was cancelled.", nil),
|
||||
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Operation was cancelled.", nil),
|
||||
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Operation was cancelled.", nil)
|
||||
};
|
||||
NSError *error = [NSError errorWithDomain:NSURLErrorDomain
|
||||
code:-57
|
||||
userInfo:userInfo];
|
||||
[self.extensionContext cancelRequestWithError:error];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
- (BOOL)tokenExists {
|
||||
return [[self.api getAccessToken] length];
|
||||
}
|
||||
|
||||
- (void)handleUploadError:(NSError *)error {
|
||||
if (error) {
|
||||
UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
|
||||
ReceiverTableViewController *receiverTableVC = (ReceiverTableViewController *)navigationController.topViewController;
|
||||
if ([receiverTableVC isKindOfClass:[ReceiverTableViewController class]]) {
|
||||
[receiverTableVC hideActivityIndicator];
|
||||
receiverTableVC.navigationItem.rightBarButtonItem.enabled = YES;
|
||||
}
|
||||
|
||||
NSString *errorMessage = [error localizedDescription];
|
||||
switch (error.code) {
|
||||
case kNixIoFileTooLargeError:
|
||||
errorMessage = NSLocalizedString(@"upload_error_file_too_large", nil);
|
||||
break;
|
||||
}
|
||||
[self showAlertViewWithTitle:NSLocalizedString(@"upload_error_title", nil)
|
||||
message:errorMessage
|
||||
actionWithTitle:NSLocalizedString(@"close", nil)
|
||||
handler:^(UIAlertAction *action) {
|
||||
[self cancel];
|
||||
}];
|
||||
}
|
||||
|
||||
//delete cache image
|
||||
[self deleteCache];
|
||||
|
||||
}
|
||||
|
||||
-(BOOL) isOptimizeResolution {
|
||||
NSUserDefaults *preferences = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.creedon.Nixplay"] ;
|
||||
|
||||
NSString *optimizeResolutionKey = @"nixSettings.settings.resolution";
|
||||
|
||||
if ([preferences objectForKey:optimizeResolutionKey] == nil)
|
||||
{
|
||||
NSLog(@"%@ not exist",optimizeResolutionKey);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get current level
|
||||
const Boolean optimizeResolution = [preferences boolForKey:optimizeResolutionKey];
|
||||
|
||||
NSLog(@"currentLevel %i",optimizeResolution);
|
||||
return optimizeResolution;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (BOOL)writeData:(NSData*) data toPath:(NSString *)path
|
||||
{
|
||||
NSError* errorWrite = nil;
|
||||
|
||||
BOOL write = [data writeToFile:path atomically:YES];
|
||||
if(errorWrite != NULL){
|
||||
NSLog(@"Failed to write to '%@': %@", path , [errorWrite localizedDescription]);
|
||||
}
|
||||
return write;
|
||||
}
|
||||
|
||||
-(void)deleteCache{
|
||||
NSArray* tmpDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL];
|
||||
for (NSString *file in tmpDirectory) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//http://stackoverflow.com/questions/9006759/how-to-write-exif-metadata-to-an-image-not-the-camera-roll-just-a-uiimage-or-j
|
||||
-(NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata {
|
||||
// create an imagesourceref
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
|
||||
|
||||
// this is the type of image (e.g., public.jpeg)
|
||||
CFStringRef UTI = CGImageSourceGetType(source);
|
||||
|
||||
// create a new data object and write the new image into it
|
||||
NSMutableData *dest_data = [NSMutableData data];
|
||||
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI, 1, NULL);
|
||||
if (!destination) {
|
||||
NSLog(@"Error: Could not create image destination");
|
||||
}
|
||||
// add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
|
||||
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) metadata);
|
||||
BOOL success = NO;
|
||||
success = CGImageDestinationFinalize(destination);
|
||||
if (!success) {
|
||||
NSLog(@"Error: Could not create data from image destination");
|
||||
}
|
||||
CFRelease(destination);
|
||||
CFRelease(source);
|
||||
return dest_data;
|
||||
}
|
||||
|
||||
#pragma mark - NixUploadDelegate
|
||||
- (void)didStartUploadWithTasks:(NSArray *)tasks {
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[self.extensionContext completeRequestReturningItems:@[]
|
||||
completionHandler:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)didUploadImageAtIndex:(NSUInteger)index error:(NSError *)error {
|
||||
[self handleUploadError:error];
|
||||
}
|
||||
|
||||
- (void)didFinishUploadingWithError:(NSError *)error {
|
||||
[self handleUploadError:error];
|
||||
|
||||
}
|
||||
|
||||
-(void) showAlertViewWithTitle:(NSString*)title message:(NSString*)message actionWithTitle:actiontitle handler:(void (^ __nullable)(UIAlertAction *action))handler{
|
||||
UIAlertController* alert = [UIAlertController alertControllerWithTitle:title
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
[self cancel];
|
||||
}];
|
||||
[alert addAction:defaultAction];
|
||||
|
||||
[self.svc presentViewController:alert animated:YES completion:nil];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// ShareViewController.h
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 28/4/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Social/Social.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
|
||||
#import "ReceiverTableViewController.h"
|
||||
#import "ContainerViewController.h"
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kNixAppUrlScheme;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kNixAppUrlActionPhotoShare;
|
||||
typedef NSString *kNixAppUrlAction;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyImageUrl;
|
||||
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyCaption;
|
||||
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyFrameId;
|
||||
typedef NSString *kNixAppUrlQueryKey;
|
||||
|
||||
FOUNDATION_EXPORT NSUInteger const kNixCaptionMaxLength;
|
||||
FOUNDATION_EXPORT NSUInteger const kNixInputFieldCells;
|
||||
|
||||
@interface ImageCaptionViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UITextViewDelegate>
|
||||
@property (nonatomic, strong) ContainerViewController *containerViewController;
|
||||
- (IBAction)cancel:(id)sender;
|
||||
@end
|
||||
@@ -0,0 +1,399 @@
|
||||
//
|
||||
// ShareViewController.m
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 27/4/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "ImageCaptionViewController.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <SDWebImage/UIImageView+WebCache.h>
|
||||
|
||||
NSString *const kNixAppUrlScheme = @"nixplay";
|
||||
|
||||
NSString *const kNixAppUrlActionPhotoShare = @"photo_share";
|
||||
|
||||
NSString *const kNixAppUrlQueryKeyImageUrl = @"image_url";
|
||||
NSString *const kNixAppUrlQueryKeyCaption = @"caption";
|
||||
NSString *const kNixAppUrlQueryKeyFrameId = @"frame_id";
|
||||
|
||||
NSUInteger const kNixCaptionMaxLength = 160;
|
||||
NSUInteger const kNixInputFieldCells = 0;
|
||||
|
||||
@interface ImageCaptionViewController ()
|
||||
@property (nonatomic, strong) NSMutableArray *imageErrors;
|
||||
@property (nonatomic, strong) NSArray *imageUrls;
|
||||
@property (nonatomic, strong) NSMutableArray *captions;
|
||||
@property (nonatomic, strong) NSNumber *selectedFrameId;
|
||||
@property (nonatomic, strong) NSString *selectedFrameName;
|
||||
|
||||
|
||||
@property (nonatomic, strong) IBOutlet UITableView *tableView;
|
||||
|
||||
@property (nonatomic, strong) NSIndexPath *activeCellIndexPath;
|
||||
@property (nonatomic, assign) BOOL keyboardShown;
|
||||
@property (nonatomic, assign) CGFloat keyboardOverlap;
|
||||
|
||||
- (void)loadExtensionItems;
|
||||
- (void)validateImages;
|
||||
- (void)loadImagesIntoCache;
|
||||
@end
|
||||
|
||||
@interface FrameTableViewCell : UITableViewCell
|
||||
@end
|
||||
@interface ImageCaptionTableViewCell : UITableViewCell
|
||||
@property (nonatomic, strong) NSError *error;
|
||||
@property (nonatomic, strong) IBOutlet UIImageView *thumbnailView;
|
||||
//@property (nonatomic, strong) IBOutlet UITextView *textView;
|
||||
@property (nonatomic, strong) IBOutlet UITextView *textView;
|
||||
@property (nonatomic, strong) IBOutlet UILabel *errorLabel;
|
||||
@property (nonatomic, strong) IBOutlet UIActivityIndicatorView *activityIndicatorView;
|
||||
@end
|
||||
|
||||
@implementation ImageCaptionViewController
|
||||
|
||||
- (ContainerViewController*)containerViewController {
|
||||
if (_containerViewController == nil) {
|
||||
_containerViewController = [[ContainerViewController alloc] init];
|
||||
}
|
||||
return _containerViewController;
|
||||
}
|
||||
|
||||
#pragma mark - IB
|
||||
- (IBAction)cancel:(id)sender {
|
||||
[self.containerViewController cancel];
|
||||
}
|
||||
|
||||
#pragma mark - UIViewController
|
||||
- (void)loadView {
|
||||
[super loadView];
|
||||
[self loadExtensionItems];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
self.navigationItem.rightBarButtonItem.enabled = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
|
||||
|
||||
[self verifyUser];
|
||||
}
|
||||
|
||||
-(void)appWillBecomeActive:(NSNotification*)note {
|
||||
|
||||
NSLog(@"appWillBecomeActive: %@", note);
|
||||
// re-validate again if the user is active logged-in
|
||||
[self verifyUser];
|
||||
|
||||
}
|
||||
|
||||
-(void)appWillResignActive:(NSNotification*)note {
|
||||
|
||||
NSLog(@"appWillResignActive: %@", note);
|
||||
|
||||
}
|
||||
|
||||
-(void)appWillTerminate:(NSNotification*)note {
|
||||
|
||||
NSLog(@"appWillTerminate: %@", note);
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
/*
|
||||
* This works around a quirk on iOS version < 8.3 where the contents of the table do not load (after the VC has been
|
||||
* loaded once) on subsequent loads until the user scrolls the tableview.
|
||||
*/
|
||||
[self.tableView reloadData];
|
||||
[self loadImagesIntoCache];
|
||||
|
||||
// [self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// self.navigationItem.rightBarButtonItem.enabled = success;
|
||||
// });
|
||||
// }]; // must be in viewWillAppear (doesn't work in viewDidAppear)
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([segue.identifier isEqualToString:@"pushToPlaylistsTableView"]) {
|
||||
[self.view endEditing:YES];
|
||||
|
||||
ReceiverTableViewController *playlistTableVC = (ReceiverTableViewController *)segue.destinationViewController;
|
||||
playlistTableVC.containerViewController = self.containerViewController;
|
||||
self.containerViewController.imageErrors = [self.imageErrors copy];
|
||||
self.containerViewController.imageUrls = [self.imageUrls copy];
|
||||
self.containerViewController.captions = [self.captions copy];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITextViewDelegate
|
||||
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
|
||||
|
||||
if([text isEqualToString:@"\n"])
|
||||
{
|
||||
[textView resignFirstResponder];
|
||||
return NO;
|
||||
}
|
||||
if ([text isEqualToString:@""])
|
||||
return YES;
|
||||
return textView.text.length < kNixCaptionMaxLength;
|
||||
}
|
||||
|
||||
- (void)textViewDidEndEditing:(UITextView *)textView {
|
||||
NSUInteger index = textView.tag;
|
||||
self.captions[index] = textView.text;
|
||||
self.activeCellIndexPath = nil;
|
||||
}
|
||||
|
||||
- (void)textViewDidBeginEditing:(UITextView *)textView {
|
||||
self.activeCellIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [self.imageUrls count];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = nil;
|
||||
NSUInteger imageIndex = indexPath.row;
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:@"ImageCaptionCell"
|
||||
forIndexPath:indexPath];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
NSURL *imageUrl = self.imageUrls[indexPath.row];
|
||||
ImageCaptionTableViewCell *imageCaptionCell = (ImageCaptionTableViewCell *)cell;
|
||||
imageCaptionCell.thumbnailView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
imageCaptionCell.thumbnailView.image = [[SDImageCache sharedImageCache]
|
||||
imageFromMemoryCacheForKey:[imageUrl absoluteString]];
|
||||
if (imageCaptionCell.thumbnailView.image) {
|
||||
[imageCaptionCell.activityIndicatorView stopAnimating];
|
||||
}
|
||||
imageCaptionCell.textView.tag = imageIndex;
|
||||
imageCaptionCell.textView.delegate = self;
|
||||
|
||||
NSError *imageError;
|
||||
if (imageIndex < self.imageErrors.count) {
|
||||
imageError = self.imageErrors[imageIndex];
|
||||
}
|
||||
|
||||
imageCaptionCell.errorLabel.hidden = YES;
|
||||
imageCaptionCell.textView.hidden = YES;
|
||||
if (![imageError isKindOfClass:[NSNull class]]) {
|
||||
imageCaptionCell.errorLabel.hidden = NO;
|
||||
imageCaptionCell.errorLabel.text = [imageError localizedDescription];
|
||||
} else {
|
||||
imageCaptionCell.textView.hidden = NO;
|
||||
imageCaptionCell.textView.layer.borderWidth = 0.5f;
|
||||
imageCaptionCell.textView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
|
||||
imageCaptionCell.textView.layer.cornerRadius = 2.0f;
|
||||
imageCaptionCell.textView.textContainer.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
}
|
||||
|
||||
NSString *caption = [self.captions objectAtIndex:imageIndex];
|
||||
if ([caption isKindOfClass:[NSString class]])
|
||||
imageCaptionCell.textView.text = caption;
|
||||
else
|
||||
imageCaptionCell.textView.text = @"";
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath{
|
||||
// @try{
|
||||
// UIImage *image = [[SDImageCache sharedImageCache]
|
||||
// imageFromMemoryCacheForKey:[self.imageUrls[indexPath.row] absoluteString]];
|
||||
// CGFloat height = image.size.height > image.size.width ? image.size.height : 256;
|
||||
// return height > 0 ? height : 256;
|
||||
// }
|
||||
// @catch(NSException * exception){
|
||||
// NSLog(@"estimatedHeightForRowAtIndexPath Exception %@",exception);
|
||||
// }
|
||||
return 300;// UITableViewAutomaticDimension;
|
||||
}
|
||||
#pragma mark - Private
|
||||
#pragma mark App flow
|
||||
- (void)loadExtensionItems {
|
||||
NSExtensionItem *extensionItem = self.containerViewController.extensionContext.inputItems.firstObject;
|
||||
NSArray *inputItems = extensionItem.attachments;
|
||||
NSMutableArray *mutableImageUrls = [[NSMutableArray alloc] init];
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
// Load images from selection
|
||||
[inputItems enumerateObjectsUsingBlock:^(NSItemProvider *itemProvider, NSUInteger idx, BOOL *stop) {
|
||||
dispatch_group_enter(group);
|
||||
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
|
||||
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage
|
||||
options:nil
|
||||
completionHandler: ^(NSURL *imageUrl, NSError *error) {
|
||||
[mutableImageUrls addObject:imageUrl];
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
} else {
|
||||
dispatch_group_leave(group);
|
||||
}
|
||||
}];
|
||||
|
||||
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
self.imageUrls = [NSArray arrayWithArray:mutableImageUrls];
|
||||
|
||||
NSUInteger imagesCount = [self.imageUrls count];
|
||||
self.imageErrors = [NSMutableArray arrayWithCapacity:imagesCount];
|
||||
self.captions = [[NSMutableArray alloc] initWithCapacity:imagesCount];
|
||||
for (NSUInteger i = 0; i < imagesCount; i++) {
|
||||
[self.captions addObject:[NSNull null]];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self validateImages];
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
});
|
||||
}
|
||||
-(BOOL) isOptimizeResolution {
|
||||
NSUserDefaults *preferences = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.creedon.Nixplay"] ;
|
||||
|
||||
NSString *optimizeResolutionKey = @"nixSettings.settings.resolution";
|
||||
|
||||
if ([preferences objectForKey:optimizeResolutionKey] == nil)
|
||||
{
|
||||
NSLog(@"%@ not exist",optimizeResolutionKey);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get current level
|
||||
const Boolean optimizeResolution = [preferences boolForKey:optimizeResolutionKey];
|
||||
|
||||
NSLog(@"currentLevel %i",optimizeResolution);
|
||||
return optimizeResolution;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
- (void)validateImages {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
||||
for (NSUInteger i = 0; i < self.imageUrls.count; i++) {
|
||||
@autoreleasepool {
|
||||
NSURL *imageUrl = self.imageUrls[i];
|
||||
NSDictionary *fileAttrs = [fm attributesOfItemAtPath:[imageUrl path] error:nil];
|
||||
NSNumber *fileSizeLimit = self.containerViewController.uploadHandler.fileSizeLimit;
|
||||
NSNumber *fileSize = [fileAttrs objectForKey:NSFileSize];
|
||||
|
||||
NSError *fileError;
|
||||
//https://www.wrike.com/open.htm?id=190326881
|
||||
//allow proess photo when isOptimizeResolution
|
||||
if(![self isOptimizeResolution]){
|
||||
if ([fileSize longLongValue] > [fileSizeLimit longLongValue]) {
|
||||
fileError = [NSError errorWithDomain:bundleIdentifier
|
||||
code:kNixIoFileTooLargeError
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey: NSLocalizedString(@"selection_error_file_too_large", nil)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
if (fileError) {
|
||||
[self.imageErrors addObject:fileError];
|
||||
} else {
|
||||
[self.imageErrors addObject:[NSNull null]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)loadImagesIntoCache {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
SDImageCache *imageCache = [SDImageCache sharedImageCache];
|
||||
CGRect screenBounds = [[UIScreen mainScreen] bounds];
|
||||
CGFloat screenScale = [[UIScreen mainScreen] scale];
|
||||
NSNumber *maxWidth = [NSNumber numberWithFloat:screenBounds.size.width * screenScale * 0.75];
|
||||
|
||||
// Create thumbnail and save to cache
|
||||
for (NSUInteger i = 0; i < self.imageUrls.count; i++) {
|
||||
@autoreleasepool {
|
||||
NSURL *imageUrl = self.imageUrls[i];
|
||||
if (![imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]]) {
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil);
|
||||
NSDictionary *options = @{
|
||||
(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceCreateThumbnailFromImageIfAbsent: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
|
||||
(id)kCGImageSourceThumbnailMaxPixelSize: (id)maxWidth
|
||||
};
|
||||
CGImageRef thumbnailRef = CGImageSourceCreateThumbnailAtIndex(imageSource,
|
||||
0,
|
||||
(__bridge CFDictionaryRef)options);
|
||||
UIImage *scaledImage = [UIImage imageWithCGImage:thumbnailRef];
|
||||
[imageCache storeImage:scaledImage forKey:[imageUrl absoluteString] completion:^{
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update visible cells with images
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSArray *visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
|
||||
|
||||
for (NSIndexPath *visibleIndexPath in visibleIndexPaths) {
|
||||
@autoreleasepool {
|
||||
NSUInteger i = visibleIndexPath.row;
|
||||
NSURL *imageUrl = self.imageUrls[i];
|
||||
|
||||
// Set up NSError to insert into cell
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
ImageCaptionTableViewCell *cell = (ImageCaptionTableViewCell *)[self.tableView
|
||||
cellForRowAtIndexPath:visibleIndexPath];
|
||||
if (!cell.thumbnailView.image) {
|
||||
cell.thumbnailView.alpha = 0.f;
|
||||
cell.thumbnailView.image = [imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]];
|
||||
[UIView animateWithDuration:0.35f animations:^{
|
||||
cell.thumbnailView.alpha = 1.f;
|
||||
}];
|
||||
|
||||
[self.tableView beginUpdates];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[visibleIndexPath]
|
||||
withRowAnimation:UITableViewRowAnimationFade];
|
||||
[self.tableView endUpdates];
|
||||
}
|
||||
[cell.activityIndicatorView stopAnimating];
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - User
|
||||
- (void) verifyUser {
|
||||
|
||||
[self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.navigationItem.rightBarButtonItem.enabled = success;
|
||||
});
|
||||
}]; // must be in viewWillAppear (doesn't work in viewDidAppear)
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation FrameTableViewCell
|
||||
@end
|
||||
@implementation ImageCaptionTableViewCell
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// SLComposeTableViewController.h
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 8/5/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ContainerViewController.h"
|
||||
|
||||
@interface ReceiverTableViewController : UITableViewController
|
||||
@property (nonatomic, strong) ContainerViewController *containerViewController;
|
||||
- (void)showActivityIndicator;
|
||||
- (void)hideActivityIndicator;
|
||||
@end
|
||||
@@ -0,0 +1,556 @@
|
||||
//
|
||||
// SLComposeTableViewController.m
|
||||
// PhotoShareExtension
|
||||
//
|
||||
// Created by Russell on 8/5/15.
|
||||
//
|
||||
//
|
||||
|
||||
#import "ReceiverTableViewController.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, NixTableViewSection) {
|
||||
kPlaylistHeader,
|
||||
kPlaylist,
|
||||
kFriendHeader,
|
||||
kFriend
|
||||
};
|
||||
|
||||
@interface ReceiverTableViewController ()
|
||||
@property (nonatomic, strong) NSArray *playlistValues;
|
||||
@property (nonatomic, strong) NSArray *friendValues;
|
||||
@property (nonatomic, strong) NSMutableArray *selectedPlaylists;
|
||||
@property (nonatomic, strong) NSMutableArray *selectedFriends;
|
||||
- (IBAction)send:(id)sender;
|
||||
- (void)preparePlaylistValues;
|
||||
- (void)prepareFriendValues;
|
||||
- (void)reloadData;
|
||||
|
||||
- (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends;
|
||||
- (void)updateHeaderTitleWithCounts;
|
||||
|
||||
- (void)loadAllData:(void (^)(NSError *))completionHandler;
|
||||
- (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler;
|
||||
- (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler;
|
||||
@end
|
||||
|
||||
@implementation ReceiverTableViewController
|
||||
|
||||
#pragma mark - IB
|
||||
- (IBAction)send:(id)sender {
|
||||
self.selectedPlaylists = [[NSMutableArray alloc]
|
||||
initWithCapacity:self.containerViewController.playlistData.count];
|
||||
self.selectedFriends = [[NSMutableArray alloc]
|
||||
initWithCapacity:self.containerViewController.friendData.count];
|
||||
|
||||
NSArray *selectedIndexPaths = [self.tableView indexPathsForSelectedRows];
|
||||
[selectedIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||
@autoreleasepool {
|
||||
switch (indexPath.section) {
|
||||
case kPlaylist:
|
||||
{
|
||||
NSString *playlistId = self.containerViewController.playlistData[indexPath.row][@"id"];
|
||||
[self.selectedPlaylists addObject:playlistId];
|
||||
break;
|
||||
}
|
||||
case kFriend:
|
||||
{
|
||||
NSString *friendUsername = self.containerViewController.friendData[indexPath.row][@"username"];
|
||||
[self.selectedFriends addObject:friendUsername];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
self.containerViewController.playlistIds = [self.selectedPlaylists copy];
|
||||
self.containerViewController.friendUsernames = [self.selectedFriends copy];
|
||||
|
||||
[self showActivityIndicator];
|
||||
self.navigationItem.rightBarButtonItem.enabled = NO;
|
||||
self.navigationItem.hidesBackButton = YES;
|
||||
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
|
||||
|
||||
[self.containerViewController send];
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// Uncomment the following line to preserve selection between presentations.
|
||||
// self.clearsSelectionOnViewWillAppear = NO;
|
||||
|
||||
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
|
||||
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
|
||||
|
||||
self.tableView.editing = YES;
|
||||
|
||||
self.refreshControl = [[UIRefreshControl alloc] init];
|
||||
[self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
if (!self.containerViewController.playlistData || !self.containerViewController.friendData) {
|
||||
[self showActivityIndicator];
|
||||
[self loadAllData:^(NSError *error) {
|
||||
[self hideActivityIndicator];
|
||||
if (error) {
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"signin_again_title", nil)
|
||||
message:NSLocalizedString(@"signin_again_message", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[self.containerViewController.extensionContext cancelRequestWithError:error];
|
||||
}]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
} else {
|
||||
if (self.playlistValues.count == 0 && self.friendValues.count == 0) {
|
||||
NSError *err;
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"no_receiver_title", nil)
|
||||
message:NSLocalizedString(@"no_receiver_message", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[self.containerViewController.extensionContext cancelRequestWithError:err];
|
||||
}]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
} else {
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
self.navigationItem.rightBarButtonItem.enabled = NO;
|
||||
|
||||
NSArray *selectedReceiverIndexPaths = self.containerViewController.selectedReceiverIndexPaths;
|
||||
|
||||
[self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
|
||||
if ([selectedReceiverIndexPaths count]) {
|
||||
self.navigationItem.rightBarButtonItem.enabled = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
if (self.containerViewController.playlistData && self.containerViewController.friendData) {
|
||||
[self preparePlaylistValues];
|
||||
[self prepareFriendValues];
|
||||
|
||||
[self.tableView reloadData];
|
||||
|
||||
if ([selectedReceiverIndexPaths count]) {
|
||||
[selectedReceiverIndexPaths
|
||||
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||
[self.tableView selectRowAtIndexPath:indexPath
|
||||
animated:NO
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}];
|
||||
[self updateHeaderTitleWithCounts];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
self.friendValues = nil;
|
||||
self.playlistValues = nil;
|
||||
self.containerViewController.friendData = nil;
|
||||
self.containerViewController.playlistData = nil;
|
||||
self.containerViewController.selectedReceiverIndexPaths = nil; //[self.tableView indexPathsForSelectedRows];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
- (void)showActivityIndicator {
|
||||
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc]
|
||||
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
||||
self.navigationItem.titleView = activityIndicator;
|
||||
[activityIndicator startAnimating];
|
||||
}
|
||||
|
||||
- (void)hideActivityIndicator {
|
||||
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)self.navigationItem.titleView;
|
||||
if ([activityIndicator isKindOfClass:[UIActivityIndicatorView class]]) {
|
||||
[activityIndicator stopAnimating];
|
||||
}
|
||||
self.navigationItem.titleView = nil;
|
||||
}
|
||||
|
||||
- (void)preparePlaylistValues {
|
||||
NSMutableArray *playlistValues = [NSMutableArray arrayWithCapacity:self.containerViewController.playlistData.count];
|
||||
for (NSDictionary *record in self.containerViewController.playlistData) {
|
||||
@autoreleasepool {
|
||||
NSString *name = record[@"name"];
|
||||
[playlistValues addObject:name];
|
||||
}
|
||||
}
|
||||
self.playlistValues = playlistValues;
|
||||
}
|
||||
|
||||
- (void)prepareFriendValues {
|
||||
NSMutableArray *friendValues = [NSMutableArray arrayWithCapacity:self.containerViewController.friendData.count];
|
||||
for (NSDictionary *record in self.containerViewController.friendData) {
|
||||
@autoreleasepool {
|
||||
NSString *name = record[@"fullName"];
|
||||
[friendValues addObject:name];
|
||||
}
|
||||
}
|
||||
self.friendValues = friendValues;
|
||||
}
|
||||
|
||||
- (void)reloadData {
|
||||
NSArray *playlistData = [self.containerViewController.playlistData copy];
|
||||
NSArray *friendData = [self.containerViewController.friendData copy];
|
||||
NSMutableArray *selectedPlaylistIds = [[NSMutableArray alloc] initWithCapacity:playlistData.count];
|
||||
NSMutableArray *selectedFriendUsernames = [[NSMutableArray alloc] initWithCapacity:friendData.count];
|
||||
|
||||
[[self.tableView indexPathsForSelectedRows]
|
||||
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||
switch (indexPath.section) {
|
||||
case kPlaylist:
|
||||
[selectedPlaylistIds addObject:playlistData[indexPath.row][@"id"]];
|
||||
break;
|
||||
case kFriend:
|
||||
[selectedFriendUsernames addObject:friendData[indexPath.row][@"username"]];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
|
||||
[self loadAllData:^(NSError *error) {
|
||||
[self.refreshControl endRefreshing];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSArray *freshPlaylistData = [self.containerViewController.playlistData copy];
|
||||
NSArray *freshFriendData = [self.containerViewController.friendData copy];
|
||||
|
||||
[freshPlaylistData enumerateObjectsUsingBlock:^(NSDictionary *playlist, NSUInteger idx, BOOL *stop) {
|
||||
@autoreleasepool {
|
||||
if ([selectedPlaylistIds containsObject:playlist[@"id"]]) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kPlaylist];
|
||||
[self.tableView selectRowAtIndexPath:indexPath
|
||||
animated:NO
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[freshFriendData enumerateObjectsUsingBlock:^(NSDictionary *friend, NSUInteger idx, BOOL *stop) {
|
||||
@autoreleasepool {
|
||||
if ([selectedFriendUsernames containsObject:friend[@"username"]]) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kFriend];
|
||||
[self.tableView selectRowAtIndexPath:indexPath
|
||||
animated:NO
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends {
|
||||
NSString *rawString;
|
||||
if (playlists == 0) {
|
||||
if (friends == 1) {
|
||||
rawString = NSLocalizedString(@"navigation_header_n_selected_friend", nil);
|
||||
} else {
|
||||
rawString = NSLocalizedString(@"navigation_header_n_selected_friends", nil);
|
||||
}
|
||||
}
|
||||
if (friends == 0) {
|
||||
if (playlists == 1) {
|
||||
rawString = NSLocalizedString(@"navigation_header_n_selected_playlist", nil);
|
||||
} else {
|
||||
rawString = NSLocalizedString(@"navigation_header_n_selected_playlists", nil);
|
||||
}
|
||||
}
|
||||
if (0 < playlists && 0 < friends) {
|
||||
rawString = NSLocalizedString(@"navigation_header_n_selected_receivers", nil);
|
||||
}
|
||||
if (playlists + friends == 0) {
|
||||
return NSLocalizedString(@"navigation_header_select_receivers", nil);
|
||||
}
|
||||
return [NSString stringWithFormat:rawString, playlists + friends];
|
||||
}
|
||||
|
||||
- (void)updateHeaderTitleWithCounts {
|
||||
__block NSUInteger playlistCount = 0;
|
||||
__block NSUInteger friendCount = 0;
|
||||
[[self.tableView indexPathsForSelectedRows]
|
||||
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||
switch (indexPath.section) {
|
||||
case kPlaylist:
|
||||
playlistCount++;
|
||||
break;
|
||||
|
||||
case kFriend:
|
||||
friendCount++;
|
||||
break;
|
||||
}
|
||||
}];
|
||||
NSString *title = [self headerTitleWithPlaylistCounts:playlistCount andFriendCounts:friendCount];
|
||||
self.navigationItem.title = title;
|
||||
}
|
||||
|
||||
#pragma mark Network
|
||||
- (void)loadAllData:(void (^)(NSError *))completionHandler {
|
||||
__block NSError *error = nil;
|
||||
|
||||
dispatch_group_t dispatchGroup = dispatch_group_create();
|
||||
dispatch_group_enter(dispatchGroup);
|
||||
|
||||
[self loadPlaylistsWithCompletionHandler:^(NSError *playlistsError) {
|
||||
if (playlistsError)
|
||||
error = playlistsError;
|
||||
dispatch_group_leave(dispatchGroup);
|
||||
}];
|
||||
|
||||
dispatch_group_enter(dispatchGroup);
|
||||
[self loadFriendsWithCompletionHandler:^(NSError *friendsError) {
|
||||
if (friendsError)
|
||||
error = friendsError;
|
||||
dispatch_group_leave(dispatchGroup);
|
||||
}];
|
||||
|
||||
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
|
||||
completionHandler(error);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler {
|
||||
[self.containerViewController.api playlists:^(id data, NSURLResponse *response, NSError *error) {
|
||||
if (error || !data || [data isKindOfClass:[NSDictionary class]]) {
|
||||
completionHandler(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([data isKindOfClass:[NSArray class]]) {
|
||||
NSPredicate *predicateByTitle = [NSPredicate predicateWithFormat:@"SELF.type == %@", @"normal"];
|
||||
NSArray *playlists = [(NSArray*)data filteredArrayUsingPredicate:predicateByTitle];
|
||||
self.containerViewController.playlistData = playlists;
|
||||
[self preparePlaylistValues];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
completionHandler(nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler {
|
||||
[self.containerViewController.api friends:^(NSDictionary *data, NSURLResponse *response, NSError *error) {
|
||||
if (error || data[@"error"]) {
|
||||
completionHandler(error);
|
||||
return;
|
||||
}
|
||||
NSString *predString = [NSString stringWithFormat:@"(status='%@')", @"success"];
|
||||
NSPredicate *pred = [NSPredicate predicateWithFormat:predString];
|
||||
NSArray *friendsArr = [data[@"friends"] filteredArrayUsingPredicate:pred];
|
||||
self.containerViewController.friendData = friendsArr;
|
||||
[self prepareFriendValues];
|
||||
[self.tableView reloadData];
|
||||
completionHandler(nil);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
// Return the number of sections.
|
||||
return 4;
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
switch (section) {
|
||||
case kPlaylistHeader:
|
||||
return self.playlistValues.count ? NSLocalizedString(@"table_header_playlist", nil) : nil;
|
||||
case kFriendHeader:
|
||||
return self.friendValues.count ? NSLocalizedString(@"table_header_friend", nil) : nil;
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
// Return the number of rows in the section.
|
||||
switch (section) {
|
||||
case kPlaylist:
|
||||
return self.playlistValues.count;
|
||||
case kFriend:
|
||||
return self.friendValues.count;
|
||||
case kPlaylistHeader:
|
||||
return self.playlistValues.count ? 1 : 0;
|
||||
case kFriendHeader:
|
||||
return self.friendValues.count ? 1 : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
static NSString *cellId = @"SLComposeTableViewControllerCell";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
|
||||
}
|
||||
|
||||
if ([[tableView indexPathsForSelectedRows] containsObject:indexPath]) {
|
||||
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
|
||||
switch (indexPath.section) {
|
||||
case kPlaylist:
|
||||
cell.textLabel.text = self.playlistValues[indexPath.row];
|
||||
break;
|
||||
case kFriend:
|
||||
cell.textLabel.text = self.friendValues[indexPath.row];
|
||||
break;
|
||||
case kPlaylistHeader:
|
||||
cell.textLabel.text = NSLocalizedString(@"table_select_all_playlists", nil);
|
||||
break;
|
||||
case kFriendHeader:
|
||||
cell.textLabel.text = NSLocalizedString(@"table_select_all_friends", nil);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
/*
|
||||
// Override to support conditional editing of the table view.
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// Return NO if you do not want the specified item to be editable.
|
||||
return YES;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// Override to support editing the table view.
|
||||
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (editingStyle == UITableViewCellEditingStyleDelete) {
|
||||
// Delete the row from the data source
|
||||
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
|
||||
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// Override to support rearranging the table view.
|
||||
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
|
||||
}
|
||||
*/
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
switch (indexPath.section) {
|
||||
case kPlaylistHeader:
|
||||
{
|
||||
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) {
|
||||
NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist];
|
||||
[tableView selectRowAtIndexPath:playlistIndexPath
|
||||
animated:NO
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kFriendHeader:
|
||||
{
|
||||
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) {
|
||||
NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend];
|
||||
[tableView selectRowAtIndexPath:friendIndexPath
|
||||
animated:NO
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem.enabled = YES;
|
||||
[self updateHeaderTitleWithCounts];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
switch (indexPath.section) {
|
||||
case kPlaylistHeader:
|
||||
{
|
||||
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) {
|
||||
NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist];
|
||||
[tableView deselectRowAtIndexPath:playlistIndexPath
|
||||
animated:NO];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kFriendHeader:
|
||||
{
|
||||
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) {
|
||||
NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend];
|
||||
[tableView deselectRowAtIndexPath:friendIndexPath
|
||||
animated:NO];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case kPlaylist:
|
||||
{
|
||||
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kPlaylistHeader];
|
||||
[tableView deselectRowAtIndexPath:headerIndexPath
|
||||
animated:NO];
|
||||
break;
|
||||
}
|
||||
|
||||
case kFriend:
|
||||
{
|
||||
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kFriendHeader];
|
||||
[tableView deselectRowAtIndexPath:headerIndexPath
|
||||
animated:NO];
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (![tableView indexPathsForSelectedRows]) {
|
||||
self.navigationItem.rightBarButtonItem.enabled = NO;
|
||||
}
|
||||
[self updateHeaderTitleWithCounts];
|
||||
}
|
||||
|
||||
/*
|
||||
// Override to support conditional rearranging of the table view.
|
||||
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// Return NO if you do not want the item to be re-orderable.
|
||||
return YES;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#pragma mark - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
// Get the new view controller using [segue destinationViewController].
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
108
ioneapps-maagapp-ee31119a522d/ios/NixShareExtension/Info.plist
Normal file
108
ioneapps-maagapp-ee31119a522d/ios/NixShareExtension/Info.plist
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BundleEntryFilename</key>
|
||||
<string>index.js</string>
|
||||
<key>BundleForced</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>cdn.shopify.com</key>
|
||||
<dict>
|
||||
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>nixplay-prod-original.s3-us-west-2.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-prod-thumbnail.s3-us-west-2.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-prod-upload.s3.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-qa-original.s3-ap-northeast-1.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-qa-thumbnail.s3-ap-northeast-1.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-qa-upload.s3.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-rnd-original.s3-ap-southeast-1.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-rnd-thumbnail.s3-ap-southeast-1.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>nixplay-rnd-upload.s3.amazonaws.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionRequiresForwardSecrecy</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>shop.nixplay.com</key>
|
||||
<dict>
|
||||
<key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
<integer>10</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.share-services</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.creedon.Nixplay.alpha</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.com.creedon.Nixplay.alpha</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.creedon.Nixplay.beta</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.com.creedon.Nixplay.beta</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.creedon.Nixplay</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.com.creedon.Nixplay</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// NixShareExtension.m
|
||||
// NixShareExtension
|
||||
//
|
||||
// Created by Haroon Shahid on 30/4/2018.
|
||||
// Copyright © 2018 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ReactNativeShareExtension.h"
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
//#import "ReactNativeShareExtension.h"
|
||||
|
||||
|
||||
@interface NixShareExtension : ReactNativeShareExtension
|
||||
@end
|
||||
|
||||
@implementation NixShareExtension
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return YES;
|
||||
}
|
||||
- (UIView*) shareView {
|
||||
NSURL *jsCodeLocation;
|
||||
|
||||
#ifdef DEBUG
|
||||
// jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
|
||||
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
||||
#else
|
||||
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
|
||||
moduleName:@"NixShareExtension"
|
||||
initialProperties:nil
|
||||
launchOptions:nil];
|
||||
rootView.backgroundColor = nil;
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.creedon.Nixplay.qa</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.com.creedon.Nixplay.qa</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.creedon.Nixplay.rnd</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)group.com.creedon.Nixplay.rnd</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
PhotoShareExtension
|
||||
|
||||
Created by Russell on 19/5/15.
|
||||
|
||||
*/
|
||||
|
||||
"send_to" = "Send To";
|
||||
"open_app" = "Open App";
|
||||
"close" = "Schließen";
|
||||
"none" = "None";
|
||||
"frame" = "Rahmen";
|
||||
|
||||
"navigation_header_n_selected_playlist" = "%tu Playlist ausgewählt";
|
||||
"navigation_header_n_selected_playlists" = "%tu Playlists ausgewählt";
|
||||
"navigation_header_n_selected_friend" = "%tu Freund ausgewählt";
|
||||
"navigation_header_n_selected_friends" = "%tu Freund ausgewählt";
|
||||
"navigation_header_n_selected_receivers" = "%tu Empfänger ausgewählt";
|
||||
"navigation_header_select_receivers" = "Empfänger ausgewählt";
|
||||
|
||||
"table_header_playlist" = "MEINE PLAYLISTS";
|
||||
"table_header_friend" = "MEINE FREUNDE";
|
||||
|
||||
"table_select_all_playlists" = "Alle Playlists auswählen";
|
||||
"table_select_all_friends" = "Alle Freunde auswählen";
|
||||
|
||||
"app_never_launched_title" = "Kein Nixplay-Konto";
|
||||
"app_never_launched_message" = "Please launch the Nixplay app for the first time to log in to your Nixplay account.";
|
||||
|
||||
"no_account_title" = "Kein Nixplay-Konto";
|
||||
"no_account_message" = "Es wurde kein Nixplay-Konto konfiguriert. Sie können sich in der Nixplay-App in Ihrem Nixplay-Konto einloggen.";
|
||||
|
||||
"signin_again_title" = "Error";
|
||||
"signin_again_message" = "Sie haben sich eine Weile nicht in Ihrem Nixplay-Konto eingeloggt. Bitte nutzen Sie die Nixplay-App, um sich wieder einzuloggen.";
|
||||
|
||||
"no_receiver_title" = "Error";
|
||||
"no_receiver_message" = "Sie haben keine Freunde und keine Playlists in Ihrem Nixplay-Konto.";
|
||||
|
||||
"upload_in_progress_title" = "Hochladen wird durchgeführt";
|
||||
"upload_in_progress_message" = "Ihre Fotos werden hochgeladen. Versuchen Sie es später noch einmal.";
|
||||
|
||||
"server_connection_error_title" = "Keine Internetverbindung";
|
||||
"server_connection_error_message" = "Prüfen Sie Ihre Geräteverbindung und versuchen Sie es später noch einmal.";
|
||||
|
||||
"upload_error_title" = "Error";
|
||||
"upload_error_file_too_large" = "A file you selected is too large.";
|
||||
"upload_error_no_files_selected" = "There are no files to upload. Please try again.";
|
||||
|
||||
"selection_error_file_too_large" = "This file is too large and will not be sent.";
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Enter Captions"; ObjectID = "9gy-vt-gU0"; */
|
||||
"9gy-vt-gU0.title" = "Bildtitel eingeben";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "ARf-0c-syw"; */
|
||||
"ARf-0c-syw.title" = "Zurück";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Cancel"; ObjectID = "Oz8-8U-jBt"; */
|
||||
"Oz8-8U-jBt.title" = "Abbrechen";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Send"; ObjectID = "jty-bC-dNd"; */
|
||||
"jty-bC-dNd.title" = "Senden";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Next"; ObjectID = "liG-JY-nUM"; */
|
||||
"liG-JY-nUM.title" = "Weiter";
|
||||
|
||||
/* Class = "UILabel"; text = "Label"; ObjectID = "rdb-lo-ZfB"; */
|
||||
"rdb-lo-ZfB.text" = "Label";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Select Receivers"; ObjectID = "zBq-p7-QR9"; */
|
||||
"zBq-p7-QR9.title" = "Empfänger auswählen";
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
PhotoShareExtension
|
||||
|
||||
Created by Russell on 19/5/15.
|
||||
|
||||
*/
|
||||
|
||||
"send_to" = "Send To";
|
||||
"open_app" = "Open App";
|
||||
"close" = "Close";
|
||||
"none" = "None";
|
||||
"frame" = "Frame";
|
||||
|
||||
"navigation_header_n_selected_playlist" = "%tu Playlist Selected";
|
||||
"navigation_header_n_selected_playlists" = "%tu Playlists Selected";
|
||||
"navigation_header_n_selected_friend" = "%tu Friend Selected";
|
||||
"navigation_header_n_selected_friends" = "%tu Friends Selected";
|
||||
"navigation_header_n_selected_receivers" = "%tu Receivers Selected";
|
||||
"navigation_header_select_receivers" = "Select Receivers";
|
||||
|
||||
"table_header_playlist" = "My Playlists";
|
||||
"table_header_friend" = "My Friends";
|
||||
|
||||
"table_select_all_playlists" = "Select all playlists";
|
||||
"table_select_all_friends" = "Select all friends";
|
||||
|
||||
"app_never_launched_title" = "No Nixplay Account";
|
||||
"app_never_launched_message" = "Please launch the Nixplay app for the first time to log in to your Nixplay account.";
|
||||
|
||||
"no_account_title" = "No Nixplay Account";
|
||||
"no_account_message" = "There is no Nixplay account configured. You can log in to your Nixplay account in the Nixplay app.";
|
||||
|
||||
"signin_again_title" = "Error";
|
||||
"signin_again_message" = "You have not logged in to your Nixplay account in awhile. Please log in again using the Nixplay app.";
|
||||
|
||||
"no_receiver_title" = "Error";
|
||||
"no_receiver_message" = "You have no friend and playlist in to your Nixplay account.";
|
||||
|
||||
"upload_in_progress_title" = "Upload in Progress";
|
||||
"upload_in_progress_message" = "Your photos are being uploaded. Please try again later.";
|
||||
|
||||
"server_connection_error_title" = "No Internet Connection";
|
||||
"server_connection_error_message" = "Please check your device connection and try again later.";
|
||||
|
||||
"upload_error_title" = "Error";
|
||||
"upload_error_file_too_large" = "A file you selected is too large.";
|
||||
"upload_error_no_files_selected" = "There are no files to upload. Please try again.";
|
||||
|
||||
"selection_error_file_too_large" = "This file is too large and will not be sent.";
|
||||
|
||||
"Oz8-8U-jBt.normalTitle" = "Cancel";
|
||||
"Oz8-8U-jBt" = "Cancel";
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Enter Captions"; ObjectID = "9gy-vt-gU0"; */
|
||||
"9gy-vt-gU0.title" = "Enter Captions";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "ARf-0c-syw"; */
|
||||
"ARf-0c-syw.title" = "Back";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Cancel"; ObjectID = "Oz8-8U-jBt"; */
|
||||
"Oz8-8U-jBt.title" = "Cancel";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Send"; ObjectID = "jty-bC-dNd"; */
|
||||
"jty-bC-dNd.title" = "Send";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Next"; ObjectID = "liG-JY-nUM"; */
|
||||
"liG-JY-nUM.title" = "Next";
|
||||
|
||||
/* Class = "UILabel"; text = "Label"; ObjectID = "rdb-lo-ZfB"; */
|
||||
"rdb-lo-ZfB.text" = "Label";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Select Receivers"; ObjectID = "zBq-p7-QR9"; */
|
||||
"zBq-p7-QR9.title" = "Select Receivers";
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Localizable.strings
|
||||
PhotoShareExtension
|
||||
|
||||
Created by Russell on 19/5/15.
|
||||
|
||||
*/
|
||||
|
||||
"send_to" = "送信先";
|
||||
"open_app" = "アプリを開く";
|
||||
"close" = "閉じる";
|
||||
"none" = "なし";
|
||||
"frame" = "フレーム";
|
||||
|
||||
"navigation_header_n_selected_playlist" = "%tu個の再生リストを選択しました";
|
||||
"navigation_header_n_selected_playlists" = "%tu個の再生リストを選択しました";
|
||||
"navigation_header_n_selected_friend" = "%tu名の友達を選択しました";
|
||||
"navigation_header_n_selected_friends" = "%tu名の友達を選択しました";
|
||||
"navigation_header_n_selected_receivers" = "%tu名の友達を選択しました";
|
||||
"navigation_header_select_receivers" = "レシーバーを選択";
|
||||
|
||||
"table_header_playlist" = "マイ再生リスト";
|
||||
"table_header_friend" = "マイフレンド";
|
||||
|
||||
"table_select_all_playlists" = "すべての再生リスト選択";
|
||||
"table_select_all_friends" = "すべての友達を選択";
|
||||
|
||||
"app_never_launched_title" = "MAAGAppアカウントなし";
|
||||
"app_never_launched_message" = "MAAGAppアカウントにログインするために、最初にMAAGAppアプリを起動させてください。";
|
||||
|
||||
"no_account_title" = "MAAGAppアカウントなし";
|
||||
"no_account_message" = "MAAGAppアカウントは設定されていません。MAAGAppアプリのMAAGAppアカウントにログインできます。";
|
||||
|
||||
"signin_again_title" = "エラー";
|
||||
"signin_again_message" = "しばらくの間、MAAGAppアカウントにログインしていませんでした。MAAGAppアプリを使ってもう一度ログインしてください。";
|
||||
|
||||
"no_receiver_title" = "エラー";
|
||||
"no_receiver_message" = "MAAGAppアカウントには友達も再生リストもありません。";
|
||||
|
||||
"upload_in_progress_title" = "アップロード進行中";
|
||||
"upload_in_progress_message" = "写真がアップロードされています。後でもう一度やり直してください。";
|
||||
|
||||
"server_connection_error_title" = "インターネット接続なし";
|
||||
"server_connection_error_message" = "デバイス接続を確認して後でもう一度やり直してください。";
|
||||
|
||||
"upload_error_title" = "エラー";
|
||||
"upload_error_file_too_large" = "選択したファイルは大きすぎます。";
|
||||
"upload_error_no_files_selected" = "アップロードするファイルがありません。もう一度やり直してください。";
|
||||
|
||||
"selection_error_file_too_large" = "このファイルは大きすぎるので送信できません。";
|
||||
|
||||
"Oz8-8U-jBt.normalTitle" = "キャンセル";
|
||||
"Oz8-8U-jBt" = "キャンセル";
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Enter Captions"; ObjectID = "9gy-vt-gU0"; */
|
||||
"9gy-vt-gU0.title" = "Enter Captions";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "ARf-0c-syw"; */
|
||||
"ARf-0c-syw.title" = "Back";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Cancel"; ObjectID = "Oz8-8U-jBt"; */
|
||||
"Oz8-8U-jBt.title" = "Cancel";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Send"; ObjectID = "jty-bC-dNd"; */
|
||||
"jty-bC-dNd.title" = "Send";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Next"; ObjectID = "liG-JY-nUM"; */
|
||||
"liG-JY-nUM.title" = "Next";
|
||||
|
||||
/* Class = "UILabel"; text = "Label"; ObjectID = "rdb-lo-ZfB"; */
|
||||
"rdb-lo-ZfB.text" = "Label";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Select Receivers"; ObjectID = "zBq-p7-QR9"; */
|
||||
"zBq-p7-QR9.title" = "Select Receivers";
|
||||
Reference in New Issue
Block a user