Using Linking.openURL('mailto:') with expo-linking opens the mail app with a 'compose mail' screen, how can i open the mail app directly on the inbox tab ?
Inspired by this stack overflow post, you can use the message:// url scheme to achieve this on iOS. If no content is provided, it defaults to the email inbox on iOS.
For android, things are a little trickier. You'll need expo-intent-launcher and a few extra params to complete the hand-off. A complete solution may look something like this:
import { startActivityAsync, ActivityAction } from 'expo-intent-launcher';
[...]
if (Platform.OS === "android") {
const activityAction = "android.intent.action.MAIN";
const intentParams: IntentLauncher.IntentLauncherParams = {
category: "android.intent.category.APP_EMAIL",
};
IntentLauncher.startActivityAsync(activityAction, intentParams);
}
else if (Platform.OS === "ios") {
Linking.openURL('message://');
}
Be sure to test on real devices if possible since the iPhone simulator doesn't have a mail client installed.
List of URL schemes on wikipedia
Update: If you don't mind an extra dependency, take a look at react-native-email-link which has the added benefit of allowing users to select from any installed email client. Neat!
Related
If the user has enlarged the font in the phone settings, how can I deactivate this setting?
I know one way for React Native. I need a web code like this:
//Here is React-Native example
if (Text.defaultProps) {
Text.defaultProps.allowFontScaling = false
} else {
Text.defaultProps = {}
Text.defaultProps.allowFontScaling = false
}
i couldn't find anything when I searched for a draft similar to this.
any idea?
thanks for your time.
I am trying to get the user to 'install' the shortcut icon to a webpage and use it as an icon to the PWA.
I am wondering if any of you had a chance to somehow discover if the user installed the shortcut on home screeen?
This is an interesting case, because when developing on Android or such one has access to such information, on the other hand I don't recall browser giving that information away.
The beforeinstallprompt will only fire if the user has NOT installed the PWA
First: Use that to check if installed
window.addEventListener('beforeinstallprompt', (e) => {
// If you get inside here, the PWA is not installed
});
Example code here
https://developers.google.com/web/fundamentals/app-install-banners
Second: If you are showing your own deferred prompt like the examples at the link above, you can listen to know if they close that without adding to home screen (A2HS)
// Wait for the user to respond to the prompt
this.deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
// User said yes to your A2HS
} else {
this.deferredPromptRejected = true;
}
});
Third: You can check if your PWA is running as a standalone
function isRunningStandalone() {
return (window.matchMedia('(display-mode: standalone)').matches);
}
I made a Google App script to answer automatically to my emails (a kind of clever email robot assistant). Nevertheless, I would like to check each answer made by the robot before sending.
So I would like to have a window over Gmail showing the user email and the robot answer, and two buttons "send" "skip". In this way, I could check the answer prepared by the robot and either send it or skip it (or potentially edit it).
How to display a window with text, editText and buttons over GMail from Google App Script ?
Thanks !
Regards.
You have to check Gmail Add-on : https://developers.google.com/gsuite/add-ons/gmail
For a first start you can check the codelab from Google, it will give you the code to set a first add-on in 5 minutes then you can adapt it to your needs : https://codelabs.developers.google.com/codelabs/apps-script-intro/
Stéphane
An easy solution would be to have the robot save the e-mail as 'draft'. That way, you can easily check the emails before manually sending them.
If you are still interested in creating the gmail add-on (which could display the original email, response, and buttons for sending or editing), you may be interested in building card-based interfaces. These will appear to the right of your Gmail web client, and will look like the following:
The code used to display such interface (with two buttons, one that automatically sends the email and another one that opens the editor on it) is the following:
function buildAddOn(e) {
// Activate temporary Gmail add-on scopes.
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
return buildDraftCard(getNextDraft());
}
function buildDraftCard(draft) {
if (!draft) {
var header = CardService.newCardHeader().setTitle('Nothing to see here');
return CardService.newCardBuilder().setHeader(header).build();
} else {
var header = CardService.newCardHeader()
.setTitle(draft.getMessage().getSubject());
var section = CardService.newCardSection();
var messageViewer = CardService.newTextParagraph()
.setText(draft.getMessage().getBody());
var sendButton = CardService.newTextButton()
.setText('Send')
.setOnClickAction(CardService.newAction()
.setFunctionName('sendMessage')
.setParameters({'draftId': draft.getId()})
);
var editButton = CardService.newTextButton()
.setText('Edit')
.setOnClickAction(CardService.newAction()
.setFunctionName('editMessage')
.setParameters({'draftId': draft.getId()})
);
var buttonSet = CardService.newButtonSet()
.addButton(sendButton)
.addButton(editButton);
section.addWidget(messageViewer);
section.addWidget(buttonSet)
return CardService.newCardBuilder()
.setHeader(header)
.addSection(section)
.build();
}
}
function sendMessage(e) {
GmailApp.getDraft(e.parameters.draftId).send();
return CardService.newActionResponseBuilder().setNavigation(
CardService.newNavigation()
.popToRoot()
.updateCard(buildDraftCard(getNextDraft()))
).build();
}
function editMessage(e) {
var messageId = GmailApp.getDraft(e.parameters.draftId).getMessageId();
var link = "https://mail.google.com/mail/#all/" + messageId;
return CardService.newActionResponseBuilder().setOpenLink(
CardService.newOpenLink()
.setUrl(link)
.setOnClose(CardService.OnClose.RELOAD_ADD_ON)
).build();
}
function getNextDraft() {
return GmailApp.getDrafts().pop()
}
And the appsscript.json configuration is the following:
{
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://mail.google.com/"
],
"gmail": {
"name": "Gmail Add-on Draft Autoresponse UI",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/label_googblue_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "buildAddOn"
}],
"openLinkUrlPrefixes": [
"https://mail.google.com/"
],
"primaryColor": "#4285F4",
"secondaryColor": "#4285F4"
}
}
Bear in mind however, that these interfaces at the moment still have some limitations. They can only be displayed whilst having a message open, and the HTML formatting of the email may look a bit off. You can find more information on how to test & run the code above by following this link.
I have been fiddling with the new web bluetooth functionality. I have one of these estimote beacons: http://developer.estimote.com/
I know the uuid for my beacon. Here is the code I am using(it is an angular app, hence $scope, $window):
$scope.runBT = runBT;
function runBT() {
let mobile = getMobileOperatingSystem();
if (mobile === 'Android' || mobile === 'iOS') {
$window.navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['b9407f30-f5f8-466e-aff9-25556b57fe6d']
})
.then(device => {
console.log('FOUND DEVICE: ', device);
device.watchAdvertisements();
device.addEventListener('advertisementreceived', interpretIBeacon);
})
.catch(error => { console.log(error); });
}
}
function interpretIBeacon(event) {
var rssi = event.rssi;
var appleData = event.manufacturerData.get(0x004C);
if (appleData.byteLength != 23 ||
appleData.getUint16(0, false) !== 0x0215) {
console.log({isBeacon: false});
}
var uuidArray = new Uint8Array(appleData.buffer, 2, 16);
var major = appleData.getUint16(18, false);
var minor = appleData.getUint16(20, false);
var txPowerAt1m = -appleData.getInt8(22);
console.log({
isBeacon: true,
uuidArray,
major,
minor,
pathLossVs1m: txPowerAt1m - rssi});
}
Sadly the watchAdvertisements method is not implemented yet. You may want to check the Implementation Status page at https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md to know when this method will be supported in Chrome and other browsers.
It's unclear what the problem is, but here are a few tips:
Understand that the web bluetooth APIs are a proposed set of standards under active development, and support is limited to certain builds of Google Chrome, and as a shim for the Noble.js bluetooth central module. If you are using Angular, you need to use the latter shim to make it work, which perhaps you already are. You can read more here: https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web
If you are getting as far as the interpretIBeacon function, then it's just a matter of parsing the bytes out, which you seem well on your way to doing. You can see more about the byte layout of the beacon in my answer here: https://stackoverflow.com/a/19040616/1461050
You don't want to filter for the beacon UUID as a service, so you need to remove optionalServices: ['b9407f30-f5f8-466e-aff9-25556b57fe6d']. A beacon ProximityUUID is not the same as a GATT ServiceUUID even though they superficially have the same format. A beacon bluetooth advertisement of the type you are looking for a manufacturer advertisement and not a *GATT service** advertisement. The two advertisement types are different, but the APIs shown above should return results from both types.
I am new to dart and I have been trying to figure out how to use the googleapis library to update a calendars events, then display the calendar/events on a webpage.
So far I have this code that I was hoping would just change the #text id's text to a list of events from the selected calendars ID:
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_io.dart';
final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
"private_key_id": "myprivatekeyid",
"private_key": "myprivatekey",
"client_email": "myclientemail",
"client_id": "myclientid",
"type": "service_account"
}
''');
const _SCOPES = const [CalendarApi.CalendarScope];
void main() {
clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
var calendar = new CalendarApi(http_client);
String adminPanelCalendarId = 'mycalendarID';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
querySelector("#text2").text = showEvents.toString();
});
});
}
But nothing displays on the webpage. I think I am misunderstanding how to use client-side and server-side code in dart... Do I break up the file into multiple files? How would I go about updating a calendar and displaying it on a web page with dart?
I'm familiar with the browser package, but this is the first time I have written anything with server-side libraries(googleapis uses dart:io so I assume it's server-side? I cannot run the code in dartium).
If anybody could point me in the right direction, or provide an example as to how this could be accomplished, I would really appreciate it!
What you might be looking for is the hybrid flow. This produces two items
access credentials (for client side API access)
authorization code (for server side API access using the user credentials)
From the documentation:
Use case: A web application might want to get consent for accessing data on behalf of a user. The client part is a dynamic webapp which wants to open a popup which asks the user for consent. The webapp might want to use the credentials to make API calls, but the server may want to have offline access to user data as well.
The page Google+ Sign-In for server-side apps describes how this flow works.
Using the following code you can display the events of a calendar associated with the logged account. In this example i used createImplicitBrowserFlow ( see the documentation at https://pub.dartlang.org/packages/googleapis_auth ) with id and key from Google Cloud Console Project.
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_browser.dart' as auth;
var id = new auth.ClientId("<yourID>", "<yourKey>");
var scopes = [CalendarApi.CalendarScope];
void main() {
auth.createImplicitBrowserFlow(id, scopes).then((auth.BrowserOAuth2Flow flow) {
flow.clientViaUserConsent().then((auth.AuthClient client) {
var calendar = new CalendarApi(client);
String adminPanelCalendarId = 'primary';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
showEvents.items.forEach((Event ev) { print(ev.summary); });
querySelector("#text2").text = showEvents.toString();
});
client.close();
flow.close();
});
});
}