├── .expo-shared └── assets.json ├── .gitignore ├── .graphqlconfig.yml ├── .vscode └── settings.json ├── App.js ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── amplify ├── .config │ └── project-config.json ├── README.md ├── backend │ ├── api │ │ └── mobileloc │ │ │ ├── parameters.json │ │ │ ├── resolvers │ │ │ └── README.md │ │ │ ├── schema.graphql │ │ │ ├── stacks │ │ │ └── CustomResources.json │ │ │ └── transform.conf.json │ ├── backend-config.json │ ├── function │ │ └── updateLocation │ │ │ ├── amplify.state │ │ │ ├── function-parameters.json │ │ │ ├── src │ │ │ ├── event.json │ │ │ ├── index.js │ │ │ ├── package-lock.json │ │ │ └── package.json │ │ │ └── updateLocation-cloudformation-template.json │ └── tags.json ├── cli.json └── team-provider-info.json ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── components └── header.js ├── package-lock.json ├── package.json ├── routes ├── mainStack.js ├── setupStack.js └── tabNavigator.js ├── screens ├── main.js └── setup.js ├── solution.png └── src ├── device.js └── graphql ├── mutations.js ├── queries.js ├── schema.json └── subscriptions.js /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | 15 | #amplify 16 | amplify/\#current-cloud-backend 17 | amplify/.config/local-* 18 | amplify/logs 19 | amplify/mock-data 20 | amplify/backend/amplify-meta.json 21 | amplify/backend/awscloudformation 22 | amplify/backend/.temp 23 | build/ 24 | dist/ 25 | node_modules/ 26 | aws-exports.js 27 | awsconfiguration.json 28 | amplifyconfiguration.json 29 | amplifyconfiguration.dart 30 | amplify-build-config.json 31 | amplify-gradle-config.json 32 | amplifytools.xcconfig 33 | .secret-* -------------------------------------------------------------------------------- /.graphqlconfig.yml: -------------------------------------------------------------------------------- 1 | projects: 2 | mobileloc: 3 | schemaPath: src/graphql/schema.json 4 | includes: 5 | - src/graphql/**/*.js 6 | excludes: 7 | - ./amplify/** 8 | extensions: 9 | amplify: 10 | codeGenTarget: javascript 11 | generatedFileName: '' 12 | docsFilePath: src/graphql 13 | extensions: 14 | amplify: 15 | version: 3 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "amplify/.config": true, 4 | "amplify/**/*-parameters.json": true, 5 | "amplify/**/amplify.state": true, 6 | "amplify/**/transform.conf.json": true, 7 | "amplify/#current-cloud-backend": true, 8 | "amplify/backend/amplify-meta.json": true, 9 | "amplify/backend/awscloudformation": true 10 | } 11 | } -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Navigator from './routes/tabNavigator'; 3 | 4 | export default function App() { 5 | return ( 6 | 7 | ); 8 | } -------------------------------------------------------------------------------- /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, or recently closed, 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 *main* 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' 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](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 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 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to 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 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Native Mobile Tracking App 2 | 3 | Demonstrates the basics of using AWS Amplify to integrate with Amazon Location Service tracker in a javascript react-natvie cross-platform mobile app. 4 | 5 | The Expo framework has been used to scafold the cross-platform mobile app with react-native. 6 | 7 | 8 | ## Solution 9 | 10 | ![alt text](./solution.png) 11 | 12 | AWS Amplify is used to create a GraphQL API, using AWS AppSync and backed by Amazon DynamoDB, in order to register a mobile device with the solution and track the mobile location through Amazon Location Service. 13 | 14 | AWS Amplify Add Function (AWS Lambda) is used as an AppSync data source to send the Location data to the Amazon Location Service tracker. 15 | 16 | ## Security 17 | 18 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 19 | 20 | ## License 21 | 22 | This library is licensed under the MIT-0 License. See the LICENSE file. 23 | 24 | -------------------------------------------------------------------------------- /amplify/.config/project-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "mobileLoc", 3 | "version": "3.1", 4 | "frontend": "javascript", 5 | "javascript": { 6 | "framework": "react-native", 7 | "config": { 8 | "SourceDir": "src", 9 | "DistributionDir": "/", 10 | "BuildCommand": "npm.cmd run-script build", 11 | "StartCommand": "npm.cmd run-script start" 12 | } 13 | }, 14 | "providers": [ 15 | "awscloudformation" 16 | ] 17 | } -------------------------------------------------------------------------------- /amplify/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Amplify CLI 2 | This directory was generated by [Amplify CLI](https://docs.amplify.aws/cli). 3 | 4 | Helpful resources: 5 | - Amplify documentation: https://docs.amplify.aws 6 | - Amplify CLI documentation: https://docs.amplify.aws/cli 7 | - More details on this folder & generated files: https://docs.amplify.aws/cli/reference/files 8 | - Join Amplify's community: https://amplify.aws/community/ 9 | -------------------------------------------------------------------------------- /amplify/backend/api/mobileloc/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSyncApiName": "mobileloc", 3 | "DynamoDBBillingMode": "PAY_PER_REQUEST", 4 | "DynamoDBEnableServerSideEncryption": false 5 | } -------------------------------------------------------------------------------- /amplify/backend/api/mobileloc/resolvers/README.md: -------------------------------------------------------------------------------- 1 | Any resolvers that you add in this directory will override the ones automatically generated by Amplify CLI and will be directly copied to the cloud. 2 | For more information, visit [https://docs.amplify.aws/cli/graphql-transformer/resolvers](https://docs.amplify.aws/cli/graphql-transformer/resolvers) -------------------------------------------------------------------------------- /amplify/backend/api/mobileloc/schema.graphql: -------------------------------------------------------------------------------- 1 | type Device @model { 2 | id: ID! 3 | name: String! 4 | trackerName: String! 5 | latitude: String 6 | longitude: String 7 | } 8 | 9 | type Mutation { 10 | updateLocation(trackerName: String, deviceId: String, Latitude: String, Longitude: String): String @function(name: "updateLocation-${env}") 11 | } -------------------------------------------------------------------------------- /amplify/backend/api/mobileloc/stacks/CustomResources.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "An auto-generated nested stack.", 4 | "Metadata": {}, 5 | "Parameters": { 6 | "AppSyncApiId": { 7 | "Type": "String", 8 | "Description": "The id of the AppSync API associated with this project." 9 | }, 10 | "AppSyncApiName": { 11 | "Type": "String", 12 | "Description": "The name of the AppSync API", 13 | "Default": "AppSyncSimpleTransform" 14 | }, 15 | "env": { 16 | "Type": "String", 17 | "Description": "The environment name. e.g. Dev, Test, or Production", 18 | "Default": "NONE" 19 | }, 20 | "S3DeploymentBucket": { 21 | "Type": "String", 22 | "Description": "The S3 bucket containing all deployment assets for the project." 23 | }, 24 | "S3DeploymentRootKey": { 25 | "Type": "String", 26 | "Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory." 27 | } 28 | }, 29 | "Resources": { 30 | "EmptyResource": { 31 | "Type": "Custom::EmptyResource", 32 | "Condition": "AlwaysFalse" 33 | } 34 | }, 35 | "Conditions": { 36 | "HasEnvironmentParameter": { 37 | "Fn::Not": [ 38 | { 39 | "Fn::Equals": [ 40 | { 41 | "Ref": "env" 42 | }, 43 | "NONE" 44 | ] 45 | } 46 | ] 47 | }, 48 | "AlwaysFalse": { 49 | "Fn::Equals": ["true", "false"] 50 | } 51 | }, 52 | "Outputs": { 53 | "EmptyOutput": { 54 | "Description": "An empty output. You may delete this if you have at least one resource above.", 55 | "Value": "" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /amplify/backend/api/mobileloc/transform.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 5, 3 | "ElasticsearchWarning": true 4 | } -------------------------------------------------------------------------------- /amplify/backend/backend-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "mobileloc": { 4 | "service": "AppSync", 5 | "providerPlugin": "awscloudformation", 6 | "output": { 7 | "authConfig": { 8 | "defaultAuthentication": { 9 | "authenticationType": "API_KEY", 10 | "apiKeyConfig": { 11 | "apiKeyExpirationDays": 365, 12 | "description": "public" 13 | } 14 | }, 15 | "additionalAuthenticationProviders": [] 16 | } 17 | } 18 | } 19 | }, 20 | "function": { 21 | "updateLocation": { 22 | "build": true, 23 | "providerPlugin": "awscloudformation", 24 | "service": "Lambda" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/amplify.state: -------------------------------------------------------------------------------- 1 | { 2 | "pluginId": "amplify-nodejs-function-runtime-provider", 3 | "functionRuntime": "nodejs", 4 | "useLegacyBuild": true, 5 | "defaultEditorFile": "src\\index.js" 6 | } -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/function-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "lambdaLayers": [] 3 | } -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/src/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "key1": "value1", 3 | "key2": "value2", 4 | "key3": "value3" 5 | } 6 | -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/src/index.js: -------------------------------------------------------------------------------- 1 | var AWS = require('aws-sdk'); 2 | 3 | exports.handler = async (event) => { 4 | 5 | var location = new AWS.Location(); 6 | console.log("update loc"); 7 | 8 | var params = { 9 | TrackerName: event.arguments.trackerName, /* required */ 10 | Updates: [ /* required */ 11 | { 12 | DeviceId: `${event.arguments.deviceId}`, /* required */ 13 | Position: [ /* required */ 14 | parseFloat(event.arguments.Longitude), 15 | parseFloat(event.arguments.Latitude) 16 | /* more items */ 17 | ], 18 | SampleTime: new Date || 123456789 /* required */ 19 | }, 20 | /* more items */ 21 | ] 22 | }; 23 | 24 | try{ 25 | const data = await location.batchUpdateDevicePosition(params).promise(); 26 | console.log(data); // successful response 27 | console.log("update loc: success"); 28 | } catch (err){ 29 | console.log("update loc: error"); 30 | console.log(err); 31 | } 32 | 33 | return params 34 | }; 35 | -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "updateLocation", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "aws-sdk": { 8 | "version": "2.931.0", 9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.931.0.tgz", 10 | "integrity": "sha512-Db97/aJq8zYl8mHzY6dNO6m9S89TqN4HEUUc2aCYQCTyMb/eNrjf+uZTnutnQbJkClqHzxFcWc3aqe5VlTac/A==", 11 | "requires": { 12 | "buffer": "4.9.2", 13 | "events": "1.1.1", 14 | "ieee754": "1.1.13", 15 | "jmespath": "0.15.0", 16 | "querystring": "0.2.0", 17 | "sax": "1.2.1", 18 | "url": "0.10.3", 19 | "uuid": "3.3.2", 20 | "xml2js": "0.4.19" 21 | } 22 | }, 23 | "base64-js": { 24 | "version": "1.5.1", 25 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 26 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 27 | }, 28 | "buffer": { 29 | "version": "4.9.2", 30 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 31 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 32 | "requires": { 33 | "base64-js": "^1.0.2", 34 | "ieee754": "^1.1.4", 35 | "isarray": "^1.0.0" 36 | } 37 | }, 38 | "events": { 39 | "version": "1.1.1", 40 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 41 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 42 | }, 43 | "ieee754": { 44 | "version": "1.1.13", 45 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 46 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 47 | }, 48 | "isarray": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 51 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 52 | }, 53 | "jmespath": { 54 | "version": "0.15.0", 55 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 56 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 57 | }, 58 | "punycode": { 59 | "version": "1.3.2", 60 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 61 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 62 | }, 63 | "querystring": { 64 | "version": "0.2.0", 65 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 66 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 67 | }, 68 | "sax": { 69 | "version": "1.2.1", 70 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 71 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 72 | }, 73 | "url": { 74 | "version": "0.10.3", 75 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 76 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 77 | "requires": { 78 | "punycode": "1.3.2", 79 | "querystring": "0.2.0" 80 | } 81 | }, 82 | "uuid": { 83 | "version": "3.3.2", 84 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 85 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 86 | }, 87 | "xml2js": { 88 | "version": "0.4.19", 89 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 90 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 91 | "requires": { 92 | "sax": ">=0.6.0", 93 | "xmlbuilder": "~9.0.1" 94 | } 95 | }, 96 | "xmlbuilder": { 97 | "version": "9.0.7", 98 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 99 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "updateLocation", 3 | "version": "2.0.0", 4 | "description": "Lambda function generated by Amplify", 5 | "main": "index.js", 6 | "license": "Apache-2.0", 7 | "dependencies": { 8 | "aws-sdk": "^2.931.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /amplify/backend/function/updateLocation/updateLocation-cloudformation-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Lambda Function resource stack creation using Amplify CLI", 4 | "Parameters": { 5 | "CloudWatchRule": { 6 | "Type": "String", 7 | "Default": "NONE", 8 | "Description": " Schedule Expression" 9 | }, 10 | "deploymentBucketName": { 11 | "Type": "String" 12 | }, 13 | "env": { 14 | "Type": "String" 15 | }, 16 | "s3Key": { 17 | "Type": "String" 18 | } 19 | }, 20 | "Conditions": { 21 | "ShouldNotCreateEnvResources": { 22 | "Fn::Equals": [ 23 | { 24 | "Ref": "env" 25 | }, 26 | "NONE" 27 | ] 28 | } 29 | }, 30 | "Resources": { 31 | "LambdaFunction": { 32 | "Type": "AWS::Lambda::Function", 33 | "Metadata": { 34 | "aws:asset:path": "./src", 35 | "aws:asset:property": "Code" 36 | }, 37 | "Properties": { 38 | "Code": { 39 | "S3Bucket": { 40 | "Ref": "deploymentBucketName" 41 | }, 42 | "S3Key": { 43 | "Ref": "s3Key" 44 | } 45 | }, 46 | "Handler": "index.handler", 47 | "FunctionName": { 48 | "Fn::If": [ 49 | "ShouldNotCreateEnvResources", 50 | "updateLocation", 51 | { 52 | "Fn::Join": [ 53 | "", 54 | [ 55 | "updateLocation", 56 | "-", 57 | { 58 | "Ref": "env" 59 | } 60 | ] 61 | ] 62 | } 63 | ] 64 | }, 65 | "Environment": { 66 | "Variables": { 67 | "ENV": { 68 | "Ref": "env" 69 | }, 70 | "REGION": { 71 | "Ref": "AWS::Region" 72 | } 73 | } 74 | }, 75 | "Role": { 76 | "Fn::GetAtt": [ 77 | "LambdaExecutionRole", 78 | "Arn" 79 | ] 80 | }, 81 | "Runtime": "nodejs14.x", 82 | "Layers": [], 83 | "Timeout": "25" 84 | } 85 | }, 86 | "LambdaExecutionRole": { 87 | "Type": "AWS::IAM::Role", 88 | "Properties": { 89 | "RoleName": { 90 | "Fn::If": [ 91 | "ShouldNotCreateEnvResources", 92 | "mobilelocLambdaRole18fee2e6", 93 | { 94 | "Fn::Join": [ 95 | "", 96 | [ 97 | "mobilelocLambdaRole18fee2e6", 98 | "-", 99 | { 100 | "Ref": "env" 101 | } 102 | ] 103 | ] 104 | } 105 | ] 106 | }, 107 | "AssumeRolePolicyDocument": { 108 | "Version": "2012-10-17", 109 | "Statement": [ 110 | { 111 | "Effect": "Allow", 112 | "Principal": { 113 | "Service": [ 114 | "lambda.amazonaws.com" 115 | ] 116 | }, 117 | "Action": [ 118 | "sts:AssumeRole" 119 | ] 120 | } 121 | ] 122 | } 123 | } 124 | }, 125 | "lambdaexecutionpolicy": { 126 | "DependsOn": [ 127 | "LambdaExecutionRole" 128 | ], 129 | "Type": "AWS::IAM::Policy", 130 | "Properties": { 131 | "PolicyName": "lambda-execution-policy", 132 | "Roles": [ 133 | { 134 | "Ref": "LambdaExecutionRole" 135 | } 136 | ], 137 | "PolicyDocument": { 138 | "Version": "2012-10-17", 139 | "Statement": [ 140 | { 141 | "Effect": "Allow", 142 | "Action": [ 143 | "logs:CreateLogGroup", 144 | "logs:CreateLogStream", 145 | "logs:PutLogEvents", 146 | "geo:BatchUpdateDevicePosition" 147 | ], 148 | "Resource": [ 149 | { 150 | "Fn::Sub": [ 151 | "arn:aws:geo:*:${account}:tracker/*", 152 | { 153 | "account": { 154 | "Ref": "AWS::AccountId" 155 | } 156 | } 157 | ] 158 | }, 159 | { 160 | "Fn::Sub": [ 161 | "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", 162 | { 163 | "region": { 164 | "Ref": "AWS::Region" 165 | }, 166 | "account": { 167 | "Ref": "AWS::AccountId" 168 | }, 169 | "lambda": { 170 | "Ref": "LambdaFunction" 171 | } 172 | } 173 | ] 174 | } 175 | ] 176 | } 177 | ] 178 | } 179 | } 180 | } 181 | }, 182 | "Outputs": { 183 | "Name": { 184 | "Value": { 185 | "Ref": "LambdaFunction" 186 | } 187 | }, 188 | "Arn": { 189 | "Value": { 190 | "Fn::GetAtt": [ 191 | "LambdaFunction", 192 | "Arn" 193 | ] 194 | } 195 | }, 196 | "Region": { 197 | "Value": { 198 | "Ref": "AWS::Region" 199 | } 200 | }, 201 | "LambdaExecutionRole": { 202 | "Value": { 203 | "Ref": "LambdaExecutionRole" 204 | } 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /amplify/backend/tags.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Key": "user:Stack", 4 | "Value": "{project-env}" 5 | }, 6 | { 7 | "Key": "user:Application", 8 | "Value": "{project-name}" 9 | } 10 | ] -------------------------------------------------------------------------------- /amplify/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "features": { 3 | "graphqltransformer": { 4 | "addmissingownerfields": true, 5 | "validatetypenamereservedwords": true, 6 | "useexperimentalpipelinedtransformer": false, 7 | "enableiterativegsiupdates": true, 8 | "secondarykeyasgsi": true, 9 | "skipoverridemutationinputtypes": true 10 | }, 11 | "frontend-ios": { 12 | "enablexcodeintegration": true 13 | }, 14 | "auth": { 15 | "enablecaseinsensitivity": true, 16 | "useinclusiveterminology": true, 17 | "breakcirculardependency": true 18 | }, 19 | "codegen": { 20 | "useappsyncmodelgenplugin": true, 21 | "usedocsgeneratorplugin": true, 22 | "usetypesgeneratorplugin": true, 23 | "cleangeneratedmodelsdirectory": true, 24 | "retaincasestyle": true, 25 | "addtimestampfields": true, 26 | "handlelistnullabilitytransparently": true 27 | }, 28 | "appsync": { 29 | "generategraphqlpermissions": true 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /amplify/team-provider-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "awscloudformation": { 4 | "AuthRoleName": "amplify-mobileloc-dev-143129-authRole", 5 | "UnauthRoleArn": "arn:aws:iam::117470732045:role/amplify-mobileloc-dev-143129-unauthRole", 6 | "AuthRoleArn": "arn:aws:iam::117470732045:role/amplify-mobileloc-dev-143129-authRole", 7 | "Region": "us-east-1", 8 | "DeploymentBucketName": "amplify-mobileloc-dev-143129-deployment", 9 | "UnauthRoleName": "amplify-mobileloc-dev-143129-unauthRole", 10 | "StackName": "amplify-mobileloc-dev-143129", 11 | "StackId": "arn:aws:cloudformation:us-east-1:117470732045:stack/amplify-mobileloc-dev-143129/4013f7b0-bddb-11eb-876d-0eb23bbe71c5", 12 | "AmplifyAppId": "d21iwh9fokjjql" 13 | }, 14 | "categories": { 15 | "function": { 16 | "updateLocation": { 17 | "deploymentBucketName": "amplify-mobileloc-dev-143129-deployment", 18 | "s3Key": "amplify-builds/updateLocation-524d7865382f66696958-build.zip" 19 | } 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile", 4 | "slug": "mobile", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-location-service-mobile-tracker-react/93c24c5c47e4f37cc131fb71db86b13242ba4984/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-location-service-mobile-tracker-react/93c24c5c47e4f37cc131fb71db86b13242ba4984/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-location-service-mobile-tracker-react/93c24c5c47e4f37cc131fb71db86b13242ba4984/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-location-service-mobile-tracker-react/93c24c5c47e4f37cc131fb71db86b13242ba4984/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /components/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, Text, View } from 'react-native'; 3 | 4 | export default function Header({title}) { 5 | return ( 6 | 7 | 8 | {title} 9 | 10 | 11 | ) 12 | } 13 | 14 | const styles = StyleSheet.create({ 15 | header:{ 16 | width: '100%', 17 | height: '100%', 18 | flexDirection: 'row', 19 | alignItems: 'center', 20 | justifyContent: 'center' 21 | }, 22 | headerText: { 23 | fontWeight: 'bold', 24 | fontSize: 20, 25 | color: '#333', 26 | letterSpacing: 1 27 | }, 28 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "node_modules/expo/AppEntry.js", 3 | "scripts": { 4 | "start": "expo start", 5 | "android": "expo start --android", 6 | "ios": "expo start --ios", 7 | "web": "expo start --web", 8 | "eject": "expo eject" 9 | }, 10 | "dependencies": { 11 | "@react-native-async-storage/async-storage": "^1.13.0", 12 | "@react-native-community/netinfo": "6.0.0", 13 | "aws-amplify": "^4.3.37", 14 | "aws-amplify-react-native": "^5.0.0", 15 | "expo": "~46.0.16", 16 | "expo-file-system": "~11.0.2", 17 | "expo-location": "~12.0.4", 18 | "expo-status-bar": "~1.0.4", 19 | "react": "16.13.1", 20 | "react-dom": "16.13.1", 21 | "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz", 22 | "react-native-gesture-handler": "~1.10.2", 23 | "react-native-reanimated": "~2.10.0", 24 | "react-native-screens": "~3.0.0", 25 | "react-native-web": "~0.18.9", 26 | "react-navigation": "^4.4.4", 27 | "react-navigation-stack": "^2.10.4", 28 | "react-navigation-tabs": "^2.11.1" 29 | }, 30 | "devDependencies": { 31 | "@babel/core": "~7.9.0" 32 | }, 33 | "private": true 34 | } 35 | -------------------------------------------------------------------------------- /routes/mainStack.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackNavigator } from 'react-navigation-stack'; 3 | import Header from '../components/header'; 4 | import Main from '../screens/main'; 5 | 6 | const screens = { 7 | Home: { 8 | screen: Main, 9 | navigationOptions: () => { 10 | return { 11 | headerTitle: () =>
12 | } 13 | } 14 | } 15 | } 16 | 17 | const MainStack = createStackNavigator(screens, { 18 | defaultNavigationOptions: { 19 | headerTintColor: '#444', 20 | headerStyle: { backgroundColor: '#eee' } 21 | } 22 | }); 23 | 24 | export default MainStack; -------------------------------------------------------------------------------- /routes/setupStack.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createStackNavigator } from 'react-navigation-stack'; 3 | import Header from '../components/header'; 4 | import Setup from '../screens/setup'; 5 | 6 | const screens = { 7 | Setup: { 8 | screen: Setup, 9 | navigationOptions: () => { 10 | return { 11 | headerTitle: () =>
12 | } 13 | } 14 | } 15 | } 16 | 17 | const SetupStack = createStackNavigator(screens, { 18 | defaultNavigationOptions: { 19 | headerTintColor: '#444', 20 | headerStyle: { backgroundColor: '#eee' } 21 | } 22 | }); 23 | 24 | export default SetupStack; -------------------------------------------------------------------------------- /routes/tabNavigator.js: -------------------------------------------------------------------------------- 1 | import { createBottomTabNavigator } from 'react-navigation-tabs'; 2 | import { createAppContainer } from 'react-navigation'; 3 | import MainStack from './mainStack'; 4 | import SetupStack from './setupStack'; 5 | 6 | const TabNavigator = createBottomTabNavigator ({ 7 | Home: { 8 | screen: MainStack 9 | }, 10 | Setup: { 11 | screen: SetupStack 12 | } 13 | }) 14 | 15 | export default createAppContainer(TabNavigator); -------------------------------------------------------------------------------- /screens/main.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Text, View, Button, Alert } from 'react-native'; 3 | import * as Location from 'expo-location'; 4 | import Device from '../src/device'; 5 | 6 | const deviceSvc = new Device(); 7 | 8 | export default function Main() { 9 | const [location, setLocation] = useState(null); 10 | const [errorMsg, setErrorMsg] = useState(null); 11 | const [tracker, setTracker] = useState(""); 12 | const [deviceName, setDeviceName] = useState(""); 13 | const [deviceId, setDeviceId] = useState(""); 14 | 15 | useEffect(() => { 16 | 17 | (async () => { 18 | 19 | deviceSvc.init() 20 | .then((resp)=> { 21 | if(resp !== null){ 22 | setTracker(resp.trackerName); 23 | setDeviceName(resp.name); 24 | setDeviceId(resp.id); 25 | } 26 | }); 27 | 28 | let { status } = await Location.requestForegroundPermissionsAsync(); 29 | if (status !== 'granted') { 30 | setErrorMsg('Permission to access location was denied'); 31 | return; 32 | } else { 33 | 34 | let isLocationServicesEnabled = await Location.hasServicesEnabledAsync(); 35 | let locationProviderStatus = await Location.getProviderStatusAsync(); 36 | console.log(`loc status: ${JSON.stringify(locationProviderStatus)}`); 37 | 38 | if(isLocationServicesEnabled){ 39 | //Get loc 40 | let loc = await Location.getCurrentPositionAsync({accuracy: Location.Accuracy.Highest}); 41 | if(loc.coords !== null){ 42 | console.log(`loc: ${JSON.stringify(loc)}`); 43 | setLocation(loc); 44 | deviceSvc.setLocation(loc.coords.latitude, loc.coords.longitude); 45 | } 46 | 47 | //Watch loc 48 | await Location.watchPositionAsync({ 49 | enableHighAccuracy: true, 50 | distanceInterval: 1, 51 | timeInterval: 30000}, newLoc => { 52 | console.log(`new loc: ${JSON.stringify(newLoc)}`); 53 | setLocation(newLoc); 54 | deviceSvc.setLocation(newLoc.coords.latitude, newLoc.coords.longitude); 55 | }); 56 | } 57 | } 58 | 59 | })(); 60 | 61 | }, []); 62 | 63 | let text = 'Waiting..'; 64 | if (errorMsg) { 65 | text = errorMsg; 66 | } else if (location) { 67 | text = "";//JSON.stringify(location); 68 | } 69 | 70 | return ( 71 | 72 | Device Name: {deviceName} 73 | Device ID: {deviceId} 74 | Tracker: {tracker} 75 | Lat: {location?.coords?.latitude} 76 | Lon: {location?.coords?.longitude} 77 | {text} 78 | 79 | ); 80 | } -------------------------------------------------------------------------------- /screens/setup.js: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react'; 2 | import { Text, View, TextInput, Button, Alert, TouchableWithoutFeedback, Keyboard } from 'react-native'; 3 | import Device from '../src/device'; 4 | 5 | const deviceSvc = new Device(); 6 | 7 | export default function Setup() { 8 | const [tracker, setTracker] = useState(""); 9 | const [device, setDevice] = useState(""); 10 | const [label, setLabel] = useState("Register"); 11 | 12 | useEffect(() => { 13 | deviceSvc.init() 14 | .then((resp)=> { 15 | if(resp !== null){ 16 | setTracker(resp.trackerName); 17 | setDevice(resp.name); 18 | setLabel("Update"); 19 | } 20 | }) 21 | 22 | }, []); 23 | 24 | const updateHandler = () => { 25 | const valid = /^[a-zA-Z0-9_\-\.]+$/; 26 | if(valid.test(tracker) && valid.test(device)){ 27 | Keyboard.dismiss(); 28 | deviceSvc.register(device, tracker); 29 | 30 | } else { 31 | Alert.alert('Validation', 'Use only letters, numbers, hyphens, or underscores with no spaces.', [ 32 | {text: 'OK', onPress: () => console.log('validation alert closed')} 33 | ]); 34 | } 35 | } 36 | 37 | return ( 38 | { 39 | Keyboard.dismiss(); 40 | }}> 41 | 42 | Device Name: 43 | setDevice(val)} 46 | value={device} /> 47 | Tracker Name: 48 | setTracker(val)} 51 | value={tracker} /> 52 | 53 |