I've been using the CGWindowListCreateImage function for years to capture the desktop as an image. In Swift, I return an image (NSImage) as follows.
func makeCGWindowImage(size: CGSize) -> NSImage? {
if let imageRef = CGWindowListCreateImage(CGRect.infinite, .optionAll, kCGNullWindowID, .bestResolution) {
let image = NSImage(cgImage: imageRef, size: size)
return image
}
return nil
}
I need to use the CGWindowListCreateImage function in order to capture the desktop area involving not just my application itself but also others (folder windows, the Safari web browser window...). The thing is that I get an alert message regarding screen recording privacy if I use the window image function above.
The worst part of using the CGWindowListCreateImage function is that the reviewer rejects the application because the application unnecessarily shows the privacy message. If I ask the reviewer what screen recording privacy is for, he or she says that it's for making a video, which sounds like B.S. to me. And the reviewer suggests that I use alternatives. I've asked, but the reviewer doesn't say what those alternatives are. So are there alternatives to the CGWindowListCreateImage function for capturing the desktop area involving not just my application but also other windows.
No matter what method or API call you use; MACOS will ask for granting access to screen recording.
However this is a one time permissions and once the user has allowed access to the app then the privacy message will not show again and again.
There is no way to avoid the privacy message from being displayed at least once.
If you can elaborate more on your question that will get you a more targeted answer.
Related
My app only needs the 'Privacy - Photo Library Additions Usage Description' property in the plist file (app writes to Photo library, but doesn't read).
I use this code to check for permissions:
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
print("Permission granted")
} else {
print("Unavailable")
}
}
In the iOS Settings for my app, even though the plist only needs 'Add Photos Only', 'Read and Write' is also listed (??), and the above code only gets the authorised status if 'Read and Write' is ticked (ticking 'Add Photos Only' just causes the above code to go through to the else statement).
I'm a bit confused to why 'Read & Write' is even listed as a setting for my app, when my plist doesn't include "Privacy - Photo Library Usage Description".
Any ideas?
It's because you are calling PHPhotoLibrary.requestAuthorization.
If all you need is to write blindly into the user's camera roll, do not call PHPhotoLibrary.requestAuthorization. Just go ahead and write, e.g. by calling UIImageWriteToSavedPhotosAlbum or UISaveVideoAtPathToSavedPhotosAlbum. The runtime will request authorization on your behalf.
But if you want to interact with the photo library itself, i.e. thru the Photos framework, then you need authorization and an entry under Privacy - Photo Library Usage Description, even if all you intend to do is write.
Judging by your comments, you may be confused about what requestAuthorization does. It tries to obtain permission. If all you want to know is what the current authorization status actually is, call authorizationStatus instead.
However, even then, we're talking about the photo library itself, and thus the value referred to as Read & Write in Settings.
If you want to know whether you have permission to do UIImageWriteToSavedPhotosAlbum or UISaveVideoAtPathToSavedPhotosAlbum, just go ahead and call it. If there's a permissions problem, you'll hear about it in the completion handler.
My Codename One app features a ShareButton that is used like this :
// Share this report on social networks (text plus screenshot)
ShareButton shareReportButton = new ShareButton();
shareReportButton.setText("Share this report!");
shareReportButton.getAllStyles().setBorder(
RoundBorder.create().rectangle(true));
FontImage.setMaterialIcon(shareReportButton, FontImage.MATERIAL_SHARE);
shareReportButton.getStyle().setBgColor(ParametresGeneraux.accentColor);
shareReportButton.getPressedStyle().setBgColor(ParametresGeneraux.darkPrimaryColor);
shareReportButton.setTextToShare("I reported this via the great app ABCD "!"
);
shareReportButton.setImageToShare(currentReport.getPhotoPath(), ImageIO.FORMAT_PNG);
I works as expected under the simulator but on an actual Android 4.4 device I get a Dialog menu with "No app can do this action".
Please note that it is possible to share eg a photo with the native apps.
I could not find any build hint to add in the doc. What should I do to make the share button offer a way to share a text + photo on the social networks ?
Any help appreciated,
Regards
EDIT
Following #James H and #Diamond answers, the image type has to be set as mime type. Consequently replacing ImageIO.FORMAT_PNG by "image/jpg" makes the share menu populated.
For completeness please notice that as stated in the doc
an image must be stored using the FileSystemStorage API and shouldn't use a different API like Storage!
So even if the photo is in the cache you have to copy it eg to your home folder and then use this copied version in the ShareButton.
I think the problem might be that
setImageToShare(String imagePath, String imageMimeType)
is looking for a Mime description with a different format, such as: "image/png"
I'm not sure that ImageIO.FORMAT_PNG works in this way. Looking at the sample in the JavaDoc, it uses:
sb.setImageToShare(imageFile, "image/png");
Use the native share functionality and check if sharing natively is support by doing the following:
// Share this report on social networks (text plus screenshot)
Button shareReportButton = new Button("Share this report!");
shareReportButton.getAllStyles().setBorder(create().rectangle(true));
FontImage.setMaterialIcon(shareReportButton, FontImage.MATERIAL_SHARE);
shareReportButton.getStyle().setBgColor(ParametresGeneraux.accentColor);
shareReportButton.getPressedStyle().setBgColor(ParametresGeneraux.darkPrimaryColor);
shareReportButton.addActionListener(e -> {
if (Display.getInstance().isNativeShareSupported()) {
Display.getInstance().share("I reported this via the great app ABCD ", currentReport.getPhotoPath(), "image/png"); // Or "image/jpg"
} else {
ToastBar.showErrorMessage("Your phone doesn't support sharing...");
}
});
I am having a problem getting Facebook events in the Facebook app. For example, to open an event with the url "https://www.facebook.com/events/1743847059178738/," I would use the following code:
let facebookURL = NSURL(string: "fb://event/1743847059178738")!
if UIApplication.sharedApplication().canOpenURL(facebookURL) {
UIApplication.sharedApplication().openURL(facebookURL)
} else {
UIApplication.sharedApplication().openURL(NSURL(string: "https://www.facebook.com/events/1743847059178738")!)
}
}
It will open the Facebook app, but no matter what event I attempt to display I get a screen saying "Unable to load event. It may have been cancelled." I have tried substituting the Facebook URL of an event for that of a profile (e.g., string: "fb://profile/100005906912309") and it works just fine. Am I mistaken in assuming that the numbers at the end of the event's URL are the same as the event's numeric ID?
So I figured out that I could bypass the whole "fb://event..." bit and just go with:
UIApplication.sharedApplication().openURL(NSURL(string: "https://www.facebook.com/events/1743847059178738")!)
I assumed this would open the link in Safari, but it automatically opened in Facebook in iOS 8.
I'm sorry for the late answer. I'd prefer to write this as comment, but I'm not able to.
First, nice work around. I think it is a perfect fallback if the fb:// scheme fails as it shown in code shared with your question. I've spent a lot of time trying to resolve the same issue. What I've found is that fb://event/{event-id} is no longer supported and it was replaced with fb://event?id={event-id}.
I've used this post as a reference.
I just upgraded my app to Xcode 7 / Swift 2.0 and I'm struggling with the Facebook Login via AppSwitch. I'm on the latest FBSDK (4.6) and did everything according to the upgrade guide. Still, on iOS9 the Facebook login happens via in-app browser, the appswitch doesn't work anymore. I also tried force setting the loginbehavior but without luck:
let manager = FBSDKLoginManager()
manager.loginBehavior = FBSDKLoginBehavior.Native
manager.logInWithReadPermissions(facebookReadPermissions, fromViewController: nil, handler: { (loginResult, error) -> Void in
Is there anything I can do to make appswitch work again?
Thanks!
Turns out that this is not an issue but the new desired behavior according to these posts from Facebook:
https://developers.facebook.com/bugs/1636969533209725/?comment_id=1011596265571252
This behavior is by design. In our latest iOS SDKs, the login behavior is now controlled on the server side in order to ensure the best user experience.
https://developers.facebook.com/bugs/786729821439894/?comment_id=1467419033584031
Because of introduced changes in iOS 9, This new behavior avoids the user to be asked if they want to go and open the Facebook Application, accept the permissions/share/etc, and then ask once more if they want to switch back to your app.
https://developers.facebook.com/bugs/1390559277910338/?comment_id=1661064587442645
System authentication doesn't give people control over the information they share with apps. And in iOS 9, fast-app-switching to the Facebook native app results in additional dialogs ("ExampleApp would like to open Facebook") which appear twice - once on the way from ExampleApp to Facebook, and once again on the return journey. We believe the default SDK behavior in v4.6 on iOS 9 offers the best experience to people logging into your app with Facebook.
You can patch FacebookSDK source code to use Fast App Switching.
The interesting part (for tag 4.6) is in file FBSDKCoreKit/Internal/ServerConfiguration/FBSDKServerConfigurationManager.m
+ (FBSDKServerConfiguration *)_defaultServerConfigurationForAppID:(NSString *)appID
{
…
BOOL useNativeFlow = ![FBSDKInternalUtility isOSRunTimeVersionAtLeast:iOS9Version];
As you can see, it disables native flow on iOS9+.
Changing the line to something like BOOL useNativeFlow = YES; should work.
P.S. I didn't check this patch myself yet.
I have an HTML5 application that uses Azure mobile services authentication to login (straight from the example code...provided below). It works fine in all desktop browsers and iPhone 5 in Safari. But from app / full screen mode, it does nothing (doesn't ask for permission to show a popup window like it does in safari and no popup windows shows up) and I can wait forever and nothing happens. If I invoke it a second time, it gives an error saying "Error: Unexpected failure"...perhaps because the 1st attempt is still running? Any help/insight is appreciated.
client.login ("facebook").done(function (results) {
alert("You are now logged in as: " + results.userId);
}, function (err) {
alert("Error: " + err);
});
edited update with more info and 2 potential ideas*
I did some more research and found a site that uses an approach that overcomes this problem and also solves two other side effects with the current Azure mobile approach to authentication. I think the Azure mobile team might be looking to do something similar because there are some hints of other authentication options in the code (although difficult to read and be sure because the minimized code is obsfucated). It might be just a matter of activating these in the code...
The "solution":
Go to http://m.bcwars.com/ and click on the Facebook login. You'll see it works perfectly in iPhone Safari in "app mode" becuase instead of doing a popup, it simply stays in the current browser window.
This approach solves two other problems with the current Azure mobile approach. First, the popup gets interpreted by most browsers as a potential ad and is either blocked automatically (desktop Chrome) ... and the user doesn't know why it's not working...or gives a warning which the user has to approve (iPhone Safari in "browser mode") which is a hassle. And if the user has a popup blocker, it gets more difficult and even more potential for the user not getting it to work properly. The bcwars.com method doesn't have this problem.
Second, in iPhone Safari, when the popup window auto closes, the original page doesn't get focus if there are other browser windows open in Safari. Instead, it's in the smaller/slide mode so they can choose which one to show. If this happens, the user has to go through one more sttep...click on the browser window to activate it and give it focus..again more of a pain and more potential for them to mess up and not do it correctly and need help. The m.bcwars.com doesn't have this problem.
Azure options:
Looking at the Azure mobile code it looks like may already have the solution. I can't read it easliy becuase it's minified/obsfucated, but it seems to have 4 options (including iFrame, etc.) for invoking the authentication, and only 1 (the "less ideal one" of a popup) is being used. An easy solution would be to set a property to allow one of the alternate authentications to work. But I can't read it well enough to figure it out. Another would be to hack the code (temporarily until a fix is put up by Microsoft).
Could I get some help there perhaps?
You can implement an authentication flow with Facebook that doesn't use a popup. The basic idea is to use the 'Web Flow' for doing the login, and once the window return from the login, use the access token to login the user in to Azure Mobile Services.
The Facebook documentation for doing this is here:
https://developers.facebook.com/docs/facebook-login/login-flow-for-web-no-jssdk/#step2
Some code samples to make it easier for you.
You would start by something like this:
(Remember to replace YOUR_APP_ID and YOUR_URL with something relevant to your site.
function logIn() {
window.location.replace('https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=http%3A%2F%2FYOUR_URL&response_type=token')
}
This redirects the window to the Facebook page for the user to log in and authorize your app. When the user is done, Facebook will redirect the user back to YOUR_URL given above.
There you can handle the redirect and do the Mobile Services Login with something like this:
function handleLoginResponse() {
var frag = $.deparam.fragment();
if (frag.hasOwnProperty("access_token")) {
client.login("facebook", { access_token: frag.access_token }).then(function () {
// you're logged in
}, function (error) {
alert(error);
});
}
}
In here you parse the access token you get as a URL fragment and pass it as argument to the login call you make to Azure Mobile Services.
This code depends on the jquery BBQ plugin to handle the URL fragment easily.
Hope this solves your problem!