Is there an example how to program the functionality with the Phonegap Framework to share a URL to email, twitter and Facebook? For Example in Android this functionality is in 90% of the apps. In Iphone it is in any Apps. In the app of techcrunch for Iphone you can see it, when You open an article. Is it possible to create this with Phonegap too?
You can do this in Android with the following code for a plugin. I haven't published this anywhere else yet, but eventually I hope to add it as a plugin in the phonegap plugin repository for Android.
JAVASCRIPT:
var Share = function() {};
Share.prototype.show = function(content) {
return PhoneGap.exec(
function(args) {
console.log("phonegap share plugin - success!")
}, function(args) {
console.log("phonegap share plugin - failed")
}, 'Share', '', content);
};
PhoneGap.addConstructor(function() {
PhoneGap.addPlugin('share', new Share());
PluginManager.addService("Share","com.COMPANYNAME(CHANGEME).android.plugins.Share");
});
JAVA IN ANDROID:
package com.COMPANYNAME(CHANGEME).android.plugins;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Intent;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
public class Share extends Plugin {
private String callback;
#Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult mPlugin = null;
try {
mPlugin = activateSharing(args.getString(0), args.getString(1));
} catch (JSONException e) {
Log.e("JSON Exception", e.toString());
}
mPlugin.setKeepCallback(true);
this.callback = callbackId;
return mPlugin;
}
private PluginResult activateSharing(String title, String body) {
final Intent shareIntent = new Intent(
android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, title);
shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, body);
shareIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(Intent.createChooser(shareIntent, "Share"));
return new PluginResult(PluginResult.Status.OK);
}
}
Almost three years later: Here's a plugin that allows sharing on Android and iOS with the same API. https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin
It's available on PhoneGap Build as well!
Example
window.plugins.socialsharing.share('Google is awesome, WOOT!', 'Google facts', 'https://www.google.com/images/srpr/logo11w.png', 'http://www.google.com');
Login Facebook and post feed, login twitter and post status using plugin appInBrowser:
https://github.com/raulduran/facebook-twitter-cordova.git
Related
I am trying to test a little Ionic/Angular sample app on an iOS Emulator.
On the web, all the requests to firestore using angularfire work perfectly fine.
Somehow if I try to execute the same app on the emulator, it keeps loading for the response of the request (if it was a empty response it would say that no results could be retrieved).
What is going on? Do i need to set something specifically for the Emulator to work and perform requests to Firestore?
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { Capacitor } from '#capacitor/core';
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
import { getAuth } from 'firebase/auth';
const firebaseApp = initializeApp({
apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId:
process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.VUE_APP_FIREBASE_APP_ID,
});
function whichAuth() {
let auth
if (Capacitor.isNativePlatform()) {
auth = initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
})
} else {
auth = getAuth()
}
return auth
}
export const auth = whichAuth()
const db = getFirestore();
export const auth = whichAuth();
export { firebaseApp, db };
Then in your component, cal your method like this await signInAnonymously(auth);. Don't forget to import the auth we exported at the top.
[Edit: updated with instructions Firebase JS SDK version 9 (modular)]
This error occurs because Firebase Auth incorrectly detects its environment as a normal browser environment and tries to load remote Google APIs, which results in the error you see in the console:
TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')
Fortunately, Firebase Auth already has logic to handle running in Cordova/Ionic apps, you just need to tell it which platform it's on.
For Firebase JS SDK version 9 (modular)
Simply import the Cordova Firebase Auth implementation:
import { getAuth } from 'firebase/auth';
For Firebase JS SDK <9 or the compatibility modules (auth/compat)
In capacitor.config set server: { iosScheme: "ionic" }:
// capacitor.config.json
{
"server": {
"iosScheme": "ionic"
}
}
There's a check in the auth/compat library here which, when it sees the URL scheme "ionic://", uses its Ionic/Cordova loading logic, and otherwise falls back to normal browser logic which fails with the error above.
Recent versions of Capacitor changed the URL scheme to "capacitor://" which fails this test but you can override it in your capacitor.config file (see the config option iosScheme).
(See also #alistairheath's comment here).
Been struggling a lot with this issue too but I managed to fix it. For those who need help here's my code.
You can delete all Firebase related imports from app.module.ts since this solution only uses Firebase.
The packages rxfire and #angular/fire can be removed from your package.json. The only dependency I have is "firebase": "^9.6.1".
I used observables for the getObject and list functions since that's what I'm used to and I didn't want to rewrite my original code.
import { Injectable } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { environment } from '#environment';
import { initializeApp } from 'firebase/app';
import { Auth, getAuth, indexedDBLocalPersistence, initializeAuth, signInWithCustomToken } from 'firebase/auth';
import { Database, getDatabase, onValue, orderByChild, query, ref } from 'firebase/database';
import { Observable, Observer, from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FirebaseService {
private readonly database: Database;
private readonly auth: Auth;
constructor() {
const firebaseApp = initializeApp(environment.firebase);
if (Capacitor.isNativePlatform()) {
initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
});
}
this.database = getDatabase(firebaseApp);
this.auth = getAuth(firebaseApp);
}
connectFirebase(firebaseToken) {
return from(signInWithCustomToken(this.auth, firebaseToken));
}
disconnectFirebase() {
return from(this.auth.signOut());
}
getObject<T>(path: string): Observable<T> {
return new Observable((observer: Observer<T>) => {
const dbRef = ref(this.database, path);
const listener = onValue(dbRef, snapshot => {
const data = snapshot.val();
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
public list<T>(path: string, orderChildBy?: string): Observable<Array<T>> {
return new Observable<Array<T>>((observer: Observer<Array<T>>) => {
const dbRef = ref(this.database, path);
const dbReference = !orderChildBy ? dbRef : query(dbRef, orderByChild(orderChildBy));
const listener = onValue(dbReference, snapshot => {
const data = Object.values<T>(snapshot.val() || {});
console.log(path, data);
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
}
For those who can't see the error message thrown by firebase try the following command in your Safari console to see the error.
window.location.reload()
The real problem: firebase-js-sdk on mobile iOS assumes google API (gapi) exists on the window, even when it isn't used.
I found a work around: Mock window.gapi before using firebase auth login:
window['gapi'] = {
load: (name: string) => Promise.resolve(),
iframes: {
getContext: () => {
return {
iframe: {
contentWindow: {
postMessage: (message: any) => {
console.log("gapi iframe message:", message);
}
}
}
}
}
}
} as any;
Our Flutter App shows a number of locations using Google Maps, if available, or else using the local browser.
Although we had already previously uploaded a binary code for iOS which was accepted by Apple and successfully published in the App Store, now that we have added some more locations and thus attempted to publish a new version, Apple has rejected our binary, stating that it is mandatory to use "Apple Maps" instead of anything that starts with a "G", like Google...
The rejection message reads as follows:
Your app's location feature is not integrated with the built-in mapping functionality, which limits users to a third-party maps app.
Next Steps
To resolve this issue, please revise your app to give users the option to launch the native Apple Maps app.
I have found that there exists some documentation about a Javascript library named MapKit JS, which serves precisely the purpose of interacting with Apple Maps: https://developer.apple.com/maps/mapkitjs/
<script src="https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js"></script>
<script>
mapkit.init({
authorizationCallback: function(done) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/services/jwt");
xhr.addEventListener("load", function() {
done(this.responseText);
});
xhr.send();
}
});
var Cupertino = new mapkit.CoordinateRegion(
new mapkit.Coordinate(37.3316850890998, -122.030067374026),
new mapkit.CoordinateSpan(0.167647972, 0.354985255)
);
var map = new mapkit.Map("map");
map.region = Cupertino;
</script>
Nevertheless, I could really use some help no how to connect with this MapKit JS using DART, instead of JAVA, for our Flutter application.
Thank you immensely for your kind help!
Daniel
Firstly, install the url_launcher plugin
Secondly, add the below code in Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
Thirdly:
var urlAppleMaps = 'https://maps.apple.com/?q=$lat,$lng';
if (await canLaunch(urlAppleMaps)) {
await launch(urlAppleMaps);
} else {
throw 'Could not launch $url';
}
We can use it like this:
_launchMap(BuildContext context, lat, lng) async {
var url = '';
var urlAppleMaps = '';
if (Platform.isAndroid) {
url = "https://www.google.com/maps/search/?api=1&query=${lat},${lng}";
} else {
urlAppleMaps = 'https://maps.apple.com/?q=$lat,$lng';
url = "comgooglemaps://?saddr=&daddr=$lat,$lng&directionsmode=driving";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
if (await canLaunch(url)) {
await launch(url);
} else if (await canLaunch(urlAppleMaps)) {
await launch(urlAppleMaps);
} else {
throw 'Could not launch $url';
}
}
You could try to use a Map Launcher plugin to launch apple/google maps depending on a platform
import 'dart:io';
import 'package:map_launcher/map_launcher.dart';
if (Platform.isIOS) {
await MapLauncher.launchMap(
mapType: MapType.apple,
coords: Coords(31.233568, 121.505504),
title: "Shanghai Tower",
description: "Asia's tallest building",
);
} else {
await MapLauncher.launchMap(
mapType: MapType.google,
coords: Coords(31.233568, 121.505504),
title: "Shanghai Tower",
description: "Asia's tallest building",
);
}
Just an idea but maybe you can try to use the url_launch plugin to launch a url following the schemata given in the apple maps url schemata given here: https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html#//apple_ref/doc/uid/TP40007899-CH5-SW1
A simple solution I have found is to replace the google maps URL with a maps.apple.com equivalent, using the same url_launcher function.
For example, the original function for Google Maps:
void _mapaBethania() async {
const url = "https://www.google.com/maps/place/Panamanian+Institute+for+Special+Training/#9.0067418,-79.5300556,17z/data=!3m1!4b1!4m5!3m4!1s0x8faca84549395297:0x9c54b1fdb96ac590!8m2!3d9.0067365!4d-79.5278669";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Lo sentimos, no es posible abrir: $url';
}
}
Has become:
void _mapaBethania() async {
const url = "https://maps.apple.com/?q=IPHE&ll=9.0067418,-79.5300556&z=16";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Lo sentimos, no es posible abrir: $url';
}
}
Nevertheless, although this change does fulfill Apple's enforced obligation to use their own software instead of the competition's, the user experience with Apple Maps is very poor, because once the Map App is shown, it becomes a bit confusing and difficult to return back to the original App.
Therefore, I am planning to write code that enables both options, Apple Maps in order to comply with Apple enforcement, and also Google Maps in order to provide a better user experience, despite Apple.
Anyway, the latter is just a personal opinion; the true fact is that replacing the URL for a maps.apple.com equivalent using the same launch_url function, does seem acceptable to comply with Apple requirements.
I am using the url_launcher plugin for call, but the dialer is not showing the # character:
String url = 'tel:*123#';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
You need to use URL encoding for special character in a URL.
So # equals %23
This will work launch('tel:\*123\%23');
Other Way is to encode the number typed by user and pass it through Uri.encodeFull(urlString) or Uri.encodeComponent(urlString)
Like this.
launch("tel:" + Uri.encodeComponent('*123#'));
Disclaimer: plugin author here.
Do you want the phone call user interface to open or would you rather make the request silently? If you prefer to do it without popping the phone call UI, Android introduced in API level 26 the method sendUssdRequest.
I made a Flutter plugin called ussd_service to be able to easily access it from dart in a Flutter application. It can be used in the following manner:
import 'package:ussd_service/ussd_service.dart';
makeMyRequest() async {
int subscriptionId = 1; // sim card subscription Id
String code = "*21#"; // ussd code payload
try {
String ussdSuccessMessage = await UssdService.makeRequest(subscriptionId, code);
print("succes! message: $ussdSuccessMessage");
} on PlatformException catch (e) {
print("error! code: ${e.code} - message: ${e.message}");
}
};
makeMyRequest();
Hope this helps! Let me know on the Github repo's issues if you have any issue with it.
Now I'm developing IOS App and I want to check if the Viber App is existing in the Phone or not.
I already use Viber:\\ URL scheme and https://ionicframework.com/docs/native/app-availability/ to check the app but the app is not detecting
There's any possible implementation?
Thanks
Try this;
import { AppAvailability } from '#ionic-native/app-availability';
import { Platform } from 'ionic-angular';
constructor(private appAvailability: AppAvailability, private platform: Platform) { }
let app;
if (this.platform.is('ios')) {
app = 'Viber://';
} else if (this.platform.is('android')) {
app = 'com.viber.voip ';
}
this.appAvailability.check(app)
.then(
(yes: boolean) => console.log(app + ' is available'),
(no: boolean) => console.log(app + ' is NOT available')
);
the URLScheme must be declared publicly in Info.plist file first
visit this UseYourLoaf tutorial for more details
i fixed it by check if these is exist
viber = "https://itunes.apple.com/ph/app/viber-messenger-chats-calls/id382617920?mt=8"
and
use app-availability
this.appAvailability.check(viber)
I use Ionic 3 on one of my projects with an authentication system. I use native storage when the user wants to connect. It works on Android but on iOS, it redirects me to the login screen even using platform.ready (). I saw that several people were a similar problem but no answer, so I wanted to know if someone was facing the same problem and if he found a solution. Here is my code:
this.plt.ready().then(() => {
this.nativeStorage.setItem('userStorage', { stayConnected: (typeof this.stayConnected == "undefined" || this.stayConnected == false ? '' : 'stayConnected'), userId: (result as any).id, userLogin: (result as any).login })
.then(
() => {
this.loader.dismiss();
this.navCtrl.setRoot(HomePage);
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
)
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
});
thank you for your answers.
I would put 2 function storeUser() and getUser() into the same provider UserService like belows
Then add UserService to the constructor of any pages required.
It works for both IOS, Android and web
import {Storage} from '#ionic/storage';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class UserService {
constructor(private storage: Storage){}
public storeUser(userData): void {
this.storage.set('userData', userData);
}
public getUser(): Observable<any>
return Observable.fromPromise(this.storage.get('userData').then((val) => {
return !!val;
}));
}
Yes, I have faced issues while using ionic native storage plugins. So I turned to javascript Window localStorage Property and it's working completely fine.
Syntax for SAVING data to localStorage:
localStorage.setItem("key", "success");
Syntax for READING data from localStorage:
var lastname = localStorage.getItem("key");
Syntax for REMOVING data from localStorage:
localStorage.removeItem("key");
and now you can write your code with this property, like this -
if (lastname == "success"){
this.navCtrl.setRoot(HomePage);
} else{
alert("Not matched")
}
You are inside a platform.ready(), which is good. The storage package also has a .ready() that you may want to leverage, which specifically checks if storage itself is ready. If this runs at startup there is a decent chance storage is initializing.
Also, this starts to get into some crazy promise chaining messiness. I'd suggest diving into async/await. Something like the (untested) code below.
try{
await this.plt.ready();
await this.nativeStorage.ready();
let stayConnectedValue = (this.stayConnected) ? 'stayConnected' : '';
await this.nativeStorage.setItem('userStorage', { stayConnected: stayConnectedValue , userId: (result as any).id, userLogin: (result as any).login });
this.navCtrl.setRoot(HomePage);
}
catch(err){
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
finally{
this.loader.dismiss();
}