I've been looking on how to get this done:
I have a contact list with an "add contact" button. My goal is to open the native add contact page (mainly for iOS) when I click on that button.
I know how to call the method from flutter, but I don't know how to do the calling add contact screen with swift.
Any suggestions on how to do that?
Thanks
Ok so I figured things out.
If someone needs it here's the code:
import UIKit
import Flutter
import Contacts
import ContactsUI
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
setUpMethodChannels(controller: controller)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func setUpMethodChannels(controller: FlutterViewController) {
let contactChannel = FlutterMethodChannel(name: "com.flutter.contact_list/addContact",
binaryMessenger: controller.binaryMessenger)
contactChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
guard call.method == "AddContact" else {
result(FlutterMethodNotImplemented)
return
}
let con = CNContact()
let mainVC = CNContactViewController(forNewContact: con)
mainVC.delegate = controller
let navigationController = UINavigationController(rootViewController: mainVC)
self.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
})
}
}
extension FlutterViewController : CNContactViewControllerDelegate, UINavigationControllerDelegate {
public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
viewController.dismiss(animated: true, completion: nil)
}
}
Related
Razor pay works properly when opening in a browser but when i convert the project into an android app, external page which is supposed to be opened to submit OTP doesn't show up. I understand i need an inAppBrowser to open the link but it is not controlled by me. Razorpay will open the link automatically when the user chooses card payment or internet banking. How to solve this? please help!
window-ref.service.ts
import {Injectable} from '#angular/core';
// declare var cordova:any;
export interface ICustomWindow extends Window {
__custom_global_stuff: string;
}
function getWindow(): any {
return window;
}
#Injectable({
providedIn: 'root'
})
export class WindowRefService {
get nativeWindow(): ICustomWindow {
return getWindow();
}
}
app.component.ts
constructor(private winRef: WindowRefService) {
this._window = this.winRef.nativeWindow;
}
private _window: ICustomWindow;
public rzp: any;
public options: any = {
key: 'KEY', // add razorpay key here
name: 'Bunto Couriers Pvt. Ltd.',
description: 'Delivery Fee',
amount: this.price*100, // razorpay takes amount in paisa
prefill: {
name: '',
email: '', // add your email id
},
image: 'https://isell-bunto.000webhostapp.com/assets/img/loader.png',
notes: {},
theme: {
color: '#3880FG'
},
handler: this.paymentHandler.bind(this),
modal: {
ondismiss: (() => {
this.zone.run(() => {
// add current page routing if payment fails
this.toastr.error("Payment Error!");
})
})
}
};
initPay(): void {
this.rzp = new this.winRef.nativeWindow['Razorpay'](this.options);
this.rzp.open();
}
paymentHandler(res: any) {
this.zone.run(() => {
// add API call here
console.log(res);
});
}
this is a pop up window which opens fine in both browser and in android app
this external page doesn't show up in the app
in the app, this is as far as it goes. external url doesn't open after this stage
I'm not sure how the Javascript library will work while we don't have the control over showing it using the InAppBrowser.
For this, I really suggest to use the native Cordova Razorpay plugin for more compatibility and stability: https://github.com/razorpay/razorpay-cordova
You can check their sample apps (Cordova and Ionic)
Add command: cordova plugin add https://github.com/razorpay/razorpay-cordova.git --save
var options = {
description: 'Credits towards consultation',
image: 'https://i.imgur.com/3g7nmJC.png',
currency: 'INR',
key: 'rzp_test_1DP5mmOlF5G5ag',
order_id: 'order_7HtFNLS98dSj8x',
amount: '5000',
name: 'foo',
theme: {
color: '#F37254'
}
}
var successCallback = function(success) {
alert('payment_id: ' + success.razorpay_payment_id)
var orderId = success.razorpay_order_id
var signature = success.razorpay_signature
}
var cancelCallback = function(error) {
alert(error.description + ' (Error '+error.code+')')
}
RazorpayCheckout.on('payment.success', successCallback)
RazorpayCheckout.on('payment.cancel', cancelCallback)
RazorpayCheckout.open(options)
From what I can tell most of the flutter guides out there can open from local storage, but nothing about file sharing. Anybody know how to do this. This is a guide in enabling it specifically for ios https://developer.apple.com/library/archive/qa/qa1587/_index.html.
I mean there is the https://pub.dartlang.org/packages/open_file extension, but opens from the file storage.
To clarify this question isn't about sharing a file from the app with another, but when sharing from an external app being prompted to open in this flutter app.
To do this in iOS you first define the Document Types and Imported UTIs in XCode as described in the guide you mentioned, and then in your AppDelegate.m file you do:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/* custom code begin */
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* myChannel = [FlutterMethodChannel
methodChannelWithName:#"my/file"
binaryMessenger:controller];
__block NSURL *initialURL = launchOptions[UIApplicationLaunchOptionsURLKey];
[myChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([#"checkintent" isEqualToString:call.method]) {
if (initialURL) {
[myChannel invokeMethod:#"loaded" arguments: [initialURL absoluteString]];
initialURL = nil;
result(#TRUE);
}
}
}];
/* custom code end */
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
On the Dart side:
class PlayTextPageState extends State<MyHomePage> with WidgetsBindingObserver{
static const platform = const MethodChannel('my/file');
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
platform.setMethodCallHandler((MethodCall call) async {
String method = call.method;
if (method == 'loaded') {
String path = call.arguments; // this is the path
...
}
});
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
...
} else if (state == AppLifecycleState.resumed) {
platform.invokeMethod("checkintent")
.then((result) {
// result == 1 if the app was opened with a file
});
}
}
}
Adding on to lastant's answer, you actually also need to override application(_:open:options:) in AppDelegate.swift for this to work.
So the idea is to use UIActivityViewController in iOS to open a file in Flutter (eg: restore a backup of the SQL DB into the Flutter app from an email).
First, you need to set the UTIs in the info.plist. Here's a good link to explain how that works. https://www.raywenderlich.com/813044-uiactivityviewcontroller-tutorial-sharing-data
Second, add the channel controller code in AppDelegate.swift.
We also need to override application(:open:options:) in AppDelegate.swift because iOS will invoke application(:open:options:) when an external application wants to send your application a file. Hence we store the filename as a variable inside AppDelegate.
Here we are have a 2-way channel controller between iOS and Flutter. Everytime the Flutter app enter the AppLifecycleState.resumed state, it will invoke "checkIntent" to check back into AppDelegate to see if the filename has been set. If a filename has been set, AppDelegate will invoke the "load" method in flutter whereby you do your required processing with the file.
Remember to delete the file given to you from AppDelegate after you are done with your processing. Otherwise, it will bloat up your application.
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
var initialURL: URL?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
/* channel controller code */
let controller: FlutterViewController = self.window?.rootViewController as! FlutterViewController
let myChannel = FlutterMethodChannel(name: "my/file", binaryMessenger: controller.binaryMessenger)
myChannel.setMethodCallHandler({(call: FlutterMethodCall, result: #escaping FlutterResult)-> Void in
if(call.method == "checkintent"){
if(self.initialURL != nil){
myChannel.invokeMethod("loaded", arguments: self.initialURL?.absoluteString );
self.initialURL = nil;
result(true);
} else{
print("initialURL is null");
}
} else{
print("no such channel method");
}
});
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("import URL: \(url)");
initialURL = url;
// should not remove here.. remove after i get into flutter...
// try? FileManager.default.removeItem(at: url);
return true;
}
}
So developing this IONIC 2 app, I discoverd that sending SMS to multiple recipients isnt so trivial at it should be.
After a long research I've found this post where people trys to deal with multiple SMS. But even using their specs it doesnt work properly.
They say we can use an array of strings representing multiple phone numbers. So far so good, except it works only for the first number.
If someone has now details on this functionality I would love to hear about it.
Thanks
import { SMS } from '#ionic-native/sms';
constructor( private sms: SMS ){
this.sendSMS();
}
sendSMS() {
var MultiNumber = [ '1234567890' , '9876543210' ];
this.sms.send(MultiNumber, 'hello all this is testing message');
}
try this it is working for me, Hope it is working for you too.
So after ages of research over internet I got this litle jam called cordova-plugin-sms ( dont confuse it with cordova-sms-plugin ).
As it says in their documentation they have a function sendSMS which reeeally sends messages to multiple recipients.
So my solution for integrating it in IONIC 2 is as follows :
ionic cordova plugin add cordova-plugin-sms
and my Ionic 2 class is :
import { Component } from '#angular/core';
import { NavController, ToastController } from 'ionic-angular';
import { Http, Response } from "#angular/http";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
declare let window: any;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(private toastCtrl: ToastController, public navCtrl: NavController, public http: Http ) { }
ionViewDidLoad() {
this.startWhatchSMS();
}
// Android ONLY
startWhatchSMS() {
if (window.SMS) {
window.SMS.startWatch(() => {
//console.log("startWatch");
}, error => {
//console.log(error);
//console.log("error startWatch");
});
}
document.addEventListener('onSMSArrive', this.smsArived);
}
// Android ONLY
smsArived = (result: any) => {
//console.log(result);
let sms = result.data;
// put your code here...
}
sendTextMessage( ) {
window.SMS.sendSMS([ '1234567890' , '0987654321' ], 'Text message for multiple recipients',
(result) => {
console.log(result); // should be 'OK' string
}, (error) => {
console.log(error);
});
}
}
The sendTextMessage() function is called from the template by clicking an button.
Well thats it ... for me is working and hope will work for you too.
Cheers
i am using React Native Share Method (https://facebook.github.io/react-native/docs/share.html) to share content on Android.
As per documentation, we can use it in following ways:
Share.share({
message: 'React Native | A framework for building native apps using React'
})
.then((result) => {
console.log(result.action) // returns sharedAction
})
.catch((error) => {
console.log(error)
})
So when we call Share method, we will get result in then and popup window is appeared which contains apps list with which we can share the message content.
Issue:
Popup window also contains a cancel button, so when user click on it, window get closed, but there is no method available/mentioned to capture it, in docs.
Is there any way to capture the cancel event, as i want to perform some action when user clicks on it.
Thanks in adv. :)
There is option in share dismissedAction but unfortunately this is IOS only . You can use react-native-share with prop "failOnCancel" to true and it will work on both android and ios
Sample Code
import React, { Component } from "react";
import { Button } from "react-native";
import Share from "react-native-share";
export default class ShareExample extends Component {
onShare = async () => {
const shareOptions = {
title: "Share file",
social: Share.Social.EMAIL,
failOnCancel: true
};
try {
const ShareResponse = await Share.open(shareOptions);
//setResult(JSON.stringify(ShareResponse, null, 2));
} catch (error) {
console.log("Error =>", error);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}
App Preview
This might help
import React, {Component} from 'react';
import {Share, Button} from 'react-native';
class ShareExample extends Component {
onShare = async () => {
try {
const result = await Share.share({
message:
'React Native | A framework for building native apps using React',
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
// shared with activity type of result.activityType
} else {
// shared
}
} else if (result.action === Share.dismissedAction) {
// dismissed
}
} catch (error) {
alert(error.message);
}
};
render() {
return <Button onPress={this.onShare} title="Share" />;
}
}
I have just read an article where it was explained how to get the token from iCloud in order to execute authentication with the iOS platform.
How can we combine Android and iOS with such an approach?
How Android will know that user has iOS account?
Which kind of credentials do I need to ask users to verify if they already have an account on the iOS platform?
Aleksey,
This code, confirms somebody is logged into iCloud by trying to access an iCloudKit container. The first method checks and sends a notification if it if it fails, the second method trigger by said failure shunts the user to the control panel so they can sign into iCloud.
func isAuthorized4Cloud() {
container = CKContainer(identifier: "iCloud.X")
publicDB = container.publicCloudDatabase
privateDB = container.privateCloudDatabase
var userID: CKRecordID!
container.fetchUserRecordID( completionHandler: { recordID, error in
if error == nil {
userID = recordID
print("files_setup userID \(userID.recordName)")
NotificationCenter.default.post(name: Notification.Name("cloudConnected"), object: nil, userInfo: nil)
} else {
NotificationCenter.default.post(name: Notification.Name("noCloud"), object: nil, userInfo: nil)
print("files_setup \(error?.localizedDescription)")
}
})
}
In your view Controller...
NotificationCenter.default.addObserver(self, selector: #selector(noCloud), name: Notification.Name("noCloud"), object: nil)
func noCloud(notification:NSNotification) {
DispatchQueue.main.async {
self.navigation.isHidden = true
let alert = UIAlertController(title: "Stop", message: "Sorry, you need to log into iCloud ...", preferredStyle: UIAlertControllerStyle.alert)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (_) -> Void in
guard let settingsUrl = URL(string: UIApplicationOpenSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("Settings opened: \(success)") // Prints true
})
}
}
alert.addAction(settingsAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
}