├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── NOTICES.md
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── microsoft
│ │ └── office365
│ │ └── connect
│ │ ├── AuthenticationManager.java
│ │ ├── ConnectActivity.java
│ │ ├── Constants.java
│ │ ├── DiscoveryManager.java
│ │ ├── MailManager.java
│ │ ├── OperationCallback.java
│ │ └── SendMailActivity.java
│ └── res
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── layout
│ ├── activity_connect.xml
│ └── activity_send_mail.xml
│ ├── menu
│ └── send_mail.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── loc
└── README-ja.md
├── readme-images
├── O365-Android-Connect-Constants.png
├── O365-Android-Connect-video_play_icon.png
└── o365-exchange-permissions.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.ap_
2 | *.apk
3 | *.class
4 | *.dex
5 | *.iml
6 | *.ipr
7 | *.iws
8 | .DS_Store
9 | .classpath
10 | .gradle
11 | .idea
12 | .project
13 | /.idea/libraries
14 | /.idea/workspace.xml
15 | /build
16 | /captures
17 | /gradle
18 | /gradlew
19 | /gradlew.bat
20 | /local.properties
21 | Thumbs.db
22 | bin/
23 | build/
24 | gen/
25 | gradle/
26 | out/
27 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: android
3 | android:
4 | components:
5 | - tools
6 | - platform-tools
7 | - extra
8 | - android-23
9 | - build-tools-23.0.3
10 | script:
11 | - gradle clean build
12 | notifications:
13 | slack:
14 | secure: JIndOGoBOL+4P1Rmty0GZ2M655HuZTwnWQJA6swCm+h4D4LVQIRqo4kgCdmteMZLVOPraIStFw6A0piKmhiwfA3SP+resO2rCoQQfJkLEfq5aJWph1bUEaoG+LMZwfS5pwHf5TSXk6WpwQ4imxYNiT3hiCA4us1rE6R4SpphupY=
15 | email:
16 | recipients:
17 | - jak@microsoft.com
18 | on_success: never
19 | on_failure: always
20 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Microsoft. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NOTICES.md:
--------------------------------------------------------------------------------
1 | This project includes the following third-party components:
2 |
3 | MS Open Tech Office 365 SDK for Android, which is Copyright (c) Microsoft Open Technologies, Inc. and is available under the [Apache License, Version 2.0](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.
4 |
5 | Android SDK, which is provided by the Android Open Source Project and is used according to terms described in the [Creative Commons 2.5 Attribution License](http://creativecommons.org/licenses/by/2.5/). The Android SDK is available at [http://developer.android.com/sdk/index.html](http://developer.android.com/sdk/index.html).
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Office 365 Connect Sample for Android
2 | [](https://travis-ci.org/OfficeDev/O365-Android-Connect)
3 |
4 | [日本 (日本語)](/loc/README-ja.md) (Japanese)
5 |
6 | [](https://www.youtube.com/watch?v=3IQIDFrqhY4 "Click to see the sample in action")
7 |
8 | Connecting to Office 365 is the first step every Android app must take to start working with Office 365 services and data. This sample shows how to connect and then call one API.
9 |
10 | ## Device requirements
11 |
12 | To run the Connect sample, your device needs to meet the following requirements:
13 |
14 | * A screen size of 800 x 480 or larger.
15 | * Android API level 15 or later.
16 |
17 | ## Prerequisites
18 |
19 | To use the Office 365 Connect sample for Android you need the following:
20 |
21 | * [Android Studio](http://developer.android.com/sdk/index.html) version 1.0 or later.
22 | * [Java Development Kit (JDK) 7](http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html).
23 | * An Office 365 account. You can [join the Office 365 Developer Program and get a free 1 year subscription to Office 365](https://aka.ms/devprogramsignup) that includes the resources that you need to start building Office 365 apps.
24 |
25 | > Note: If you already have a subscription, the previous link sends you to a page that says *Sorry, you can’t add that to your current account*. In that case use an account from your current Office 365 subscription.
26 | If you are already signed-in to Office 365, the Sign-in button in the previous link shows the message *Sorry, we can't process your request*. In that case sign-out from Office 365 in that same page and sign-in again.
27 |
28 | * A Microsoft Azure tenant to register your application. Azure Active Directory provides identity services that applications use for authentication and authorization. A trial subscription can be acquired here: [Microsoft Azure](https://account.windowsazure.com/SignUp).
29 |
30 | > Important: You will also need to ensure your Azure subscription is bound to your Office 365 tenant. To do this see the Active Directory team's blog post, [Creating and Managing Multiple Windows Azure Active Directories](http://blogs.technet.com/b/ad/archive/2013/11/08/creating-and-managing-multiple-windows-azure-active-directories.aspx). The section **Adding a new directory** will explain how to do this. You can also see [Set up your Office 365 development environment](https://msdn.microsoft.com/office/office365/howto/setup-development-environment#bk_CreateAzureSubscription) and the section **Associate your Office 365 account with Azure AD to create and manage apps** for more information.
31 |
32 | * A client id and redirect uri values of an application registered in Azure. The application must be granted the **Send mail as a user** permission. [Add a native client application in Azure](https://msdn.microsoft.com/office/office365/HowTo/add-common-consent-manually#bk_RegisterNativeApp) and [grant proper permissions](https://github.com/OfficeDev/O365-Android-Connect/wiki/Grant-permissions-to-the-Connect-application-in-Azure) to it.
33 |
34 | ## Open the sample using Android Studio
35 |
36 | 1. Install [Android Studio](http://developer.android.com/tools/studio/index.html#install-updates) and add the Android SDK packages according to the [instructions](http://developer.android.com/sdk/installing/adding-packages.html) on developer.android.com.
37 | 2. Download or clone this sample.
38 | 3. Start Android Studio.
39 | 1. Close any projects that you might have open, then select **Open an existing Android Studio project**.
40 | 2. Browse to your local repository and select the O365-Android-Connect project. Click **OK**.
41 |
42 | > Note: Android Studio might display a dialog asking if you want to use Gradle wrapper. Click **OK**.
43 | >
44 | > Additionally, Android Studio shows a **Frameworks detected** notification if you don't have the **Android Support Repository** installed. Open the SDK manager and add the Android Support Repository to avoid the Frameworks detected notification.
45 | 4. Open the [```Constants.java```](app/src/main/java/com/microsoft/office365/connect/Constants.java) file.
46 | 1. Find the [```CLIENT_ID```](app/src/main/java/com/microsoft/office365/connect/Constants.java#L12) constant and set its String value equal to the client id you registered in Azure Active Directory.
47 | 2. Find the [```REDIRECT_URI```](/app/src/main/java/com/microsoft/office365/connect/Constants.java#L13) constant and set its String value equal to the redirect URI you registered in Azure Active Directory.
48 | 
49 |
50 | > Note: If you have don't have CLIENT\_ID and REDIRECT\_URI values, [add a native client application in Azure](https://msdn.microsoft.com/library/azure/dn132599.aspx#BKMK_Adding) and take note of the CLIENT\_ID and REDIRECT_URI.
51 |
52 | Once you've built the Connect sample, you can run it on an emulator or device. Pick a device with API level 15 or higher from the **Choose device** dialog.
53 |
54 | To learn more about the sample, visit our [understanding the code](https://github.com/OfficeDev/O365-Android-Connect/wiki/Understanding-the-Connect-sample-code) wiki page. If you just want to use this code sample in your app, visit the [Using the O365 Android Connect sample code in your app](https://github.com/OfficeDev/O365-Android-Connect/wiki/Using-the-O365-Android-Connect-sample-code-in-your-app).
55 |
56 | ## Questions and comments
57 |
58 | We'd love to get your feedback on the O365 Android Connect project. You can send your questions and suggestions to us in the [Issues](https://github.com/OfficeDev/O365-Android-Connect/issues) section of this repository.
59 |
60 | Questions about Office 365 development in general should be posted to [Stack Overflow](http://stackoverflow.com/questions/tagged/Office365+API). Make sure that your questions or comments are tagged with [Office365] and [API].
61 |
62 | ## Next steps
63 |
64 | This sample just shows the essentials that your apps need to work with Office 365. There is so much more that your apps can do using the Office 365 APIs, like helping your users to manage their work day with calendar, find just the information they need in all the files they store in OneDrive, or find the exact person they need from their list of contacts. We have more to share with you in the [Office 365 APIs Starter Project for Android](https://github.com/officedev/O365-Android-Start/). We think it can help you fuel your ideas.
65 |
66 | ## Additional resources
67 |
68 | * [Office 365 APIs platform overview](https://msdn.microsoft.com/office/office365/howto/platform-development-overview)
69 | * [Office 365 SDK for Android](https://github.com/OfficeDev/Office-365-SDK-for-Android)
70 | * [Get started with Office 365 APIs in apps](https://msdn.microsoft.com/office/office365/howto/getting-started-Office-365-APIs)
71 | * [Office 365 APIs starter projects and code samples](https://msdn.microsoft.com/office/office365/howto/starter-projects-and-code-samples)
72 | * [Office 365 Code Snippets for Android](https://github.com/OfficeDev/O365-Android-Snippets)
73 | * [Office 365 APIs Starter Project for Android](https://github.com/OfficeDev/O365-Android-Start)
74 | * [Office 365 Profile sample for Android](https://github.com/OfficeDev/O365-Android-Profile)
75 |
76 | ## Copyright
77 | Copyright (c) 2015 Microsoft. All rights reserved.
78 |
79 |
80 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
81 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.microsoft.office365.connect"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile 'com.android.support:appcompat-v7:23.3.0'
25 |
26 | // Discovery and Outlook services
27 | compile(group: 'com.microsoft.services', name: 'discovery-services', version: '1.0.0', ext: 'aar'){
28 | transitive = true
29 | }
30 | compile(group: 'com.microsoft.services', name: 'outlook-services', version: '1.0.0', ext: 'aar'){
31 | transitive = true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Program Files (x86)\Android\android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
36 |
39 |
40 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/AuthenticationManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | import android.app.Activity;
8 | import android.content.Context;
9 | import android.content.SharedPreferences;
10 | import android.os.Build;
11 | import android.provider.Settings;
12 | import android.util.Log;
13 |
14 | import com.microsoft.aad.adal.ADALError;
15 | import com.microsoft.aad.adal.AuthenticationCallback;
16 | import com.microsoft.aad.adal.AuthenticationContext;
17 | import com.microsoft.aad.adal.AuthenticationException;
18 | import com.microsoft.aad.adal.AuthenticationResult;
19 | import com.microsoft.aad.adal.AuthenticationResult.AuthenticationStatus;
20 | import com.microsoft.aad.adal.AuthenticationSettings;
21 | import com.microsoft.aad.adal.PromptBehavior;
22 | import com.microsoft.services.orc.core.DependencyResolver;
23 | import com.microsoft.services.orc.log.LogLevel;
24 | import com.microsoft.services.orc.resolvers.ADALDependencyResolver;
25 |
26 | import java.io.UnsupportedEncodingException;
27 |
28 | /**
29 | * Handles setup of ADAL Dependency Resolver for use in API clients.
30 | * Check the {@link AuthenticationManager#connect(AuthenticationCallback)} method to learn how to
31 | * get Azure AD tokens for your app.
32 | * You can also check {@link AuthenticationManager#authenticatePrompt(AuthenticationCallback)} to
33 | * learn how to get tokens by prompting the user for credentials, or
34 | * {@link AuthenticationManager#authenticateSilent(AuthenticationCallback)} to learn how to get
35 | * tokens silently.
36 | * To learn how to dispose the tokens, see {@link AuthenticationManager#disconnect()}.
37 | */
38 |
39 | public class AuthenticationManager {
40 | private static final String TAG = "AuthenticationManager";
41 | private static final String PREFERENCES_FILENAME = "ConnectFile";
42 | private static final String USER_ID_VAR_NAME = "userId";
43 | private AuthenticationContext mAuthenticationContext;
44 | private ADALDependencyResolver mDependencyResolver;
45 | private Activity mContextActivity;
46 | private String mResourceId;
47 |
48 | static{
49 | // Devices with API level lower than 18 must setup an encryption key.
50 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 &&
51 | AuthenticationSettings.INSTANCE.getSecretKeyData() == null) {
52 | AuthenticationSettings.INSTANCE.setSecretKey(generateSecretKey());
53 | }
54 |
55 | // We're not using Microsoft Intune Company portal app,
56 | // skip the broker check so we don't get warnings about the following permissions
57 | // in manifest:
58 | // GET_ACCOUNTS
59 | // USE_CREDENTIALS
60 | // MANAGE_ACCOUNTS
61 | AuthenticationSettings.INSTANCE.setSkipBroker(true);
62 | }
63 |
64 | /**
65 | * Calls {@link AuthenticationManager#authenticatePrompt(AuthenticationCallback)} if no user id is stored in the shared preferences.
66 | * Calls {@link AuthenticationManager#authenticateSilent(AuthenticationCallback)} otherwise.
67 | * @param authenticationCallback The callback to notify when the processing is finished.
68 | */
69 | public void connect(final AuthenticationCallback authenticationCallback) {
70 | // Since we're doing considerable work, let's get out of the main thread
71 | new Thread(new Runnable() {
72 | @Override
73 | public void run() {
74 | if (verifyAuthenticationContext()) {
75 | if (isConnected()) {
76 | authenticateSilent(authenticationCallback);
77 | } else {
78 | authenticatePrompt(authenticationCallback);
79 | }
80 | } else {
81 | Log.e(TAG, "connect - Auth context verification failed. Did you set a context activity?");
82 | throw new AuthenticationException(
83 | ADALError.ACTIVITY_REQUEST_INTENT_DATA_IS_NULL,
84 | "Auth context verification failed. Did you set a context activity?");
85 | }
86 | }
87 | }).start();
88 | }
89 |
90 | /**
91 | * Calls acquireTokenSilent with the user id stored in shared preferences.
92 | * In case of an error, it falls back to {@link AuthenticationManager#authenticatePrompt(AuthenticationCallback)}.
93 | * @param authenticationCallback The callback to notify when the processing is finished.
94 | */
95 | private void authenticateSilent(final AuthenticationCallback authenticationCallback) {
96 | getAuthenticationContext().acquireTokenSilent(
97 | this.mResourceId,
98 | Constants.CLIENT_ID,
99 | getUserId(),
100 | new AuthenticationCallback() {
101 | @Override
102 | public void onSuccess(final AuthenticationResult authenticationResult) {
103 | if (authenticationResult != null && authenticationResult.getStatus() == AuthenticationStatus.Succeeded) {
104 | mDependencyResolver = new ADALDependencyResolver(
105 | getAuthenticationContext(),
106 | mResourceId,
107 | Constants.CLIENT_ID);
108 | authenticationCallback.onSuccess(authenticationResult);
109 | } else if (authenticationResult != null) {
110 | // I could not authenticate the user silently,
111 | // falling back to prompt the user for credentials.
112 | authenticatePrompt(authenticationCallback);
113 | }
114 | }
115 |
116 | @Override
117 | public void onError(Exception e) {
118 | // I could not authenticate the user silently,
119 | // falling back to prompt the user for credentials.
120 | authenticatePrompt(authenticationCallback);
121 | }
122 | }
123 | );
124 | }
125 |
126 | /**
127 | * Calls acquireToken to prompt the user for credentials.
128 | * @param authenticationCallback The callback to notify when the processing is finished.
129 | */
130 | private void authenticatePrompt(final AuthenticationCallback authenticationCallback) {
131 | getAuthenticationContext().acquireToken(
132 | this.mContextActivity,
133 | this.mResourceId,
134 | Constants.CLIENT_ID,
135 | Constants.REDIRECT_URI,
136 | PromptBehavior.Always,
137 | new AuthenticationCallback() {
138 | @Override
139 | public void onSuccess(final AuthenticationResult authenticationResult) {
140 | if (authenticationResult != null && authenticationResult.getStatus() == AuthenticationStatus.Succeeded) {
141 | setUserId(authenticationResult.getUserInfo().getUserId());
142 | mDependencyResolver = new ADALDependencyResolver(
143 | getAuthenticationContext(),
144 | mResourceId,
145 | Constants.CLIENT_ID);
146 | authenticationCallback.onSuccess(authenticationResult);
147 | } else if (authenticationResult != null) {
148 | // We need to make sure that there is no data stored with the failed auth
149 | AuthenticationManager.getInstance().disconnect();
150 | // This condition can happen if user signs in with an MSA account
151 | // instead of an Office 365 account
152 | authenticationCallback.onError(
153 | new AuthenticationException(
154 | ADALError.AUTH_FAILED,
155 | authenticationResult.getErrorDescription()
156 | )
157 | );
158 | }
159 | }
160 |
161 | @Override
162 | public void onError(Exception e) {
163 | // We need to make sure that there is no data stored with the failed auth
164 | AuthenticationManager.getInstance().disconnect();
165 | authenticationCallback.onError(e);
166 | }
167 | }
168 | );
169 | }
170 |
171 | /**
172 | * Disconnects the app from Office 365 by clearing the token cache, setting the client objects
173 | * to null, and removing the user id from shred preferences.
174 | */
175 | public void disconnect(){
176 | // Clear tokens.
177 | if(getAuthenticationContext().getCache() != null) {
178 | getAuthenticationContext().getCache().removeAll();
179 | }
180 |
181 | // Reset the AuthenticationManager object
182 | AuthenticationManager.resetInstance();
183 |
184 | // Forget the user
185 | removeUserId();
186 | }
187 |
188 | public static synchronized AuthenticationManager getInstance() {
189 | if (INSTANCE == null) {
190 | INSTANCE = new AuthenticationManager();
191 | }
192 | return INSTANCE;
193 | }
194 |
195 | private static synchronized void resetInstance() {
196 | INSTANCE = null;
197 | }
198 |
199 | private static AuthenticationManager INSTANCE;
200 |
201 | private AuthenticationManager() {
202 | mResourceId = Constants.DISCOVERY_RESOURCE_ID;
203 | }
204 |
205 | /**
206 | * Set the context activity before connecting to the currently active activity.
207 | * @param contextActivity Currently active activity which can be utilized for interactive
208 | * prompt.
209 | */
210 | public void setContextActivity(final Activity contextActivity) {
211 | this.mContextActivity = contextActivity;
212 | }
213 |
214 | /**
215 | * Change from the default Resource ID set in ServiceConstants to a different
216 | * resource ID.
217 | * This can be called at anytime without requiring another interactive prompt.
218 | * @param resourceId URL of resource ID to be accessed on behalf of user.
219 | */
220 | public void setResourceId(final String resourceId) {
221 | this.mResourceId = resourceId;
222 | this.mDependencyResolver.setResourceId(resourceId);
223 | }
224 |
225 | /**
226 | * Gets authentication context for Azure Active Directory.
227 | * @return an authentication context, if successful.
228 | */
229 | public AuthenticationContext getAuthenticationContext() {
230 | if (mAuthenticationContext == null) {
231 | try {
232 | mAuthenticationContext = new AuthenticationContext(this.mContextActivity, Constants.AUTHORITY_URL, false);
233 | } catch (Throwable t) {
234 | Log.e(TAG, t.toString());
235 | }
236 | }
237 | return mAuthenticationContext;
238 | }
239 |
240 | /**
241 | * Dependency resolver that can be used to create client objects.
242 | * The {@link DiscoveryManager#getServiceInfo} method uses it to create a DiscoveryClient object.
243 | * The {@link MailManager#sendMail(String, String, String, OperationCallback)} uses it to create an OutlookClient object.
244 | * @return The dependency resolver object.
245 | */
246 | public DependencyResolver getDependencyResolver() {
247 | return getInstance().mDependencyResolver;
248 | }
249 |
250 | private boolean verifyAuthenticationContext() {
251 | if (this.mContextActivity == null) {
252 | Log.e(TAG, "Must set context activity");
253 | return false;
254 | }
255 | return true;
256 | }
257 |
258 | private boolean isConnected(){
259 | SharedPreferences settings = this
260 | .mContextActivity
261 | .getSharedPreferences(PREFERENCES_FILENAME, Context.MODE_PRIVATE);
262 |
263 | return settings.contains(USER_ID_VAR_NAME);
264 | }
265 |
266 | private String getUserId(){
267 | SharedPreferences settings = this
268 | .mContextActivity
269 | .getSharedPreferences(PREFERENCES_FILENAME, Context.MODE_PRIVATE);
270 |
271 | return settings.getString(USER_ID_VAR_NAME, "");
272 | }
273 |
274 | private void setUserId(String value){
275 | SharedPreferences settings = this
276 | .mContextActivity
277 | .getSharedPreferences(PREFERENCES_FILENAME, Context.MODE_PRIVATE);
278 |
279 | SharedPreferences.Editor editor = settings.edit();
280 | editor.putString(USER_ID_VAR_NAME, value);
281 | editor.apply();
282 | }
283 |
284 | private void removeUserId(){
285 | SharedPreferences settings = this
286 | .mContextActivity
287 | .getSharedPreferences(PREFERENCES_FILENAME, Context.MODE_PRIVATE);
288 |
289 | SharedPreferences.Editor editor = settings.edit();
290 | editor.remove(USER_ID_VAR_NAME);
291 | editor.apply();
292 | }
293 |
294 | /**
295 | * Generates an encryption key for devices with API level lower than 18 using the
296 | * ANDROID_ID value as a seed.
297 | * In production scenarios, you should come up with your own implementation of this method.
298 | * Consider that your algorithm must return the same key so it can encrypt/decrypt values
299 | * successfully.
300 | * @return The encryption key in a 32 byte long array.
301 | */
302 | private static byte[] generateSecretKey() {
303 | byte[] key = new byte[32];
304 | byte[] android_id;
305 |
306 | try{
307 | android_id = Settings.Secure.ANDROID_ID.getBytes("UTF-8");
308 | } catch (UnsupportedEncodingException e){
309 | Log.e(TAG, "generateSecretKey - " + e.getMessage());
310 | throw new RuntimeException(e);
311 | }
312 |
313 | for(int i = 0; i < key.length; i++){
314 | key[i] = android_id[i % android_id.length];
315 | }
316 |
317 | return key;
318 | }
319 |
320 | /**
321 | * Turn logging on.
322 | * @param level LogLevel to set.
323 | */
324 | public void enableLogging(LogLevel level) {
325 | this.mDependencyResolver.getLogger().setEnabled(true);
326 | this.mDependencyResolver.getLogger().setLogLevel(level);
327 | }
328 |
329 | /**
330 | * Turn logging off.
331 | */
332 | public void disableLogging() {
333 | this.mDependencyResolver.getLogger().setEnabled(false);
334 | }
335 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/ConnectActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | import android.content.Intent;
8 | import android.os.Bundle;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.ProgressBar;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import com.microsoft.aad.adal.AuthenticationCallback;
18 | import com.microsoft.aad.adal.AuthenticationResult;
19 |
20 | import java.net.URI;
21 | import java.util.UUID;
22 |
23 | /**
24 | * Starting activity of the app. Handles the connection to Office 365.
25 | * When it first starts it only displays a button to Connect to Office 365.
26 | * If there are no cached tokens, the user is required to sign in to Office 365.
27 | * If there are cached tokens, the app tries to reuse them.
28 | * The activity redirects the user to the SendMailActivity upon successful connection.
29 | */
30 | public class ConnectActivity extends AppCompatActivity {
31 |
32 | private static final String TAG = "ConnectActivity";
33 |
34 | private Button mConnectButton;
35 | private TextView mTitleTextView;
36 | private ProgressBar mConnectProgressBar;
37 | private TextView mDescriptionTextView;
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_connect);
43 |
44 | initializeViews();
45 | }
46 |
47 | /**
48 | * Event handler for the onclick event of the button.
49 | * @param v The view that sent the event.
50 | */
51 | public void onConnectButtonClick(View v) {
52 | showConnectingInProgressUI();
53 |
54 | //check that client id and redirect have been set correctly
55 | try {
56 | UUID.fromString(Constants.CLIENT_ID);
57 | URI.create(Constants.REDIRECT_URI);
58 | }
59 | catch (IllegalArgumentException e) {
60 | Toast.makeText(
61 | this
62 | , getString(R.string.warning_client_id_redirect_uri_incorrect)
63 | , Toast.LENGTH_LONG).show();
64 |
65 | resetUIForConnect();
66 | return;
67 | }
68 |
69 | final Intent sendMailIntent = new Intent(this, SendMailActivity.class);
70 | AuthenticationManager.getInstance().setContextActivity(this);
71 |
72 | AuthenticationManager.getInstance().connect(
73 | new AuthenticationCallback() {
74 | /**
75 | * If the connection is successful, the activity extracts the username and
76 | * displayableId values from the authentication result object and sends them
77 | * to the SendMail activity.
78 | * @param result The authentication result object that contains information about
79 | * the user and the tokens.
80 | */
81 | @Override
82 | public void onSuccess(AuthenticationResult result) {
83 | Log.i(TAG, "onConnectButtonClick - Successfully connected to Office 365");
84 |
85 | sendMailIntent.putExtra("givenName", result
86 | .getUserInfo()
87 | .getGivenName());
88 | sendMailIntent.putExtra("displayableId", result
89 | .getUserInfo()
90 | .getDisplayableId());
91 | startActivity(sendMailIntent);
92 |
93 | resetUIForConnect();
94 | }
95 |
96 | @Override
97 | public void onError(final Exception e) {
98 | Log.e(TAG, "onCreate - " + e.getMessage());
99 | showConnectErrorUI();
100 | }
101 | });
102 | }
103 |
104 | /**
105 | * This activity gets notified about the completion of the ADAL activity through this method.
106 | * @param requestCode The integer request code originally supplied to startActivityForResult(),
107 | * allowing you to identify who this result came from.
108 | * @param resultCode The integer result code returned by the child activity through its
109 | * setResult().
110 | * @param data An Intent, which can return result data to the caller (various data
111 | * can be attached to Intent "extras").
112 | */
113 | @Override
114 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
115 | Log.i(TAG, "onActivityResult - AuthenticationActivity has come back with results");
116 | super.onActivityResult(requestCode, resultCode, data);
117 | AuthenticationManager
118 | .getInstance()
119 | .getAuthenticationContext()
120 | .onActivityResult(requestCode, resultCode, data);
121 | }
122 |
123 | private void initializeViews(){
124 | mConnectButton = (Button)findViewById(R.id.connectButton);
125 | mConnectProgressBar = (ProgressBar)findViewById(R.id.connectProgressBar);
126 | mTitleTextView = (TextView)findViewById(R.id.titleTextView);
127 | mDescriptionTextView = (TextView)findViewById(R.id.descriptionTextView);
128 | }
129 |
130 | private void resetUIForConnect(){
131 | mConnectButton.setVisibility(View.VISIBLE);
132 | mTitleTextView.setVisibility(View.GONE);
133 | mDescriptionTextView.setVisibility(View.GONE);
134 | mConnectProgressBar.setVisibility(View.GONE);
135 | }
136 |
137 | private void showConnectingInProgressUI(){
138 | mConnectButton.setVisibility(View.GONE);
139 | mTitleTextView.setVisibility(View.GONE);
140 | mDescriptionTextView.setVisibility(View.GONE);
141 | mConnectProgressBar.setVisibility(View.VISIBLE);
142 | }
143 |
144 | private void showConnectErrorUI(){
145 | mConnectButton.setVisibility(View.VISIBLE);
146 | mConnectProgressBar.setVisibility(View.GONE);
147 | mTitleTextView.setText(R.string.title_text_error);
148 | mTitleTextView.setVisibility(View.VISIBLE);
149 | mDescriptionTextView.setText(R.string.connect_text_error);
150 | mDescriptionTextView.setVisibility(View.VISIBLE);
151 | Toast.makeText(
152 | ConnectActivity.this,
153 | R.string.connect_toast_text_error,
154 | Toast.LENGTH_LONG).show();
155 | }
156 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/Constants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | interface Constants {
8 | String AUTHORITY_URL = "https://login.microsoftonline.com/common";
9 | String DISCOVERY_RESOURCE_URL = "https://api.office.com/discovery/v1.0/me/";
10 | String DISCOVERY_RESOURCE_ID = "https://api.office.com/discovery/";
11 | String MAIL_CAPABILITY = "Mail";
12 | // Update these two constants with the values for your application:
13 | String CLIENT_ID = "";
14 | String REDIRECT_URI = "";
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/DiscoveryManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | import android.util.Log;
8 |
9 | import com.microsoft.services.discovery.ServiceInfo;
10 | import com.microsoft.services.discovery.fetchers.DiscoveryClient;
11 | import com.microsoft.services.orc.resolvers.ADALDependencyResolver;
12 |
13 | import java.util.List;
14 | import java.util.NoSuchElementException;
15 | import java.util.concurrent.ExecutionException;
16 |
17 | /**
18 | * Handles the discovery of the service endpoints
19 | * for the capabilities that the user has access to
20 | * in Office 365.
21 | */
22 | public class DiscoveryManager {
23 |
24 | private static final String TAG = "DiscoveryManager";
25 |
26 | private List mServices;
27 |
28 | public static synchronized DiscoveryManager getInstance() {
29 | if (INSTANCE == null) {
30 | INSTANCE = new DiscoveryManager();
31 | }
32 | return INSTANCE;
33 | }
34 |
35 | private static DiscoveryManager INSTANCE;
36 |
37 | /**
38 | * Provides information about the service that corresponds to the provided capability.
39 | * Gets the info from a local cache.
40 | * Calls {@link DiscoveryManager#getServiceInfoFromDiscoveryService(String, OperationCallback)}
41 | * if the service info was not found in cache.
42 | * @param capability A string that contains the capability of the service that
43 | * is going to be discovered.
44 | * @param operationCallback The callback to which return the result or error.
45 | */
46 | public void getServiceInfo(final String capability, final OperationCallback operationCallback) {
47 | // Since we're doing considerable work, let's get out of the main thread
48 | new Thread(new Runnable() {
49 | @Override
50 | public void run() {
51 | // First, look in the locally cached services.
52 | if(mServices != null) {
53 | for (ServiceInfo serviceInfo : mServices) {
54 | if (serviceInfo.getCapability().equals(capability)) {
55 | Log.i(TAG, "getServiceInfo - " + serviceInfo.getServiceName() + " service for " + capability + " was found in local cached services");
56 | operationCallback.onSuccess(serviceInfo);
57 | return;
58 | }
59 | }
60 |
61 | // We already cached the services but couldn't find the requested service in local cache
62 | Log.e(TAG, "getServiceInfo - The " + capability + " capability was not found in the local cached services. "
63 | + "Falling back to the discovery service");
64 | getServiceInfoFromDiscoveryService(capability, operationCallback);
65 | } else {
66 | // The services have not been cached yet. Go ask the discovery service.
67 | getServiceInfoFromDiscoveryService(capability, operationCallback);
68 | }
69 | }
70 | }).start();
71 | }
72 |
73 | /**
74 | * Provides information about the service that corresponds to the provided capability.
75 | * Gets the info from the discovery service.
76 | * @param capability A string that contains the capability of the service that
77 | * is going to be discovered.
78 | * @param operationCallback The callback to which return the result or error.
79 | */
80 | private void getServiceInfoFromDiscoveryService(final String capability, final OperationCallback operationCallback) {
81 | try {
82 | AuthenticationManager.getInstance().setResourceId(Constants.DISCOVERY_RESOURCE_ID);
83 | ADALDependencyResolver dependencyResolver = (ADALDependencyResolver) AuthenticationManager
84 | .getInstance()
85 | .getDependencyResolver();
86 |
87 | DiscoveryClient discoveryClient = new DiscoveryClient(Constants.DISCOVERY_RESOURCE_URL, dependencyResolver);
88 |
89 | List services =
90 | discoveryClient
91 | .getServices()
92 | .select("serviceResourceId,serviceEndpointUri,serviceName,capability")
93 | .read().get();
94 |
95 | Log.i(TAG, "getServiceInfoFromDiscoveryService - Services discovered\n");
96 | // Save the discovered services to serve further requests from the local cache.
97 | mServices = services;
98 |
99 | for (ServiceInfo serviceInfo : services) {
100 | if (serviceInfo.getCapability().equals(capability)) {
101 | // We found the service, send the info to the caller and end this method call
102 | Log.i(TAG, "getServiceInfoFromDiscoveryService - " + serviceInfo.getServiceName() + " service for " + capability + " was found in services retrieved from discovery");
103 | operationCallback.onSuccess(serviceInfo);
104 | return;
105 | }
106 | }
107 |
108 | // We haven't cached the services but couldn't find the requested service in discovery service
109 | NoSuchElementException noSuchElementException = new NoSuchElementException("The " + capability + " capability was not found in the user services.");
110 | Log.e(TAG, "getServiceInfoFromDiscoveryService - " + noSuchElementException.getMessage());
111 | operationCallback.onError(noSuchElementException);
112 | } catch (InterruptedException | ExecutionException e) {
113 | Log.e(TAG, "getServiceInfoFromDiscoveryService - " + e.getMessage());
114 | operationCallback.onError(e);
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/MailManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | import android.util.Log;
8 |
9 | import com.microsoft.services.orc.resolvers.ADALDependencyResolver;
10 | import com.microsoft.services.outlook.BodyType;
11 | import com.microsoft.services.outlook.EmailAddress;
12 | import com.microsoft.services.outlook.ItemBody;
13 | import com.microsoft.services.outlook.Message;
14 | import com.microsoft.services.outlook.Recipient;
15 | import com.microsoft.services.outlook.fetchers.OutlookClient;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.MissingResourceException;
20 | import java.util.concurrent.ExecutionException;
21 |
22 | /**
23 | * Handles the creation of the message and contacting the mail service to send the message.
24 | * The app must have connected to Office 365 and discovered the mail service endpoints before
25 | * sending an email.
26 | */
27 | public class MailManager {
28 |
29 | private static final String TAG = "MailManager";
30 |
31 | private String mServiceResourceId;
32 | private String mServiceEndpointUri;
33 |
34 | /**
35 | * Sends an email message using the Office 365 mail capability from the address of the
36 | * signed in user. You need to initialize the MailManager by calling
37 | * - {@link MailManager#setServiceResourceId(String)}
38 | * - {@link MailManager#setServiceEndpointUri(String)}
39 | * @param emailAddress The recipient email address.
40 | * @param subject The subject to use in the mail message.
41 | * @param body The body of the message.
42 | * @param operationCallback The callback to which return the result or error.
43 | */
44 | public void sendMail(final String emailAddress, final String subject, final String body, final OperationCallback operationCallback) {
45 |
46 | if(!isReady()){
47 | throw new MissingResourceException(
48 | "You must set the ServiceResourceId and ServiceEndPointUri before using sendMail",
49 | "MailManager",
50 | "ServiceResourceId, ServiceEndPointUri"
51 | );
52 | }
53 |
54 | // Since we're doing considerable work, let's get out of the main thread
55 | new Thread(new Runnable() {
56 | @Override
57 | public void run() {
58 | try {
59 | AuthenticationManager.getInstance().setResourceId(mServiceResourceId);
60 | ADALDependencyResolver dependencyResolver = (ADALDependencyResolver) AuthenticationManager
61 | .getInstance()
62 | .getDependencyResolver();
63 |
64 | OutlookClient mailClient = new OutlookClient(mServiceEndpointUri, dependencyResolver);
65 |
66 | // Prepare the message.
67 | List recipientList = new ArrayList<>();
68 |
69 | Recipient recipient = new Recipient();
70 | EmailAddress email = new EmailAddress();
71 | email.setAddress(emailAddress);
72 | recipient.setEmailAddress(email);
73 | recipientList.add(recipient);
74 |
75 | Message messageToSend = new Message();
76 | messageToSend.setToRecipients(recipientList);
77 |
78 | ItemBody bodyItem = new ItemBody();
79 | bodyItem.setContentType(BodyType.HTML);
80 | bodyItem.setContent(body);
81 | messageToSend.setBody(bodyItem);
82 | messageToSend.setSubject(subject);
83 |
84 | // Contact the Office 365 service and deliver the message.
85 | Integer mailId = mailClient
86 | .getMe()
87 | .getOperations()
88 | .sendMail(messageToSend, true).get();
89 |
90 | Log.i(TAG, "sendMail - Email with ID: " + mailId + "sent");
91 | operationCallback.onSuccess(mailId);
92 | } catch (InterruptedException | ExecutionException e) {
93 | Log.e(TAG, "sendMail - " + e.getMessage());
94 | operationCallback.onError(e);
95 | }
96 | }
97 | }).start();
98 | }
99 |
100 | public static synchronized MailManager getInstance() {
101 | if (INSTANCE == null) {
102 | INSTANCE = new MailManager();
103 | }
104 | return INSTANCE;
105 | }
106 |
107 | private static MailManager INSTANCE;
108 |
109 | /**
110 | * Store the service resource id from the discovered service.
111 | * @param serviceResourceId The service resource id obtained from the discovery service.
112 | */
113 | public void setServiceResourceId(final String serviceResourceId) {
114 | this.mServiceResourceId = serviceResourceId;
115 | }
116 |
117 | /**
118 | * Store the service endpoint uri from the discovered service.
119 | * @param serviceEndpointUri The service endpoint uri obtained from the discovery service.
120 | */
121 | public void setServiceEndpointUri(final String serviceEndpointUri) {
122 | this.mServiceEndpointUri = serviceEndpointUri;
123 | }
124 |
125 | /**
126 | * Check to see if the service resource id and service endpoint uri values have been set.
127 | * @return True if service resource id and service endpoint uri have been set, false otherwise.
128 | */
129 | private boolean isReady(){
130 | return mServiceEndpointUri != null && mServiceResourceId != null;
131 | }
132 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/OperationCallback.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | /**
8 | * Callback interface for Office 365 operations
9 | * such as discovering a service or sending email.
10 | * @param The result of the operation in case of success.
11 | */
12 | interface OperationCallback {
13 | /**
14 | * The method to call in case of success.
15 | * @param result The result of the operation.
16 | */
17 | void onSuccess(T result);
18 |
19 | /**
20 | * The method to call in case of failure.
21 | * @param e The exception or reason of failure.
22 | */
23 | void onError(Exception e);
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/microsoft/office365/connect/SendMailActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
3 | * See LICENSE in the project root for license information.
4 | */
5 | package com.microsoft.office365.connect;
6 |
7 | import android.content.Intent;
8 | import android.os.Bundle;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.util.Log;
11 | import android.view.Menu;
12 | import android.view.MenuItem;
13 | import android.view.View;
14 | import android.widget.Button;
15 | import android.widget.EditText;
16 | import android.widget.ProgressBar;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 | import com.microsoft.services.discovery.ServiceInfo;
21 |
22 | import java.text.MessageFormat;
23 |
24 | /**
25 | * This activity handles the send mail operation of the app.
26 | * The app must be connected to Office 365 before this activity can send an email.
27 | * The activity uses the DiscoveryManager class to get the service endpoint. It also
28 | * uses the MailManager to send the message.
29 | */
30 | public class SendMailActivity extends AppCompatActivity {
31 |
32 | private static final String TAG = "SendMailActivity";
33 |
34 | private TextView mTitleTextView;
35 | private TextView mDescriptionTextView;
36 | private EditText mEmailEditText;
37 | private Button mSendMailButton;
38 | private ProgressBar mSendMailProgressBar;
39 | private TextView mConclusionTextView;
40 |
41 | @Override
42 | protected void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_send_mail);
45 |
46 | initializeViews();
47 |
48 | // Extract the givenName and displayableId and use it in the UI.
49 | mTitleTextView.append(getIntent()
50 | .getStringExtra("givenName") + "!");
51 | mEmailEditText.setText(getIntent()
52 | .getStringExtra("displayableId"));
53 |
54 | // We don't need to wait for user input to discover the mail service,
55 | // so we just do it
56 | discoverMailService();
57 | }
58 |
59 | /**
60 | * Locates the service endpoints for the mail service using the DiscoveryManager class.
61 | */
62 | public void discoverMailService(){
63 | resetUIForDiscoverMailService();
64 |
65 | // DiscoveryManager does its job in a worker thread
66 | // we can just call getServiceInfo
67 | DiscoveryManager
68 | .getInstance()
69 | .getServiceInfo(Constants.MAIL_CAPABILITY,
70 | new OperationCallback() {
71 | @Override
72 | public void onSuccess(final ServiceInfo serviceInfo) {
73 | Log.i(TAG, "discoverMailService - Mail service discovered");
74 |
75 | // Initialize MailManager with ResourceID and ServiceEndpointURI
76 | MailManager
77 | .getInstance()
78 | .setServiceResourceId(
79 | serviceInfo.getServiceResourceId()
80 | );
81 | MailManager
82 | .getInstance()
83 | .setServiceEndpointUri(
84 | serviceInfo.getServiceEndpointUri()
85 | );
86 |
87 | showDiscoverSuccessUI();
88 | }
89 |
90 | @Override
91 | public void onError(Exception e) {
92 | Log.e(TAG, "discoverMailService - " + e.getMessage());
93 | showDiscoverErrorUI();
94 | }
95 | }
96 | );
97 | }
98 |
99 | /**
100 | * Handler for the onclick event of the send mail button. It uses the MailManager
101 | * class to send an email to the address stored in the mEmailEditText view.
102 | * The subject and body of the message is stored in the strings.xml file.
103 | * @param v The view that sent the event.
104 | */
105 | public void onSendMailButtonClick(View v){
106 | resetUIForSendMail();
107 |
108 | // MailManager does its job in a worker thread
109 | // we can just call sendMail
110 | MailManager.getInstance().sendMail(
111 | mEmailEditText.getText().toString(),
112 | getResources().getString(R.string.mail_subject_text),
113 | MessageFormat.format(
114 | getResources().getString(R.string.mail_body_text),
115 | getIntent().getStringExtra("givenName")
116 | ),
117 | new OperationCallback() {
118 | @Override
119 | public void onSuccess(Integer result) {
120 | Log.i(TAG, "onSendMailButtonClick - Mail sent");
121 | showSendMailSuccessUI();
122 | }
123 |
124 | @Override
125 | public void onError(Exception e) {
126 | Log.e(TAG, "onSendMailButtonClick - " + e.getMessage());
127 | showSendMailErrorUI();
128 | }
129 | }
130 | );
131 | }
132 |
133 | @Override
134 | public boolean onCreateOptionsMenu(Menu menu) {
135 | // Inflate the menu; this adds items to the action bar if it is present.
136 | getMenuInflater().inflate(R.menu.send_mail, menu);
137 | return true;
138 | }
139 |
140 | @Override
141 | public boolean onOptionsItemSelected(MenuItem item) {
142 | try {
143 | switch (item.getItemId()) {
144 | case R.id.disconnectMenuItem:
145 | AuthenticationManager.getInstance().disconnect();
146 | showDisconnectSuccessUI();
147 | Intent connectIntent = new Intent(this, ConnectActivity.class);
148 | startActivity(connectIntent);
149 | return true;
150 | default:
151 | return super.onOptionsItemSelected(item);
152 | }
153 |
154 | } catch (Throwable t) {
155 | if (t.getMessage() == null)
156 | Log.e(TAG, " ");
157 | else
158 | Log.e(TAG, t.getMessage());
159 | }
160 | return true;
161 | }
162 |
163 | private void initializeViews(){
164 | mTitleTextView = (TextView)findViewById(R.id.titleTextView);
165 | mDescriptionTextView = (TextView)findViewById(R.id.descriptionTextView);
166 | mEmailEditText = (EditText)findViewById(R.id.emailEditText);
167 | mSendMailButton = (Button)findViewById(R.id.sendMailButton);
168 | mSendMailProgressBar = (ProgressBar)findViewById(R.id.sendMailProgressBar);
169 | mConclusionTextView = (TextView)findViewById(R.id.conclusionTextView);
170 | }
171 |
172 | private void resetUIForDiscoverMailService(){
173 | mSendMailButton.setVisibility(View.GONE);
174 | mConclusionTextView.setVisibility(View.GONE);
175 | mSendMailProgressBar.setVisibility(View.VISIBLE);
176 | }
177 |
178 | private void resetUIForSendMail(){
179 | mSendMailButton.setVisibility(View.GONE);
180 | mConclusionTextView.setVisibility(View.GONE);
181 | mSendMailProgressBar.setVisibility(View.VISIBLE);
182 | }
183 |
184 | private void showDiscoverSuccessUI(){
185 | runOnUiThread(new Runnable() {
186 | @Override
187 | public void run() {
188 | // Now that we have discovered the mail service, show the send mail button
189 | mSendMailButton.setVisibility(View.VISIBLE);
190 | mSendMailProgressBar.setVisibility(View.GONE);
191 |
192 | Toast.makeText(
193 | SendMailActivity.this,
194 | R.string.discover_toast_text,
195 | Toast.LENGTH_SHORT).show();
196 | }
197 | });
198 | }
199 |
200 | private void showDiscoverErrorUI(){
201 | runOnUiThread(new Runnable() {
202 | @Override
203 | public void run(){
204 | mSendMailProgressBar.setVisibility(View.GONE);
205 | mSendMailButton.setVisibility(View.VISIBLE);
206 | mConclusionTextView.setText(R.string.discover_text_error);
207 | mConclusionTextView.setVisibility(View.VISIBLE);
208 | Toast.makeText(
209 | SendMailActivity.this,
210 | R.string.discover_toast_text_error,
211 | Toast.LENGTH_LONG).show();
212 | }
213 | });
214 | }
215 |
216 | private void showSendMailSuccessUI(){
217 | runOnUiThread(new Runnable() {
218 | @Override
219 | public void run(){
220 | mSendMailProgressBar.setVisibility(View.GONE);
221 | mSendMailButton.setVisibility(View.VISIBLE);
222 | mConclusionTextView.setText(R.string.conclusion_text);
223 | mConclusionTextView.setVisibility(View.VISIBLE);
224 | Toast.makeText(
225 | SendMailActivity.this,
226 | R.string.send_mail_toast_text,
227 | Toast.LENGTH_SHORT).show();
228 | }
229 | });
230 | }
231 |
232 | private void showSendMailErrorUI(){
233 | runOnUiThread(new Runnable() {
234 | @Override
235 | public void run(){
236 | mSendMailProgressBar.setVisibility(View.GONE);
237 | mSendMailButton.setVisibility(View.VISIBLE);
238 | mConclusionTextView.setText(R.string.send_mail_text_error);
239 | mConclusionTextView.setVisibility(View.VISIBLE);
240 | Toast.makeText(
241 | SendMailActivity.this,
242 | R.string.send_mail_toast_text_error,
243 | Toast.LENGTH_LONG).show();
244 | }
245 | });
246 | }
247 |
248 | private void showDisconnectSuccessUI(){
249 | mTitleTextView.setVisibility(View.GONE);
250 | mDescriptionTextView.setVisibility(View.GONE);
251 | mEmailEditText.setVisibility(View.GONE);
252 | mSendMailButton.setVisibility(View.GONE);
253 | mConclusionTextView.setVisibility(View.GONE);
254 |
255 | Toast.makeText(
256 | SendMailActivity.this,
257 | R.string.disconnect_toast_text,
258 | Toast.LENGTH_SHORT).show();
259 | }
260 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OfficeDev/O365-Android-Connect/fa87d9c355bcea1e9b7c75ab09b4f01fbce05659/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_connect.xml:
--------------------------------------------------------------------------------
1 |
5 |
13 |
14 |
22 |
23 |
30 |
31 |
40 |
41 |
50 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_send_mail.xml:
--------------------------------------------------------------------------------
1 |
5 |
13 |
14 |
23 |
24 |
33 |
34 |
42 |
43 |
52 |
53 |
60 |
61 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/send_mail.xml:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 48dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | Office 365 Connect sample
10 | Connect to Office 365
11 | Send e-mail
12 | Disconnect
13 | Hi\u00A0
14 | \nYou\'re now connected to Office 365. Tap the button below to send a message from your account using the Office 365 SDK.\n
15 | \nCheck your inbox, you have a new message :)
16 |
17 |
18 | Connected to Office 365
19 | Found Office 365 mail service
20 | Mail sent
21 | Disconnected from Office 365
22 |
23 |
24 | Oops!
25 | \nSorry, can\'t connect to Office 365.\nHint: Check logcat for details.
26 | \nSorry, can\'t find the mail service.\nHint: Check logcat for details.
27 | \nSorry, can\'t send the email.\nHint: Check logcat for details.
28 |
29 |
30 | Error connecting to Office 365\nCheck logcat for details
31 | Error finding mail service\nCheck logcat for details
32 | Error sending email\nCheck logcat for details
33 | The client ID or redirect URI is not valid. Enter a valid client ID and redirect URI in the Constants.java file and run again
34 |
35 |
36 | Welcome to Office 365 development on Android with the Office 365 Connect sample
37 | <html><head>
38 | <meta http-equiv=\'Content-Type\' content=\'text/html; charset=us-ascii\'>
39 | <title></title>
40 | </head>
41 | <body style=\'font-family:calibri\'>
42 | <h2>Congratulations, {0}!</h2>
43 | <p>This is a message from the Office 365 Connect sample. You are well on your way to incorporating Office 365 services
44 | in your apps.
45 | </p>
46 | <p>What’s next?</p>
47 | <p>You can use the classes in the Android Studio project to integrate Office 365 services and data into your own apps.</p>
48 | <h2>Give us feedback</h2>
49 | <p>We hope you found this sample useful. We’d love to hear from you, so drop us an email at
50 | <a href=\'mailto:docthis@microsoft.com?subject=Feedback%20on%20the%20Office%20365%20Connect%20sample\'>
51 | docthis@microsoft.com</a> with your comments or <a href=\'http://aka.ms/o365-android-connect-issues\'>
52 | log an issue</a> on our repository. </p>
53 | <p>For more details on what else you can do with the Office 365 services in your Android app, start with the
54 | <a href=\'http://aka.ms/o365-android-connect-getstarted\'>Getting started</a> page on dev.office.com.
55 | </p>
56 | <p>Thanks, and happy coding!<br>
57 | Your Office 365 Development team </p>
58 | <div style=\'text-align:center; font-family:calibri\'>
59 | <table style=\'width:100%; font-family:calibri\'>
60 | <tbody>
61 | <tr>
62 | <td><a href=\'http://aka.ms/o365-android-connect\'>See on GitHub</a>
63 | </td>
64 | <td><a href=\'http://aka.ms/o365-android-connect-uservoice\'>Suggest on UserVoice</a>
65 | </td>
66 | <td><a href=\'http://twitter.com/share?text=I%20just%20started%20developing%20apps%20for%20%23Android%20using%20the%20%23Office365%20Connect%20app%20%40OfficeDev&url=https://github.com/OfficeDev/O365-Android-Connect\'>Share on Twitter</a>
67 | </td>
68 | </tr>
69 | </tbody>
70 | </table>
71 | </div>
72 | </body>
73 | </html>
74 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.5.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/loc/README-ja.md:
--------------------------------------------------------------------------------
1 | # Android 用 Office 365 Connect のサンプル
2 | [](https://travis-ci.org/OfficeDev/O365-Android-Connect)
3 |
4 | [日本 (日本語)](/loc/README-ja.md) (日本語)
5 |
6 | [](https://www.youtube.com/watch?v=3IQIDFrqhY4 "稼働中のサンプルを確認するにはこちらをクリックしてください")
7 |
8 | Office 365 への接続は、各 Android アプリが Office 365 のサービスおよびデータの操作を開始するために必要な最初の手順です。このサンプルは、1 つの API に接続してから呼び出す方法を示しています。
9 |
10 | ## デバイスの要件
11 |
12 | Connect のサンプルを実行するには、デバイスが次の要件を満たしている必要があります。
13 |
14 | * 画面のサイズが 800 x 480 以上である。
15 | * Android の API レベルが 15 以上である。
16 |
17 | ## 前提条件
18 |
19 | Android 用 Office 365 Connect のサンプルを使用するには以下が必要です。
20 |
21 | * [Android Studio](http://developer.android.com/sdk/index.html) バージョン 1.0 以上。
22 | * [Java 開発キット (JDK) 7](http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html)。
23 | * Office 365 アカウント。[Office 365 開発者プログラムに参加し、Office 365 の 1 年間の無料サブスクリプションを取得](https://aka.ms/devprogramsignup)できます。このサブスクリプションには、Office 365 アプリのビルドを開始するために必要なリソースが含まれています。
24 |
25 | > 注:サブスクリプションが既に存在する場合、上記のリンクをクリックすると、*[申し訳ありません、現在のアカウントに追加できません]* と表示されたページに移動します。その場合は、現在使っている Office 365 サブスクリプションのアカウントをご利用いただけます。