How to set request orientation in runtime? - gluon-mobile

I want my gluon application to be able to change orientation from Landscape to portrait in run time. I have check the Gluon Charmdown SDK, It seem that it have only getOrientation and don't have an option to set an orientation in runtime. I don't want to set fixed orientation in Manifest

Charm Down has an OrientationService, but as you have mentioned, it is "read only", it just listens to orientation changes and gives you the current orientation at any time. But so far you can set the orientation programatically.
To include this feature there are two options: clone Charm Down, modify the Orientation Service, build and use your custom build, or directly create a new service, like ExtendedOrientationService, that you can include in your project directly.
Assuming the latter, this is a very basic implementation of a service that allows setting the orientation programmatically:
ExtendedOrientationService.java
package com.gluonhq.charm.down.plugins;
import javafx.geometry.Orientation;
public interface ExtendedOrientationService {
void coerceOrientation(Orientation orientation);
void releaseOrientation();
}
ExtendedOrientationServiceFactory.java
package com.gluonhq.charm.down.plugins;
import com.gluonhq.charm.down.DefaultServiceFactory;
public class ExtendedOrientationServiceFactory extends DefaultServiceFactory<ExtendedOrientationService> {
public ExtendedOrientationServiceFactory() {
super(ExtendedOrientationService.class);
}
}
For Android:
AndroidExtendedOrientationService.java
package com.gluonhq.charm.down.plugins.android;
import android.content.pm.ActivityInfo;
import com.gluonhq.charm.down.plugins.ExtendedOrientationService;
import javafx.geometry.Orientation;
import javafxports.android.FXActivity;
public class AndroidExtendedOrientationService implements ExtendedOrientationService {
private final FXActivity instance = FXActivity.getInstance();
#Override
public void coerceOrientation(Orientation orientation) {
if (orientation.equals(Orientation.HORIZONTAL)) {
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if (orientation.equals(Orientation.VERTICAL)) {
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
}
#Override
public void releaseOrientation() {
instance.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}
On iOS:
ExtendedOrientation.h
#import <UIKit/UIKit.h>
#include "jni.h"
#interface ExtendedOrientation : UIViewController {}
#property (nonatomic, assign) BOOL shouldAutoRotate;
- (void) setOrientation:(NSString *)orientation;
- (void) release;
#end
ExtendedOrientation.m
#include "ExtendedOrientation.h"
extern JNIEnv *jEnv;
#define GET_MAIN_JENV \
if (jEnv == NULL) NSLog(#"ERROR: Java has been detached already, but someone is still trying to use it at %s:%s:%d\n", __FUNCTION__, __FILE__, __LINE__);\
JNIEnv *env = jEnv;
JNIEXPORT jint JNICALL
JNI_OnLoad_ExtendedOrientation(JavaVM *vm, void *reserved)
{
#ifdef JNI_VERSION_1_8
//min. returned JNI_VERSION required by JDK8 for builtin libraries
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK) {
return JNI_VERSION_1_4;
}
return JNI_VERSION_1_8;
#else
return JNI_VERSION_1_4;
#endif
}
static int ExtendedOrientationInited = 0;
// ExtendedOrientation
ExtendedOrientation *_extendedOrientation;
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_plugins_ios_IOSExtendedOrientationService_initOrientation
(JNIEnv *env, jclass jClass)
{
if (ExtendedOrientationInited)
{
return;
}
ExtendedOrientationInited = 1;
_extendedOrientation = [[ExtendedOrientation alloc] init];
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_plugins_ios_IOSExtendedOrientationService_setOrientation
(JNIEnv *env, jclass jClass, jstring jOrientation)
{
const jchar *charsOrientation = (*env)->GetStringChars(env, jOrientation, NULL);
NSString *orientation = [NSString stringWithCharacters:(UniChar *)charsOrientation length:(*env)->GetStringLength(env, jOrientation)];
(*env)->ReleaseStringChars(env, jOrientation, charsOrientation);
[_extendedOrientation setOrientation:orientation];
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_plugins_ios_IOSExtendedOrientationService_release
(JNIEnv *env, jclass jClass)
{
[_extendedOrientation release];
}
#implementation ExtendedOrientation
-(void) setOrientation:(NSString*)orientation
{
_shouldAutoRotate = YES;
NSLog(#"Set orientation: %#", orientation);
if ([orientation isEqualToString:#"HORIZONTAL"])
{
[[UIDevice currentDevice] setValue:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft] forKey:#"orientation"];
} else
{
[[UIDevice currentDevice] setValue:[NSNumber numberWithInt:UIInterfaceOrientationPortrait] forKey:#"orientation"];
}
}
- (void) release
{
_shouldAutoRotate = NO;
}
- (BOOL)shouldAutorotate
{
return _shouldAutoRotate;
}
#end
IOSExtendedOrientationService.java
package com.gluonhq.charm.down.plugins.ios;
import com.gluonhq.charm.down.plugins.ExtendedOrientationService;
import javafx.geometry.Orientation;
public class IOSExtendedOrientationService implements ExtendedOrientationService {
static {
System.loadLibrary("ExtendedOrientation");
initOrientation();
}
#Override
public void coerceOrientation(Orientation orientation) {
setOrientation(orientation.toString());
}
#Override
public void releaseOrientation() {
release();
}
// native
private static native void initOrientation();
private static native void setOrientation(String orientation);
private static native void release();
}
Now add the ios-gradle.build file from this sample.
Finally we need to build and include the native library:
build.gradle
apply from: 'ios-build.gradle'
task xcodebuild {
doLast {
xcodebuildIOS("$project.buildDir","$project.projectDir", "ExtendedOrientation")
}
}
task installNativeLib (type:Copy, dependsOn: xcodebuild) {
from("$project.buildDir/native")
into("src/ios/jniLibs")
include("*.a")
}
You can build and add the library to the project with:
./gradlew installNativeLib
SAMPLE
This snippet shows how to use this service: If the orientation is vertical it will force horizontal orientation, else it will release the orientation:
Services.get(ExtendedOrientationService.class).ifPresent(o -> {
Orientation orientation = Services.get(OrientationService.class)
.flatMap(OrientationService::getOrientation)
.orElse(Orientation.HORIZONTAL);
if (orientation == Orientation.VERTICAL) {
o.coerceOrientation(Orientation.HORIZONTAL);
} else {
o.releaseOrientation();
}
});

Related

Objective c binding not loading in xamarin forms ios

I am building my first objective c library binding. I am following this documentation from Microsoft. I have successfully created a fat binary from IronSource.framework and have also generated apidefinition and structs using objective sharpie and added my binary to it.
After doing that I added binding library to my xamarin.ios project. when i try to create a new instance of a class from the exposed API I get an error.
The type or namespace name 'IronSource' could not be found (are you missing a using directive or an assembly reference?) Stock.iOS D:\Backup Version for Stock Adviser\Version 1.6\Version code 37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
'AdControlViewRenderer.BannerWrapper' does not implement inherited
abstract member
'ISBannerDelegate.DidClickBanner()' Stock.iOS D:\Backup Version for
Stock Adviser\Version 1.6\Version code
37\StockAdviserCode\Stock\Stock\Stock.iOS\AdControlViewRenderer.cs
I think my objective c library and xamarin.ios project are not linking properly
My Binding project name is IronSource and my xamarin forms ios project name is Stock.iOS
//APIDefinition
using System;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace IronSource
{
// #interface ISBannerView : UIView
[BaseType(typeof(UIView))]
interface ISBannerView
{
}
// #protocol ISBannerDelegate <NSObject>
[BaseType(typeof(NSObject))]
[Model]
interface ISBannerDelegate
{
// #required -(void)bannerDidLoad:(ISBannerView *)bannerView;
[Abstract]
[Export("bannerDidLoad:")]
void BannerDidLoad(ISBannerView bannerView);
// #required -(void)bannerDidFailToLoadWithError:(NSError *)error;
[Abstract]
[Export("bannerDidFailToLoadWithError:")]
void BannerDidFailToLoadWithError(NSError error);
// #required -(void)didClickBanner;
[Abstract]
[Export("didClickBanner")]
void DidClickBanner();
// #required -(void)bannerWillPresentScreen;
[Abstract]
[Export("bannerWillPresentScreen")]
void BannerWillPresentScreen();
// #required -(void)bannerDidDismissScreen;
[Abstract]
[Export("bannerDidDismissScreen")]
void BannerDidDismissScreen();
// #required -(void)bannerWillLeaveApplication;
[Abstract]
[Export("bannerWillLeaveApplication")]
void BannerWillLeaveApplication();
}
}
//my xamarin.ios
using System;
using CoreGraphics;
using Foundation;
using Stock.iOS;
using Stock.Services;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using IronSource;
[assembly: ExportRenderer(typeof(AdControlView), typeof(AdControlViewRenderer))]
namespace Stock.iOS
{
public class AdControlViewRenderer : ViewRenderer
{
public AdControlViewRenderer()
{ }
protected AdControlView AdViewControl => (AdControlView)Element;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
return;
IronSource ironSource = new IronSource();
SetNativeControl();
}
private void SetNativeControl()
{
try
{
BannerWrapper bannerWrapper = new BannerWrapper(ViewController);
IronSource.SetBannerDelegate(bannerWrapper);
var bannerSize = new ISBannerSize("BANNER");
IronSource.LoadBannerWithViewController(ViewController, bannerSize);
var adview = bannerWrapper.BannerView();
SetNativeControl(adview);
}
catch (Exception ex)
{
}
}
}
public class BannerWrapper : ISBannerDelegate
{
readonly UIViewController parent;
ISBannerView bannerView = null;
public bool DestroyBanner()
{
if (bannerView != null)
{
IronSource.DestroyBanner(bannerView);
bannerView = null;
return true;
}
return false;
}
public BannerWrapper(UIViewController viewController)
{
this.parent = viewController;
}
public override void BannerDidClick()
{
}
public override void BannerDidDismissScreen()
{
}
public override void BannerDidFailToLoadWithError(NSError error)
{
}
public ISBannerView BannerView()
{
ISBannerView bannerView = new ISBannerView();
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
return bannerView;
}
public override void BannerDidLoad(ISBannerView bnView)
{
InvokeOnMainThread(() =>
{
bannerView = bnView;
nfloat y = this.parent.View.Frame.Size.Height - (bannerView.Frame.Size.Height / 2);
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
y -= this.parent.View.SafeAreaInsets.Bottom;
}
bannerView.Center = new CGPoint(this.parent.View.Frame.Size.Width / 2, y);
this.parent.View.AddSubview(bannerView);
bannerView.AccessibilityLabel = "bannerContainer";
});
}
public override void BannerWillLeaveApplication()
{
}
public override void BannerWillPresentScreen()
{
}
}
}
Any help is welcome
So, I was able to make it work by adding fat binary to Xamarin.ios and changed the build type from "Do not copy" to "Copy Always" and then added a linker to that fat binary using
-cxx -gcc_flags "-L${ProjectDir} -lIronSource -force_load ${ProjectDir}/IronSource.a
I used this GitHub repo for guidance.
Overall to setup IronSoruce, I had to add dll in references, native framework to native reference, fat binary to projectdir and add linker to fat binary.

Unity app crashes when built for iOS with camera enabled

I have an app which uses zxing to scan qr codes in the app. However when I build the app with these scripts in the scene the app crashes on startup. I thought it was something in the Awake() or Start() but I've wrapped those methods in a try catch, and even then I'm not getting any errors, and it doesn't crash on android and in the editor.
I don't have access to a Mac, and am using Unity Cloud Build to build it.
I also don't know how to enable permissions, I thought I did when creating the .p12 file, but I've also found that there's an info.plist file that I have to request permissions with.
Prior research I found this Unity Question about adding items to the Xcode project but not only did including the xcodeapi give me errors, but the using statements didn't work.
There are two scripts
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System;
public class WebCamController : MonoBehaviour {
public int desiredWidth = 1280;
public int desiredHeight = 720;
public int desiredFPS = 60;
public RawImage output;
[HideInInspector]
public WebCamTexture webcamTexture;
void Start ()
{
webcamTexture = new WebCamTexture(desiredWidth, desiredHeight, desiredFPS);
output.texture = webcamTexture;
Play();
}
public void Play()
{
webcamTexture.Play();
}
public void Pause()
{
webcamTexture.Stop();
}
}
and
using UnityEngine;
using System.Collections;
using ZXing;
using ZXing.QrCode;
using ZXing.Common;
using System;
public class CodeScanner : MonoBehaviour {
private static CodeScanner _instance;
public static CodeScanner Instance
{
get
{
if(null == _instance)
{
Debug.Log("Code Scanner Instance not found");
}
return _instance;
}
}
[Header("References")]
public WebCamController wcc;
[Header("Properties")]
private BarcodeReader codeScanner;
private string lastScanned = "";
public delegate void Found(string text, string type);
public event Found OnCodeScanned;
private bool active;
public void Awake()
{
_instance = this;
}
void Start () {
codeScanner = new BarcodeReader();
StartCoroutine(ReadCode());
wcc.Play();
}
IEnumerator ReadCode()
{
while (active)
{
try
{
var data = codeScanner.Decode(wcc.webcamTexture.GetPixels32(), wcc.webcamTexture.width, wcc.webcamTexture.height);
if (data != null)
{
//if (data.Text != lastScanned)
//{
OnCodeScanned(data.Text, data.BarcodeFormat.ToString());
//}
lastScanned = data.Text;
}
}
catch(Exception e)
{
}
yield return new WaitForSeconds(1.0f);
}
}
public void Activate()
{
wcc.Play();
active = true;
StartCoroutine(ReadCode());
}
public void Stop()
{
active = false;
wcc.Pause();
}
}
My device is added properly to the .p12 certificate I can compile and run the program without these scripts in the scene.

How to pass arguments from iOS (Objc/Swift) to Unity that are structs/classes? (without using UnitySendMessage)

I am trying to find a good way to pass parameters from my native unity plugin in objc/swift to C# unity.
It seems that I will need to use Marshall but I couldn't find a good example of this anywhere.
The only thing I did found is the UnitySendMessage, but that passes strings only as parameters, and even those are limited to 1024 bytes, which is not enough for a JSON representation of the objects, and handling multiple messages seems a bit of an overkill for this.
The idea is to be able to question the plugin for object detection from a MTLTexture and return the objects that are recognised.
Code samples:
SwiftBridge
import Foundation
import UIKit
import Vision
#objc public class SwiftBridge: NSObject {
var delegate: DelegateCallbackFunction?
#objc static let shared = SwiftBridge()
#objc func evaluate(texture: MTLTexture) -> Bool {
guard let delegate = self.delegate else {
return false
}
let rect = CGRect(x: 1, y: 2, width: 100, height: 200)
delegate(rect)
return true
}
#objc func setDelegate(callback: #escaping DelegateCallbackFunction) -> Bool {
self.delegate = callback
return true
}
}
Unity
using System;
using UnityEngine;
using System.Runtime.InteropServices;
using AOT;
[StructLayout(LayoutKind.Sequential)]
public struct CGPoint {
public float x;
public float y;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGSize {
public float width;
public float height;
};
[StructLayout(LayoutKind.Sequential)]
public struct CGRect {
public CGPoint origin;
public CGSize size;
}
public class UnityBridge : MonoBehaviour {
#region Declare external C interface
// #if UNITY_IOS && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern int _vision_detectObjectsIn(IntPtr texture);
delegate bool ObjectDetectedCallback(ref CGRect rect);
[DllImport("__Internal")]
private static extern void _vision_setDelegate(ObjectDetectedCallback callback);
[MonoPInvokeCallback(typeof(ObjectDetectedCallback))]
private static bool delegateMessageReceived(ref CGRect rect) {
Debug.Log("Message received: " + rect.origin.x);
return true;
}
// #endif
#endregion
public void initializeDelegate() {
if (Application.platform == RuntimePlatform.IPhonePlayer) {
_vision_setDelegate(delegateMessageReceived);
}
}
#region Wrapped methods and properties
public void EvaluateTexture(IntPtr texture) {
initializeDelegate();
if (texture == IntPtr.Zero) {
Debug.LogError("[Texture] Pointer to buffer is null.");
return;
}
bool success;
#if UNITY_IOS && !UNITY_EDITOR
_vision_detectObjectsIn(texture);
#endif
}
#endregion
#region Singleton implementation
private static WeRDetectorUnity _instance;
public static WeRDetectorUnity Instance {
get {
if (_instance == null) {
var obj = new GameObject("WeRDetectorUnity");
_instance = obj.AddComponent<WeRDetectorUnity>();
}
return _instance;
}
}
void Awake() {
if (_instance != null) {
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
#endregion
}
The message receive print in Unity does not return a 1 as it should but rather a strange exponent small number.
Any idea??
Blockquote
You need to create struct for pass parameter its you can use parameters on multiple time and also access in main class.
struct Location {
let latitude:"latitude"
let longitude: "longitude"
}

After binding using Objective Sharpie Protocol Methods are not getting invoked in xamarin.iOS

I have few issues with Binding using Objective sharpie.I am binding IndoorAtlas iOS native sdk with Xamarin.ios.
Issue is while Implementing Protocols methods as those are not getting invoked. Do we need to handle it in special way?
I am attaching API defination file and Implementation file.
// #protocol IALocationManagerDelegate
[Protocol, Model]
[BaseType(typeof(NSObject))]
interface IALocationManagerDelegate
{
// #optional -(void)indoorLocationManager:(IALocationManager *
_Nonnull)manager didUpdateLocations:(NSArray * _Nonnull)locations;
[Export("indoorLocationManager:didUpdateLocations:")]
void DidUpdateLocations(IALocationManager manager, IALocation[] locations);
// #optional -(void)indoorLocationManager:(IALocationManager *
_Nonnull)manager didEnterRegion:(IARegion * _Nonnull)region;
[Export("indoorLocationManager:didEnterRegion:")]
void DidEnterRegion(IALocationManager manager, IARegion region);
}
// #interface IALocationManager : NSObject
[BaseType(typeof(NSObject))]
interface IALocationManager
{
[Wrap("WeakDelegate")]
[NullAllowed]
IALocationManagerDelegate Delegate { get; set; }
// #property (readwrite, nonatomic, weak) id<IALocationManagerDelegate>
_Nullable delegate;
[NullAllowed, Export("delegate", ArgumentSemantic.Weak)]
NSObject WeakDelegate { get; set; }
}
////ViewController --Calling delegate methods
[Export("indoorLocationManager:didUpdateLocations:")]
public void DidUpdateLocations(IALocationManager manager , IALocation[] locations)
{
IALocation loc = locations[locations.Length - 1];
if (mFloorPlan != null)
{
CoreGraphics.CGPoint cg = mFloorPlan.CoordinateToPoint(loc.Location.Coordinate);
this.map.Center = cg;
}
}
[Export("indoorLocationManager:didEnterRegion:")]
public void DidEnterRegion(IALocationManager manager, IARegion region)
{
if (region.Type != ia_region_type.FloorPlan)
Console.WriteLine("Region Changed to {0} " + region.Identifier);
else
{
FetchFloorPlan();
}
}
Don't forget to assign the viewcontroller to the weak delegate.
IALocationManager manager = new IALocationManager();
manager.WeakDelegate = this;

NSItemProviderReading on Xamarin

To do files drag & drop for files in iOS 11 you need to implement NSItemProviderReading, there is the sample code for swift here: iOS 11 dropInteraction performDrop for files
However, how to do that in Xamarin, I guess the class definition should look like this, but how to implement the methods?
public class DocumentProvider : NSObject, INSItemProviderReading
{
}
You can implement the NSItemProviderReading like this:
class MyItemProvider : UIView, INSItemProviderReading {
[Export ("readableTypeIdentifiersForItemProvider")]
public static string [] ReadableTypeIdentifiersForItemProvider => new string [] { "public.image", "public.data" }
[Export ("objectWithItemProviderData:typeIdentifier:error:")]
public static MyItemProvider GetObject (NSData data, string typeIdentifier, out NSError outError)
{
outError = null;
switch (typeIdentifier) {
case "public.image": return new ...;
case "public.data": return new ...;
default:
outError = new NSError (...);
return null;
}
}
}
Reference:NSItemProviderReading requirements.

Resources