11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/aws-samples/aws-mobile-android-kotlin-notes/issues), or [recently closed](https://github.com/aws-samples/aws-mobile-android-kotlin-notes/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/aws-mobile-android-kotlin-notes/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/aws-samples/aws-mobile-android-kotlin-notes/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the "Software"), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify,
6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7 | permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | ** Android Architecture Components; version 1.1.1 -- https://developer.android.com
2 | ** Android Support Library; version 27.1.1 -- https://developer.android.com
3 |
4 | Apache License
5 | Version 2.0, January 2004
6 | http://www.apache.org/licenses/
7 |
8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
9 |
10 | 1. Definitions.
11 |
12 | "License" shall mean the terms and conditions for use, reproduction, and
13 | distribution as defined by Sections 1 through 9 of this document.
14 |
15 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
16 | owner that is granting the License.
17 |
18 | "Legal Entity" shall mean the union of the acting entity and all other entities
19 | that control, are controlled by, or are under common control with that entity.
20 | For the purposes of this definition, "control" means (i) the power, direct or
21 | indirect, to cause the direction or management of such entity, whether by
22 | contract or 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 exercising
26 | permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications, including
29 | but not limited to software source code, documentation source, and
30 | configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical transformation or
33 | translation of a Source form, including but not limited to compiled object
34 | code, generated documentation, and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or Object form,
37 | made available under the License, as indicated by a copyright notice that is
38 | included in or attached to the work (an example is provided in the Appendix
39 | below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object form, that
42 | is based on (or derived from) the Work and for which the editorial revisions,
43 | annotations, elaborations, or other modifications represent, as a whole, an
44 | original work of authorship. For the purposes of this License, Derivative Works
45 | shall not include works that remain separable from, or merely link (or bind by
46 | name) to the interfaces of, the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including the original
49 | version of the Work and any modifications or additions to that Work or
50 | Derivative Works thereof, that is intentionally submitted to Licensor for
51 | inclusion in the Work by the copyright owner or by an individual or Legal
52 | Entity authorized to submit on behalf of the copyright owner. For the purposes
53 | of this definition, "submitted" means any form of electronic, verbal, or
54 | written communication sent to the Licensor or its representatives, including
55 | but not limited to communication on electronic mailing lists, source code
56 | control systems, and issue tracking systems that are managed by, or on behalf
57 | of, the Licensor for the purpose of discussing and improving the Work, but
58 | excluding communication that is conspicuously marked or otherwise designated in
59 | writing by the copyright owner as "Not a Contribution."
60 |
61 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
62 | of whom a Contribution has been received by Licensor and subsequently
63 | incorporated within the Work.
64 |
65 | 2. Grant of Copyright License. Subject to the terms and conditions of this
66 | License, each Contributor hereby grants to You a perpetual, worldwide,
67 | non-exclusive, no-charge, royalty-free, irrevocable copyright license to
68 | reproduce, prepare Derivative Works of, publicly display, publicly perform,
69 | sublicense, and distribute the Work and such Derivative Works in Source or
70 | Object form.
71 |
72 | 3. Grant of Patent License. Subject to the terms and conditions of this
73 | License, each Contributor hereby grants to You a perpetual, worldwide,
74 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
75 | section) patent license to make, have made, use, offer to sell, sell, import,
76 | and otherwise transfer the Work, where such license applies only to those
77 | patent claims licensable by such Contributor that are necessarily infringed by
78 | their Contribution(s) alone or by combination of their Contribution(s) with the
79 | Work to which such Contribution(s) was submitted. If You institute patent
80 | litigation against any entity (including a cross-claim or counterclaim in a
81 | lawsuit) alleging that the Work or a Contribution incorporated within the Work
82 | constitutes direct or contributory patent infringement, then any patent
83 | licenses granted to You under this License for that Work shall terminate as of
84 | the date such litigation is filed.
85 |
86 | 4. Redistribution. You may reproduce and distribute copies of the Work or
87 | Derivative Works thereof in any medium, with or without modifications, and in
88 | Source or Object form, provided that You meet the following conditions:
89 |
90 | (a) You must give any other recipients of the Work or Derivative Works a copy
91 | of this License; and
92 |
93 | (b) You must cause any modified files to carry prominent notices stating that
94 | You changed the files; and
95 |
96 | (c) You must retain, in the Source form of any Derivative Works that You
97 | distribute, all copyright, patent, trademark, and attribution notices from the
98 | Source form of the Work, excluding those notices that do not pertain to any
99 | part of the Derivative Works; and
100 |
101 | (d) If the Work includes a "NOTICE" text file as part of its distribution, then
102 | any Derivative Works that You distribute must include a readable copy of the
103 | attribution notices contained within such NOTICE file, excluding those notices
104 | that do not pertain to any part of the Derivative Works, in at least one of the
105 | following places: within a NOTICE text file distributed as part of the
106 | Derivative Works; within the Source form or documentation, if provided along
107 | with the Derivative Works; or, within a display generated by the Derivative
108 | Works, if and wherever such third-party notices normally appear. The contents
109 | of the NOTICE file are for informational purposes only and do not modify the
110 | License. You may add Your own attribution notices within Derivative Works that
111 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
112 | provided that such additional attribution notices cannot be construed as
113 | modifying the License.
114 |
115 | You may add Your own copyright statement to Your modifications and may provide
116 | additional or different license terms and conditions for use, reproduction, or
117 | distribution of Your modifications, or for any such Derivative Works as a
118 | whole, provided Your use, reproduction, and distribution of the Work otherwise
119 | complies with the conditions stated in this License.
120 |
121 | 5. Submission of Contributions. Unless You explicitly state otherwise, any
122 | Contribution intentionally submitted for inclusion in the Work by You to the
123 | Licensor shall be under the terms and conditions of this License, without any
124 | additional terms or conditions. Notwithstanding the above, nothing herein shall
125 | supersede or modify the terms of any separate license agreement you may have
126 | executed with Licensor regarding such Contributions.
127 |
128 | 6. Trademarks. This License does not grant permission to use the trade names,
129 | trademarks, service marks, or product names of the Licensor, except as required
130 | for reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
134 | writing, Licensor provides the Work (and each Contributor provides its
135 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
136 | KIND, either express or implied, including, without limitation, any warranties
137 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
138 | PARTICULAR PURPOSE. You are solely responsible for determining the
139 | appropriateness of using or redistributing the Work and assume any risks
140 | associated with Your exercise of permissions under this License.
141 |
142 | 8. Limitation of Liability. In no event and under no legal theory, whether in
143 | tort (including negligence), contract, or otherwise, unless required by
144 | applicable law (such as deliberate and grossly negligent acts) or agreed to in
145 | writing, shall any Contributor be liable to You for damages, including any
146 | direct, indirect, special, incidental, or consequential damages of any
147 | character arising as a result of this License or out of the use or inability to
148 | use the Work (including but not limited to damages for loss of goodwill, work
149 | stoppage, computer failure or malfunction, or any and all other commercial
150 | damages or losses), even if such Contributor has been advised of the
151 | possibility of such damages.
152 |
153 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or
154 | Derivative Works thereof, You may choose to offer, and charge a fee for,
155 | acceptance of support, warranty, indemnity, or other liability obligations
156 | and/or rights consistent with this License. However, in accepting such
157 | obligations, You may act only on Your own behalf and on Your sole
158 | responsibility, not on behalf of any other Contributor, and only if You agree
159 | to indemnify, defend, and hold each Contributor harmless for any liability
160 | incurred by, or claims asserted against, such Contributor by reason of your
161 | accepting any such warranty or additional liability.
162 |
163 | END OF TERMS AND CONDITIONS
164 |
165 | APPENDIX: How to apply the Apache License to your work.
166 |
167 | To apply the Apache License to your work, attach the following boilerplate
168 | notice, with the fields enclosed by brackets "[]" replaced with your own
169 | identifying information. (Don't include the brackets!) The text should be
170 | enclosed in the appropriate comment syntax for the file format. We also
171 | recommend that a file or class name and description of purpose be included on
172 | the same "printed page" as the copyright notice for easier identification
173 | within third-party archives.
174 |
175 | Copyright [yyyy] [name of copyright owner]
176 |
177 | Licensed under the Apache License, Version 2.0 (the "License");
178 | you may not use this file except in compliance with the License.
179 | You may obtain a copy of the License at
180 |
181 | http://www.apache.org/licenses/LICENSE-2.0
182 |
183 | Unless required by applicable law or agreed to in writing, software
184 | distributed under the License is distributed on an "AS IS" BASIS,
185 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
186 | See the License for the specific language governing permissions and
187 | limitations under the License.
188 |
189 | * For Android Architecture Components see also this required NOTICE:
190 | Copyright 2018 The Android Open Source Project, Inc.
191 | * For Android Support Library see also this required NOTICE:
192 | Copyright (C) 2011 The Android Open Source Project
193 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## AWS Mobile Android Kotlin Notes
2 |
3 | The AWS Mobile Notes Tutorial code for Android / Java. You can start the tutorial by looking
4 | in the [tutorial directory](./tutorial/index.md)
5 |
6 | ## License Summary
7 |
8 | This sample code is made available under a modified MIT license. See the [LICENSE](./LICENSE) file.
9 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the "Software"), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify,
6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7 | permit persons to whom the Software is furnished to do so.
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
9 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
10 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
11 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
12 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
13 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 | */
15 | apply plugin: 'com.android.application'
16 |
17 | android {
18 | compileSdkVersion 27
19 | buildToolsVersion '27.0.3'
20 |
21 | defaultConfig {
22 | applicationId "com.amazonaws.mobile.samples.notes"
23 | minSdkVersion 23
24 | targetSdkVersion 27
25 | versionCode 1
26 | versionName "1.0"
27 | }
28 |
29 | buildTypes {
30 | release {
31 | minifyEnabled true
32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
33 | }
34 |
35 | debug {
36 | minifyEnabled false
37 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
38 | }
39 | }
40 |
41 | compileOptions {
42 | sourceCompatibility JavaVersion.VERSION_1_8
43 | targetCompatibility JavaVersion.VERSION_1_8
44 | }
45 | }
46 |
47 | dependencies {
48 | def support_version = '27.1.1'
49 | def constraint_version = '1.1.2'
50 | def lifecycle_version = '1.1.1'
51 | def paging_version = '1.0.1'
52 |
53 | implementation fileTree(dir: 'libs', include: ['*.jar'])
54 | implementation "com.android.support:appcompat-v7:$support_version"
55 | implementation "com.android.support:support-v4:$support_version"
56 | implementation "com.android.support:cardview-v7:$support_version"
57 | implementation "com.android.support:recyclerview-v7:$support_version"
58 | implementation "com.android.support:design:$support_version"
59 | implementation "com.android.support.constraint:constraint-layout:$constraint_version"
60 |
61 | // Android Architecture Components
62 | implementation "android.arch.lifecycle:extensions:$lifecycle_version"
63 | implementation "android.arch.paging:runtime:$paging_version"
64 | }
65 |
--------------------------------------------------------------------------------
/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 /Users/adrianha/Library/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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
20 |
21 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/Injection.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes;
18 |
19 | import android.content.Context;
20 |
21 | import com.amazonaws.mobile.samples.mynotes.repository.NotesRepository;
22 | import com.amazonaws.mobile.samples.mynotes.services.AnalyticsService;
23 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
24 | import com.amazonaws.mobile.samples.mynotes.services.mock.MockAnalyticsService;
25 | import com.amazonaws.mobile.samples.mynotes.services.mock.MockDataService;
26 |
27 | /**
28 | * This is a "fake" dependency injection system.
29 | */
30 | public class Injection {
31 | private static DataService dataService = null;
32 | private static AnalyticsService analyticsService = null;
33 | private static NotesRepository notesRepository = null;
34 |
35 | public static synchronized DataService getDataService() {
36 | return dataService;
37 | }
38 |
39 | public static synchronized AnalyticsService getAnalyticsService() {
40 | return analyticsService;
41 | }
42 |
43 | public static synchronized NotesRepository getNotesRepository() {
44 | return notesRepository;
45 | }
46 |
47 | public static synchronized void initialize(Context context) {
48 | if (analyticsService == null) {
49 | analyticsService = new MockAnalyticsService();
50 | }
51 |
52 | if (dataService == null) {
53 | dataService = new MockDataService();
54 | }
55 |
56 | if (notesRepository == null) {
57 | notesRepository = new NotesRepository(dataService);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/NotesApp.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes;
18 |
19 | import android.app.Application;
20 |
21 | /**
22 | * Application class responsible for initializing singletons and other
23 | * common components
24 | */
25 | public class NotesApp extends Application {
26 | /**
27 | * The name of the field used to hold the ID of the note when information is passed
28 | * internally between activities.
29 | */
30 | public static String ITEM_ID = "noteId";
31 |
32 | @Override
33 | public void onCreate() {
34 | super.onCreate();
35 | Injection.initialize(getApplicationContext());
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/models/Note.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.models;
18 |
19 | import java.util.UUID;
20 |
21 | /**
22 | * Model for a single note
23 | */
24 | public class Note {
25 | private String noteId;
26 | private String title;
27 | private String content;
28 |
29 | public Note() {
30 | noteId = UUID.randomUUID().toString();
31 | title = "";
32 | content = "";
33 | }
34 |
35 | public Note(String noteId) {
36 | this.noteId = noteId;
37 | title = "";
38 | content = "";
39 | }
40 |
41 | public Note(String noteId, String title, String content) {
42 | this.noteId = noteId;
43 | this.title = title;
44 | this.content = content;
45 | }
46 |
47 | public String getNoteId() {
48 | return noteId;
49 | }
50 |
51 | public void setNoteId(String noteId) {
52 | this.noteId = noteId;
53 | }
54 |
55 | public String getTitle() {
56 | return title;
57 | }
58 |
59 | public void setTitle(String title) {
60 | this.title = title;
61 | }
62 |
63 | public String getContent() {
64 | return content;
65 | }
66 |
67 | public void setContent(String content) {
68 | this.content = content;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/models/OnClickCallback.java:
--------------------------------------------------------------------------------
1 | package com.amazonaws.mobile.samples.mynotes.models;
2 |
3 | public interface OnClickCallback {
4 | void onClick(Note item);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/models/PagedListConnectionResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.models;
18 |
19 | import java.util.List;
20 |
21 | /**
22 | * Model class used as the response to an API call. In this particular instance, it's
23 | * a paged list response where the next page is denoted by a nextToken - ostensibly a
24 | * string, but really an opaque blob that you should not mess with - just submit it along
25 | * with the API call to get the next page.
26 | * @param the type of elements within the response
27 | */
28 | public class PagedListConnectionResponse {
29 | private List items;
30 | private String nextToken;
31 |
32 | public PagedListConnectionResponse(List items, String nextToken) {
33 | this.items = items;
34 | this.nextToken = nextToken;
35 | }
36 |
37 | public List getItems() {
38 | return items;
39 | }
40 |
41 | public String getNextToken() {
42 | return nextToken;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/models/RemoveCallback.java:
--------------------------------------------------------------------------------
1 | package com.amazonaws.mobile.samples.mynotes.models;
2 |
3 | public interface RemoveCallback {
4 | void onRemove(Note item);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/models/ResultCallback.java:
--------------------------------------------------------------------------------
1 | package com.amazonaws.mobile.samples.mynotes.models;
2 |
3 | public interface ResultCallback {
4 | void onResult(T result);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/repository/NotesDataSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.repository;
18 |
19 | import android.arch.paging.PageKeyedDataSource;
20 | import android.support.annotation.NonNull;
21 | import android.util.Log;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.models.Note;
24 | import com.amazonaws.mobile.samples.mynotes.models.PagedListConnectionResponse;
25 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
26 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
27 |
28 | import javax.xml.transform.Result;
29 |
30 | /**
31 | * A DataSource implements a paging system for a RecyclerView. This one uses the DataService
32 | * to provide a paged view into the Notes API.
33 | */
34 | public class NotesDataSource extends PageKeyedDataSource {
35 | private static final String TAG = "NotesDataSource";
36 | private DataService dataService;
37 |
38 | NotesDataSource(DataService dataService) {
39 | this.dataService = dataService;
40 | }
41 |
42 | /**
43 | * Part of the PageKeyedDataSource - load the first page of a list. This is called when
44 | * the data source is first created or invalidated and assumes no nextToken has been
45 | * provided
46 | * @param params information about what to load
47 | * @param callback callback that receives the response
48 | */
49 | @Override
50 | public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback callback) {
51 | Log.d(TAG, String.format("loadInitial(%d)", params.requestedLoadSize));
52 | dataService.loadNotes(params.requestedLoadSize, null, (PagedListConnectionResponse result) -> {
53 | callback.onResult(result.getItems(), null, result.getNextToken());
54 | });
55 | }
56 |
57 | /**
58 | * Part of the PageKeyedDataSource - load the next page of a list. This is called after
59 | * the loadInitial() has returned a response that includes a nextToken to load the next
60 | * page of items.
61 | */
62 | @Override
63 | public void loadBefore(@NonNull LoadParams params, @NonNull final LoadCallback callback) {
64 | Log.d(TAG, String.format("loadAfter(%d, %s)", params.requestedLoadSize, params.key));
65 | dataService.loadNotes(params.requestedLoadSize, params.key, (PagedListConnectionResponse result) -> {
66 | callback.onResult(result.getItems(), result.getNextToken());
67 | });
68 | }
69 |
70 | /**
71 | * Part of the PageKeyedDataSource - normally used to load the previous page, but this
72 | * version does not support paging backwards, so it becomes an invalidation.
73 | */
74 | @Override
75 | public void loadAfter(@NonNull LoadParams params, @NonNull LoadCallback callback) {
76 | Log.d(TAG, String.format("loadBefore(%d, %s)", params.requestedLoadSize, params.key));
77 | invalidate();
78 | }
79 |
80 | /**
81 | * The notes repository needs to do non-paged list type operations as well. These operations
82 | * are passed through directly to the data service. However, deletions (and saves) affect
83 | * the list, so we must ensure that the list is invalidated when they succeed. This call is
84 | * to delete an item.
85 | */
86 | public void deleteItem(String noteId, @NonNull final ResultCallback callback) {
87 | dataService.deleteNote(noteId, (Boolean result) -> {
88 | if (result) invalidate();
89 | callback.onResult(result);
90 | });
91 | }
92 |
93 | /**
94 | * Obtain a single item from the data service.
95 | */
96 | public void getItem(String noteId, @NonNull ResultCallback callback) {
97 | dataService.getNote(noteId, callback);
98 | }
99 |
100 | /**
101 | * The notes repository needs to do non-paged list type operations as well. These operations
102 | * are passed through directly to the data service. However, deletions (and saves) affect
103 | * the list, so we must ensure that the list is invalidated when they succeed. This call is
104 | * to save an item.
105 | */
106 | public void createItem(@NonNull String title, @NonNull String content, @NonNull final ResultCallback callback) {
107 | dataService.createNote(title, content, (Note result) -> {
108 | if (result != null) invalidate();
109 | callback.onResult(result);
110 | });
111 | }
112 |
113 | public void updateItem(@NonNull Note note, @NonNull final ResultCallback callback) {
114 | dataService.updateNote(note, (Note result) -> {
115 | if (result != null) invalidate();
116 | callback.onResult(result);
117 | });
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/repository/NotesDataSourceFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.repository;
18 |
19 | import android.arch.lifecycle.LiveData;
20 | import android.arch.lifecycle.MutableLiveData;
21 | import android.arch.paging.DataSource;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.models.Note;
24 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
25 |
26 | /**
27 | * Factory for creating data sources. When the NotesDataSource is invalidated (because
28 | * of reverse paging or because the list has been altered), we have to create a new
29 | * data source.
30 | */
31 | public class NotesDataSourceFactory extends DataSource.Factory {
32 | private DataService dataService;
33 | private MutableLiveData mDataSource;
34 | private LiveData currentDataSource;
35 |
36 | NotesDataSourceFactory(DataService dataService) {
37 | this.dataService = dataService;
38 | mDataSource = new MutableLiveData<>();
39 | currentDataSource = mDataSource;
40 | }
41 |
42 | public LiveData getCurrentDataSource() {
43 | return currentDataSource;
44 | }
45 |
46 | @Override
47 | public DataSource create() {
48 | NotesDataSource dataSource = new NotesDataSource(dataService);
49 | mDataSource.postValue(dataSource);
50 | return dataSource;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/repository/NotesRepository.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.repository;
18 |
19 | import android.arch.lifecycle.LiveData;
20 | import android.arch.paging.LivePagedListBuilder;
21 | import android.arch.paging.PagedList;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.models.Note;
24 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
25 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
26 |
27 | public class NotesRepository {
28 | private LiveData> pagedList;
29 | private LiveData dataSource;
30 |
31 | public NotesRepository(DataService dataService) {
32 | NotesDataSourceFactory factory = new NotesDataSourceFactory(dataService);
33 | dataSource = factory.getCurrentDataSource();
34 | pagedList = new LivePagedListBuilder<>(factory, 20).build();
35 | }
36 |
37 | /**
38 | * An observable lifecycle-aware version of the paged list of notes. This is used
39 | * to render a RecyclerView of all the notes.
40 | */
41 | public LiveData> getPagedList() {
42 | return pagedList;
43 | }
44 |
45 | /**
46 | * API operation to create an item in the data store
47 | */
48 | public void create(String title, String content, ResultCallback callback) {
49 | dataSource.getValue().createItem(title, content, callback);
50 | }
51 |
52 | /**
53 | * API operation to update an item in the data store
54 | */
55 | public void update(Note note, ResultCallback callback) {
56 | dataSource.getValue().updateItem(note, callback);
57 | }
58 |
59 | /**
60 | * API operation to delete an item from the data store
61 | */
62 | public void delete(String noteId, ResultCallback callback) {
63 | dataSource.getValue().deleteItem(noteId, callback);
64 | }
65 |
66 | /**
67 | * API operation to get an item from the data store
68 | */
69 | public void get(String noteId, ResultCallback callback) {
70 | dataSource.getValue().getItem(noteId, callback);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/services/AnalyticsService.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.services;
18 |
19 | import java.util.Map;
20 |
21 | public interface AnalyticsService {
22 | /**
23 | * Records a start session event into the analytics stream
24 | */
25 | void startSession();
26 |
27 | /**
28 | * Records a stop session event into the analytics stream
29 | */
30 | void stopSession();
31 |
32 | /**
33 | * Record a custom event into the analytics stream
34 | *
35 | * @param eventName the custom event name
36 | * @param attributes a list of key-value pairs for recording string attributes
37 | * @param metrics a list of key-value pairs for recording numeric metrics
38 | */
39 | void recordEvent(String eventName, Map attributes, Map metrics);
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/services/DataService.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.services;
18 |
19 | import com.amazonaws.mobile.samples.mynotes.models.Note;
20 | import com.amazonaws.mobile.samples.mynotes.models.PagedListConnectionResponse;
21 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
22 |
23 | /**
24 | * Definition of a data service. This maps to an API definition on the cloud backend.
25 | * Each call should be async and run on a background thread.
26 | */
27 | public interface DataService {
28 | /**
29 | * Load a single page of notes.
30 | *
31 | * @param limit the requested number of items
32 | * @param after the "next token" from a prior call
33 | * @param callback the response from the server
34 | */
35 | void loadNotes(int limit, String after, ResultCallback> callback);
36 |
37 | /**
38 | * Load a single note
39 | *
40 | * @param noteId the request ID
41 | * @param callback the response from the server
42 | */
43 | void getNote(String noteId, ResultCallback callback);
44 |
45 | /**
46 | * Create a new note a note to the backing store
47 | *
48 | * @param title the title of the new note
49 | * @param content the content for the new note
50 | * @param callback the response from the server (null would indicate that the operation failed)
51 | */
52 | void createNote(String title, String content, ResultCallback callback);
53 |
54 | /**
55 | * Update an existing note in the backing store
56 | *
57 | * @param note the new contents of the note
58 | * @param callback the response from the server (null would indicate that the operation failed)
59 | */
60 | void updateNote(Note note, ResultCallback callback);
61 |
62 | /**
63 | * Delete a note from the backing store
64 | *
65 | * @param noteId the ID of the note to be deleted
66 | * @param callback the response from the server (Boolean = true indicates success)
67 | */
68 | void deleteNote(String noteId, ResultCallback callback);
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/services/mock/MockAnalyticsService.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.services.mock;
18 |
19 | import android.util.Log;
20 |
21 | import com.amazonaws.mobile.samples.mynotes.services.AnalyticsService;
22 |
23 | import java.util.Locale;
24 | import java.util.Map;
25 |
26 | public class MockAnalyticsService implements AnalyticsService {
27 | private static final String TAG = "MockAnalyticsService";
28 |
29 | @Override
30 | public void startSession() {
31 | Log.v(TAG, "startSession()");
32 | }
33 |
34 | @Override
35 | public void stopSession() {
36 | Log.v(TAG, "stopSession()");
37 |
38 | }
39 |
40 | @Override
41 | public void recordEvent(String eventName, Map attributes, Map metrics) {
42 | StringBuilder event = new StringBuilder("");
43 | if (attributes != null) {
44 | for (Map.Entry entry : attributes.entrySet()) {
45 | event.append(String.format(Locale.US, ", %s=\"%s\"", entry.getKey(), entry.getValue()));
46 | }
47 | }
48 | if (metrics != null) {
49 | for (Map.Entry entry : metrics.entrySet()) {
50 | event.append(String.format(Locale.US, ", %s=%.2f", entry.getKey(), entry.getValue()));
51 | }
52 | }
53 | if (!event.toString().isEmpty()) {
54 | event.setCharAt(0, ':');
55 | }
56 | Log.v(TAG, String.format(Locale.US, "recordEvent(%s)%s", eventName, event.toString()));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/services/mock/MockDataService.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.services.mock;
18 |
19 | import com.amazonaws.mobile.samples.mynotes.models.Note;
20 | import com.amazonaws.mobile.samples.mynotes.models.PagedListConnectionResponse;
21 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
22 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Collections;
26 | import java.util.List;
27 | import java.util.Locale;
28 |
29 | /**
30 | * A mock data store. This will create 30 notes so you can see the scrolling action, but
31 | * otherwise acts as a data service. This should be easily rewritten to use an actual cloud API
32 | */
33 | public class MockDataService implements DataService {
34 | private ArrayList items;
35 |
36 | public MockDataService() {
37 | items = new ArrayList<>();
38 | for (int i = 0 ; i < 30 ; i++) {
39 | Note item = new Note();
40 | item.setTitle(String.format(Locale.US, "Note %d", i));
41 | item.setContent(String.format(Locale.US, "Content for note %d", i));
42 | items.add(item);
43 | }
44 | }
45 |
46 | /**
47 | * Simulate an API call to a network service that returns paged data.
48 | *
49 | * @param limit the requested number of items
50 | * @param after the "next token" from a prior call
51 | * @param callback the response from the server
52 | */
53 | @Override
54 | public void loadNotes(int limit, String after, ResultCallback> callback) {
55 | if (limit < 1 || limit > 100) throw new IllegalArgumentException("Limit must be between 1 and 100");
56 |
57 | int firstItem = 0;
58 | if (after != null) {
59 | firstItem = indexOfFirst(after);
60 | if (firstItem < 0) {
61 | callback.onResult(new PagedListConnectionResponse<>(Collections.emptyList(), null));
62 | return;
63 | }
64 | firstItem++;
65 | }
66 | if (firstItem > items.size() - 1) {
67 | callback.onResult(new PagedListConnectionResponse<>(Collections.emptyList(), null));
68 | return;
69 | }
70 | int nItems = Math.min(limit, items.size() - firstItem);
71 | if (nItems == 0) {
72 | callback.onResult(new PagedListConnectionResponse<>(Collections.emptyList(), null));
73 | return;
74 | }
75 |
76 | List sublist = new ArrayList<>(items.subList(firstItem, firstItem + nItems));
77 | String nextToken = (firstItem + nItems - 1 == items.size()) ? null : sublist.get(sublist.size() - 1).getNoteId();
78 | callback.onResult(new PagedListConnectionResponse<>(sublist, nextToken));
79 | }
80 |
81 | /**
82 | * Load a single note from the current list of notes
83 | *
84 | * @param noteId the request ID
85 | * @param callback the response from the server
86 | */
87 | @Override
88 | public void getNote(String noteId, ResultCallback callback) {
89 | if (noteId == null || noteId.isEmpty()) throw new IllegalArgumentException();
90 |
91 | int idx = indexOfFirst(noteId);
92 | callback.onResult(idx >= 0 ? items.get(idx) : null);
93 | }
94 |
95 | /**
96 | * Create a new note a note to the backing store
97 | *
98 | * @param title the title of the new note
99 | * @param content the content for the new note
100 | * @param callback the response from the server (null would indicate that the operation failed)
101 | */
102 | @Override
103 | public void createNote(String title, String content, ResultCallback callback) {
104 | Note note = new Note();
105 | note.setTitle(title);
106 | note.setContent(content);
107 | items.add(note);
108 | callback.onResult(note);
109 | }
110 |
111 | /**
112 | * Update an existing note in the backing store
113 | *
114 | * @param note the new contents of the note
115 | * @param callback the response from the server (null would indicate that the operation failed)
116 | */
117 | @Override
118 | public void updateNote(Note note, ResultCallback callback) {
119 | int idx = indexOfFirst(note.getNoteId());
120 | if (idx >= 0) {
121 | items.set(idx, note);
122 | callback.onResult(note);
123 | } else {
124 | callback.onResult(null);
125 | }
126 | }
127 |
128 | /**
129 | * Delete a note from the backing store
130 | *
131 | * @param noteId the ID of the note to be deleted
132 | * @param callback the response from the server (Boolean = true indicates success)
133 | */
134 | @Override
135 | public void deleteNote(String noteId, ResultCallback callback) {
136 | if (noteId == null || noteId.isEmpty()) throw new IllegalArgumentException();
137 |
138 | int idx = indexOfFirst(noteId);
139 | if (idx >= 0) items.remove(idx);
140 | callback.onResult(idx >= 0);
141 | }
142 |
143 | /**
144 | * Returns the index of the first note that matches
145 | * @param noteId the note to match
146 | * @return the index of the note, or -1 if not found
147 | */
148 | private int indexOfFirst(String noteId) {
149 | if (items.isEmpty()) throw new IndexOutOfBoundsException();
150 | for (int i = 0 ; i < items.size() ; i++) {
151 | if (items.get(i).getNoteId().equals(noteId))
152 | return i;
153 | }
154 | return -1;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/NoteDetailActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.content.Intent;
20 | import android.os.Bundle;
21 | import android.support.v7.app.AppCompatActivity;
22 | import android.support.v7.widget.Toolbar;
23 | import android.view.MenuItem;
24 |
25 | import com.amazonaws.mobile.samples.mynotes.Injection;
26 | import com.amazonaws.mobile.samples.mynotes.NotesApp;
27 | import com.amazonaws.mobile.samples.mynotes.R;
28 | import com.amazonaws.mobile.samples.mynotes.services.AnalyticsService;
29 |
30 | public class NoteDetailActivity extends AppCompatActivity {
31 | /**
32 | * Injection of the Analytics Service
33 | */
34 | private AnalyticsService analyticsService = Injection.getAnalyticsService();
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_note_detail);
40 |
41 | // Set up the action bar so that it has a back button
42 | Toolbar detail_toolbar = findViewById(R.id.detail_toolbar);
43 | setSupportActionBar(detail_toolbar);
44 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
45 |
46 | // Load the fragment
47 | NoteDetailFragment fragment = new NoteDetailFragment();
48 | Bundle arguments = new Bundle();
49 | arguments.putString(NotesApp.ITEM_ID, getIntent().getStringExtra(NotesApp.ITEM_ID));
50 | fragment.setArguments(arguments);
51 |
52 | // Add the fragment to the UI
53 | getSupportFragmentManager()
54 | .beginTransaction()
55 | .add(R.id.note_detail_container, fragment)
56 | .commit();
57 | }
58 |
59 | @Override
60 | public void onResume() {
61 | super.onResume();
62 | analyticsService.recordEvent("NoteDetailActivity", null, null);
63 | }
64 |
65 | @Override
66 | public boolean onOptionsItemSelected(MenuItem item) {
67 | switch (item.getItemId()) {
68 | case android.R.id.home:
69 | Intent intent = new Intent(this, NoteListActivity.class);
70 | navigateUpTo(intent);
71 | return true;
72 | default:
73 | return super.onOptionsItemSelected(item);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/NoteDetailFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.arch.lifecycle.ViewModelProviders;
20 | import android.os.Bundle;
21 | import android.support.annotation.NonNull;
22 | import android.support.v4.app.Fragment;
23 | import android.text.Editable;
24 | import android.text.TextWatcher;
25 | import android.view.LayoutInflater;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 | import android.widget.EditText;
29 |
30 | import com.amazonaws.mobile.samples.mynotes.NotesApp;
31 | import com.amazonaws.mobile.samples.mynotes.R;
32 | import com.amazonaws.mobile.samples.mynotes.models.Note;
33 | import com.amazonaws.mobile.samples.mynotes.viewmodels.NoteDetailViewModel;
34 |
35 | public class NoteDetailFragment extends Fragment {
36 | NoteDetailViewModel viewModel;
37 | String noteId;
38 |
39 | @Override
40 | public void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 |
43 | Bundle arguments = getArguments();
44 | if (arguments != null) {
45 | String noteId = arguments.getString(NotesApp.ITEM_ID);
46 | this.noteId = (noteId.equals("new")) ? null : noteId;
47 | }
48 | }
49 |
50 | @Override
51 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
52 | View view = inflater.inflate(R.layout.note_detail, container, false);
53 |
54 | final EditText titleField = view.findViewById(R.id.edit_title);
55 | titleField.setEnabled(false); // Disable the field by default
56 |
57 | final EditText contentField = view.findViewById(R.id.edit_content);
58 | contentField.setEnabled(false);
59 |
60 | viewModel = ViewModelProviders.of(this).get(NoteDetailViewModel.class);
61 | // Observe the view model values. Once we receive the value, enable the field.
62 | viewModel.getTitle().observe(this, (String title) -> {
63 | titleField.setText(title);
64 | titleField.setEnabled(true);
65 | });
66 | viewModel.getContent().observe(this, (String content) -> {
67 | contentField.setText(content);
68 | contentField.setEnabled(true);
69 | });
70 |
71 | // If this is a new note, create the note, then enable the fields. Otherwise just load the fields
72 | // - the fields are received via observables
73 | if (noteId == null) {
74 | viewModel.create("", "", (Note result) -> {
75 | titleField.setEnabled(true);
76 | contentField.setEnabled(true);
77 | });
78 | } else {
79 | viewModel.setNoteId(noteId);
80 | }
81 |
82 | TextWatcher saveHandler = new TextWatcher() {
83 | @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
84 | @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
85 | @Override public void afterTextChanged(Editable s) {
86 | String title = titleField.getText().toString();
87 | String content = contentField.getText().toString();
88 | viewModel.update(title, content);
89 | }
90 | };
91 |
92 | titleField.addTextChangedListener(saveHandler);
93 | contentField.addTextChangedListener(saveHandler);
94 |
95 | return view;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/NoteListActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.arch.lifecycle.ViewModelProviders;
20 | import android.content.Intent;
21 | import android.os.Bundle;
22 | import android.support.design.widget.FloatingActionButton;
23 | import android.support.v4.app.Fragment;
24 | import android.support.v7.app.AppCompatActivity;
25 | import android.support.v7.widget.RecyclerView;
26 | import android.support.v7.widget.Toolbar;
27 | import android.support.v7.widget.helper.ItemTouchHelper;
28 | import android.view.View;
29 |
30 | import com.amazonaws.mobile.samples.mynotes.Injection;
31 | import com.amazonaws.mobile.samples.mynotes.NotesApp;
32 | import com.amazonaws.mobile.samples.mynotes.R;
33 | import com.amazonaws.mobile.samples.mynotes.models.Note;
34 | import com.amazonaws.mobile.samples.mynotes.services.AnalyticsService;
35 | import com.amazonaws.mobile.samples.mynotes.viewmodels.NoteListViewModel;
36 |
37 | import java.util.HashMap;
38 |
39 | public class NoteListActivity extends AppCompatActivity {
40 | /**
41 | * If the device is running in two-pane mode, then this is set to true. In two-pane mode,
42 | * the UI is a side-by-side, with the list on the left and the details on the right. In one
43 | * pane mode, the list and details are separate pages.
44 | */
45 | private boolean twoPane = false;
46 |
47 | /**
48 | * The view model
49 | */
50 | private NoteListViewModel viewModel;
51 |
52 | /**
53 | * The analytics service
54 | */
55 | private AnalyticsService analyticsService = Injection.getAnalyticsService();
56 |
57 | @Override
58 | public void onCreate(Bundle savedInstanceState) {
59 | super.onCreate(savedInstanceState);
60 |
61 | viewModel = ViewModelProviders.of(this).get(NoteListViewModel.class);
62 | setContentView(R.layout.activity_note_list);
63 | if (findViewById(R.id.note_detail_container) != null) twoPane = true;
64 |
65 | // Configure the action bar
66 | Toolbar toolbar = findViewById(R.id.toolbar);
67 | toolbar.setTitle(getTitle());
68 |
69 | // Add an item click handler to the floating action button for adding a note
70 | FloatingActionButton fab = findViewById(R.id.fab);
71 | fab.setOnClickListener((View v) -> loadNoteDetailFragment("new"));
72 |
73 | // Create the adapter that will be used to load items into the recycler view
74 | final NoteListAdapter adapter = new NoteListAdapter((Note item) -> loadNoteDetailFragment(item.getNoteId()));
75 |
76 | // Create the swipe-to-delete handler
77 | SwipeToDelete swipeHandler = new SwipeToDelete(this, (Note item) -> viewModel.removeNote(item.getNoteId()));
78 | ItemTouchHelper swipeToDelete = new ItemTouchHelper(swipeHandler);
79 |
80 | // Configure the note list
81 | RecyclerView note_list = findViewById(R.id.note_list);
82 | swipeToDelete.attachToRecyclerView(note_list);
83 | note_list.setAdapter(adapter);
84 |
85 | // Ensure the note list is updated whenever the repository is updated
86 | viewModel.getNotesList().observe(this, adapter::submitList);
87 | }
88 |
89 | @Override
90 | public void onResume() {
91 | super.onResume();
92 | HashMap attributes = new HashMap<>();
93 | attributes.put("twoPane", twoPane ? "true" : "false");
94 | analyticsService.recordEvent("NoteListActivity", attributes, null);
95 | }
96 |
97 | /**
98 | * Loads the note details the right way, depending on if this is two-pane mode.
99 | *
100 | * @param noteId the ID of the note to load
101 | */
102 | private void loadNoteDetailFragment(String noteId) {
103 | if (twoPane) {
104 | Fragment fragment = new NoteDetailFragment();
105 | Bundle arguments = new Bundle();
106 | arguments.putString(NotesApp.ITEM_ID, noteId);
107 | fragment.setArguments(arguments);
108 | getSupportFragmentManager()
109 | .beginTransaction()
110 | .replace(R.id.note_detail_container, fragment)
111 | .commit();
112 | } else {
113 | Intent intent = new Intent(this, NoteDetailActivity.class);
114 | intent.putExtra(NotesApp.ITEM_ID, noteId);
115 | startActivity(intent);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/NoteListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.arch.paging.PagedListAdapter;
20 | import android.support.annotation.NonNull;
21 | import android.support.v7.util.DiffUtil;
22 | import android.view.LayoutInflater;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 |
26 | import com.amazonaws.mobile.samples.mynotes.R;
27 | import com.amazonaws.mobile.samples.mynotes.models.Note;
28 | import com.amazonaws.mobile.samples.mynotes.models.OnClickCallback;
29 |
30 | public class NoteListAdapter extends PagedListAdapter {
31 | private static DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() {
32 | @Override
33 | public boolean areItemsTheSame(Note oldItem, Note newItem) {
34 | return oldItem.getNoteId().equals(newItem.getNoteId());
35 | }
36 |
37 | @Override
38 | public boolean areContentsTheSame(Note oldItem, Note newItem) {
39 | return oldItem.getNoteId().equals(newItem.getNoteId()) &&
40 | oldItem.getTitle().equals(newItem.getTitle()) &&
41 | oldItem.getContent().equals(newItem.getContent());
42 | }
43 | };
44 |
45 | private OnClickCallback callback;
46 |
47 | public NoteListAdapter(OnClickCallback callback) {
48 | super(DIFF_CALLBACK);
49 | this.callback = callback;
50 | }
51 |
52 | @NonNull
53 | @Override
54 | public NoteListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
55 | LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
56 | View view = layoutInflater.inflate(R.layout.note_list_content, parent, false);
57 | return new NoteListViewHolder(view);
58 | }
59 |
60 | @Override
61 | public void onBindViewHolder(@NonNull final NoteListViewHolder holder, int position) {
62 | holder.setNote(getItem(position));
63 | holder.itemView.setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | callback.onClick(holder.getNote());
67 | }
68 | });
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/NoteListViewHolder.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.support.v7.widget.RecyclerView;
20 | import android.view.View;
21 | import android.widget.TextView;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.R;
24 | import com.amazonaws.mobile.samples.mynotes.models.Note;
25 |
26 | public class NoteListViewHolder extends RecyclerView.ViewHolder {
27 | private TextView titleField;
28 | private TextView idField;
29 | private Note note;
30 |
31 | public NoteListViewHolder(View view) {
32 | super(view);
33 |
34 | titleField = view.findViewById(R.id.list_title);
35 | idField = view.findViewById(R.id.list_id);
36 | }
37 |
38 | public Note getNote() {
39 | return note;
40 | }
41 |
42 | public void setNote(Note note) {
43 | this.note = note;
44 | String title = note.getTitle();
45 | titleField.setText(title == null ? "null" : title);
46 | idField.setText(note.getNoteId());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/ui/SwipeToDelete.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.ui;
18 |
19 | import android.content.Context;
20 | import android.graphics.Canvas;
21 | import android.graphics.Color;
22 | import android.graphics.PorterDuff;
23 | import android.graphics.drawable.ColorDrawable;
24 | import android.graphics.drawable.Drawable;
25 | import android.support.v4.content.ContextCompat;
26 | import android.support.v7.widget.RecyclerView;
27 | import android.support.v7.widget.helper.ItemTouchHelper;
28 | import com.amazonaws.mobile.samples.mynotes.R;
29 | import com.amazonaws.mobile.samples.mynotes.models.RemoveCallback;
30 |
31 | public class SwipeToDelete extends ItemTouchHelper.SimpleCallback {
32 | private RemoveCallback callback;
33 | private Drawable background, xMark;
34 | private int xMarkMargin;
35 |
36 | SwipeToDelete(Context context, RemoveCallback callback) {
37 | super(0, ItemTouchHelper.LEFT);
38 |
39 | this.callback = callback;
40 | this.background = new ColorDrawable(Color.RED);
41 | this.xMark = ContextCompat.getDrawable(context, R.drawable.ic_clear_24dp);
42 | this.xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
43 | this.xMarkMargin = (int) context.getResources().getDimension(R.dimen.ic_clear_margin);
44 | }
45 |
46 | @Override
47 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
48 | return false;
49 | }
50 |
51 | @Override
52 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
53 | callback.onRemove(((NoteListViewHolder) viewHolder).getNote());
54 | }
55 |
56 | @Override
57 | public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
58 | if (viewHolder != null) {
59 | // If the item has already been swiped away,ignore it
60 | if (viewHolder.getAdapterPosition() == -1) return;
61 |
62 | int vr = viewHolder.itemView.getRight();
63 | int vt = viewHolder.itemView.getTop();
64 | int vb = viewHolder.itemView.getBottom();
65 | int vh = vb - vt;
66 | int iw = xMark.getIntrinsicWidth();
67 | int ih = xMark.getIntrinsicHeight();
68 | background.setBounds(vr + (int)dX, vt, vr, vb);
69 | background.draw(c);
70 |
71 | int xml = vr - xMarkMargin - iw;
72 | int xmr = vr - xMarkMargin;
73 | int xmt = vt + (vh - ih) / 2;
74 | int xmb = xmt + ih;
75 | xMark.setBounds(xml, xmt, xmr, xmb);
76 | xMark.draw(c);
77 | }
78 |
79 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/viewmodels/NoteDetailViewModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.viewmodels;
18 |
19 | import android.arch.lifecycle.LiveData;
20 | import android.arch.lifecycle.MutableLiveData;
21 | import android.arch.lifecycle.ViewModel;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.Injection;
24 | import com.amazonaws.mobile.samples.mynotes.models.Note;
25 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
26 | import com.amazonaws.mobile.samples.mynotes.repository.NotesRepository;
27 |
28 | public class NoteDetailViewModel extends ViewModel {
29 | private String noteId;
30 | private MutableLiveData mTitle;
31 | private MutableLiveData mContent;
32 | private NotesRepository notesRepository;
33 |
34 | public NoteDetailViewModel() {
35 | this.notesRepository = Injection.getNotesRepository();
36 | this.mTitle = new MutableLiveData<>();
37 | this.mContent = new MutableLiveData<>();
38 | }
39 |
40 | public void setNoteId(final String noteId) {
41 | this.noteId = noteId;
42 | notesRepository.get(noteId, (Note result) -> {
43 | if (result != null)
44 | mTitle.postValue(result.getTitle());
45 | mContent.postValue(result.getContent());
46 | this.noteId = result.getNoteId();
47 | });
48 | }
49 |
50 | public LiveData getTitle() {
51 | return mTitle;
52 | }
53 |
54 | public LiveData getContent() {
55 | return mContent;
56 | }
57 |
58 | public synchronized void create(String title, String content, ResultCallback callback) {
59 | notesRepository.create(title, content, (Note result) -> {
60 | if (result != null) {
61 | noteId = result.getNoteId();
62 | callback.onResult(result);
63 | }
64 | });
65 | }
66 |
67 | public synchronized void update(String title, String content) {
68 | Note newNote = new Note(noteId, title, content);
69 | notesRepository.update(newNote, (Note result) -> {
70 | /* Do Nothing */
71 | });
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amazonaws/mobile/samples/mynotes/viewmodels/NoteListViewModel.java:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
5 | software and associated documentation files (the "Software"), to deal in the Software
6 | without restriction, including without limitation the rights to use, copy, modify,
7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 | */
17 | package com.amazonaws.mobile.samples.mynotes.viewmodels;
18 |
19 | import android.arch.lifecycle.LiveData;
20 | import android.arch.lifecycle.ViewModel;
21 | import android.arch.paging.PagedList;
22 |
23 | import com.amazonaws.mobile.samples.mynotes.Injection;
24 | import com.amazonaws.mobile.samples.mynotes.models.Note;
25 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
26 | import com.amazonaws.mobile.samples.mynotes.repository.NotesRepository;
27 |
28 | public class NoteListViewModel extends ViewModel {
29 | private NotesRepository notesRepository;
30 |
31 | public NoteListViewModel() {
32 | this.notesRepository = Injection.getNotesRepository();
33 | }
34 |
35 | public LiveData> getNotesList() {
36 | return notesRepository.getPagedList();
37 | }
38 |
39 | public void removeNote(String noteId) {
40 | notesRepository.delete(noteId, new ResultCallback() {
41 | @Override
42 | public void onResult(Boolean result) {
43 | /* Do nothing */
44 | }
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-w900dp/note_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
26 |
27 |
31 |
32 |
42 |
43 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_note_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
26 |
27 |
33 |
34 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_note_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
25 |
26 |
31 |
32 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/note_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
26 |
27 |
40 |
41 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/note_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/note_list_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
26 |
27 |
32 |
33 |
45 |
46 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 | #3F51B5
18 | #303F9F
19 | #FF4081
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 | 16dp
18 | 200dp
19 | 200dp
20 | 16dp
21 | 16dp
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 | AWSome Notes
18 | Note Detail
19 | Note Title
20 | Content
21 |
22 |
23 |
24 | 89dd210b–4493–439f–b895–cb1dd300e985
25 | Sample Title
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the "Software"), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify,
6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7 | permit persons to whom the Software is furnished to do so.
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
9 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
10 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
11 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
12 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
13 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 | */
15 | buildscript {
16 | ext.gradle_version = '3.1.4'
17 |
18 | repositories {
19 | jcenter()
20 | google()
21 | }
22 |
23 | dependencies {
24 | classpath "com.android.tools.build:gradle:$gradle_version"
25 |
26 | // NOTE: Do not place your application dependencies here; they belong
27 | // in the individual module build.gradle files
28 | }
29 | }
30 |
31 | allprojects {
32 | repositories {
33 | jcenter()
34 | google()
35 | }
36 | }
37 |
38 | task clean(type: Delete) {
39 | delete rootProject.buildDir
40 | }
41 |
--------------------------------------------------------------------------------
/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 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Aug 02 10:03:43 PDT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/tutorial/analytics.md:
--------------------------------------------------------------------------------
1 | # Add Analytics to the Notes App
2 |
3 | In the [previous section](./index.md) of this tutorial, you installed Android Studio, downloaded a sample note-taking app from GitHub, and then compiled and ran it in the Android Emulator. This tutorial assumes you have completed these steps. In this section, you extend the notes app to include application analytics. Application analytics enable you to gather demographic information about the application usage.
4 |
5 | You should be able to complete this section in 10-15 minutes.
6 |
7 | ## Create an AWS Backend
8 |
9 | 1. Open the project in Android Studio.
10 | 2. Choose **View**, choose **Tool Windows**, and then choose **Terminal**. This opens a terminal prompt within Android Studio at the bottom of the window.
11 | 3. In the terminal window, enter the following commands:
12 |
13 | ```
14 | $ amplify init
15 | ```
16 |
17 | The CLI prompts you through the process of initializing your backend project. When prompted for the `Res` directory:
18 |
19 | ```
20 | ? Where is your Res directory: app/src/main/res
21 | ```
22 |
23 | 4. Next, add the analytics service to your backend:
24 |
25 | ```
26 | $ amplify add analytics
27 | ```
28 |
29 | Again, the CLI prompts you through the process of initializing your backend project.
30 | 5. Finally, deploy the resources you have provisioned:
31 |
32 | ```
33 | $ amplify push
34 | ```
35 |
36 | The `amplify init` command does the following within your project:
37 |
38 | * Creates a basic backend definition in the `amplify` directory.
39 | * Creates an `awsconfiguration.json` file describing the backend in the `app/src/main/res/raw` resource directory.
40 |
41 | The `amplify analytics add` command adds the appropriate entries into the backend definition file for deploying Amazon Pinpoint as a service for this project. The `amplify push` command deploys any new services that are defined and updates the `awsconfiguration.json` file so that the new services can be used within your app.
42 |
43 | ## Add Permissions to the AndroidManifest.xml
44 |
45 | 1. Open the project in Android Studio.
46 | 2. On the left side of the project, choose **Project** to open the project browser.
47 | 3. To find the app manifest, change the project browser view menu at the top to **Android**, and open the `app/manifests` folder.
48 | 4. Add the `INTERNET`, `ACCESS_NETWORK_STATE`, and `ACCESS_WIFI_STATE` permissions to your project's `AndroidManifest.xml` file.
49 |
50 | ```xml
51 |
52 |
54 |
55 |
56 |
57 |
58 |
59 |
67 |
68 |
69 | ```
70 |
71 | ## Add AWS SDK for Android Library
72 |
73 | 1. Edit the `app/build.gradle` file. Add the following lines to the `dependencies` section:
74 |
75 | ```gradle
76 | dependencies {
77 | // Other dependencies will be here already
78 |
79 | // AWS Mobile SDK for Android
80 | def aws_version = '2.6.27'
81 | implementation "com.amazonaws:aws-android-sdk-core:$aws_version"
82 | implementation "com.amazonaws:aws-android-sdk-auth-core:$aws_version@aar"
83 | implementation "com.amazonaws:aws-android-sdk-pinpoint:$aws_version"
84 | }
85 | ```
86 |
87 | 2. Choose **Sync Now** on the upper-right corner to incorporate the dependencies you just declared.
88 |
89 | ## Create an AWSService.java Class
90 |
91 | In the sample, you need to provide a class to provide access to the configuration and identity provider objects. These objects are central to how Android applications locate and communicate with AWS resources. This object should be a singleton. In the sample, you use a central dependency class to ensure that the `AWSService` object is created one time only.
92 |
93 | 1. Expand `app/java` in the Android Studio project explorer.
94 | 2. Right-click the `services` directory.
95 | 3. Choose **New** > **Package**.
96 | 4. For **Name**, enter `aws` and then choose **OK**.
97 | 5. Right-click the `aws` directory.
98 | 6. Choose **New** > **Java Class**.
99 | 7. For **Name**, enter `AWSService` and then choose **OK**.
100 |
101 | The following is the initial code in this class:
102 |
103 | ```java
104 | package com.amazonaws.mobile.samples.mynotes.services.aws;
105 |
106 | import android.content.Context;
107 |
108 | import com.amazonaws.mobile.auth.core.IdentityManager;
109 | import com.amazonaws.mobile.config.AWSConfiguration;
110 |
111 | public class AWSService {
112 | private AWSConfiguration awsConfiguration;
113 | private IdentityManager identityManager;
114 |
115 | public AWSService(Context context) {
116 | awsConfiguration = new AWSConfiguration(context);
117 | identityManager = new IdentityManager(context, awsConfiguration);
118 | IdentityManager.setDefaultIdentityManager(identityManager);
119 | }
120 |
121 | public IdentityManager getIdentityManager() {
122 | return identityManager;
123 | }
124 |
125 | public AWSConfiguration getConfiguration() {
126 | return awsConfiguration;
127 | }
128 | }
129 | ```
130 |
131 | ## Create an AWSAnalyticsService.java Class
132 |
133 | In our sample, the analytics service is provided through a mock dependency injection service. The analytics service must be an object that implements the `AnalyticsService` interface. All other parts of the application will use the `AnalyticsService` that is defined in the `Injection` class.
134 |
135 | 1. Right-click the `aws` directory.
136 | 2. Choose **New** > **Java Class**.
137 | 3. For **Name**, enter `AWSAnalyticsService` and then choose **OK**.
138 |
139 | The following is the initial code in this class:
140 |
141 | ```java
142 | package com.amazonaws.mobile.samples.mynotes.services.aws;
143 |
144 | import android.content.Context;
145 |
146 | import com.amazonaws.auth.AWSCredentialsProvider;
147 | import com.amazonaws.mobile.samples.mynotes.services.AnalyticsService;
148 | import com.amazonaws.mobileconnectors.pinpoint.PinpointConfiguration;
149 | import com.amazonaws.mobileconnectors.pinpoint.PinpointManager;
150 | import com.amazonaws.mobileconnectors.pinpoint.analytics.AnalyticsEvent;
151 | import java.util.Map;
152 |
153 | public class AWSAnalyticsService implements AnalyticsService {
154 | private PinpointManager pinpointManager;
155 |
156 | public AWSAnalyticsService(Context context, AWSService awsService) {
157 | AWSCredentialsProvider cp = awsService.getIdentityManager().getCredentialsProvider();
158 | PinpointConfiguration config = new PinpointConfiguration(context, cp, awsService.getConfiguration());
159 | pinpointManager = new PinpointManager(config);
160 |
161 | // Automatically record a startSession event
162 | startSession();
163 | }
164 |
165 | @Override
166 | public void startSession() {
167 | pinpointManager.getSessionClient().startSession();
168 | pinpointManager.getAnalyticsClient().submitEvents();
169 | }
170 |
171 | @Override
172 | public void stopSession() {
173 | pinpointManager.getSessionClient().stopSession();
174 | pinpointManager.getAnalyticsClient().submitEvents();
175 | }
176 |
177 | @Override
178 | public void recordEvent(String eventName, Map attributes, Map metrics) {
179 | final AnalyticsEvent event = pinpointManager.getAnalyticsClient().createEvent(eventName);
180 | if (attributes != null) {
181 | for (Map.Entry entry : attributes.entrySet()) {
182 | event.addAttribute(entry.getKey(), entry.getValue());
183 | }
184 | }
185 | if (metrics != null) {
186 | for (Map.Entry entry : metrics.entrySet()) {
187 | event.addMetric(entry.getKey(), entry.getValue());
188 | }
189 | }
190 | pinpointManager.getAnalyticsClient().recordEvent(event);
191 | pinpointManager.getAnalyticsClient().submitEvents();
192 | }
193 | }
194 | ```
195 |
196 | ## Register the AWSAnalyticsService with the Injection Service
197 |
198 | Similar to the `AWSService` class, the `AWSAnalyticsService` class should be instantiated as a singleton object. You use the `Injection` service to do this. Open the `Injection` class, and replace the `initialize()` method with the following code:
199 |
200 | ```java
201 | private static AWSService awsService = null;
202 |
203 | public static synchronized void initialize(Context context) {
204 | if (awsService == null) {
205 | awsService = new AWSService(context);
206 | }
207 |
208 | if (analyticsService == null) {
209 | analyticsService = new AWSAnalyticsService(context, awsService);
210 | }
211 |
212 | if (dataService == null) {
213 | dataService = new MockDataService();
214 | }
215 |
216 | if (notesRepository == null) {
217 | notesRepository = new NotesRepository(dataService);
218 | }
219 | }
220 | ```
221 |
222 | You should also add the `AWSService` and `AWSAnalyticsService` classes to the list of imports for the class. You can easily do this using **Alt+Enter** in the editor.
223 |
224 | > **Tip**
225 | >
226 | > You can set up Auto Import to automatically import classes that you need. On Windows or Linux, you can find Auto Import under **File** > **Settings**. On a Mac, you can find it under **Android Studio** > **Preferences**. The Auto Import setting is under **Editor** > **General** > **Auto Import** > **Java**. Change **Insert imports on paste** to **All** and select the **Add unambiguous imports on the fly** option.
227 |
228 | ## Run the Project and Validate Results
229 |
230 | Run the application in the emulator using **Run** > **Run 'app'**. Add and delete some notes to generate some traffic that will appear in the Amazon Pinpoint console.
231 |
232 | To view the demographics and session events, run the following command:
233 |
234 | ```bash
235 | $ amplify console analytics
236 | ```
237 |
238 | It can take up to 5 minutes for the first data to be shown in the graphs. You should see an increase in several graphs:
239 |
240 | 
241 |
242 | Choose **Demographics** to view the demographics information.
243 |
244 | 
245 |
246 | If you see data in each page, you have successfully added analytics to your app. Should you release your app on the App Store, you can return here to see more details about your users.
247 |
248 | Next Steps
249 | ----------
250 |
251 | * Continue by adding [Authentication](./auth.md).
252 | * Learn more about [Amazon Pinpoint](https://aws.amazon.com/pinpoint/).
253 |
--------------------------------------------------------------------------------
/tutorial/auth.md:
--------------------------------------------------------------------------------
1 | # Add Authentication to the Notes App
2 |
3 | In the [previous section](./analytics.md) of this tutorial, you created a mobile backend project using the AWS Amplify CLI, and then added analytics to the sample note-taking app. This section assumes you have completed these steps. If you jumped ahead to this step, [go back to the beginning](./index.md) and start from there. In this tutorial, you configure a sign-up and sign-in flow in our mobile backend. Then, you add a new authentication activity to the note-taking app.
4 |
5 | You should be able to complete this section of the tutorial in 20-30 minutes.
6 |
7 | ## Set Up Your Backend
8 |
9 | Before you work on the client-side code, you need to add user sign-in to the backend project. These steps assume you have already completed the [analytics](./analytics.md) portion of this tutorial.
10 |
11 | 1. Open the project in Android Studio.
12 | 2. Choose **View** > **Tool Windows** > **Terminal**. This opens a terminal prompt in Android Studio at the bottom of the window.
13 | 3. In the terminal window, enter the following commands:
14 |
15 | ```
16 | $ amplify update auth
17 | ```
18 |
19 | 4. When prompted, use the default configuration. When asked to overwrite the default authentication and security configuration, answer **Yes**.
20 | 5. Deploy your new resources with the following command:
21 |
22 | ```
23 | $ amplify push
24 | ```
25 |
26 | The `amplify auth add` command creates an Amazon Cognito user pool configured for username and password authentication with phone verification of the sign-up and forgot password flows. You can adjust this to include multi-factor authentication, TOTP, phone number sign-up, and more.
27 |
28 | ## Add the Authentication UI Library
29 |
30 | 1. Open the `app/build.gradle` file and add the following lines to the `dependencies` section:
31 |
32 | ```gradle
33 | dependencies {
34 | // Other dependencies will be here already
35 |
36 | // AWS Mobile SDK for Android
37 | def aws_version = '2.6.27'
38 | implementation "com.amazonaws:aws-android-sdk-core:$aws_version"
39 | implementation "com.amazonaws:aws-android-sdk-auth-core:$aws_version@aar"
40 | implementation "com.amazonaws:aws-android-sdk-auth-ui:$aws_version@aar"
41 | implementation "com.amazonaws:aws-android-sdk-auth-userpools:$aws_version@aar"
42 | implementation "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$aws_version"
43 | implementation "com.amazonaws:aws-android-sdk-pinpoint:$aws_version"
44 | }
45 | ```
46 |
47 | 2. On the upper right, choose **Sync Now** to incorporate the dependencies you just declared.
48 | 3. Open the `Injection.java` file and add the following method declaration:
49 |
50 | ```java
51 | public static synchronized AWSService getAWSService() {
52 | return awsService;
53 | }
54 | ```
55 |
56 | ## Register the Email and Password Sign-in Provider
57 |
58 | The sign-in UI is provided by `IdentityManager`. Each method of establishing identity (email and password, Facebook and Google) requires a plug-in provider that handles the appropriate sign-in flow.
59 |
60 | 1. Open your project in Android Studio.
61 | 2. Open the `service/aws/AWSService.java` class.
62 | 3. Add the following to the import declarations:
63 |
64 | ```java
65 | import com.amazonaws.mobile.auth.userpools.CognitoUserPoolsSignInProvider;
66 | ```
67 |
68 | 4. Adjust the constructor to add the `CognitoUserPoolsSignInProvider`.
69 |
70 | ```java
71 | public AWSService(Context context) {
72 | awsConfiguration = new AWSConfiguration(context);
73 | identityManager = new IdentityManager(context, awsConfiguration);
74 | identityManager.addSignInProvider(CognitoUserPoolsSignInProvider.class);
75 | IdentityManager.setDefaultIdentityManager(identityManager);
76 | }
77 | ```
78 |
79 | ## Add a AuthenticatorActivity to the Project
80 |
81 | You can call the IdentityProvider at any point in your application. In this tutorial, you add a new screen to the project that is displayed before the list. The user will be prompted to sign-up or sign-in prior to seeing the list of notes. This ensures that all connections to the backend are authenticated.
82 |
83 | **To add a AuthenticatorActivity to the project in Android Studio**
84 |
85 | 1. Right-click the `ui` package.
86 | 2. Choose **New** > **Activity** > **Empty Activity**.
87 | 3. For **Activity Name**, enter `AuthenticatorActivity`.
88 | 4. Choose **Finish**.
89 |
90 | Add the following imports to the top of the `AuthenticatorActivity.java`:
91 |
92 | ```java
93 | import android.app.Activity;
94 | import android.content.Intent;
95 | import android.support.v7.app.AppCompatActivity;
96 | import android.os.Bundle;
97 | import android.widget.Toast;
98 |
99 | import com.amazonaws.mobile.auth.core.DefaultSignInResultHandler;
100 | import com.amazonaws.mobile.auth.core.IdentityManager;
101 | import com.amazonaws.mobile.auth.core.IdentityProvider;
102 | import com.amazonaws.mobile.auth.ui.AuthUIConfiguration;
103 | import com.amazonaws.mobile.auth.ui.SignInActivity;
104 | import com.amazonaws.mobile.samples.mynotes.Injection;
105 | import com.amazonaws.mobile.samples.mynotes.R;
106 | ```
107 |
108 | Edit the `onCreate()` method of `AuthenticatorActivity.java` as follows:
109 |
110 | ```java
111 | @Override
112 | protected void onCreate(Bundle savedInstanceState) {
113 | super.onCreate(savedInstanceState);
114 | setContentView(R.layout.activity_authenticator);
115 |
116 | final IdentityManager identityManager = Injection.getAWSService().getIdentityManager();
117 | // Set up the callbacks to handle the authentication response
118 | identityManager.login(this, new DefaultSignInResultHandler() {
119 | @Override
120 | public void onSuccess(Activity activity, IdentityProvider identityProvider) {
121 | Toast.makeText(AuthenticatorActivity.this,
122 | String.format("Logged in as %s", identityManager.getCachedUserID()),
123 | Toast.LENGTH_LONG).show();
124 | // Go to the main activity
125 | final Intent intent = new Intent(activity, NoteListActivity.class)
126 | .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
127 | activity.startActivity(intent);
128 | activity.finish();
129 | }
130 |
131 | @Override
132 | public boolean onCancel(Activity activity) {
133 | return false;
134 | }
135 | });
136 |
137 | // Start the authentication UI
138 | AuthUIConfiguration config = new AuthUIConfiguration.Builder()
139 | .userPools(true)
140 | .build();
141 | SignInActivity.startSignInActivity(this, config);
142 | AuthenticatorActivity.this.finish();
143 | }
144 | ```
145 |
146 | > The AWS SDK for Android contains an in-built activity for handling the authentication UI. This Activity sets up the authentication UI to work for just email and password, then sets up an activity listener to handle the response. In this case, we transition to the `NoteListActivity` when a successful sign-in occurs, and stay on this activity when it fails. Finally, we transition to the Sign-In activity from the AWS SDK for Android library.
147 |
148 | ## Update the AndroidManifest.xml
149 |
150 | The `AuthenticatorActivity` will be added to the `AndroidManifest.xml` automatically, but it will not be set as the default (starting) activity. To make the `AuthenticatorActivity` primary, edit the `AndroidManifest.xml`:
151 |
152 | ```xml
153 |
157 |
158 |
159 |
160 |
161 |
162 |
166 |
167 |
168 | ```
169 |
170 | The `.AuthenticatorActivity` section is added at the end. Ensure it is not duplicated. If the section is duplicated, build errors occur.
171 |
172 | ## Run the Project and Validate Results
173 |
174 | In the emulator, run the project using **Run** > **Run 'app'**. You should see a sign-in screen. Choose **Create new account** to create a new account. After the information is submitted, you should receive a confirmation code via email. Enter the confirmation code to complete registration, and then sign-in with your new account.
175 |
176 | > **Tip**
177 | >
178 | > If you don't want to use your own email account as a test account, create an [Amazon WorkMail](https://aws.amazon.com/workmail/) service within AWS for test accounts. You can get started for free with a 30-day trial for up to 25 accounts.
179 |
180 | 
181 |
182 | ## Next Steps
183 |
184 | * Continue by integrating a [Serverless Backend](./data.md).
185 | * Learn more about [Amazon Cognito](https://aws.amazon.com/cognito/).
186 |
--------------------------------------------------------------------------------
/tutorial/data.md:
--------------------------------------------------------------------------------
1 | # Add Serverless Backend to the Notes App
2 |
3 | In the [previous section](./auth.md) of this tutorial , you added a simple sign-up and sign-in flow to the sample note-taking app with email validation. This tutorial assumes you have completed the previous tutorials. If you jumped to this step, [go back to the beginning](./index.md) and start from there. In this tutorial, you add a NoSQL database to the mobile backend, and then configure a data access service to the note-taking app.
4 |
5 | You should be able to complete this section of the tutorial in 45-60 minutes.
6 |
7 | ## Add a Data Access API to the Backend
8 |
9 | 1. In Android Studio, switch to the Project view.
10 | 2. Right-click on the project, and then select **New** > **Directory**.
11 | 3. For directory name, enter `server`, and then choose **OK**.
12 | 4. Right-click on the `server` directory, and then select **New** > **File**.
13 | 5. For file name, enter `schema.graphql`, and then choose **OK**.
14 | 6. Copy the following code into the `schema.graphql` file:
15 |
16 | ```graphql
17 | type Note @model @auth(rules:[{allow: owner}]) {
18 | id: ID!
19 | title: String!
20 | content: String!
21 | }
22 | ```
23 |
24 | 7. In the terminal window, enter the following commands:
25 |
26 | ```
27 | $ amplify add api
28 | ```
29 |
30 | When you run this command the CLI prompts you to make API configuration choices, and stores the configuration in local metadata using an Amazon CloudFormation template.
31 | 8. When prompted by the CLI, do the following:
32 | * Select a service type: **GraphQL**.
33 | * Choose an authorization type: **Amazon Cognito User Pool**.
34 | * Do you have an annotated GraphQL schema: **Y**.
35 | * Provide your schema file path: **./server/schema.graphql**.
36 |
37 | 9. To deploy the new service, enter the following:
38 |
39 | ```
40 | $ amplify push
41 | ```
42 |
43 | When you run this command, the CLI uses your configuration choices to create or update the AWS resources that make up your cloud API backend. Once the services are configured, the CLI creates or updates configuration files in your app to connect it to your services.
44 |
45 | 10. When prompted by the CLI, do the following:
46 | * Do you want to generate code for your newly created GraphQL API: **Yes**.
47 | * Enter the file name pattern of graphql queries, mutations and subscriptions: *(enter return to use default)*
48 | * Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions (Y/n): **Yes**
49 |
50 | The AWS CloudFormation template that is generated creates an Amazon DynamoDB table that is protected by Amazon Cognito user pool authentication. Access is provided by AWS AppSync. AWS AppSync tags each record that is inserted into the database with the user ID of the authenticated user. The authenticated user can read only the records that they own.
51 |
52 | In addition to updating the `awsconfiguration.json` file, the Amplify CLI generates the `schema.json` file in the `app/src/main/graphql` directory. The `schema.json` file is required by the AWS Mobile SDK for Android to run code generation for GraphQL operations.
53 |
54 | ## Add Required Libraries to the Project
55 |
56 | Edit the project-level `build.gradle` file and add the AWS AppSync plugin path to the dependencies as follows:
57 |
58 | ```gradle
59 | dependencies {
60 | classpath "com.android.tools.build:gradle:$gradle_version"
61 | classpath "com.amazonaws:aws-android-sdk-appsync-gradle-plugin:2.6.+"
62 |
63 | // NOTE: Do not place your application dependencies here; they belong
64 | // in the individual module build.gradle files
65 | }
66 | ```
67 |
68 | Edit the `app/build.gradle` file. Add the AWS AppSync plugin below the other plugins:
69 |
70 | ```gradle
71 | apply plugin: 'com.android.application'
72 | apply plugin: 'com.amazonaws.appsync'
73 | ```
74 |
75 | Add the AWS AppSync dependencies with the other SDKs.
76 |
77 | ```gradle
78 | dependencies {
79 |
80 | // . . .
81 |
82 | // AWS SDK for Android
83 | def aws_version = '2.6.27'
84 | implementation "com.amazonaws:aws-android-sdk-core:$aws_version"
85 | implementation "com.amazonaws:aws-android-sdk-auth-core:$aws_version@aar"
86 | implementation "com.amazonaws:aws-android-sdk-auth-ui:$aws_version@aar"
87 | implementation "com.amazonaws:aws-android-sdk-auth-userpools:$aws_version@aar"
88 | implementation "com.amazonaws:aws-android-sdk-cognitoidentityprovider:$aws_version"
89 | implementation "com.amazonaws:aws-android-sdk-pinpoint:$aws_version"
90 |
91 | // AWS AppSync SDK
92 | implementation "com.amazonaws:aws-android-sdk-appsync:2.6.+"
93 | }
94 | ```
95 |
96 | On the upper-right side, choose **Sync Now** to incorporate the dependencies you just declared.
97 |
98 | Finally, choose **Build** > **Make project** from the top menu.
99 |
100 | > Why is it important to build at this point? To enable your mobile app to send GraphQL commands (mutations and queries) to the AWS AppSync service, it needs classes that represent your APIs. Building your project causes gradle to activate the appsync gradle plugin to generate Java classes from the CLI-generated configuration files.
101 |
102 | ## Add Permissions to the AndroidManifest.xml
103 |
104 | 1. To find the app manifest, change the project browser view menu at the top to **Android**, and then open the `app/manifests` folder.
105 | 2. Add the `WAKE_LOCK`, `READ_PHONE_STATE`, `WRITE_EXTERNAL_STORAGE`, and `READ_EXTERNAL_STORAGE`: permissions to your project's `AndroidManifest.xml` file.
106 |
107 | ```xml
108 |
109 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
128 |
129 |
130 |
131 |
132 | ```
133 |
134 | ## Create an AWSDataService Class
135 |
136 | Data access is proxied through a class that implements the `DataService` interface. At this point, the data access is provided by the `MockDataService` class that stores a number of notes in memory. In this section, you replace this class with an `AWSDataService` class that provides access to the API that you recently deployed.
137 |
138 | 1. Right-click on the `services/aws` folder, and then select **New** > **Java Class**.
139 | 2. For class name, enter `AWSDataService`, and then choose **OK**.
140 | 3. Replace the contents of the file with the following:
141 |
142 | ```java
143 | package com.amazonaws.mobile.samples.mynotes.services.aws;
144 |
145 | import android.content.Context;
146 | import android.util.Log;
147 |
148 | import com.amazonaws.mobile.config.AWSConfiguration;
149 | import com.amazonaws.amplify.generated.graphql.CreateNoteMutation;
150 | import com.amazonaws.amplify.generated.graphql.DeleteNoteMutation;
151 | import com.amazonaws.amplify.generated.graphql.GetNoteQuery;
152 | import com.amazonaws.amplify.generated.graphql.ListNotesQuery;
153 | import com.amazonaws.amplify.generated.graphql.UpdateNoteMutation;
154 | import com.amazonaws.mobile.samples.mynotes.models.Note;
155 | import com.amazonaws.mobile.samples.mynotes.models.PagedListConnectionResponse;
156 | import com.amazonaws.mobile.samples.mynotes.models.ResultCallback;
157 | import com.amazonaws.mobile.samples.mynotes.services.DataService;
158 | import com.amazonaws.mobileconnectors.appsync.AWSAppSyncClient;
159 | import com.amazonaws.mobileconnectors.appsync.fetcher.AppSyncResponseFetchers;
160 | import com.amazonaws.mobileconnectors.appsync.sigv4.BasicCognitoUserPoolsAuthProvider;
161 | import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool;
162 | import com.apollographql.apollo.GraphQLCall;
163 | import com.apollographql.apollo.api.Error;
164 | import com.apollographql.apollo.api.Response;
165 | import com.apollographql.apollo.exception.ApolloException;
166 |
167 | import java.util.ArrayList;
168 | import java.util.List;
169 | import java.util.Locale;
170 | import javax.annotation.Nonnull;
171 |
172 | import type.CreateNoteInput;
173 | import type.UpdateNoteInput;
174 | import type.DeleteNoteInput;
175 |
176 | import static com.amazonaws.mobile.auth.core.internal.util.ThreadUtils.runOnUiThread;
177 |
178 | public class AWSDataService implements DataService {
179 | private static final String TAG = "AWSDataService";
180 | private AWSAppSyncClient client;
181 |
182 | public AWSDataService(Context context, AWSService awsService) {
183 | // Create an AppSync client from the AWSConfiguration
184 | AWSConfiguration config = awsService.getConfiguration();
185 | CognitoUserPool userPool = new CognitoUserPool(context, awsService.getConfiguration());
186 | client = AWSAppSyncClient.builder()
187 | .context(context)
188 | .awsConfiguration(config)
189 | .cognitoUserPoolsAuthProvider(new BasicCognitoUserPoolsAuthProvider(userPool))
190 | .build();
191 | }
192 |
193 | @Override
194 | public void loadNotes(int limit, String after, ResultCallback> callback) {
195 | // Load notes will go here
196 | }
197 |
198 | @Override
199 | public void getNote(String noteId, ResultCallback callback) {
200 | // Get note will go here
201 | }
202 |
203 | @Override
204 | public void deleteNote(String noteId, ResultCallback callback) {
205 | // Delete note will go here
206 | }
207 |
208 | @Override
209 | public void createNote(String title, String content, ResultCallback callback) {
210 | // Create note will go here
211 | }
212 |
213 | @Override
214 | public void updateNote(Note note, ResultCallback callback) {
215 | // Update note will go here
216 | }
217 |
218 | private void showErrors(List errors) {
219 | Log.e(TAG, "Response has errors:");
220 | for (Error e : errors) {
221 | Log.e(TAG, String.format(Locale.ENGLISH, "Error: %s", e.message()));
222 | }
223 | Log.e(TAG, "End of Response errors");
224 | }
225 | }
226 | ```
227 |
228 | ## Register the AWSDataService with the Injection Service
229 |
230 | Similar to the `AWSService` class, the `AWSDataService` class should be instantiated as a singleton object. You use the `Injection` service to do this. Open the `Injection` class, and replace the `initialize()` method with the following code:
231 |
232 | ```java
233 | public static synchronized void initialize(Context context) {
234 | if (awsService == null) {
235 | awsService = new AWSService(context);
236 | }
237 |
238 | if (analyticsService == null) {
239 | analyticsService = new AWSAnalyticsService(context, awsService);
240 | }
241 |
242 | if (dataService == null) {
243 | dataService = new AWSDataService(context, awsService);
244 | }
245 |
246 | if (notesRepository == null) {
247 | notesRepository = new NotesRepository(dataService);
248 | }
249 | }
250 | ```
251 |
252 | You should also add the `AWSDataService` class to the list of imports for the class. You can easily do this using **Alt+Enter** within the editor.
253 |
254 | ## Add the Create, Update, and Delete Mutations
255 |
256 | We added some placeholder methods in the `AWSDataService`. These placeholders should contain the API calls to the backend. Mutations follow a pattern:
257 |
258 | 1. Create an input object to represent the arguments that are required to perform the mutation.
259 | 2. Create a request object with the input object.
260 | 3. Enqueue the request with the AppSync client object.
261 | 4. When the request returns, handle the response on the UI thread.
262 |
263 | Use the following code for the `createNote()` and `updateNote()` methods:
264 |
265 | ```java
266 | @Override
267 | public void createNote(String title, String content, ResultCallback callback) {
268 | CreateNoteInput input = CreateNoteInput.builder()
269 | .title(title.isEmpty() ? " " : title)
270 | .content(content.isEmpty() ? " " : content)
271 | .build();
272 | CreateNoteMutation mutation = CreateNoteMutation.builder().input(input).build();
273 |
274 | client.mutate(mutation)
275 | .enqueue(new GraphQLCall.Callback() {
276 | @Override
277 | public void onResponse(@Nonnull Response response) {
278 | if (response.hasErrors()) {
279 | showErrors(response.errors());
280 | runOnUiThread(() -> callback.onResult(null));
281 | } else {
282 | CreateNoteMutation.CreateNote item = response.data().createNote();
283 | final Note returnedNote = new Note(item.id());
284 | returnedNote.setTitle(item.title().equals(" ") ? "" : item.title());
285 | returnedNote.setContent(item.content().equals(" ") ? "" : item.content());
286 | runOnUiThread(() -> callback.onResult(returnedNote));
287 | }
288 | }
289 |
290 | @Override
291 | public void onFailure(@Nonnull ApolloException e) {
292 | Log.e(TAG, String.format(Locale.ENGLISH, "Error during GraphQL Operation: %s", e.getMessage()), e);
293 | }
294 | });
295 | }
296 |
297 | @Override
298 | public void updateNote(Note note, ResultCallback callback) {
299 | UpdateNoteInput input = UpdateNoteInput.builder()
300 | .id(note.getNoteId())
301 | .title(note.getTitle().isEmpty() ? " " : note.getTitle())
302 | .content(note.getContent().isEmpty() ? " " : note.getContent())
303 | .build();
304 | UpdateNoteMutation mutation = UpdateNoteMutation.builder().input(input).build();
305 |
306 | client.mutate(mutation)
307 | .enqueue(new GraphQLCall.Callback() {
308 | @Override
309 | public void onResponse(@Nonnull Response response) {
310 | if (response.hasErrors()) {
311 | showErrors(response.errors());
312 | runOnUiThread(() -> callback.onResult(null));
313 | } else {
314 | UpdateNoteMutation.UpdateNote item = response.data().updateNote();
315 | final Note returnedNote = new Note(item.id());
316 | returnedNote.setTitle(item.title().equals(" ") ? "" : item.title());
317 | returnedNote.setContent(item.content().equals(" ") ? "" : item.content());
318 | runOnUiThread(() -> callback.onResult(returnedNote));
319 | }
320 | }
321 |
322 | @Override
323 | public void onFailure(@Nonnull ApolloException e) {
324 | Log.e(TAG, String.format(Locale.ENGLISH, "Error during GraphQL Operation: %s", e.getMessage()), e);
325 | }
326 | });
327 | }
328 | ```
329 |
330 | The classes for the input, mutation, and response data are all generated from the information within the files that were generated as part of the `amplify codegen` process. The names of the classes are based on the query or mutation name within the file.
331 |
332 | Note that Amazon DynamoDB does not allow blank string values. The code here ensures that blanks are replaced with something that is not blank for the purposes of storage.
333 |
334 | The code for the `deleteNote()` method is similar to the `createNote()` and `deleteNote()` methods. However, the `DeleteNote` operation does not take an input object as an argument. We can feed the `noteId` directly into the mutation operation object:
335 |
336 | ```java
337 | @Override
338 | public void deleteNote(String noteId, ResultCallback callback) {
339 | DeleteNoteInput input = DeleteNoteInput.builder().id(noteId).build();
340 | DeleteNoteMutation mutation = DeleteNoteMutation.builder().input(input).build();
341 |
342 | client.mutate(mutation)
343 | .enqueue(new GraphQLCall.Callback() {
344 | @Override
345 | public void onResponse(@Nonnull Response response) {
346 | runOnUiThread(() -> callback.onResult(true));
347 | }
348 |
349 | @Override
350 | public void onFailure(@Nonnull ApolloException e) {
351 | Log.e(TAG, String.format(Locale.ENGLISH, "Error during GraphQL Operation: %s", e.getMessage()), e);
352 | callback.onResult(false);
353 | }
354 | });
355 | }
356 | ```
357 |
358 | ## Add the LoadNotes and GetNote Queries
359 |
360 | Queries operate very similarly to the mutations. However, you have to take care to convert all the records that are received to the proper form for the application, and you have to deal with caching. The AWS Mobile SDK performs caching for you, but you have to select the appropriate cache policy.
361 |
362 | * `CACHE_ONLY` consults the cache only and never requests data from the backend. This is useful in an offline scenario.
363 | * `NETWORK_ONLY` is the reverse of `CACHE_ONLY`. It consults the backend only and never uses the cache.
364 | * `CACHE_FIRST` fetches the data from the cache if available, and fetches from the backend if it is not available in the cache.
365 | * `NETWORK_FIRST` fetches the data from the network. If the network is unavailable, it uses the cache.
366 | * `CACHE_AND_NETWORK` consults both the cache and network for data. If both are available, you get two callbacks.
367 |
368 | In the sample application, use a `NETWORK_FIRST` cache policy. This guarantees that the callback is only called once, but it still uses the cache when the application goes offline.
369 |
370 | The `getNote()` method looks very similar to the mutations covered earlier:
371 |
372 | ```java
373 | @Override
374 | public void getNote(String noteId, ResultCallback callback) {
375 | GetNoteQuery query = GetNoteQuery.builder().id(noteId).build();
376 | client.query(query)
377 | .responseFetcher(AppSyncResponseFetchers.NETWORK_FIRST)
378 | .enqueue(new GraphQLCall.Callback() {
379 | @Override
380 | public void onResponse(@Nonnull Response response) {
381 | GetNoteQuery.GetNote item = response.data().getNote();
382 | final Note note = new Note(noteId);
383 | note.setTitle(item != null ? (item.title().equals(" ") ? "" : item.title()) : "");
384 | note.setContent(item != null ? (item.content().equals(" ") ? "" : item.content()) : "");
385 | runOnUiThread(() -> callback.onResult(note));
386 | }
387 |
388 | @Override
389 | public void onFailure(@Nonnull ApolloException e) {
390 | Log.e(TAG, String.format(Locale.ENGLISH, "Error during GraphQL Operation: %s", e.getMessage()), e);
391 | }
392 | });
393 | }
394 | ```
395 |
396 | You need to convert the return value to the internal representation prior to returning the data to the main application. The `loadNotes()` method is a little more involved because the return value is a complex type that needs to be decoded before returning:
397 |
398 | ```java
399 | @Override
400 | public void loadNotes(int limit, String after, ResultCallback> callback) {
401 | ListNotesQuery query = ListNotesQuery.builder().limit(limit).nextToken(after).build();
402 | client.query(query)
403 | .responseFetcher(AppSyncResponseFetchers.NETWORK_FIRST)
404 | .enqueue(new GraphQLCall.Callback() {
405 | @Override
406 | public void onResponse(@Nonnull Response response) {
407 | String nextToken = response.data().listNotes().nextToken();
408 | List rItems = response.data().listNotes().items();
409 |
410 | List items = new ArrayList<>();
411 | for (ListNotesQuery.Item item : rItems) {
412 | Note n = new Note(item.id());
413 | n.setTitle(item.title().equals(" ") ? "" : item.title());
414 | n.setContent(item.content().equals(" ") ? "" : item.content());
415 | items.add(n);
416 | }
417 | runOnUiThread(() -> callback.onResult(new PagedListConnectionResponse<>(items, nextToken)));
418 | }
419 |
420 | @Override
421 | public void onFailure(@Nonnull ApolloException e) {
422 | Log.e(TAG, String.format(Locale.ENGLISH, "Error during GraphQL Operation: %s", e.getMessage()), e);
423 | }
424 | });
425 | }
426 | ```
427 |
428 | ## Run the Application
429 |
430 | You must be online in order to run this application. Run the application in the emulator. Note that the initial startup after logging in is slightly longer. This happens because the app is reading the data from the remote database.
431 |
432 | Data is available immediately in the mobile backend. Create a few notes, and then view the records in the AWS Console:
433 |
434 | 1. Open the [AWS AppSync console](https://console.aws.amazon.com/appsync/home).
435 | 2. Choose the API that you created for your app, the name should match the one you provided to the CLI.
436 | 3. In the left navigation, choose **Data Sources**.
437 | 4. Choose the resource of the table with the name you provided to the CLI. This will open table that the CLI created for you in the Amazon DynamoDB console.
438 |
439 | When you insert, edit, or delete notes in the app, you should be able to see that the data on the server reflect your actions immediately.
440 |
441 | ## Next Steps
442 |
443 | * Learn about [AWS AppSync](https://aws.amazon.com/appsync/).
444 | * Learn about [Amazon DynamoDB](https://aws.amazon.com/dynamodb/).
445 |
--------------------------------------------------------------------------------
/tutorial/index.md:
--------------------------------------------------------------------------------
1 | # Tutorial: Create a Simple Android Note-Taking App
2 |
3 | In this tutorial, you start with a working app, driven by locally stored data, and then add cloud-enabled features as follows:
4 |
5 | * [Add analytics to your app](./analytics.md) so you can view demographic information about your users.
6 | * [Add a simple sign-in/sign-up flow](./auth.md) for authentication.
7 | - [Access data stores in the AWS cloud](./data.md), so that a user's notes are available to them on any device with the app installed.
8 |
9 | 
10 |
11 | You should be able to complete the setup section of this tutorial within 10-15 minutes after you have installed all required software. After you complete the following instructions, you can run the project on your local system.
12 |
13 | ## Getting Started
14 |
15 | Before you begin, do the following:
16 |
17 | 1. Complete the [Getting Started](https://aws-amplify.github.io/media/get_started) instructions to install the Amplify CLI.
18 | 2. Download and install [Android Studio](https://developer.android.com/studio/index.html) version 3.0.1 or later.
19 | 3. Download and install [Android SDK](https://developer.android.com/studio/intro/update.html#sdk-manager) version 8.0 (Oreo), API level 26.
20 | 4. Install an [Android Emulator](https://developer.android.com/studio/run/managing-avds.html). The app works with both phone and tablet emulators (for example, the Nexus 5X or Pixel C).
21 |
22 | ## Download the Source Code
23 |
24 | Download the source code as a [ZIP file](https://github.com/aws-samples/aws-mobile-android-notes-tutorial/archive/master.zip). After the download is complete, unpack the downloaded ZIP file.
25 |
26 | ## Compile the Source Code
27 |
28 | To compile the source code, do the following:
29 |
30 | 1. Start Android Studio.
31 | 2. If you have a project open already, choose **File** > **Close Project**.
32 | 3. Choose **Open an existing Android Studio project**.
33 | 4. Find and choose the _aws-mobile-android-notes-tutorial-master_ project in your file system, and then choose **OK**.
34 |
35 | 
36 |
37 | 5. On the menu bar, choose **File** > **Sync Project with Gradle Files**.
38 | 6. On the menu bar, choose **Build** > **Make Project**.
39 |
40 | The compilation step should finish with no errors. Errors are displayed in the **Messages** window, which is available on the status bar at the bottom of the project.
41 |
42 | ## Run the Project in an Emulator
43 |
44 | If you haven't already done so, create a new emulator as follows:
45 |
46 | 1. Choose **Tools** > **Android** > **AVD Manager**.
47 | 2. Choose **Create Virtual Device...**.
48 | 3. Choose **Phone** > **Nexus 5X**, and then choose **Next**.
49 | 4. Choose the **x86 Images** tab, and then choose **Android 8.0 (Google APIs)**.
50 | * If you didn't previously downloaded the image, you can download it from this screen.
51 | 5. Choose **Next**.
52 | 6. Choose **Finish**.
53 | 7. Close the AVD Manager.
54 |
55 | Run the project in an emulator as follows:
56 |
57 | 1. Choose **Run** > **Run 'app'**.
58 | 2. Choose the **Nexus 5X API 26** virtual device.
59 | 3. Choose **OK**.
60 |
61 | If it isn't already running, the Android emulator starts and then the application runs. You should be able to interact with the application as you would any other mobile app. Try pressing on the plus sign + at the of the app to create a note, or choose a note to show in the editor screen. A unique ID for each note is displayed in the list view underneath the note's title.
62 |
63 | ## Troubleshooting
64 |
65 | The most common problems at this stage involve issues with the installation of Java, Android Studio, the Android SDK, or the Android Emulator. Google provides detailed instructions about [Android Studio](https://developer.android.com/studio/index.html>) and dependent features.
66 |
67 | ## Next Step
68 |
69 | Next, [integrate application analytics](./analytics.md) into your project.
70 |
--------------------------------------------------------------------------------
/tutorial/open-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/open-project.png
--------------------------------------------------------------------------------
/tutorial/pinpoint-addnote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/pinpoint-addnote.png
--------------------------------------------------------------------------------
/tutorial/pinpoint-demographics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/pinpoint-demographics.png
--------------------------------------------------------------------------------
/tutorial/pinpoint-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/pinpoint-overview.png
--------------------------------------------------------------------------------
/tutorial/tutorial-notes-app-anim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/tutorial-notes-app-anim.gif
--------------------------------------------------------------------------------
/tutorial/tutorial-notes-authentication-anim.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-archives/aws-mobile-android-notes-tutorial/59c5b1085949d04daa73ab630ce2bb28dbd00833/tutorial/tutorial-notes-authentication-anim.gif
--------------------------------------------------------------------------------