I'm building an ios plugin for a native library.
The issue I am facing is the library starts to work and the view controller presenter is working fine, but the callbacks which are passed through a delegate are never raised in typescript.
Here is the code snippet:
import { Common } from './nsjumioplugin.common';
import * as frameModule from "tns-core-modules/ui/frame/frame";
export class Nsjumioplugin extends Common {
private cancelWithError;
private finishInitWithError;
private finishedScan;
private netverifyViewController: NetverifyViewController;
constructor() {
super();
this.netverifyViewController = NetverifyViewController.alloc();
}
public start(merchantApiToken, merchantApiSecret, customerEmail, cancelWithError, finishInitWithError, finishedScan) {
this.cancelWithError = cancelWithError;
this.finishInitWithError = finishInitWithError;
this.finishedScan = finishedScan;
console.log("EEEEEEEEEEEE greet 8");
const that = this;
const config = NetverifyConfiguration.new();
config.merchantApiToken = merchantApiToken;
config.merchantApiSecret = merchantApiSecret;
config.delegate = NsjumiopluginDelegateImpl.new().initWithCallback((netverifyViewController: NetverifyViewController, documentData: NetverifyDocumentData, scanReference: string) => {
console.log("EEEEEEEEEEE 4");
that.rootVC().dismissViewControllerAnimatedCompletion(true, null);
that.finishedScan(documentData);
});
config.customerId = customerEmail;
try {
this.netverifyViewController.initWithConfiguration(config);
this.rootVC().presentViewControllerAnimatedCompletion(
this.netverifyViewController, false, function completion() {
console.log('EEEEEEEEEEEE done');
});
} catch (e) {
console.log('EEEEEEEEEEEE EXCEPTION HANDLED:', e);
}
}
rootVC() {
/*
const appWindow = UIApplication.sharedApplication.keyWindow;
return appWindow.rootViewController;
*/
let frame: typeof frameModule = require("tns-core-modules/ui/frame");
let topMostFrame = frame.topmost();
if (topMostFrame) {
let viewController: UIViewController = topMostFrame.currentPage && topMostFrame.currentPage.ios;
if (viewController) {
while (viewController.parentViewController) {
// find top-most view controler
viewController = viewController.parentViewController;
}
while (viewController.presentedViewController) {
// find last presented modal
viewController = viewController.presentedViewController;
}
return viewController;
}
}
}
}
class NsjumiopluginDelegateImpl extends NSObject implements NetverifyViewControllerDelegate {
static new(): NsjumiopluginDelegateImpl {
return <NsjumiopluginDelegateImpl>super.new();
}
private _callback: (netverifyViewController: NetverifyViewController, documentData: NetverifyDocumentData, scanReference: string) => void;
public initWithCallback(callback: (netverifyViewController: NetverifyViewController, documentData: NetverifyDocumentData, scanReference: string) => void): NsjumiopluginDelegateImpl {
this._callback = callback;
return this;
}
netverifyViewControllerDidCancelWithErrorScanReference(netverifyViewController: NetverifyViewController, error: NetverifyError, scanReference: string): void {
console.log("EEEEEEEEEEE 1");
this._callback(netverifyViewController, null, scanReference);
}
netverifyViewControllerDidFinishInitializingWithError?(netverifyViewController: NetverifyViewController, error: NetverifyError): void {
console.log("EEEEEEEEEEE 2");
// this.finishInitWithError();
}
netverifyViewControllerDidFinishWithDocumentDataScanReference(netverifyViewController: NetverifyViewController, documentData: NetverifyDocumentData, scanReference: string): void {
console.log("EEEEEEEEEEE 3");
console.log("finished successfully with scan reference: %#", scanReference);
this._callback(netverifyViewController, documentData, scanReference);
}
}
The only console logs I am getting are "EEEEEEEEEEEE greet 8" and "EEEEEEEEEEEE done".
The method in delegate are never raised.
Here is the SDK docs:
https://github.com/Jumio/mobile-sdk-ios
I am wondering which part I am doing wrong.
Thanks
You have to let the runtime know you are using particular delegate by add #ObjCClass(...) annotation.
#ObjCClass(NetverifyViewControllerDelegate)
class NsjumiopluginDelegateImpl extends NSObject implements NetverifyViewControllerDelegate {
.....
}
An alternative syntax is to define static ObjCProtocols array,
class NsjumiopluginDelegateImpl extends NSObject implements NetverifyViewControllerDelegate {
public static ObjCProtocols = [NetverifyViewControllerDelegate];
.....
}
Related
Been working on a small project to and whenever I call any function of assemblyscript contract, I get "instantiation error". Heres the log:
Log Image
Scheduling a call:
dev-1643739174146-18174234067579.init({"owner":"$OWNER"})
Doing
account.functionCall() Receipt:
5CnMQ2RB2hHbDYTW6BecYfcYznLVqWuz3icVSXuXJcCj
Failure [dev-1643739174146-18174234067579]: Error: Error happened during instantiation
ServerTransactionError: Error happened
during instantiation
at Object.parseResultError (/mnt/c/Users/d4r18/AppData/Roaming/npm/node_modules/near-cli/node_modules/near-api-js/lib/utils/rpc_errors.js:31:29)
at Account.signAndSendTransactionV2 (/mnt/c/Users/d4r18/AppData/Roaming/npm/node_modules/near-cli/node_modules/near-api-js/lib/account.js:160:36)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async scheduleFunctionCall (/mnt/c/Users/d4r18/AppData/Roaming/npm/node_modules/near-cli/commands/call.js:57:38)
at async Object.handler (/mnt/c/Users/d4r18/AppData/Roaming/npm/node_modules/near-cli/utils/exit-on-error.js:52:9)
Heres my modules.ts:
import { Context, u128, PersistentVector } from 'near-sdk-as';
#nearBindgen
export class Note {
private content: string;
constructor(public text: string) {
this.content = text;
}
delete(): void {
this.content = "";
}
edit(note: string, append: bool = false): string {
if (append)
this.content.concat(note);
else
this.content = note;
return this.content;
}
get(): string {
return this.content;
}
}
#nearBindgen
export class Vector<T> extends PersistentVector<T>{
get_all(): Array<T> {
const result = new Array<T>();
for (let i = 0; i < this.length; i++) {
const entry = this[i];
result.push(entry);
}
return result;
}
}
and heres the index.ts
import { Context, logging, storage } from 'near-sdk-as'
import { Note, Vector } from './modules'
#nearBindgen
export class Contract {
private owner: string;
private notes: Vector<Note> = new Vector<Note>('m');
private message: Note;
constructor(master: string) {
this.owner = master;
}
#mutateState()
create(note: string): bool {
// this.message = new Note(note);
console.log("this.message.get()");
return true;
}
list(): Array<Note> {
return this.notes.get_all();
}
private assert_owner(): void {
const caller = Context.predecessor;
assert(
this.owner == caller,
'Only the owner of this contract may call this method'
);
}
}
Would be great help if anyone could help me with this.
The "console.log" statement was causing the problem. Removing it fixed the issue.
I instead used logger.log() method for it. It can be imported from "near-sdk-as"
import { Context, logging, storage } from 'near-sdk-as'
import { Note, Vector } from './modules'
#nearBindgen
export class Contract {
private owner: string;
private notes: Vector<Note> = new Vector<Note>('m');
private message: Note;
constructor(master: string) {
this.owner = master;
}
#mutateState()
create(note: string): bool {
// this.message = new Note(note);
// Remove the console.log line
// console.log("this.message.get()");
return true;
}
list(): Array<Note> {
return this.notes.get_all();
}
private assert_owner(): void {
const caller = Context.predecessor;
assert(
this.owner == caller,
'Only the owner of this contract may call this method'
);
}
}
I am trying to implement below java code in c# referring to Android documentation
List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
}
});
I have here 2 questions. I thought that i would run this code on a separate thread than UI thread like below to keep my ui responsive while network connection is done. is that the correct approach? QuerySkuDetailsAsync is called async but doesnt implement as async. how should this be working and how to handle in c# because it will fire and forget but Listener to handle the response.
public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
{
var getSkuDetailsTask = Task.Factory.StartNew(() =>
{
var prms = SkuDetailsParams.NewBuilder();
var type = BillingClient.SkuType.Inapp;
prms.SetSkusList(ProductIds).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());
return InAppBillingProducts;
});
return await getSkuDetailsTask;
}
2nd question regarding how to handle with the listener as below. How do I return value from the listener. I need return list of InAppBillingProduct object.
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
FYI. This is how I did it. This is not a complete code but this will give you and idea.
Listener - PCL
============
private async Task EventClicked()
{
var skuList = new List<string>();
skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");
if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
{
var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if (firstProduct != null)
{
//purchase here
}
}
}
private void DisconnectedConnection()
{
//Todo.alfon. handle disconnection here...
}
Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
List<InAppBillingPurchase> PurchasedProducts { get; set; }
List<InAppBillingProduct> ProductsInStore { get; set; }
Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}
Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
, ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
private Activity Context => CrossCurrentActivity.Current.Activity
?? throw new NullReferenceException("Current Context/Activity is null");
private BillingClient _billingClient;
private List<string> _skuList = new List<string>();
private TaskCompletionSource<bool> _tcsInitialized;
private Action _disconnectedAction;
private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();
public List<InAppBillingPurchase> PurchasedProducts { get; set; }
public List<InAppBillingProduct> ProductsInStore { get; set; }
public IntPtr Handle => throw new NotImplementedException();
public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
{
_disconnectedAction = disconnectedAction;
_tcsInitialized = new TaskCompletionSource<bool>();
var taskInit = _tcsInitialized.Task;
_skuList = skuList;
_billingClient = BillingClient.NewBuilder(Context)
.SetListener(this)
.EnablePendingPurchases()
.Build();
if (!_billingClient.IsReady)
{
_billingClient.StartConnection(this);
}
return taskInit;
}
#region IBillingClientStateListener
public void OnBillingServiceDisconnected()
{
Console.WriteLine($"Connection disconnected.");
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
public void OnBillingSetupFinished(BillingResult billingResult)
{
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
if (responseCode == BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(true);
}
else
{
Console.WriteLine($"Failed connection {debugMessage}");
_tcsInitialized?.TrySetResult(false);
}
}
#endregion
#region ISkuDetailsResponseListener
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
{
if (billingResult == null)
{
Console.WriteLine("onSkuDetailsResponse: null BillingResult");
return;
}
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
switch (responseCode)
{
case BillingResponseCode.Ok:
if (skuDetailsList == null)
{
_skusWithSkuDetails.Clear();
}
else
{
if (skuDetailsList.Count > 0)
{
ProductsInStore = new List<InAppBillingProduct>();
}
foreach (var skuDetails in skuDetailsList)
{
_skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);
//ToDo.alfon. make use mapper here
ProductsInStore.Add(new InAppBillingProduct
{
Name = skuDetails.Title,
Description = skuDetails.Description,
ProductId = skuDetails.Sku,
CurrencyCode = skuDetails.PriceCurrencyCode,
LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
LocalizedPrice = skuDetails.Price,
MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
MicrosPrice = skuDetails.PriceAmountMicros
});
}
}
break;
case BillingResponseCode.ServiceDisconnected:
case BillingResponseCode.ServiceUnavailable:
case BillingResponseCode.BillingUnavailable:
case BillingResponseCode.ItemUnavailable:
case BillingResponseCode.DeveloperError:
case BillingResponseCode.Error:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
case BillingResponseCode.UserCancelled:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
// These response codes are not expected.
case BillingResponseCode.FeatureNotSupported:
case BillingResponseCode.ItemAlreadyOwned:
case BillingResponseCode.ItemNotOwned:
default:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
}
}
#endregion
#region Helper Methods Private
private void ProcessPurchases(List<Purchase> purchasesList)
{
if (purchasesList == null)
{
Console.WriteLine("No purchases done.");
return;
}
if (IsUnchangedPurchaseList(purchasesList))
{
Console.WriteLine("Purchases has not changed.");
return;
}
_purchases.AddRange(purchasesList);
PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
{
PurchaseToken = sku.PurchaseToken
})?.ToList();
if (purchasesList != null)
{
LogAcknowledgementStatus(purchasesList);
}
}
private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
{
// TODO: Optimize to avoid updates with identical data.
return false;
}
private void LogAcknowledgementStatus(List<Purchase> purchasesList)
{
int ack_yes = 0;
int ack_no = 0;
foreach (var purchase in purchasesList)
{
if (purchase.IsAcknowledged)
{
ack_yes++;
}
else
{
ack_no++;
}
}
//Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
// " unacknowledged=" + ack_no);
}
private void QuerySkuDetails()
{
var parameters = SkuDetailsParams
.NewBuilder()
.SetType(BillingClient.SkuType.Subs)
.SetSkusList(_skuList)
.Build();
_billingClient.QuerySkuDetailsAsync(parameters, this);
}
private void QueryPurchases()
{
if (!_billingClient.IsReady)
{
Console.WriteLine("queryPurchases: BillingClient is not ready");
}
var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
ProcessPurchases(result?.PurchasesList?.ToList());
}
#endregion
}
i'm having a problem with receiving data from HC-05 bluetooth module. Sending data from mobile to module works fine, but i can't figure out how to receive data from module. I think there will be problem in with Thread which contains listener function.
Thank's for help
here's the code:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Bluetooth;
using Microcharts;
using Entry = Microcharts.Entry;
using Microcharts.Droid;
using SkiaSharp;
using System.Linq;
using System.Collections.Generic;
namespace BLE
{
[Activity(Label = "BLE", MainLauncher = true)]
public class MainActivity : Activity
{
BluetoothConnection myConnection = new BluetoothConnection();
protected override void OnCreate(Bundle savedInstanceState)
{
var metrics = Resources.DisplayMetrics;
var width = metrics.WidthPixels;
var height = metrics.HeightPixels;
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
Button buttonConnect = FindViewById<Button>(Resource.Id.button1);
Button buttonDisconnect = FindViewById<Button>(Resource.Id.button2);
buttonConnect.LayoutParameters.Width = Convert.ToInt32(width * 0.5);
buttonDisconnect.LayoutParameters.Width = Convert.ToInt32(width * 0.5);
TextView connected = FindViewById<TextView>(Resource.Id.textView1);
BluetoothSocket _socket = null;
System.Threading.Thread listenThread = new System.Threading.Thread(listener);
listenThread.Abort();
buttonConnect.Click += delegate
{
try
{
buttonDisconnect.Enabled = false;
buttonConnect.Enabled = true;
listenThread.Abort();
myConnection.thisDevice.Dispose();
myConnection.thisSocket.OutputStream.WriteByte(187);
myConnection.thisSocket.OutputStream.Close();
myConnection.thisSocket.Close();
myConnection = new BluetoothConnection();
_socket = null;
connected.Text = "Disconnected!";
}
catch { }
listenThread.Start();
myConnection = new BluetoothConnection();
myConnection.thisSocket = null;
_socket = null;
myConnection.getAdapter();
myConnection.thisAdapter.StartDiscovery();
try
{
myConnection.getDevice();
myConnection.thisDevice.SetPairingConfirmation(false);
myConnection.thisDevice.Dispose();
myConnection.thisDevice.SetPairingConfirmation(true);
myConnection.thisDevice.CreateBond();
}
catch (Exception deviceEX)
{
}
myConnection.thisAdapter.CancelDiscovery();
_socket = myConnection.thisDevice.CreateRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
myConnection.thisSocket = _socket;
try
{
myConnection.thisSocket.Connect();
connected.Text = "Connected!";
buttonDisconnect.Enabled = true;
buttonConnect.Enabled = false;
if (listenThread.IsAlive == false)
{
listenThread.Start();
}
}
catch (Exception CloseEX)
{
}
};
buttonDisconnect.Click += delegate
{
try
{
buttonConnect.Enabled = true;
listenThread.Abort();
myConnection.thisDevice.Dispose();
myConnection.thisSocket.OutputStream.WriteByte(187);
myConnection.thisSocket.OutputStream.Close();
myConnection.thisSocket.Close();
myConnection = new BluetoothConnection();
_socket = null;
connected.Text = "Disconnected!";
}
catch { }
};
void listener()
{
TextView readTextView = FindViewById<TextView>(Resource.Id.textView2);
while (true)
{
try
{
byte[] buffer = new byte[1];
myConnection.thisSocket.InputStream.Read(buffer, 0, 1);
myConnection.thisSocket.InputStream.Close();
String dispString = System.Text.ASCIIEncoding.Default.GetString(buffer);
RunOnUiThread(() =>
{
//readTextView.Text = dispString;
System.Console.WriteLine(dispString);
});
}
catch (Java.IO.IOException)
{
RunOnUiThread(() =>
{
readTextView.Text = string.Empty;
});
break;
}
}
}
}
public class BluetoothConnection
{
public void getAdapter() { this.thisAdapter = BluetoothAdapter.DefaultAdapter; }
public void getDevice() { this.thisDevice = (from bd in this.thisAdapter.BondedDevices where bd.Name == "HC-05" select bd).FirstOrDefault(); }
public BluetoothAdapter thisAdapter { get; set; }
public BluetoothDevice thisDevice { get; set; }
public BluetoothSocket thisSocket { get; set; }
}
}
}
`
Thank's for help
I don't understand what this is used for in buttonConnect.Click event
try
{
buttonDisconnect.Enabled = false;
buttonConnect.Enabled = true;
listenThread.Abort();
myConnection.thisDevice.Dispose();
myConnection.thisSocket.OutputStream.WriteByte(187);
myConnection.thisSocket.OutputStream.Close();
myConnection.thisSocket.Close();
myConnection = new BluetoothConnection();
_socket = null;
connected.Text = "Disconnected!";
}
catch { }
and usually receive data like this(Simple usage):
System.Threading.Thread listenThread = new System.Threading.Thread(Listener);
buttonConnect.Click += delegate {
myConnection = new BluetoothConnection();
myConnection.getAdapter();
myConnection.getDevice();
_socket = myConnection.thisDevice.CreateRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
myConnection.thisSocket = _socket;
myConnection.thisSocket.Connect();
listenThread.Start();
}
private void Listener()
{
while (true)
{
try
{
byte[] buffer = new byte[1];
TextView readTextView = FindViewById<TextView>(Resource.Id.textView2);
myConnection.thisSocket.InputStream.Read(buffer, 0, 1);
myConnection.thisSocket.InputStream.Close();
String dispString = System.Text.ASCIIEncoding.Default.GetString(buffer);
RunOnUiThread(() =>
{
readTextView.Text = dispString;
System.Console.WriteLine(dispString);
});
}
catch (Java.IO.IOException)
{
TextView readTextView = FindViewById<TextView>(Resource.Id.textView2);
RunOnUiThread(() =>
{
readTextView.Text = string.Empty;
});
break;
}
}
}
}
public class BluetoothConnection
{
public void getAdapter() { this.thisAdapter = BluetoothAdapter.DefaultAdapter; }
public void getDevice() { this.thisDevice = (from bd in this.thisAdapter.BondedDevices where bd.Name == "hc-05" select bd).FirstOrDefault(); }
public BluetoothAdapter thisAdapter { get; set; }
public BluetoothDevice thisDevice { get; set; }
public BluetoothSocket thisSocket { get; set; }
}
I'm using Xamarin forms and writing a dependency service for the following objectives :
Open iOS files app. (UIDocumentPickerViewController )
Select any kind of a document.
Copy that document into my application Documents directory. (For app access)
Show that document into my application by storing its path into my SQLite DB.
What I am trying to do here is call the Files app from my application on an Entry click and the click event seems to be working well my dependency service calls perfectly but now when I try to use the UIDocumentPickerViewController I am unable to get View controller context in my dependency service to call the PresentViewController method. Now I know about the xamarin forms context but I don't know if it will work here and I don't even know if it would be a smart idea to use it as it has already been marked as obsolete and since I am not from the iOS background, I don't know what would be the right solution for it.
My code is as follows :
public class DocumentPickerRenderer : IDocumentPicker
{
public object PickFile()
{
var docPicker = new UIDocumentPickerViewController(new string[] { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += (sender, wasCancelledArgs) =>
{
};
docPicker.DidPickDocumentAtUrls += (object sender, UIDocumentPickedAtUrlsEventArgs e) =>
{
Console.WriteLine("url = {0}", e.Urls[0].AbsoluteString);
//bool success = await MoveFileToApp(didPickDocArgs.Url);
var success = true;
string filename = e.Urls[0].LastPathComponent;
string msg = success ? string.Format("Successfully imported file '{0}'", filename) : string.Format("Failed to import file '{0}'", filename);
var alertController = UIAlertController.Create("import", msg, UIAlertControllerStyle.Alert);
var okButton = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>
{
alertController.DismissViewController(true, null);
});
alertController.AddAction(okButton);
PresentViewController(alertController, true, null);
};
PresentViewController(docPicker, true, null);
}
}
My questions:
Is my methodology correct for picking files?
what will be the object that I will be getting as a callback from a file selection and how will I get the callback?
Is there any other way or something available for xamarin forms, some guide or something that allows me to pick documents from my native file systems and gives a brief on how to handle it in both ios and android?
Hello Guys, You can use following code for picking any type of documents to mention in code using iOS Devices-
use follwing interface:
public interface IMedia
{
Task<string> OpenDocument();
}
public Task<string> OpenDocument()
{
var task = new TaskCompletionSource<string>();
try
{
OpenDoc(GetController(), (obj) =>
{
if (obj == null)
{
task.SetResult(null);
return;
}
var aa = obj.AbsoluteUrl;
task.SetResult(aa.Path);
});
}
catch (Exception ex)
{
task.SetException(ex);
}
return task.Task;
}
static Action<NSUrl> _callbackDoc;
public static void OpenDoc(UIViewController parent, Action<NSUrl> callback)
{
_callbackDoc = callback;
var version = UIDevice.CurrentDevice.SystemVersion;
int verNum = 0;
Int32.TryParse(version.Substring(0, 2), out verNum);
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image,
UTType.Spreadsheet,
"com.microsoft.word.doc",
"org.openxmlformats.wordprocessingml.document",
"com.microsoft.powerpoint.ppt",
"org.openxmlformats.spreadsheetml.sheet",
"org.openxmlformats.presentationml.presentation",
"com.microsoft.excel.xls",
};
// Display the picker
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Import);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
if (verNum < 11)
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Url);
pArgs.Url.StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Url.AbsoluteUrl);
};
}
else
{
args.DocumentPicker.DidPickDocumentAtUrls += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Urls[0]);
pArgs.Urls[0].StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Urls[0].AbsoluteUrl);
};
}
// Display the document picker
parent.PresentViewController(args.DocumentPicker, true, null);
};
pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
parent.PresentViewController(pickerMenu, true, null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover != null)
{
presentationPopover.SourceView = parent.View;
presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
}
}
Now you need to call using following code:
var filePath = await DependencyService.Get<IMedia>().OpenDocument();
For pick document in Android, you can use following code
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{ if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
For pick document in Android, you can use following code:
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
Use below code to access the helper class: public class Media:
IMedia {
public Task<string> OpenDocument() {
var task = new TaskCompletionSource<string>();
try {
IntentHelper.OpenDocPicker((path) => { task.SetResult(path); });
} catch (Exception ex) {
task.SetResult(null);
}
return task.Task;
}
}
Since I was looking for UIDocumentPickerViewController and not UIDocumentMenuViewController the other answer was not what I was looking for :
So this is how I ended up doing it:
Calling the document picker:
var docPicker = new UIDocumentPickerViewController(new string[]
{ UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += DocPicker_WasCancelled;
docPicker.DidPickDocumentAtUrls += DocPicker_DidPickDocumentAtUrls;
docPicker.DidPickDocument += DocPicker_DidPickDocument;
var _currentViewController = GetCurrentUIController();
if (_currentViewController != null)
_currentViewController.PresentViewController(docPicker, true, null);
Where GetCurrentUIController is the function to get the current UI controller something like this :
public UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
return null;
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
For below iOS 11 i added the DidPickDocument event:
private void DocPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
try
{
NSUrl filePath = e.Url.AbsoluteUrl;
//This is the url for your document and you can use it as you please.
}
catch (Exception ex)
{
}
}
For above iOS 11 you use the DidPickDocumentUrls since multipick is supported there :
private void DocPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
{
try
{
List<NSUrl> filePath = e.Urls.ToList().Select(y => y.AbsoluteUrl).ToList();
//returns the list of images selected
}
catch (Exception ex)
{
AppLogger.LogException(ex);
}
}
I am currently using v3.2.5 of Abp.AspNetCore.
I am trying to integrate an Alpha package of Microsoft.AspNetCore.OData into the project which is so far looking ok.
However when i try and query the metadata controller http://localhost:51078/odata/v1/$metadata the result is wrapped.
Now this was an issue for the ODataControllers as well, but i could simply add
the [DontWrapResult] attribute.
I dont have direct access to the MetadataController so i am unable to add the attribute. Is there anyway to disable wrapping for an Abp project?
Thanks
Edit
Here is the current ConfigureServices method
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });
services
.AddAuthentication()
.AddCsDeviceAuth(options => { });
services
.AddOData();
//Configure Abp and Dependency Injection
var provider = services.AddAbp<PortalWebODataModule>(options =>
{
//Configure Log4Net logging
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.LogUsing<Log4NetLoggerFactory>().WithConfig("log4net.config")
);
});
services.Configure<MvcOptions>(options =>
{
var abpResultFilter = options.Filters.First(f => f is AbpResultFilter);
options.Filters.Remove(abpResultFilter);
options.Filters.AddService(typeof(ODataResultFilter));
});
return provider;
}
You can implement IResultFilter and set WrapOnSuccess to false:
public class ResultFilter : IResultFilter, ITransientDependency
{
private readonly IAbpAspNetCoreConfiguration _configuration;
public ResultFilter(IAbpAspNetCoreConfiguration configuration)
{
_configuration = configuration;
}
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.HttpContext.Request.Path.Value.Contains("odata"))
{
var methodInfo = context.ActionDescriptor.GetMethodInfo();
var wrapResultAttribute =
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
methodInfo,
_configuration.DefaultWrapResultAttribute
);
wrapResultAttribute.WrapOnSuccess = false;
}
}
public void OnResultExecuted(ResultExecutedContext context)
{
// No action
}
private TAttribute GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<TAttribute>(MemberInfo memberInfo, TAttribute defaultValue = default(TAttribute), bool inherit = true)
where TAttribute : class
{
return memberInfo.GetCustomAttributes(true).OfType<TAttribute>().FirstOrDefault()
?? memberInfo.DeclaringType?.GetTypeInfo().GetCustomAttributes(true).OfType<TAttribute>().FirstOrDefault()
?? defaultValue;
}
}
Then, in Startup class, add the filter in ConfigureServices method:
services.AddMvc(options =>
{
options.Filters.AddService(typeof(ResultFilter));
});
References:
AbpResultFilter.OnResultExecuting
ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault
Alternative solution; to completely disable WrapResult behavior within the system ( at the Core module registration):
var abpAspNetCoreConfiguration = Configuration.Modules.AbpAspNetCore();
abpAspNetCoreConfiguration.DefaultWrapResultAttribute.WrapOnSuccess = false;
abpAspNetCoreConfiguration.DefaultWrapResultAttribute.WrapOnError = false;
abpAspNetCoreConfiguration
.CreateControllersForAppServices(
typeof(AccessApplicationModule).GetAssembly()
);
WrapOnSuccess and WrapOnError flags can be set to false values.
ABP v6.5 and later
Implement IWrapResultFilter and add it to WrapResultFilters in the module's PreInitialize method.
See https://stackoverflow.com/questions/70947461/how-to-control-response-wrapping-in-abp-on-a-per-route-basis/70955045#70955045 for more details.
Before ABP v6.5
...including ABP v3.2.5 mentioned in the question.
Subclass AbpResultFilter:
using Abp.AspNetCore.Configuration;
using Abp.AspNetCore.Mvc.Results;
using Abp.AspNetCore.Mvc.Results.Wrapping;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
namespace AbpODataDemo.Web.Host.Filters
{
public class ODataResultFilter : AbpResultFilter
{
public ODataResultFilter(IAbpAspNetCoreConfiguration configuration, IAbpActionResultWrapperFactory actionResultWrapperFactory)
: base(configuration, actionResultWrapperFactory)
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
if (context.HttpContext.Request.Path.Value.StartsWith("/odata", StringComparison.InvariantCultureIgnoreCase))
{
return;
}
base.OnResultExecuting(context);
}
}
}
Replace AbpResultFilter with it in the Startup ConfigureServices method:
services.PostConfigure<MvcOptions>(options =>
{
var index = options.Filters.IndexOf(new ServiceFilterAttribute(typeof(AbpResultFilter)));
if (index != -1)
{
options.Filters.RemoveAt(index);
options.Filters.Insert(index, new ServiceFilterAttribute(typeof(ODataResultFilter)));
}
});
Reference: https://github.com/aspnetboilerplate/sample-odata/pull/16