├── mobile-translate
├── app
│ ├── src
│ │ └── main
│ │ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── layout
│ │ │ │ ├── input_text_pane.xml
│ │ │ │ ├── output_text_pane.xml
│ │ │ │ ├── launch_default.xml
│ │ │ │ ├── buttons.xml
│ │ │ │ └── activity_main.xml
│ │ │ └── layout-land
│ │ │ │ └── activity_main.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── google
│ │ │ │ └── samples
│ │ │ │ └── mobiledoctranslate
│ │ │ │ ├── DefaultLaunchActivity.java
│ │ │ │ └── MainActivity.java
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── README.md
└── Code.gs
├── packaging.yaml
├── CONTRIB.md
├── README.md
└── LICENSE
/mobile-translate/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ECECEC
4 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googlearchive/apps-script-mobile-addons/HEAD/mobile-translate/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googlearchive/apps-script-mobile-addons/HEAD/mobile-translate/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googlearchive/apps-script-mobile-addons/HEAD/mobile-translate/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googlearchive/apps-script-mobile-addons/HEAD/mobile-translate/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/googlearchive/apps-script-mobile-addons/HEAD/mobile-translate/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/packaging.yaml:
--------------------------------------------------------------------------------
1 | # GOOGLE SAMPLE PACKAGING DATA
2 | #
3 | # This file is used by Google as part of our samples packaging process.
4 | # End users may safely ignore this file. It has no relevance to other systems.
5 | ---
6 | status: DONE
7 | technologies: [Google Apps]
8 | categories: [Web Services]
9 | languages: [Android, Google Apps Script]
10 | solutions: [Web Developer]
11 | github: https://github.com/googlesamples/apps-script-mobile-addons
12 | difficulty: INTERMEDIATE
13 | api_refs: []
14 | dev_console_apis: []
15 | license: apache2
16 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout/input_text_pane.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 8dp
3 | 8dp
4 | 16dp
5 | 16dp
6 |
7 | 8dp
8 | 4dp
9 |
10 | 24dp
11 |
12 | 8dp
13 | 8dp
14 | 64dp
15 | 88dp
16 |
17 | 8dp
18 | 16sp
19 |
20 | 40dp
21 |
22 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout/output_text_pane.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
22 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout/launch_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
27 |
28 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout/buttons.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
15 |
16 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CONTRIB.md:
--------------------------------------------------------------------------------
1 | # How to become a contributor and submit your own code
2 |
3 | ## Contributor License Agreements
4 |
5 | We'd love to accept your sample apps and patches! Before we can take them, we
6 | have to jump a couple of legal hurdles.
7 |
8 | Please fill out either the individual or corporate Contributor License Agreement
9 | (CLA).
10 |
11 | * If you are an individual writing original source code and you're sure you
12 | own the intellectual property, then you'll need to sign an [individual CLA]
13 | (https://developers.google.com/open-source/cla/individual).
14 | * If you work for a company that wants to allow you to contribute your work,
15 | then you'll need to sign a [corporate CLA]
16 | (https://developers.google.com/open-source/cla/corporate).
17 |
18 | Follow either of the two links above to access the appropriate CLA and
19 | instructions for how to sign and return it. Once we receive it, we'll be able to
20 | accept your pull requests.
21 |
22 | ## Contributing A Patch
23 |
24 | 1. Submit an issue describing your proposed change to the repo in question.
25 | 1. The repo owner will respond to your issue.
26 | 1. If your proposed change is accepted, and you haven't already done so, sign a
27 | Contributor License Agreement (see details above).
28 | 1. Fork the desired repo, develop and test your code changes.
29 | 1. Ensure that your code adheres to the existing style in the sample to which
30 | you are contributing. Refer to the
31 | [Google Cloud Platform Samples Style Guide]
32 | (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the
33 | recommended coding standards for this organization.
34 | 1. Ensure that your code has an appropriate set of unit tests which all pass.
35 | 1. Submit a pull request.
36 |
--------------------------------------------------------------------------------
/mobile-translate/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | def keystorePropertiesFile = rootProject.file("keystore.properties")
4 | def keystoreProperties = new Properties()
5 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
6 |
7 | android {
8 | signingConfigs {
9 | mainRelease {
10 | keyAlias keystoreProperties['keyAlias']
11 | keyPassword keystoreProperties['keyPassword']
12 | storeFile file(keystoreProperties['storeFile'])
13 | storePassword keystoreProperties['storePassword']
14 | }
15 | }
16 | compileSdkVersion 23
17 | buildToolsVersion "24.0.0 rc4"
18 | defaultConfig {
19 | applicationId "com.google.samples.mobiledoctranslate"
20 | minSdkVersion 17
21 | targetSdkVersion 23
22 | versionCode 2
23 | versionName "1.0.1"
24 | }
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android.txt'),
29 | 'proguard-rules.pro'
30 | signingConfig signingConfigs.mainRelease
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | compile fileTree(include: ['*.jar'], dir: 'libs')
37 | testCompile 'junit:junit:4.12'
38 | compile 'com.android.support:appcompat-v7:23.4.0'
39 | compile 'com.google.android.gms:play-services-auth:9.0.2'
40 | compile 'com.android.support:cardview-v7:23.4.0'
41 | compile 'pub.devrel:easypermissions:0.1.5'
42 | compile('com.google.api-client:google-api-client-android:1.20.0') {
43 | exclude group: 'org.apache.httpcomponents'
44 | }
45 | compile('com.google.apis:google-api-services-script:v1-rev1-1.20.0') {
46 | exclude group: 'org.apache.httpcomponents'
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/java/com/google/samples/mobiledoctranslate/DefaultLaunchActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.samples.mobiledoctranslate;
18 |
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.view.View;
22 |
23 | /**
24 | * Since this add-on needs context from the Docs editor app, it should only be
25 | * launched from that app (via context menus).
26 | *
27 | * This activity handles the edge case where the app is (erroneously) launched
28 | * from the home screen or a notification. This activity simply presents a
29 | * message to the user and provides an Exit button.
30 | */
31 | public class DefaultLaunchActivity extends Activity {
32 |
33 | /**
34 | * Create the default launch activity.
35 | * @param savedInstanceState previously saved instance data
36 | */
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.launch_default);
41 | }
42 |
43 | /**
44 | * Cancel the add-on and return without action.
45 | * @param v The button's View context
46 | */
47 | public void cancel(View v) {
48 | setResult(Activity.RESULT_CANCELED);
49 | finish();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
17 |
20 |
21 |
25 |
27 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
42 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This repo has been moved to [apps-script-samples](https://github.com/gsuitedevs/apps-script-samples/tree/master/android/mobile-translate).
2 |
3 | ---
4 |
5 | ## Apps Script Mobile Add-ons
6 |
7 | Android applications that interact with the Google Docs and Sheets
8 | editor apps, and use the
9 | [Apps Script Execution API](https://developers.google.com/apps-script/guides/)
10 | to call Apps Script functions to interact with Google services.
11 |
12 | Introduction
13 | ------------
14 |
15 | Google Apps Script now allows developers to construct mobile add-ons --
16 | Android applications which extend and support Google Docs and Sheets.
17 |
18 | This repository contains sample mobile add-ons that are meant to
19 | provide an example of a mobile add-on's project structure.
20 |
21 | Learn more
22 | ----------
23 |
24 | To continue learning about mobile add-ons for Google Docs and Sheets,
25 | take a look at the following resources:
26 |
27 | * [Mobile Add-ons](https://developers.google.com/apps-script/add-ons/mobile)
28 | * [Apps Script Execution API](https://developers.google.com/apps-script/guides/)
29 |
30 | Support
31 | -------
32 |
33 | For general Apps Script support, check the following:
34 |
35 | - Stack Overflow Tag: [google-apps-script](http://stackoverflow.com/questions/tagged/google-apps-script)
36 | - Issue Tracker: [google-apps-script-issues](https://code.google.com/p/google-apps-script-issues/issues/list)
37 |
38 | If you've found an error in this sample, please file an issue:
39 | https://github.com/googlesamples/apps-script-mobile-addons
40 |
41 | Patches are encouraged, and may be submitted by forking this project and
42 | submitting a pull request through GitHub.
43 |
44 | License
45 | -------
46 |
47 | Copyright 2016 Google, Inc.
48 |
49 | Licensed to the Apache Software Foundation (ASF) under one
50 | or more contributor license agreements. See the NOTICE file
51 | distributed with this work for additional information
52 | regarding copyright ownership. The ASF licenses this file
53 | to you under the Apache License, Version 2.0 (the
54 | "License"); you may not use this file except in compliance
55 | with the License. You may obtain a copy of the License at
56 |
57 | http://www.apache.org/licenses/LICENSE-2.0
58 |
59 | Unless required by applicable law or agreed to in writing,
60 | software distributed under the License is distributed on an
61 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
62 | KIND, either express or implied. See the License for the
63 | specific language governing permissions and limitations
64 | under the License.
65 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Mobile Doc Translate
3 |
4 |
5 | Retrieving selected text…
6 | Translating…
7 | Replacing text with translation…
8 |
9 |
10 | Replace
11 | Cancel
12 | Exit
13 | Original selected text
14 | Select languages to acquire and edit
15 | translated text.
16 | Selected language
17 | Translate into
18 |
19 | Auto-detect
20 | Arabic
21 | Chinese
22 | English
23 | French
24 | German
25 | Hindi
26 | Japanese
27 | Portuguese
28 | Spanish
29 |
30 |
31 | Arabic
32 | Chinese
33 | English
34 | French
35 | German
36 | Hindi
37 | Japanese
38 | Portuguese
39 | Spanish
40 |
41 |
42 | Mobile Doc Translate is a mobile add-on. It
43 | is not meant to be launched directly. Rather, it should be called from
44 | the Google Docs editor app by highlighting text and selecting
45 | \"Mobile Doc Translate\" from the context menu.
46 |
47 |
48 | Called from unexpected app:
49 | Google Play Services required.
50 | After installing, close and relaunch this app.
51 | This app needs to access your
52 | Google account (via Contacts).
53 | Missing required
54 | permissions!
55 | Authorization not provided.
56 | No network connection available.
57 | Unrecoverable error occurred
58 | \nScript error!\n
59 | \nScript error stacktrace:
60 |
61 |
62 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/res/layout-land/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
28 |
29 |
38 |
39 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
58 |
59 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/mobile-translate/README.md:
--------------------------------------------------------------------------------
1 | Mobile Doc Translate Add-on
2 | ===========================
3 |
4 | A sample Google Apps Script mobile add-on for Google Docs. This add-on is
5 | essentially a mobile version of the Docs
6 | [Translate Add-on Quickstart](https://developers.google.com/apps-script/quickstart/docs).
7 |
8 | Introduction
9 | ------------
10 |
11 | Google Apps Script now allows developers to construct Mobile Add-ons -- Android
12 | applications which extend and support Google Docs and Sheets.
13 |
14 | This sample shows how to construct a mobile add-on called
15 | **Mobile Doc Translate**. This add-on allows users to select text in a
16 | Google Doc on their mobile device and see a translation of that text in one
17 | of several languages. The user can then edit the translation as needed and
18 | replace the original selected text in the Doc with the translation.
19 |
20 |
21 | Getting Started
22 | ---------------
23 |
24 | The add-on will need to call an Apps Script project to get Doc text, make
25 | translations, and insert text into the Doc. Users can access this add-on from
26 | the Google Docs Android app by highlighting text and selecting the add-on in the
27 | text context menu.
28 |
29 | The Apps Script code file for this project is `Code.gs`. This is the same code
30 | used in the [Translate Add-on Quickstart](https://developers.google.com/apps-script/quickstart/docs),
31 | but does not include the HTML code that defines the quickstart's sidebar.
32 |
33 | The mobile add-on will make use of the
34 | [Apps Script Execution API](https://developers.google.com/apps-script/guides/rest/)
35 | to call the `Code.gs` functions. The
36 | [Execution API quickstart for Android](https://developers.google.com/apps-script/guides/rest/quickstart/android)
37 | describes how to call Apps Script functions from Android applications.
38 |
39 | To build this sample:
40 |
41 | 1. The `app/` folder in this repository contains all the required Android files
42 | for this add-on. These can be manually copied or imported into a new Android
43 | Studio project.
44 | 1. Create a new Apps Script project.
45 | 1. Replace the code in the new project's `Code.gs` file with the code from this
46 | repo.
47 | 1. Save the project.
48 | 1. In the code editor, select **Publish > Deploy as API** executable.
49 | 1. In the dialog that opens, leave the **Version** as "New" and enter
50 | "Target-v1" into the text box. Click **Deploy**.
51 | 1. Follow the
52 | [Keytool SHA1 Fingerprint](https://developers.google.com/apps-script/guides/rest/quickstart/android#step_1_acquire_a_sha1_fingerprint)
53 | instructions to acquire a SHA1 fingerprint for your project.
54 | 1. Using that SHA code, follow the
55 | [Turn on the Execution API](https://developers.google.com/apps-script/guides/rest/quickstart/android#step_2_turn_on_the_api_name)
56 | instructions to enable the API for your script project and create OAuth
57 | credentials. Be sure to match the same package name used in your Android
58 | code.
59 | 1. Edit the `MainActivity.java` file so that the `SCRIPT_ID` constant is set to
60 | your Apps Script project ID (in the script editor, select
61 | **File > Project properties**, and use the **Project key**).
62 |
63 | These steps should allow you to build the Android app and have it successfully
64 | call the Apps Script code. You can test it by:
65 |
66 | 1. Install the app on a test Android device.
67 | 1. Set the app as the debug app on the device by running this
68 | [ADB](https://developer.android.com/studio/command-line/adb.html)
69 | command:
70 | `$ adb shell am set-debug-app --persistent `
71 | 1. Open a docucment using the Google Docs app on the device.
72 | 1. Highlight some text in the doc and select the three-dot icon to open the
73 | context menu, and then select **Mobile Doc Translate**.
74 |
75 | Learn more
76 | ----------
77 |
78 | To continue learning about mobile add-ons for Google Docs and Sheets,
79 | take a look at the following resources:
80 |
81 | * [Mobile Add-ons](https://developers.google.com/apps-script/add-ons/mobile)
82 | * [Apps Script Execution API](https://developers.google.com/apps-script/guides/)
83 |
84 | Support
85 | -------
86 |
87 | For general Apps Script support, check the following:
88 |
89 | - Stack Overflow Tag: [google-apps-script](http://stackoverflow.com/questions/tagged/google-apps-script)
90 | - Issue Tracker: [google-apps-script-issues](https://code.google.com/p/google-apps-script-issues/issues/list)
91 |
92 | If you've found an error in this sample, please file an issue:
93 | https://github.com/googlesamples/apps-script-mobile-addons
94 |
95 | Patches are encouraged, and may be submitted by forking this project and
96 | submitting a pull request through GitHub.
97 |
98 | License
99 | -------
100 |
101 | Copyright 2016 Google, Inc.
102 |
103 | Licensed to the Apache Software Foundation (ASF) under one
104 | or more contributor license agreements. See the NOTICE file
105 | distributed with this work for additional information
106 | regarding copyright ownership. The ASF licenses this file
107 | to you under the Apache License, Version 2.0 (the
108 | "License"); you may not use this file except in compliance
109 | with the License. You may obtain a copy of the License at
110 |
111 | http://www.apache.org/licenses/LICENSE-2.0
112 |
113 | Unless required by applicable law or agreed to in writing,
114 | software distributed under the License is distributed on an
115 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
116 | KIND, either express or implied. See the License for the
117 | specific language governing permissions and limitations
118 | under the License.
--------------------------------------------------------------------------------
/mobile-translate/Code.gs:
--------------------------------------------------------------------------------
1 | /**
2 | * @OnlyCurrentDoc
3 | *
4 | * The above comment directs Apps Script to limit the scope of file
5 | * access for this add-on. It specifies that this add-on will only
6 | * attempt to read or modify the files in which the add-on is used,
7 | * and not all of the user's files. The authorization request message
8 | * presented to users will reflect this limited scope.
9 | */
10 |
11 | /**
12 | * Creates a menu entry in the Google Docs UI when the document is opened.
13 | * This method is only used by the regular add-on, and is never called by
14 | * the mobile add-on version.
15 | *
16 | * @param {object} e The event parameter for a simple onOpen trigger. To
17 | * determine which authorization mode (ScriptApp.AuthMode) the trigger is
18 | * running in, inspect e.authMode.
19 | */
20 | function onOpen(e) {
21 | DocumentApp.getUi().createAddonMenu()
22 | .addItem('Start', 'showSidebar')
23 | .addToUi();
24 | }
25 |
26 | /**
27 | * Runs when the add-on is installed.
28 | * This method is only used by the regular add-on, and is never called by
29 | * the mobile add-on version.
30 | *
31 | * @param {object} e The event parameter for a simple onInstall trigger. To
32 | * determine which authorization mode (ScriptApp.AuthMode) the trigger is
33 | * running in, inspect e.authMode. (In practice, onInstall triggers always
34 | * run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or
35 | * AuthMode.NONE.)
36 | */
37 | function onInstall(e) {
38 | onOpen(e);
39 | }
40 |
41 | /**
42 | * Opens a sidebar in the document containing the add-on's user interface.
43 | * This method is only used by the regular add-on, and is never called by
44 | * the mobile add-on version.
45 | */
46 | function showSidebar() {
47 | var ui = HtmlService.createHtmlOutputFromFile('Sidebar')
48 | .setTitle('Translate');
49 | DocumentApp.getUi().showSidebar(ui);
50 | }
51 |
52 | /**
53 | * Gets the text the user has selected. If there is no selection,
54 | * this function displays an error message.
55 | *
56 | * @return {Array.} The selected text.
57 | */
58 | function getSelectedText() {
59 | var selection = DocumentApp.getActiveDocument().getSelection();
60 | if (selection) {
61 | var text = [];
62 | var elements = selection.getSelectedElements();
63 | for (var i = 0; i < elements.length; i++) {
64 | if (elements[i].isPartial()) {
65 | var element = elements[i].getElement().asText();
66 | var startIndex = elements[i].getStartOffset();
67 | var endIndex = elements[i].getEndOffsetInclusive();
68 |
69 | text.push(element.getText().substring(startIndex, endIndex + 1));
70 | } else {
71 | var element = elements[i].getElement();
72 | // Only translate elements that can be edited as text; skip images and
73 | // other non-text elements.
74 | if (element.editAsText) {
75 | var elementText = element.asText().getText();
76 | // This check is necessary to exclude images, which return a blank
77 | // text element.
78 | if (elementText != '') {
79 | text.push(elementText);
80 | }
81 | }
82 | }
83 | }
84 | if (text.length == 0) {
85 | throw 'Please select some text.';
86 | }
87 | return text;
88 | } else {
89 | throw 'Please select some text.';
90 | }
91 | }
92 |
93 | /**
94 | * Gets the stored user preferences for the origin and destination languages,
95 | * if they exist.
96 | * This method is only used by the regular add-on, and is never called by
97 | * the mobile add-on version.
98 | *
99 | * @return {Object} The user's origin and destination language preferences, if
100 | * they exist.
101 | */
102 | function getPreferences() {
103 | var userProperties = PropertiesService.getUserProperties();
104 | var languagePrefs = {
105 | originLang: userProperties.getProperty('originLang'),
106 | destLang: userProperties.getProperty('destLang')
107 | };
108 | return languagePrefs;
109 | }
110 |
111 | /**
112 | * Gets the user-selected text and translates it from the origin language to the
113 | * destination language. The languages are notated by their two-letter short
114 | * form. For example, English is 'en', and Spanish is 'es'. The origin language
115 | * may be specified as an empty string to indicate that Google Translate should
116 | * auto-detect the language.
117 | *
118 | * @param {string} origin The two-letter short form for the origin language.
119 | * @param {string} dest The two-letter short form for the destination language.
120 | * @param {boolean} savePrefs Whether to save the origin and destination
121 | * language preferences.
122 | * @return {Object} Object containing the original text and the result of the
123 | * translation.
124 | */
125 | function getTextAndTranslation(origin, dest, savePrefs) {
126 | var result = {};
127 | var text = getSelectedText();
128 | result['text'] = text.join('\n');
129 |
130 | if (savePrefs == true) {
131 | var userProperties = PropertiesService.getUserProperties();
132 | userProperties.setProperty('originLang', origin);
133 | userProperties.setProperty('destLang', dest);
134 | }
135 |
136 | result['translation'] = translateText(result['text'], origin, dest);
137 |
138 | return result;
139 | }
140 |
141 | /**
142 | * Replaces the text of the current selection with the provided text, or
143 | * inserts text at the current cursor location. (There will always be either
144 | * a selection or a cursor.) If multiple elements are selected, only inserts the
145 | * translated text in the first element that can contain text and removes the
146 | * other elements.
147 | *
148 | * @param {string} newText The text with which to replace the current selection.
149 | */
150 | function insertText(newText) {
151 | var selection = DocumentApp.getActiveDocument().getSelection();
152 | if (selection) {
153 | var replaced = false;
154 | var elements = selection.getSelectedElements();
155 | if (elements.length == 1 &&
156 | elements[0].getElement().getType() ==
157 | DocumentApp.ElementType.INLINE_IMAGE) {
158 | throw "Can't insert text into an image.";
159 | }
160 | for (var i = 0; i < elements.length; i++) {
161 | if (elements[i].isPartial()) {
162 | var element = elements[i].getElement().asText();
163 | var startIndex = elements[i].getStartOffset();
164 | var endIndex = elements[i].getEndOffsetInclusive();
165 |
166 | var remainingText = element.getText().substring(endIndex + 1);
167 | element.deleteText(startIndex, endIndex);
168 | if (!replaced) {
169 | element.insertText(startIndex, newText);
170 | replaced = true;
171 | } else {
172 | // This block handles a selection that ends with a partial element. We
173 | // want to copy this partial text to the previous element so we don't
174 | // have a line-break before the last partial.
175 | var parent = element.getParent();
176 | parent.getPreviousSibling().asText().appendText(remainingText);
177 | // We cannot remove the last paragraph of a doc. If this is the case,
178 | // just remove the text within the last paragraph instead.
179 | if (parent.getNextSibling()) {
180 | parent.removeFromParent();
181 | } else {
182 | element.removeFromParent();
183 | }
184 | }
185 | } else {
186 | var element = elements[i].getElement();
187 | if (!replaced && element.editAsText) {
188 | // Only translate elements that can be edited as text, removing other
189 | // elements.
190 | element.clear();
191 | element.asText().setText(newText);
192 | replaced = true;
193 | } else {
194 | // We cannot remove the last paragraph of a doc. If this is the case,
195 | // just clear the element.
196 | if (element.getNextSibling()) {
197 | element.removeFromParent();
198 | } else {
199 | element.clear();
200 | }
201 | }
202 | }
203 | }
204 | } else {
205 | var cursor = DocumentApp.getActiveDocument().getCursor();
206 | var surroundingText = cursor.getSurroundingText().getText();
207 | var surroundingTextOffset = cursor.getSurroundingTextOffset();
208 |
209 | // If the cursor follows or preceds a non-space character, insert a space
210 | // between the character and the translation. Otherwise, just insert the
211 | // translation.
212 | if (surroundingTextOffset > 0) {
213 | if (surroundingText.charAt(surroundingTextOffset - 1) != ' ') {
214 | newText = ' ' + newText;
215 | }
216 | }
217 | if (surroundingTextOffset < surroundingText.length) {
218 | if (surroundingText.charAt(surroundingTextOffset) != ' ') {
219 | newText += ' ';
220 | }
221 | }
222 | cursor.insertText(newText);
223 | }
224 | }
225 |
226 |
227 | /**
228 | * Given text, translate it from the origin language to the destination
229 | * language. The languages are notated by their two-letter short form. For
230 | * example, English is 'en', and Spanish is 'es'. The origin language may be
231 | * specified as an empty string to indicate that Google Translate should
232 | * auto-detect the language.
233 | *
234 | * @param {string} text text to translate.
235 | * @param {string} origin The two-letter short form for the origin language.
236 | * @param {string} dest The two-letter short form for the destination language.
237 | * @return {string} The result of the translation, or the original text if
238 | * origin and dest languages are the same.
239 | */
240 | function translateText(text, origin, dest) {
241 | if (origin === dest) {
242 | return text;
243 | }
244 | return LanguageApp.translate(text, origin, dest);
245 | }
246 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | --------------
3 |
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "{}"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright {yyyy} {name of copyright owner}
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
205 | All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav
206 | and *.ogg) are licensed under the CC-BY-NC license. All other files are
207 | licensed under the Apache 2 license.
208 |
209 | CC-BY-NC License
210 | ----------------
211 |
212 | Attribution-NonCommercial-ShareAlike 4.0 International
213 |
214 | =======================================================================
215 |
216 | Creative Commons Corporation ("Creative Commons") is not a law firm and
217 | does not provide legal services or legal advice. Distribution of
218 | Creative Commons public licenses does not create a lawyer-client or
219 | other relationship. Creative Commons makes its licenses and related
220 | information available on an "as-is" basis. Creative Commons gives no
221 | warranties regarding its licenses, any material licensed under their
222 | terms and conditions, or any related information. Creative Commons
223 | disclaims all liability for damages resulting from their use to the
224 | fullest extent possible.
225 |
226 | Using Creative Commons Public Licenses
227 |
228 | Creative Commons public licenses provide a standard set of terms and
229 | conditions that creators and other rights holders may use to share
230 | original works of authorship and other material subject to copyright
231 | and certain other rights specified in the public license below. The
232 | following considerations are for informational purposes only, are not
233 | exhaustive, and do not form part of our licenses.
234 |
235 | Considerations for licensors: Our public licenses are
236 | intended for use by those authorized to give the public
237 | permission to use material in ways otherwise restricted by
238 | copyright and certain other rights. Our licenses are
239 | irrevocable. Licensors should read and understand the terms
240 | and conditions of the license they choose before applying it.
241 | Licensors should also secure all rights necessary before
242 | applying our licenses so that the public can reuse the
243 | material as expected. Licensors should clearly mark any
244 | material not subject to the license. This includes other CC-
245 | licensed material, or material used under an exception or
246 | limitation to copyright. More considerations for licensors:
247 | wiki.creativecommons.org/Considerations_for_licensors
248 |
249 | Considerations for the public: By using one of our public
250 | licenses, a licensor grants the public permission to use the
251 | licensed material under specified terms and conditions. If
252 | the licensor's permission is not necessary for any reason--for
253 | example, because of any applicable exception or limitation to
254 | copyright--then that use is not regulated by the license. Our
255 | licenses grant only permissions under copyright and certain
256 | other rights that a licensor has authority to grant. Use of
257 | the licensed material may still be restricted for other
258 | reasons, including because others have copyright or other
259 | rights in the material. A licensor may make special requests,
260 | such as asking that all changes be marked or described.
261 | Although not required by our licenses, you are encouraged to
262 | respect those requests where reasonable. More_considerations
263 | for the public:
264 | wiki.creativecommons.org/Considerations_for_licensees
265 |
266 | =======================================================================
267 |
268 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
269 | Public License
270 |
271 | By exercising the Licensed Rights (defined below), You accept and agree
272 | to be bound by the terms and conditions of this Creative Commons
273 | Attribution-NonCommercial-ShareAlike 4.0 International Public License
274 | ("Public License"). To the extent this Public License may be
275 | interpreted as a contract, You are granted the Licensed Rights in
276 | consideration of Your acceptance of these terms and conditions, and the
277 | Licensor grants You such rights in consideration of benefits the
278 | Licensor receives from making the Licensed Material available under
279 | these terms and conditions.
280 |
281 |
282 | Section 1 -- Definitions.
283 |
284 | a. Adapted Material means material subject to Copyright and Similar
285 | Rights that is derived from or based upon the Licensed Material
286 | and in which the Licensed Material is translated, altered,
287 | arranged, transformed, or otherwise modified in a manner requiring
288 | permission under the Copyright and Similar Rights held by the
289 | Licensor. For purposes of this Public License, where the Licensed
290 | Material is a musical work, performance, or sound recording,
291 | Adapted Material is always produced where the Licensed Material is
292 | synched in timed relation with a moving image.
293 |
294 | b. Adapter's License means the license You apply to Your Copyright
295 | and Similar Rights in Your contributions to Adapted Material in
296 | accordance with the terms and conditions of this Public License.
297 |
298 | c. BY-NC-SA Compatible License means a license listed at
299 | creativecommons.org/compatiblelicenses, approved by Creative
300 | Commons as essentially the equivalent of this Public License.
301 |
302 | d. Copyright and Similar Rights means copyright and/or similar rights
303 | closely related to copyright including, without limitation,
304 | performance, broadcast, sound recording, and Sui Generis Database
305 | Rights, without regard to how the rights are labeled or
306 | categorized. For purposes of this Public License, the rights
307 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
308 | Rights.
309 |
310 | e. Effective Technological Measures means those measures that, in the
311 | absence of proper authority, may not be circumvented under laws
312 | fulfilling obligations under Article 11 of the WIPO Copyright
313 | Treaty adopted on December 20, 1996, and/or similar international
314 | agreements.
315 |
316 | f. Exceptions and Limitations means fair use, fair dealing, and/or
317 | any other exception or limitation to Copyright and Similar Rights
318 | that applies to Your use of the Licensed Material.
319 |
320 | g. License Elements means the license attributes listed in the name
321 | of a Creative Commons Public License. The License Elements of this
322 | Public License are Attribution, NonCommercial, and ShareAlike.
323 |
324 | h. Licensed Material means the artistic or literary work, database,
325 | or other material to which the Licensor applied this Public
326 | License.
327 |
328 | i. Licensed Rights means the rights granted to You subject to the
329 | terms and conditions of this Public License, which are limited to
330 | all Copyright and Similar Rights that apply to Your use of the
331 | Licensed Material and that the Licensor has authority to license.
332 |
333 | j. Licensor means the individual(s) or entity(ies) granting rights
334 | under this Public License.
335 |
336 | k. NonCommercial means not primarily intended for or directed towards
337 | commercial advantage or monetary compensation. For purposes of
338 | this Public License, the exchange of the Licensed Material for
339 | other material subject to Copyright and Similar Rights by digital
340 | file-sharing or similar means is NonCommercial provided there is
341 | no payment of monetary compensation in connection with the
342 | exchange.
343 |
344 | l. Share means to provide material to the public by any means or
345 | process that requires permission under the Licensed Rights, such
346 | as reproduction, public display, public performance, distribution,
347 | dissemination, communication, or importation, and to make material
348 | available to the public including in ways that members of the
349 | public may access the material from a place and at a time
350 | individually chosen by them.
351 |
352 | m. Sui Generis Database Rights means rights other than copyright
353 | resulting from Directive 96/9/EC of the European Parliament and of
354 | the Council of 11 March 1996 on the legal protection of databases,
355 | as amended and/or succeeded, as well as other essentially
356 | equivalent rights anywhere in the world.
357 |
358 | n. You means the individual or entity exercising the Licensed Rights
359 | under this Public License. Your has a corresponding meaning.
360 |
361 |
362 | Section 2 -- Scope.
363 |
364 | a. License grant.
365 |
366 | 1. Subject to the terms and conditions of this Public License,
367 | the Licensor hereby grants You a worldwide, royalty-free,
368 | non-sublicensable, non-exclusive, irrevocable license to
369 | exercise the Licensed Rights in the Licensed Material to:
370 |
371 | a. reproduce and Share the Licensed Material, in whole or
372 | in part, for NonCommercial purposes only; and
373 |
374 | b. produce, reproduce, and Share Adapted Material for
375 | NonCommercial purposes only.
376 |
377 | 2. Exceptions and Limitations. For the avoidance of doubt, where
378 | Exceptions and Limitations apply to Your use, this Public
379 | License does not apply, and You do not need to comply with
380 | its terms and conditions.
381 |
382 | 3. Term. The term of this Public License is specified in Section
383 | 6(a).
384 |
385 | 4. Media and formats; technical modifications allowed. The
386 | Licensor authorizes You to exercise the Licensed Rights in
387 | all media and formats whether now known or hereafter created,
388 | and to make technical modifications necessary to do so. The
389 | Licensor waives and/or agrees not to assert any right or
390 | authority to forbid You from making technical modifications
391 | necessary to exercise the Licensed Rights, including
392 | technical modifications necessary to circumvent Effective
393 | Technological Measures. For purposes of this Public License,
394 | simply making modifications authorized by this Section 2(a)
395 | (4) never produces Adapted Material.
396 |
397 | 5. Downstream recipients.
398 |
399 | a. Offer from the Licensor -- Licensed Material. Every
400 | recipient of the Licensed Material automatically
401 | receives an offer from the Licensor to exercise the
402 | Licensed Rights under the terms and conditions of this
403 | Public License.
404 |
405 | b. Additional offer from the Licensor -- Adapted Material.
406 | Every recipient of Adapted Material from You
407 | automatically receives an offer from the Licensor to
408 | exercise the Licensed Rights in the Adapted Material
409 | under the conditions of the Adapter's License You apply.
410 |
411 | c. No downstream restrictions. You may not offer or impose
412 | any additional or different terms or conditions on, or
413 | apply any Effective Technological Measures to, the
414 | Licensed Material if doing so restricts exercise of the
415 | Licensed Rights by any recipient of the Licensed
416 | Material.
417 |
418 | 6. No endorsement. Nothing in this Public License constitutes or
419 | may be construed as permission to assert or imply that You
420 | are, or that Your use of the Licensed Material is, connected
421 | with, or sponsored, endorsed, or granted official status by,
422 | the Licensor or others designated to receive attribution as
423 | provided in Section 3(a)(1)(A)(i).
424 |
425 | b. Other rights.
426 |
427 | 1. Moral rights, such as the right of integrity, are not
428 | licensed under this Public License, nor are publicity,
429 | privacy, and/or other similar personality rights; however, to
430 | the extent possible, the Licensor waives and/or agrees not to
431 | assert any such rights held by the Licensor to the limited
432 | extent necessary to allow You to exercise the Licensed
433 | Rights, but not otherwise.
434 |
435 | 2. Patent and trademark rights are not licensed under this
436 | Public License.
437 |
438 | 3. To the extent possible, the Licensor waives any right to
439 | collect royalties from You for the exercise of the Licensed
440 | Rights, whether directly or through a collecting society
441 | under any voluntary or waivable statutory or compulsory
442 | licensing scheme. In all other cases the Licensor expressly
443 | reserves any right to collect such royalties, including when
444 | the Licensed Material is used other than for NonCommercial
445 | purposes.
446 |
447 |
448 | Section 3 -- License Conditions.
449 |
450 | Your exercise of the Licensed Rights is expressly made subject to the
451 | following conditions.
452 |
453 | a. Attribution.
454 |
455 | 1. If You Share the Licensed Material (including in modified
456 | form), You must:
457 |
458 | a. retain the following if it is supplied by the Licensor
459 | with the Licensed Material:
460 |
461 | i. identification of the creator(s) of the Licensed
462 | Material and any others designated to receive
463 | attribution, in any reasonable manner requested by
464 | the Licensor (including by pseudonym if
465 | designated);
466 |
467 | ii. a copyright notice;
468 |
469 | iii. a notice that refers to this Public License;
470 |
471 | iv. a notice that refers to the disclaimer of
472 | warranties;
473 |
474 | v. a URI or hyperlink to the Licensed Material to the
475 | extent reasonably practicable;
476 |
477 | b. indicate if You modified the Licensed Material and
478 | retain an indication of any previous modifications; and
479 |
480 | c. indicate the Licensed Material is licensed under this
481 | Public License, and include the text of, or the URI or
482 | hyperlink to, this Public License.
483 |
484 | 2. You may satisfy the conditions in Section 3(a)(1) in any
485 | reasonable manner based on the medium, means, and context in
486 | which You Share the Licensed Material. For example, it may be
487 | reasonable to satisfy the conditions by providing a URI or
488 | hyperlink to a resource that includes the required
489 | information.
490 | 3. If requested by the Licensor, You must remove any of the
491 | information required by Section 3(a)(1)(A) to the extent
492 | reasonably practicable.
493 |
494 | b. ShareAlike.
495 |
496 | In addition to the conditions in Section 3(a), if You Share
497 | Adapted Material You produce, the following conditions also apply.
498 |
499 | 1. The Adapter's License You apply must be a Creative Commons
500 | license with the same License Elements, this version or
501 | later, or a BY-NC-SA Compatible License.
502 |
503 | 2. You must include the text of, or the URI or hyperlink to, the
504 | Adapter's License You apply. You may satisfy this condition
505 | in any reasonable manner based on the medium, means, and
506 | context in which You Share Adapted Material.
507 |
508 | 3. You may not offer or impose any additional or different terms
509 | or conditions on, or apply any Effective Technological
510 | Measures to, Adapted Material that restrict exercise of the
511 | rights granted under the Adapter's License You apply.
512 |
513 |
514 | Section 4 -- Sui Generis Database Rights.
515 |
516 | Where the Licensed Rights include Sui Generis Database Rights that
517 | apply to Your use of the Licensed Material:
518 |
519 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
520 | to extract, reuse, reproduce, and Share all or a substantial
521 | portion of the contents of the database for NonCommercial purposes
522 | only;
523 |
524 | b. if You include all or a substantial portion of the database
525 | contents in a database in which You have Sui Generis Database
526 | Rights, then the database in which You have Sui Generis Database
527 | Rights (but not its individual contents) is Adapted Material,
528 | including for purposes of Section 3(b); and
529 |
530 | c. You must comply with the conditions in Section 3(a) if You Share
531 | all or a substantial portion of the contents of the database.
532 |
533 | For the avoidance of doubt, this Section 4 supplements and does not
534 | replace Your obligations under this Public License where the Licensed
535 | Rights include other Copyright and Similar Rights.
536 |
537 |
538 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
539 |
540 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
541 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
542 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
543 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
544 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
545 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
546 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
547 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
548 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
549 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
550 |
551 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
552 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
553 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
554 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
555 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
556 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
557 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
558 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
559 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
560 |
561 | c. The disclaimer of warranties and limitation of liability provided
562 | above shall be interpreted in a manner that, to the extent
563 | possible, most closely approximates an absolute disclaimer and
564 | waiver of all liability.
565 |
566 |
567 | Section 6 -- Term and Termination.
568 |
569 | a. This Public License applies for the term of the Copyright and
570 | Similar Rights licensed here. However, if You fail to comply with
571 | this Public License, then Your rights under this Public License
572 | terminate automatically.
573 |
574 | b. Where Your right to use the Licensed Material has terminated under
575 | Section 6(a), it reinstates:
576 |
577 | 1. automatically as of the date the violation is cured, provided
578 | it is cured within 30 days of Your discovery of the
579 | violation; or
580 |
581 | 2. upon express reinstatement by the Licensor.
582 |
583 | For the avoidance of doubt, this Section 6(b) does not affect any
584 | right the Licensor may have to seek remedies for Your violations
585 | of this Public License.
586 |
587 | c. For the avoidance of doubt, the Licensor may also offer the
588 | Licensed Material under separate terms or conditions or stop
589 | distributing the Licensed Material at any time; however, doing so
590 | will not terminate this Public License.
591 |
592 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
593 | License.
594 |
595 |
596 | Section 7 -- Other Terms and Conditions.
597 |
598 | a. The Licensor shall not be bound by any additional or different
599 | terms or conditions communicated by You unless expressly agreed.
600 |
601 | b. Any arrangements, understandings, or agreements regarding the
602 | Licensed Material not stated herein are separate from and
603 | independent of the terms and conditions of this Public License.
604 |
605 |
606 | Section 8 -- Interpretation.
607 |
608 | a. For the avoidance of doubt, this Public License does not, and
609 | shall not be interpreted to, reduce, limit, restrict, or impose
610 | conditions on any use of the Licensed Material that could lawfully
611 | be made without permission under this Public License.
612 |
613 | b. To the extent possible, if any provision of this Public License is
614 | deemed unenforceable, it shall be automatically reformed to the
615 | minimum extent necessary to make it enforceable. If the provision
616 | cannot be reformed, it shall be severed from this Public License
617 | without affecting the enforceability of the remaining terms and
618 | conditions.
619 |
620 | c. No term or condition of this Public License will be waived and no
621 | failure to comply consented to unless expressly agreed to by the
622 | Licensor.
623 |
624 | d. Nothing in this Public License constitutes or may be interpreted
625 | as a limitation upon, or waiver of, any privileges and immunities
626 | that apply to the Licensor or You, including from the legal
627 | processes of any jurisdiction or authority.
628 |
629 | =======================================================================
630 |
631 | Creative Commons is not a party to its public licenses.
632 | Notwithstanding, Creative Commons may elect to apply one of its public
633 | licenses to material it publishes and in those instances will be
634 | considered the "Licensor." Except for the limited purpose of indicating
635 | that material is shared under a Creative Commons public license or as
636 | otherwise permitted by the Creative Commons policies published at
637 | creativecommons.org/policies, Creative Commons does not authorize the
638 | use of the trademark "Creative Commons" or any other trademark or logo
639 | of Creative Commons without its prior written consent including,
640 | without limitation, in connection with any unauthorized modifications
641 | to any of its public licenses or any other arrangements,
642 | understandings, or agreements concerning use of licensed material. For
643 | the avoidance of doubt, this paragraph does not form part of the public
644 | licenses.
645 |
646 | Creative Commons may be contacted at creativecommons.org.
647 |
648 |
--------------------------------------------------------------------------------
/mobile-translate/app/src/main/java/com/google/samples/mobiledoctranslate/MainActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.samples.mobiledoctranslate;
18 |
19 | import com.google.android.gms.auth.GoogleAuthException;
20 | import com.google.android.gms.common.ConnectionResult;
21 | import com.google.android.gms.common.GoogleApiAvailability;
22 | import com.google.api.client.extensions.android.http.AndroidHttp;
23 | import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
24 | import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
25 | import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
26 | import com.google.api.client.http.HttpRequest;
27 | import com.google.api.client.http.HttpRequestInitializer;
28 | import com.google.api.client.http.HttpTransport;
29 | import com.google.api.client.json.JsonFactory;
30 | import com.google.api.client.json.jackson2.JacksonFactory;
31 | import com.google.api.client.util.ExponentialBackOff;
32 | import com.google.api.services.script.model.*;
33 | import com.google.api.services.script.Script;
34 |
35 | import android.Manifest;
36 | import android.accounts.Account;
37 | import android.app.Activity;
38 | import android.app.AlertDialog;
39 | import android.app.Dialog;
40 | import android.app.ProgressDialog;
41 | import android.content.BroadcastReceiver;
42 | import android.content.Context;
43 | import android.content.ContextWrapper;
44 | import android.content.DialogInterface;
45 | import android.content.Intent;
46 | import android.content.IntentFilter;
47 | import android.content.SharedPreferences;
48 | import android.net.ConnectivityManager;
49 | import android.net.NetworkInfo;
50 | import android.os.AsyncTask;
51 | import android.os.Bundle;
52 | import android.support.annotation.NonNull;
53 | import android.text.method.ScrollingMovementMethod;
54 | import android.view.View;
55 | import android.widget.AdapterView;
56 | import android.widget.Button;
57 | import android.widget.EditText;
58 | import android.widget.Spinner;
59 | import android.widget.TextView;
60 | import android.widget.Toast;
61 |
62 | import java.io.IOException;
63 | import java.lang.StringBuilder;
64 | import java.util.Arrays;
65 | import java.util.List;
66 | import java.util.Map;
67 |
68 | import pub.devrel.easypermissions.AfterPermissionGranted;
69 | import pub.devrel.easypermissions.EasyPermissions;
70 |
71 | /**
72 | * This is the main (and only) activity of the add-on. It shows the user what
73 | * text or cells were selected, the results of translation and provides some
74 | * UI controls.
75 | */
76 | public class MainActivity extends Activity
77 | implements EasyPermissions.PermissionCallbacks {
78 |
79 | /**
80 | * The script ID for the Apps Script the add-on will call
81 | */
82 | private static final String SCRIPT_ID = "ENTER_YOUR_SCRIPT_ID_HERE";
83 |
84 | // Constants
85 | private static final String FUNCTION_GET_TEXT = "getTextAndTranslation";
86 | private static final String FUNCTION_TRANSLATE_TEXT = "translateText";
87 | private static final String FUNCTION_INSERT_TEXT = "insertText";
88 | static final int REQUEST_AUTHORIZATION = 1001;
89 | static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
90 | static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
91 | private static final String[] SCOPES = {
92 | "https://www.googleapis.com/auth/documents.currentonly",
93 | "https://www.googleapis.com/auth/script.scriptapp",
94 | "https://www.googleapis.com/auth/script.storage"
95 | };
96 | static final String SAVED_ORIG_LANG = "origLangPosition";
97 | static final String SAVED_DEST_LANG = "destLangPosition";
98 | private static final int CALL_GET_TEXT = 0;
99 | private static final int CALL_TRANSLATE_TEXT = 1;
100 | private static final int CALL_REPLACE_TEXT = 2;
101 |
102 | /**
103 | * An Apps Script API service object used to access the API, and related
104 | * objects
105 | */
106 | Script mService = null;
107 | GoogleAccountCredential mCredential = null;
108 | final HttpTransport mTransport = AndroidHttp.newCompatibleTransport();
109 | final JsonFactory mJsonFactory = JacksonFactory.getDefaultInstance();
110 |
111 | // Layout components
112 | private TextView mSelectedText;
113 | private EditText mTranslationText;
114 | private Button mReplaceButton;
115 | private ProgressDialog mProgress;
116 |
117 | // Translation language controls
118 | private String mOrigLang;
119 | private String mDestLang;
120 | private int mPrevOrigSpinnerPos;
121 | private int mPrevDestSpinnerPos;
122 |
123 | // Other variables
124 | private NetworkReceiver mReceiver;
125 | private boolean mConnectionAvailable;
126 | private int mLastFunctionCalled;
127 | private String mState;
128 | private Account mAccount;
129 |
130 | /**
131 | * Create the main activity.
132 | * @param savedInstanceState previously saved instance data
133 | */
134 | @Override
135 | protected void onCreate(Bundle savedInstanceState) {
136 | super.onCreate(savedInstanceState);
137 | setContentView(R.layout.activity_main);
138 |
139 | // Verify the add-on was called from the Docs editor.
140 | if (! "com.google.android.apps.docs.editors.docs".equals(
141 | getCallingPackage())) {
142 | showErrorDialog(getString(R.string.unexpected_app)
143 | + getCallingPackage());
144 | }
145 |
146 | // Acquire the doc/sheet state from the incoming intent.
147 | // It's also possible to acquire the docId from the intent;
148 | // that is not used in this example, however.
149 | mState = getIntent().getStringExtra(
150 | "com.google.android.apps.docs.addons.SessionState");
151 | mAccount = getIntent().getParcelableExtra(
152 | "com.google.android.apps.docs.addons.Account");
153 |
154 | // Load previously chosen language selections, if any
155 | SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
156 | mPrevOrigSpinnerPos = settings.getInt(SAVED_ORIG_LANG, 0);
157 | mOrigLang = getLangIdFromSpinnerPosition(mPrevOrigSpinnerPos, false);
158 | mPrevDestSpinnerPos = settings.getInt(SAVED_DEST_LANG, 0);
159 | mDestLang = getLangIdFromSpinnerPosition(mPrevDestSpinnerPos, true);
160 |
161 | // Initialize layout objects
162 | mProgress = new ProgressDialog(MainActivity.this);
163 |
164 | mSelectedText = (TextView) findViewById(R.id.selected_text);
165 | mSelectedText.setVerticalScrollBarEnabled(true);
166 | mSelectedText.setMovementMethod(new ScrollingMovementMethod());
167 |
168 | mTranslationText = (EditText) findViewById(R.id.translated_text);
169 | mTranslationText.setVerticalScrollBarEnabled(true);
170 | mTranslationText.setMovementMethod(new ScrollingMovementMethod());
171 |
172 | mReplaceButton = (Button) findViewById(R.id.replace_button);
173 |
174 | Spinner origLangSpinner = (Spinner) findViewById(R.id.origin_lang);
175 | Spinner destLangSpinner = (Spinner) findViewById(R.id.dest_lang);
176 | origLangSpinner.setSelection(mPrevOrigSpinnerPos);
177 | destLangSpinner.setSelection(mPrevDestSpinnerPos);
178 | origLangSpinner.setOnItemSelectedListener(
179 | new AdapterView.OnItemSelectedListener() {
180 | @Override
181 | public void onItemSelected(
182 | AdapterView> parent, View view, int pos, long id) {
183 | if (pos != mPrevOrigSpinnerPos) {
184 | mPrevOrigSpinnerPos = pos;
185 | mOrigLang = getLangIdFromSpinnerPosition(pos, false);
186 | SharedPreferences settings =
187 | getPreferences(Context.MODE_PRIVATE);
188 | SharedPreferences.Editor editor = settings.edit();
189 | editor.putInt(SAVED_ORIG_LANG, pos);
190 | editor.apply();
191 | translate();
192 | }
193 | }
194 |
195 | @Override
196 | public void onNothingSelected(AdapterView> parent) {}
197 | });
198 | destLangSpinner.setOnItemSelectedListener(
199 | new AdapterView.OnItemSelectedListener() {
200 | @Override
201 | public void onItemSelected(
202 | AdapterView> parent, View view, int pos, long id) {
203 | if (pos != mPrevDestSpinnerPos) {
204 | mPrevDestSpinnerPos = pos;
205 | mDestLang = getLangIdFromSpinnerPosition(pos, true);
206 | SharedPreferences settings =
207 | getPreferences(Context.MODE_PRIVATE);
208 | SharedPreferences.Editor editor = settings.edit();
209 | editor.putInt(SAVED_DEST_LANG, pos);
210 | editor.apply();
211 | translate();
212 | }
213 | }
214 |
215 | @Override
216 | public void onNothingSelected(AdapterView> parent) {}
217 | });
218 |
219 | // Register BroadcastReceiver to track connection changes, and
220 | // determine if a connection is initially available
221 | IntentFilter filter =
222 | new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
223 | mReceiver = new NetworkReceiver();
224 | MainActivity.this.registerReceiver(mReceiver, filter);
225 | updateButtonEnableStatus();
226 |
227 | // Start the add-on by attempting to retrieve the selected text from
228 | // the Doc that fired the add-on
229 | callAppsScriptTask(CALL_GET_TEXT);
230 | }
231 |
232 | /**
233 | * Extend the given HttpRequestInitializer (usually a Credentials object)
234 | * with additional initialize() instructions.
235 | *
236 | * @param requestInitializer the initializer to copy and adjust; typically
237 | * a Credential object
238 | * @return an initializer with an extended read timeout
239 | */
240 | private static HttpRequestInitializer setHttpTimeout(
241 | final HttpRequestInitializer requestInitializer) {
242 | return new HttpRequestInitializer() {
243 | @Override
244 | public void initialize(HttpRequest httpRequest)
245 | throws java.io.IOException {
246 | requestInitializer.initialize(httpRequest);
247 | // This allows the API to call (and avoid timing out on)
248 | // functions that take up to 30 seconds to complete. Note that
249 | // the maximum allowed script run time is 6 minutes.
250 | httpRequest.setReadTimeout(30000);
251 | }
252 | };
253 | }
254 |
255 | /**
256 | * Clean up and destroy the main activity.
257 | */
258 | @Override
259 | public void onDestroy() {
260 | super.onDestroy();
261 | // Unregister the connectivity broadcast receiver.
262 | if (mReceiver != null) {
263 | MainActivity.this.unregisterReceiver(mReceiver);
264 | }
265 | }
266 |
267 | /**
268 | * Called when an activity launched here (specifically, AccountPicker
269 | * and authorization) exits, giving you the requestCode you started it with,
270 | * the resultCode it returned, and any additional data from it.
271 | * @param requestCode code indicating which activity result is incoming
272 | * @param resultCode code indicating the result of the incoming
273 | * activity result
274 | * @param data Intent (containing result data) returned by incoming
275 | * activity result
276 | */
277 | @Override
278 | protected void onActivityResult(
279 | int requestCode, int resultCode, Intent data) {
280 | super.onActivityResult(requestCode, resultCode, data);
281 | switch(requestCode) {
282 | case REQUEST_GOOGLE_PLAY_SERVICES:
283 | if (resultCode == RESULT_OK) {
284 | callAppsScriptTask(mLastFunctionCalled);
285 | } else {
286 | showErrorDialog(getString(R.string.gps_required));
287 | }
288 | break;
289 | case REQUEST_AUTHORIZATION:
290 | if (resultCode == RESULT_OK) {
291 | callAppsScriptTask(mLastFunctionCalled);
292 | } else {
293 | showErrorDialog(getString(R.string.no_auth_provided));
294 | }
295 | break;
296 | }
297 | }
298 |
299 | /**
300 | * Call the API to execute an Apps Script function, after verifying
301 | * all the preconditions are satisfied. The preconditions are: Google
302 | * Play Services is installed, the device has a network connection, and
303 | * the Execution API service and credentials have been created.
304 | * @param functionToCall code indicating which function to call using
305 | * the API
306 | */
307 | private void callAppsScriptTask(int functionToCall) {
308 | mLastFunctionCalled = functionToCall;
309 | if (! isGooglePlayServicesAvailable()) {
310 | toast(getString(R.string.gps_required));
311 | acquireGooglePlayServices();
312 | } else if (! mConnectionAvailable) {
313 | toast(getString(R.string.no_network));
314 | } else if (! hasValidCredentials()) {
315 | createCredentialsAndService();
316 | } else {
317 | switch (functionToCall) {
318 | case CALL_GET_TEXT:
319 | new GetTextTask().execute(mOrigLang, mDestLang, false);
320 | break;
321 | case CALL_TRANSLATE_TEXT:
322 | String originalText = mSelectedText.getText().toString();
323 | new TranslateTextTask().execute(
324 | originalText, mOrigLang, mDestLang);
325 | break;
326 | case CALL_REPLACE_TEXT:
327 | String translation = mTranslationText.getText().toString();
328 | new ReplaceTextTask().execute(translation);
329 | break;
330 | }
331 | }
332 | }
333 |
334 | /**
335 | * Attempts to initialize credentials and service object (prior to a call
336 | * to the API); uses the account provided by the calling app. This
337 | * requires the GET_ACCOUNTS permission to be explicitly granted by the
338 | * user; this will be requested here if it is not already granted. The
339 | * AfterPermissionGranted annotation indicates that this function will be
340 | * rerun automatically whenever the GET_ACCOUNTS permission is granted.
341 | */
342 | @AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
343 | private void createCredentialsAndService() {
344 | if (EasyPermissions.hasPermissions(
345 | MainActivity.this, Manifest.permission.GET_ACCOUNTS)) {
346 | mCredential = GoogleAccountCredential.usingOAuth2(
347 | getApplicationContext(), Arrays.asList(SCOPES))
348 | .setBackOff(new ExponentialBackOff())
349 | .setSelectedAccountName(mAccount.name);
350 | mService = new com.google.api.services.script.Script.Builder(
351 | mTransport, mJsonFactory, setHttpTimeout(mCredential))
352 | .setApplicationName(getString(R.string.app_name))
353 | .build();
354 | updateButtonEnableStatus();
355 |
356 | // Callback to retry the API call with valid service/credentials
357 | callAppsScriptTask(mLastFunctionCalled);
358 | } else {
359 | // Request the GET_ACCOUNTS permission via a user dialog
360 | EasyPermissions.requestPermissions(
361 | MainActivity.this,
362 | getString(R.string.get_accounts_rationale),
363 | REQUEST_PERMISSION_GET_ACCOUNTS,
364 | Manifest.permission.GET_ACCOUNTS);
365 | }
366 | }
367 |
368 | /**
369 | * Returns true if a valid service object has been created and instantiated
370 | * with valid OAuth credentials; returns false otherwise.
371 | * @return true if the service and credentials are valid; false otherwise.
372 | */
373 | private boolean hasValidCredentials() {
374 | return mService != null
375 | && mCredential != null
376 | && mCredential.getSelectedAccountName() != null;
377 | }
378 |
379 | /**
380 | * Respond to requests for permissions at runtime for SDK 23 and above.
381 | * @param requestCode The request code passed in
382 | * requestPermissions(android.app.Activity, String, int, String[])
383 | * @param permissions The requested permissions. Never null.
384 | * @param grantResults The grant results for the corresponding permissions
385 | * which is either PERMISSION_GRANTED or PERMISSION_DENIED. Never null.
386 | */
387 | @Override
388 | public void onRequestPermissionsResult(int requestCode,
389 | @NonNull String[] permissions,
390 | @NonNull int[] grantResults) {
391 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
392 | EasyPermissions.onRequestPermissionsResult(
393 | requestCode, permissions, grantResults, MainActivity.this);
394 | }
395 |
396 | /**
397 | * Callback for when a permission is granted using the EasyPermissions
398 | * library.
399 | * @param requestCode The request code associated with the requested
400 | * permission
401 | * @param list The requested permission list. Never null.
402 | */
403 | @Override
404 | public void onPermissionsGranted(int requestCode, List list) {
405 | // Do nothing.
406 | }
407 |
408 | /**
409 | * Callback for when a permission is denied using the EasyPermissions
410 | * library. Displays status message and disables functionality that
411 | * would require that permission.
412 | * @param requestCode The request code associated with the requested
413 | * permission
414 | * @param list The requested permission list. Never null.
415 | */
416 | @Override
417 | public void onPermissionsDenied(int requestCode, List list) {
418 | toast(getString(R.string.get_accounts_denied_message));
419 | updateButtonEnableStatus();
420 | }
421 |
422 | /**
423 | * Given the position of one of the language spinners, return the language
424 | * id corresponding to that position.
425 | * @param pos spinner position
426 | * @param omitAutoDetect true if the spinner does not include 'Auto-detect'
427 | * as the first option
428 | * @return String two-letter language id
429 | */
430 | private String getLangIdFromSpinnerPosition(int pos, boolean omitAutoDetect) {
431 | String id;
432 | if (omitAutoDetect) {
433 | pos++;
434 | }
435 | switch (pos) {
436 | case 0: id = ""; break; // Auto-detect (input language only)
437 | case 1: id = "ar"; break; // Arabic
438 | case 2: id = "zh-CN"; break; // Chinese (Simplified)
439 | case 3: id = "en"; break; // English
440 | case 4: id = "fr"; break; // French
441 | case 5: id = "de"; break; // German
442 | case 6: id = "hi"; break; // Hindi
443 | case 7: id = "ja"; break; // Japanese
444 | case 8: id = "pt"; break; // Portuguese
445 | case 9: id = "es"; break; // Spanish
446 | default: id = "en"; break;
447 | }
448 | return id;
449 | }
450 |
451 | /**
452 | * Call the API to translate the selected text.
453 | */
454 | private void translate() {
455 | String originalText = mSelectedText.getText().toString();
456 | if (originalText.length() != 0) {
457 | callAppsScriptTask(CALL_TRANSLATE_TEXT);
458 | }
459 | }
460 |
461 | /**
462 | * Call the API to replace the translated text back to the original
463 | * document.
464 | * @param v The button's View context
465 | */
466 | public void replace(View v) {
467 | String translation = mTranslationText.getText().toString();
468 | if (translation.length() != 0) {
469 | callAppsScriptTask(CALL_REPLACE_TEXT);
470 | }
471 | }
472 |
473 | /**
474 | * Cancel the add-on and return without action to the calling app.
475 | * @param v The button's View context
476 | */
477 | public void cancel(View v) {
478 | finishWithState(Activity.RESULT_CANCELED);
479 | }
480 |
481 | /**
482 | * End the add-on and return to the calling application.
483 | * @param state result code for add-on: one of Activity.RESULT_CANCELED or
484 | * Activity.RESULT_OK
485 | */
486 | private void finishWithState(int state) {
487 | dismissProgressDialog();
488 | setResult(state);
489 | finish();
490 | }
491 |
492 | /**
493 | * Display a short toast message.
494 | * @param message text to display
495 | */
496 | private void toast(String message) {
497 | Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
498 | }
499 |
500 | /**
501 | * Checks whether the device currently has a network connection.
502 | * @return true if the device has a network connection, false otherwise
503 | */
504 | private boolean isDeviceOnline() {
505 | ConnectivityManager connMgr =
506 | (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
507 | NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
508 | return (networkInfo != null && networkInfo.isConnected());
509 | }
510 |
511 | /**
512 | * Check that Google Play services APK is installed and up to date.
513 | * @return true if Google Play Services is available and up to
514 | * date on this device; false otherwise.
515 | */
516 | private boolean isGooglePlayServicesAvailable() {
517 | GoogleApiAvailability apiAvailability =
518 | GoogleApiAvailability.getInstance();
519 | final int connectionStatusCode =
520 | apiAvailability.isGooglePlayServicesAvailable(MainActivity.this);
521 | return connectionStatusCode == ConnectionResult.SUCCESS;
522 | }
523 |
524 | /**
525 | * Attempt to resolve a missing, out-of-date, invalid or disabled Google
526 | * Play Services installation via a user dialog, if possible.
527 | */
528 | private void acquireGooglePlayServices() {
529 | GoogleApiAvailability apiAvailability =
530 | GoogleApiAvailability.getInstance();
531 | final int connectionStatusCode =
532 | apiAvailability.isGooglePlayServicesAvailable(MainActivity.this);
533 | if (apiAvailability.isUserResolvableError(connectionStatusCode)) {
534 | showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
535 | }
536 | }
537 | /**
538 | * Display an error dialog showing that Google Play Services is missing
539 | * or out of date.
540 | * @param connectionStatusCode code describing the presence (or lack of)
541 | * Google Play Services on this device
542 | */
543 | private void showGooglePlayServicesAvailabilityErrorDialog(
544 | final int connectionStatusCode) {
545 | Dialog dialog =
546 | GoogleApiAvailability.getInstance().getErrorDialog(
547 | MainActivity.this,
548 | connectionStatusCode,
549 | REQUEST_GOOGLE_PLAY_SERVICES);
550 | dialog.show();
551 | }
552 |
553 | /**
554 | * Check the current connectivity status of the device and enable/disable
555 | * the highlight buttons if the device is online/offline, respectively.
556 | */
557 | private void updateButtonEnableStatus() {
558 | mConnectionAvailable = isDeviceOnline();
559 | boolean enable = mConnectionAvailable && hasValidCredentials();
560 | mReplaceButton.setEnabled(enable);
561 | }
562 |
563 | /**
564 | * Show a dialog with an error message, with a button to cancel out of
565 | * the add-on.
566 | * @param errorMessage Error message to display
567 | */
568 | protected void showErrorDialog(String errorMessage) {
569 | AlertDialog.Builder alertDialogBuilder =
570 | new AlertDialog.Builder(MainActivity.this);
571 | alertDialogBuilder.setTitle(getString(R.string.error_occurred));
572 | alertDialogBuilder
573 | .setMessage(errorMessage)
574 | .setCancelable(false)
575 | .setNegativeButton(
576 | getString(R.string.exit_button),
577 | new DialogInterface.OnClickListener() {
578 | public void onClick(DialogInterface dialog, int id) {
579 | finishWithState(Activity.RESULT_CANCELED);
580 | }
581 | });
582 | dismissProgressDialog();
583 | AlertDialog alertDialog = alertDialogBuilder.create();
584 | alertDialog.show();
585 | }
586 |
587 | /**
588 | * Dismiss the ProgressDialog, if it is visible.
589 | */
590 | public void dismissProgressDialog() {
591 | if (mProgress != null && mProgress.isShowing()) {
592 | Context context =
593 | ((ContextWrapper) mProgress.getContext()).getBaseContext();
594 | // Dismiss only if launching activity hasn't been finished or
595 | // destroyed
596 | if(! (context instanceof Activity &&
597 | ((Activity)context).isFinishing() ||
598 | ((Activity)context).isDestroyed())) {
599 | mProgress.dismiss();
600 | }
601 | }
602 | }
603 |
604 | /**
605 | * This BroadcastReceiver intercepts the
606 | * android.net.ConnectivityManager.CONNECTIVITY_ACTION, which indicates a
607 | * connection change. This is used to determine if the API can be called.
608 | */
609 | public class NetworkReceiver extends BroadcastReceiver {
610 | /**
611 | * Responds to a connection change, recording whether a connection is
612 | * available.
613 | * @param context The Context in which the receiver is running
614 | * @param intent The Intent being received
615 | */
616 | @Override
617 | public void onReceive(Context context, Intent intent) {
618 | // Checks the network connection. Based on the
619 | // result, enables/disables flag to allow API calls and
620 | // enables/disables buttons.
621 | updateButtonEnableStatus();
622 | if (!mConnectionAvailable) {
623 | toast(getString(R.string.no_network));
624 | }
625 | }
626 | }
627 |
628 | /**
629 | * Abstract class for handling Execution API calls. Typically a subclass of
630 | * this is created for each Apps Script function that will be called.
631 | * Placing the API calls in their own task ensures the UI stays responsive.
632 | */
633 | public abstract class CallApiTask extends AsyncTask