-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
1,711 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
/** | ||
* @OnlyCurrentDoc | ||
* | ||
* The above comment directs Apps Script to limit the scope of file | ||
* access for this add-on. It specifies that this add-on will only | ||
* attempt to read or modify the files in which the add-on is used, | ||
* and not all of the user's files. The authorization request message | ||
* presented to users will reflect this limited scope. | ||
*/ | ||
|
||
/** | ||
* Creates a menu entry in the Google Docs UI when the document is opened. | ||
* This method is only used by the regular add-on, and is never called by | ||
* the mobile add-on version. | ||
* | ||
* @param {object} e The event parameter for a simple onOpen trigger. To | ||
* determine which authorization mode (ScriptApp.AuthMode) the trigger is | ||
* running in, inspect e.authMode. | ||
*/ | ||
function onOpen(e) { | ||
DocumentApp.getUi().createAddonMenu() | ||
.addItem('Start', 'showSidebar') | ||
.addToUi(); | ||
} | ||
|
||
/** | ||
* Runs when the add-on is installed. | ||
* This method is only used by the regular add-on, and is never called by | ||
* the mobile add-on version. | ||
* | ||
* @param {object} e The event parameter for a simple onInstall trigger. To | ||
* determine which authorization mode (ScriptApp.AuthMode) the trigger is | ||
* running in, inspect e.authMode. (In practice, onInstall triggers always | ||
* run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or | ||
* AuthMode.NONE.) | ||
*/ | ||
function onInstall(e) { | ||
onOpen(e); | ||
} | ||
|
||
/** | ||
* Opens a sidebar in the document containing the add-on's user interface. | ||
* This method is only used by the regular add-on, and is never called by | ||
* the mobile add-on version. | ||
*/ | ||
function showSidebar() { | ||
var ui = HtmlService.createHtmlOutputFromFile('Sidebar') | ||
.setTitle('Translate'); | ||
DocumentApp.getUi().showSidebar(ui); | ||
} | ||
|
||
/** | ||
* Gets the text the user has selected. If there is no selection, | ||
* this function displays an error message. | ||
* | ||
* @return {Array.<string>} The selected text. | ||
*/ | ||
function getSelectedText() { | ||
var selection = DocumentApp.getActiveDocument().getSelection(); | ||
if (selection) { | ||
var text = []; | ||
var elements = selection.getSelectedElements(); | ||
for (var i = 0; i < elements.length; i++) { | ||
if (elements[i].isPartial()) { | ||
var element = elements[i].getElement().asText(); | ||
var startIndex = elements[i].getStartOffset(); | ||
var endIndex = elements[i].getEndOffsetInclusive(); | ||
|
||
text.push(element.getText().substring(startIndex, endIndex + 1)); | ||
} else { | ||
var element = elements[i].getElement(); | ||
// Only translate elements that can be edited as text; skip images and | ||
// other non-text elements. | ||
if (element.editAsText) { | ||
var elementText = element.asText().getText(); | ||
// This check is necessary to exclude images, which return a blank | ||
// text element. | ||
if (elementText != '') { | ||
text.push(elementText); | ||
} | ||
} | ||
} | ||
} | ||
if (text.length == 0) { | ||
throw 'Please select some text.'; | ||
} | ||
return text; | ||
} else { | ||
throw 'Please select some text.'; | ||
} | ||
} | ||
|
||
/** | ||
* Gets the stored user preferences for the origin and destination languages, | ||
* if they exist. | ||
* This method is only used by the regular add-on, and is never called by | ||
* the mobile add-on version. | ||
* | ||
* @return {Object} The user's origin and destination language preferences, if | ||
* they exist. | ||
*/ | ||
function getPreferences() { | ||
var userProperties = PropertiesService.getUserProperties(); | ||
var languagePrefs = { | ||
originLang: userProperties.getProperty('originLang'), | ||
destLang: userProperties.getProperty('destLang') | ||
}; | ||
return languagePrefs; | ||
} | ||
|
||
/** | ||
* Gets the user-selected text and translates it from the origin language to the | ||
* destination language. The languages are notated by their two-letter short | ||
* form. For example, English is 'en', and Spanish is 'es'. The origin language | ||
* may be specified as an empty string to indicate that Google Translate should | ||
* auto-detect the language. | ||
* | ||
* @param {string} origin The two-letter short form for the origin language. | ||
* @param {string} dest The two-letter short form for the destination language. | ||
* @param {boolean} savePrefs Whether to save the origin and destination | ||
* language preferences. | ||
* @return {Object} Object containing the original text and the result of the | ||
* translation. | ||
*/ | ||
function getTextAndTranslation(origin, dest, savePrefs) { | ||
var result = {}; | ||
var text = getSelectedText(); | ||
result['text'] = text.join('\n'); | ||
|
||
if (savePrefs == true) { | ||
var userProperties = PropertiesService.getUserProperties(); | ||
userProperties.setProperty('originLang', origin); | ||
userProperties.setProperty('destLang', dest); | ||
} | ||
|
||
result['translation'] = translateText(result['text'], origin, dest); | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* Replaces the text of the current selection with the provided text, or | ||
* inserts text at the current cursor location. (There will always be either | ||
* a selection or a cursor.) If multiple elements are selected, only inserts the | ||
* translated text in the first element that can contain text and removes the | ||
* other elements. | ||
* | ||
* @param {string} newText The text with which to replace the current selection. | ||
*/ | ||
function insertText(newText) { | ||
var selection = DocumentApp.getActiveDocument().getSelection(); | ||
if (selection) { | ||
var replaced = false; | ||
var elements = selection.getSelectedElements(); | ||
if (elements.length == 1 && | ||
elements[0].getElement().getType() == | ||
DocumentApp.ElementType.INLINE_IMAGE) { | ||
throw "Can't insert text into an image."; | ||
} | ||
for (var i = 0; i < elements.length; i++) { | ||
if (elements[i].isPartial()) { | ||
var element = elements[i].getElement().asText(); | ||
var startIndex = elements[i].getStartOffset(); | ||
var endIndex = elements[i].getEndOffsetInclusive(); | ||
|
||
var remainingText = element.getText().substring(endIndex + 1); | ||
element.deleteText(startIndex, endIndex); | ||
if (!replaced) { | ||
element.insertText(startIndex, newText); | ||
replaced = true; | ||
} else { | ||
// This block handles a selection that ends with a partial element. We | ||
// want to copy this partial text to the previous element so we don't | ||
// have a line-break before the last partial. | ||
var parent = element.getParent(); | ||
parent.getPreviousSibling().asText().appendText(remainingText); | ||
// We cannot remove the last paragraph of a doc. If this is the case, | ||
// just remove the text within the last paragraph instead. | ||
if (parent.getNextSibling()) { | ||
parent.removeFromParent(); | ||
} else { | ||
element.removeFromParent(); | ||
} | ||
} | ||
} else { | ||
var element = elements[i].getElement(); | ||
if (!replaced && element.editAsText) { | ||
// Only translate elements that can be edited as text, removing other | ||
// elements. | ||
element.clear(); | ||
element.asText().setText(newText); | ||
replaced = true; | ||
} else { | ||
// We cannot remove the last paragraph of a doc. If this is the case, | ||
// just clear the element. | ||
if (element.getNextSibling()) { | ||
element.removeFromParent(); | ||
} else { | ||
element.clear(); | ||
} | ||
} | ||
} | ||
} | ||
} else { | ||
var cursor = DocumentApp.getActiveDocument().getCursor(); | ||
var surroundingText = cursor.getSurroundingText().getText(); | ||
var surroundingTextOffset = cursor.getSurroundingTextOffset(); | ||
|
||
// If the cursor follows or preceds a non-space character, insert a space | ||
// between the character and the translation. Otherwise, just insert the | ||
// translation. | ||
if (surroundingTextOffset > 0) { | ||
if (surroundingText.charAt(surroundingTextOffset - 1) != ' ') { | ||
newText = ' ' + newText; | ||
} | ||
} | ||
if (surroundingTextOffset < surroundingText.length) { | ||
if (surroundingText.charAt(surroundingTextOffset) != ' ') { | ||
newText += ' '; | ||
} | ||
} | ||
cursor.insertText(newText); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Given text, translate it from the origin language to the destination | ||
* language. The languages are notated by their two-letter short form. For | ||
* example, English is 'en', and Spanish is 'es'. The origin language may be | ||
* specified as an empty string to indicate that Google Translate should | ||
* auto-detect the language. | ||
* | ||
* @param {string} text text to translate. | ||
* @param {string} origin The two-letter short form for the origin language. | ||
* @param {string} dest The two-letter short form for the destination language. | ||
* @return {string} The result of the translation, or the original text if | ||
* origin and dest languages are the same. | ||
*/ | ||
function translateText(text, origin, dest) { | ||
if (origin === dest) { | ||
return text; | ||
} | ||
return LanguageApp.translate(text, origin, dest); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
Mobile Doc Translate Add-on | ||
=========================== | ||
|
||
A sample Google Apps Script mobile add-on for Google Docs. This add-on is | ||
essentially a mobile version of the Docs | ||
[Translate Add-on Quickstart](https://developers.google.com/apps-script/quickstart/docs). | ||
|
||
Introduction | ||
------------ | ||
|
||
Google Apps Script now allows developers to construct Mobile Add-ons -- Android | ||
applications which extend and support Google Docs and Sheets. | ||
|
||
This sample shows how to construct a mobile add-on called | ||
**Mobile Doc Translate**. This add-on allows users to select text in a | ||
Google Doc on their mobile device and see a translation of that text in one | ||
of several languages. The user can then edit the translation as needed and | ||
replace the original selected text in the Doc with the translation. | ||
|
||
|
||
Getting Started | ||
--------------- | ||
|
||
The add-on will need to call an Apps Script project to get Doc text, make | ||
translations, and insert text into the Doc. Users can access this add-on from | ||
the Google Docs Android app by highlighting text and selecting the add-on in the | ||
text context menu. | ||
|
||
The Apps Script code file for this project is `Code.gs`. This is the same code | ||
used in the [Translate Add-on Quickstart](https://developers.google.com/apps-script/quickstart/docs), | ||
but does not include the HTML code that defines the quickstart's sidebar. | ||
|
||
The mobile add-on will make use of the | ||
[Apps Script Execution API](https://developers.google.com/apps-script/guides/rest/) | ||
to call the `Code.gs` functions. The | ||
[Execution API quickstart for Android](https://developers.google.com/apps-script/guides/rest/quickstart/android) | ||
describes how to call Apps Script functions from Android applications. | ||
|
||
To build this sample: | ||
|
||
1. The `app/` folder in this repository contains all the required Android files | ||
for this add-on. These can be manually copied or imported into a new Android | ||
Studio project. | ||
1. Create a new Apps Script project. | ||
1. Replace the code in the new project's `Code.gs` file with the code from this | ||
repo. | ||
1. Save the project. | ||
1. In the code editor, select **Publish > Deploy as API** executable. | ||
1. In the dialog that opens, leave the **Version** as "New" and enter | ||
"Target-v1" into the text box. Click **Deploy**. | ||
1. Follow the | ||
[Keytool SHA1 Fingerprint](https://developers.google.com/apps-script/guides/rest/quickstart/android#step_1_acquire_a_sha1_fingerprint) | ||
instructions to acquire a SHA1 fingerprint for your project. | ||
1. Using that SHA code, follow the | ||
[Turn on the Execution API](https://developers.google.com/apps-script/guides/rest/quickstart/android#step_2_turn_on_the_api_name) | ||
instructions to enable the API for your script project and create OAuth | ||
credentials. Be sure to match the same package name used in your Android | ||
code. | ||
1. Edit the `MainActivity.java` file so that the `SCRIPT_ID` constant is set to | ||
your Apps Script project ID (in the script editor, select | ||
**File > Project properties**, and use the **Project key**). | ||
|
||
These steps should allow you to build the Android app and have it successfully | ||
call the Apps Script code. You can test it by: | ||
|
||
1. Install the app on a test Android device. | ||
1. Set the app as the debug app on the device by running this | ||
[ADB](https://developer.android.com/studio/command-line/adb.html) | ||
command: | ||
`$ adb shell am set-debug-app --persistent <YOUR_PACKAGE_NAME>` | ||
1. Open a docucment using the Google Docs app on the device. | ||
1. Highlight some text in the doc and select the three-dot icon to open the | ||
context menu, and then select **Mobile Doc Translate**. | ||
|
||
Learn more | ||
---------- | ||
|
||
To continue learning about mobile add-ons for Google Docs and Sheets, | ||
take a look at the following resources: | ||
|
||
* [Mobile Add-ons](https://developers.google.com/apps-script/add-ons/mobile) | ||
* [Apps Script Execution API](https://developers.google.com/apps-script/guides/) | ||
|
||
Support | ||
------- | ||
|
||
For general Apps Script support, check the following: | ||
|
||
- Stack Overflow Tag: [google-apps-script](http://stackoverflow.com/questions/tagged/google-apps-script) | ||
- Issue Tracker: [google-apps-script-issues](https://code.google.com/p/google-apps-script-issues/issues/list) | ||
|
||
If you've found an error in this sample, please file an issue: | ||
https://github.com/googlesamples/apps-script-mobile-addons | ||
|
||
Patches are encouraged, and may be submitted by forking this project and | ||
submitting a pull request through GitHub. | ||
|
||
License | ||
------- | ||
|
||
Copyright 2016 Google, Inc. | ||
|
||
Licensed to the Apache Software Foundation (ASF) under one | ||
or more contributor license agreements. See the NOTICE file | ||
distributed with this work for additional information | ||
regarding copyright ownership. The ASF licenses this file | ||
to you under the Apache License, Version 2.0 (the | ||
"License"); you may not use this file except in compliance | ||
with the License. You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, | ||
software distributed under the License is distributed on an | ||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, either express or implied. See the License for the | ||
specific language governing permissions and limitations | ||
under the License. |
Oops, something went wrong.