diff --git a/README.md b/README.md
index d191a0f0c..9dfa8d8dd 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,21 @@
-# ArcGIS Maps SDK Kotlin Samples
+# ArcGIS Maps SDK Kotlin Samples
+
+[![Link: ArcGIS Developers home](https://img.shields.io/badge/ArcGIS%20Developers%20Home-633b9b?style=flat-square)](https://developers.arcgis.com)
+[![Link: Documentation](https://img.shields.io/badge/Documentation-633b9b?style=flat-square)](https://developers.arcgis.com/kotlin/)
+[![Link: Tutorials](https://img.shields.io/badge/Tutorials-633b9b?style=flat-square)](https://developers.arcgis.com/documentation/mapping-apis-and-services/tutorials/)
+[![Badge: Samples](https://img.shields.io/badge/Samples-633b9b?style=flat-square)](https://developers.arcgis.com/kotlin/sample-code/)
+[![Link: Toolkit](https://img.shields.io/badge/Toolkit-633b9b?style=flat-square)](https://developers.arcgis.com/kotlin/toolkit/)
+[![Link: Esri Community](https://img.shields.io/badge/🙋-Get%20help%20in%20Esri%20Community-633b9b?style=flat-square)](https://community.esri.com/t5/kotlin-maps-sdk-questions/bd-p/kotlin-maps-sdk-questions)
+
+
## Overview
-ArcGIS Maps SDK for Kotlin v200.5.0 samples. The `main` branch of this repository contains sample app modules for the latest available version of the [ArcGIS Maps SDK Android Kotlin](https://developers.arcgis.com/kotlin/). Samples released under older versions can be found through the [git tags](https://github.com/Esri/arcgis-maps-sdk-kotlin-samples/tags). Please read our [wiki](https://github.com/Esri/arcgis-maps-sdk-kotlin-samples/wiki) for help with working with this repository.
+ArcGIS Maps SDK for Kotlin v200.6.0 samples. The `main` branch of this repository contains sample app modules for the latest available version of the [ArcGIS Maps Kotlin SDK](https://developers.arcgis.com/kotlin/). Samples released under older versions can be found through the [git tags](https://github.com/Esri/arcgis-maps-sdk-kotlin-samples/tags). Please read our [wiki](https://github.com/Esri/arcgis-maps-sdk-kotlin-samples/wiki) for help with working with this repository.
## Prerequisites
-* The samples are building with `compileSdkVersion 33`
+* The samples are building with `compileSdkVersion 35`
* [Android Studio](http://developer.android.com/sdk/index.html)
* [An ArcGIS Developers API key](https://developers.arcgis.com/kotlin/get-started/#3-get-an-api-key)
@@ -31,11 +40,11 @@ A long-lived access token that gives your application access to ArcGIS location
* **Location services** > **Geocoding**
* **Location services** > **Routing**
-The Android samples in this repository have been structured to use an API key, set once, which will run in all samples.
-Set your API key in the `gradle.properties` file located in the `/.gradle` folder within your home directory.
-The API_KEY property should contain quotes around the key itself:
+The Kotlin samples in this repository have been structured to use an access token, set once, which will run in all samples.
+Set your access token in the `local.properties` in the same folder as the `secrets.defaults.properties`.
+The ACCESS_TOKEN property should contain quotes around the key itself:
```gradle
-API_KEY = "YOUR_API_KEY" // path: /Users/
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,describe:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,describe:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",describe:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,describe:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,describe:"Open all links in new windows",type:"boolean"},backslashEscapesHTMLTags:{defaultValue:!1,describe:"Support for HTML Tag escaping. ex: [^\r]+?<\/pre>)/gm,function(e,r){return r.replace(/^ /gm,"¨0").replace(/¨0/g,"")}),x.subParser("hashBlock")("
","gim"),e=s.converter._dispatch("hashPreCodeTags.after",e,n,s)}),x.subParser("headers",function(e,n,s){"use strict";e=s.converter._dispatch("headers.before",e,n,s);var o=isNaN(parseInt(n.headerLevelStart))?1:parseInt(n.headerLevelStart),r=n.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,t=n.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm,r=(e=(e=e.replace(r,function(e,r){var t=x.subParser("spanGamut")(r,n,s),r=n.noHeaderId?"":' id="'+i(r)+'"',r="\n"+e+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),x.subParser("codeBlocks",function(e,n,s){"use strict";e=s.converter._dispatch("codeBlocks.before",e,n,s);return e=(e=(e+="¨0").replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g,function(e,r,t){var a="\n",r=x.subParser("outdent")(r,n,s);return r=x.subParser("encodeCode")(r,n,s),r="
",x.subParser("hashBlock")(r,n,s)+t})).replace(/¨0/,""),e=s.converter._dispatch("codeBlocks.after",e,n,s)}),x.subParser("codeSpans",function(e,n,s){"use strict";return e=(e=void 0===(e=s.converter._dispatch("codeSpans.before",e,n,s))?"":e).replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,r,t,a){return a=(a=a.replace(/^([ \t]*)/g,"")).replace(/[ \t]*$/g,""),a=r+""+(r=(r=(r=x.subParser("detab")(r,n,s)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+(a=n.omitExtraWLInCodeBlocks?"":a)+"
"+(a=x.subParser("encodeCode")(a,n,s))+"
",a=x.subParser("hashHTMLSpans")(a,n,s)}),e=s.converter._dispatch("codeSpans.after",e,n,s)}),x.subParser("completeHTMLDocument",function(e,r,t){"use strict";if(!r.completeHTMLDocument)return e;e=t.converter._dispatch("completeHTMLDocument.before",e,r,t);var a,n="html",s="\n",o="",i='\n',l="",c="";for(a in void 0!==t.metadata.parsed.doctype&&(s="\n","html"!==(n=t.metadata.parsed.doctype.toString().toLowerCase())&&"html5"!==n||(i='')),t.metadata.parsed)if(t.metadata.parsed.hasOwnProperty(a))switch(a.toLowerCase()){case"doctype":break;case"title":o="
",a=x.subParser("hashBlock")(a,s,o),"\n\n¨G"+(o.ghCodeBlocks.push({text:e,codeblock:a})-1)+"G\n\n"})).replace(/¨0/,""),o.converter._dispatch("githubCodeBlocks.after",e,s,o)):e}),x.subParser("hashBlock",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("hashBlock.before",e,r,t)).replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),x.subParser("hashCodeTags",function(e,n,s){"use strict";e=s.converter._dispatch("hashCodeTags.before",e,n,s);return e=x.helper.replaceRecursiveRegExp(e,function(e,r,t,a){t=t+x.subParser("encodeCode")(r,n,s)+a;return"¨C"+(s.gHtmlSpans.push(t)-1)+"C"},""+(a=(a=(a=x.subParser("detab")(a,s,o)).replace(/^\n+/g,"")).replace(/\n+$/g,""))+n+"
]*>","
","gim"),e=s.converter._dispatch("hashCodeTags.after",e,n,s)}),x.subParser("hashElement",function(e,r,t){"use strict";return function(e,r){return r=(r=(r=r.replace(/\n\n/g,"\n")).replace(/^\n/,"")).replace(/\n+$/g,""),r="\n\n¨K"+(t.gHtmlBlocks.push(r)-1)+"K\n\n"}}),x.subParser("hashHTMLBlocks",function(e,r,n){"use strict";e=n.converter._dispatch("hashHTMLBlocks.before",e,r,n);function t(e,r,t,a){return-1!==t.search(/\bmarkdown\b/)&&(e=t+n.converter.makeHtml(r)+a),"\n\n¨K"+(n.gHtmlBlocks.push(e)-1)+"K\n\n"}var a=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"];r.backslashEscapesHTMLTags&&(e=e.replace(/\\<(\/?[^>]+?)>/g,function(e,r){return"<"+r+">"}));for(var s=0;s]*>","^ {0,3}
\\s*
",r,t);return e=(e=(e=e.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,a)).replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,a),e=t.converter._dispatch("horizontalRule.after",e,r,t)}),x.subParser("images",function(e,r,d){"use strict";function l(e,r,t,a,n,s,o,i){var l=d.gUrls,c=d.gTitles,u=d.gDimensions;if(t=t.toLowerCase(),i=i||"",-1
]*>/.test(c)&&(u=!0)}n[o]=c}return e=(e=(e=n.join("\n")).replace(/^\n+/g,"")).replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),x.subParser("runExtension",function(e,r,t,a){"use strict";return e.filter?r=e.filter(r,a.converter,t):e.regex&&((a=e.regex)instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)),r}),x.subParser("spanGamut",function(e,r,t){"use strict";return e=t.converter._dispatch("spanGamut.before",e,r,t),e=x.subParser("codeSpans")(e,r,t),e=x.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=x.subParser("encodeBackslashEscapes")(e,r,t),e=x.subParser("images")(e,r,t),e=x.subParser("anchors")(e,r,t),e=x.subParser("autoLinks")(e,r,t),e=x.subParser("simplifiedAutoLinks")(e,r,t),e=x.subParser("emoji")(e,r,t),e=x.subParser("underline")(e,r,t),e=x.subParser("italicsAndBold")(e,r,t),e=x.subParser("strikethrough")(e,r,t),e=x.subParser("ellipsis")(e,r,t),e=x.subParser("hashHTMLSpans")(e,r,t),e=x.subParser("encodeAmpsAndAngles")(e,r,t),r.simpleLineBreaks?/\n\n¨K/.test(e)||(e=e.replace(/\n+/g,"
\n")):e=e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),x.subParser("strikethrough",function(e,t,a){"use strict";return t.strikethrough&&(e=(e=a.converter._dispatch("strikethrough.before",e,t,a)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return r=r,""+(r=t.simplifiedAutoLink?x.subParser("simplifiedAutoLinks")(r,t,a):r)+""}),e=a.converter._dispatch("strikethrough.after",e,t,a)),e}),x.subParser("stripLinkDefinitions",function(i,l,c){"use strict";function e(e,r,t,a,n,s,o){return r=r.toLowerCase(),i.toLowerCase().split(r).length-1<2?e:(t.match(/^data:.+?\/.+?;base64,/)?c.gUrls[r]=t.replace(/\s/g,""):c.gUrls[r]=x.subParser("encodeAmpsAndAngles")(t,l,c),s?s+o:(o&&(c.gTitles[r]=o.replace(/"|'/g,""")),l.parseImgDimensions&&a&&n&&(c.gDimensions[r]={width:a,height:n}),""))}return i=(i=(i=(i+="¨0").replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm,e)).replace(/^ {0,3}\[([^\]]+)]:[ \t]*\n?[ \t]*([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,e)).replace(/¨0/,"")}),x.subParser("tables",function(e,y,P){"use strict";if(!y.tables)return e;function r(e){for(var r=e.split("\n"),t=0;t"+(n=x.subParser("spanGamut")(n,y,P))+"\n"));for(t=0;t"+x.subParser("spanGamut")(i,y,P)+"\n"));h.push(_)}for(var m=d,f=h,b="\n\n\n",w=m.length,k=0;k\n \n\n",k=0;k\n";for(var v=0;v\n"}return b+=" \n
\n"}return e=(e=(e=(e=P.converter._dispatch("tables.before",e,y,P)).replace(/\\(\|)/g,x.helper.escapeCharactersCallback)).replace(/^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,r)).replace(/^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm,r),e=P.converter._dispatch("tables.after",e,y,P)}),x.subParser("underline",function(e,r,t){"use strict";return r.underline?(e=t.converter._dispatch("underline.before",e,r,t),e=(e=r.literalMidWordUnderscores?(e=e.replace(/\b___(\S[\s\S]*?)___\b/g,function(e,r){return""+r+""})).replace(/\b__(\S[\s\S]*?)__\b/g,function(e,r){return""+r+""}):(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?""+r+"":e})).replace(/(_)/g,x.helper.escapeCharactersCallback),t.converter._dispatch("underline.after",e,r,t)):e}),x.subParser("unescapeSpecialChars",function(e,r,t){"use strict";return e=(e=t.converter._dispatch("unescapeSpecialChars.before",e,r,t)).replace(/¨E(\d+)E/g,function(e,r){r=parseInt(r);return String.fromCharCode(r)}),e=t.converter._dispatch("unescapeSpecialChars.after",e,r,t)}),x.subParser("makeMarkdown.blockquote",function(e,r){"use strict";var t="";if(e.hasChildNodes())for(var a=e.childNodes,n=a.length,s=0;s ")}),x.subParser("makeMarkdown.codeBlock",function(e,r){"use strict";var t=e.getAttribute("language"),e=e.getAttribute("precodenum");return"```"+t+"\n"+r.preList[e]+"\n```"}),x.subParser("makeMarkdown.codeSpan",function(e){"use strict";return"`"+e.innerHTML+"`"}),x.subParser("makeMarkdown.emphasis",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="*";for(var a=e.childNodes,n=a.length,s=0;s",e.hasAttribute("width")&&e.hasAttribute("height")&&(r+=" ="+e.getAttribute("width")+"x"+e.getAttribute("height")),e.hasAttribute("title")&&(r+=' "'+e.getAttribute("title")+'"'),r+=")"),r}),x.subParser("makeMarkdown.links",function(e,r){"use strict";var t="";if(e.hasChildNodes()&&e.hasAttribute("href")){for(var a=e.childNodes,n=a.length,t="[",s=0;s"),e.hasAttribute("title")&&(t+=' "'+e.getAttribute("title")+'"'),t+=")"}return t}),x.subParser("makeMarkdown.list",function(e,r,t){"use strict";var a="";if(!e.hasChildNodes())return"";for(var n=e.childNodes,s=n.length,o=e.getAttribute("start")||1,i=0;i"+r.preList[e]+""}),x.subParser("makeMarkdown.strikethrough",function(e,r){"use strict";var t="";if(e.hasChildNodes()){t+="~~";for(var a=e.childNodes,n=a.length,s=0;str>th"),s=e.querySelectorAll("tbody>tr"),o=0;o/g,"\\$1>")).replace(/^#/gm,"\\#")).replace(/^(\s*)([-=]{3,})(\s*)$/,"$1\\$2$3")).replace(/^( {0,3}\d+)\./gm,"$1\\.")).replace(/^( {0,3})([+-])/gm,"$1\\$2")).replace(/]([\s]*)\(/g,"\\]$1\\(")).replace(/^ {0,3}\[([\S \t]*?)]:/gm,"\\[$1]:")});"function"==typeof define&&define.amd?define(function(){"use strict";return x}):"undefined"!=typeof module&&module.exports?module.exports=x:this.showdown=x}.call(this);
+//# sourceMappingURL=showdown.min.js.map
\ No newline at end of file
diff --git a/app/src/main/assets/www/highlight/styles/github-dark.css b/app/src/main/assets/www/highlight/styles/github-dark.css
new file mode 100644
index 000000000..1e37b83bb
--- /dev/null
+++ b/app/src/main/assets/www/highlight/styles/github-dark.css
@@ -0,0 +1,122 @@
+pre code.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 1em
+}
+code.hljs {
+ padding: 3px 5px
+}
+* {
+ margin: 0;
+ padding: 0;
+}
+/*!
+ Theme: GitHub Dark
+ Description: Dark theme as seen on github.com
+ Author: github.com
+ Maintainer: @Hirse
+ Updated: 2021-05-15
+
+ Outdated base version: https://github.com/primer/github-syntax-dark
+ Current colors taken from GitHub's CSS
+*/
+.hljs {
+ color: #c9d1d9;
+ background: #0d1117
+}
+.hljs-doctag,
+.hljs-keyword,
+.hljs-meta .hljs-keyword,
+.hljs-template-tag,
+.hljs-template-variable,
+.hljs-type,
+.hljs-variable.language_ {
+ /* prettylights-syntax-keyword */
+ color: #ff7b72
+}
+.hljs-title,
+.hljs-title.class_,
+.hljs-title.class_.inherited__,
+.hljs-title.function_ {
+ /* prettylights-syntax-entity */
+ color: #d2a8ff
+}
+.hljs-attr,
+.hljs-attribute,
+.hljs-literal,
+.hljs-meta,
+.hljs-number,
+.hljs-operator,
+.hljs-variable,
+.hljs-selector-attr,
+.hljs-selector-class,
+.hljs-selector-id {
+ /* prettylights-syntax-constant */
+ color: #79c0ff
+}
+.hljs-regexp,
+.hljs-string,
+.hljs-meta .hljs-string {
+ /* prettylights-syntax-string */
+ color: #a5d6ff
+}
+.hljs-built_in,
+.hljs-symbol {
+ /* prettylights-syntax-variable */
+ color: #ffa657
+}
+.hljs-comment,
+.hljs-code,
+.hljs-formula {
+ /* prettylights-syntax-comment */
+ color: #8b949e
+}
+.hljs-name,
+.hljs-quote,
+.hljs-selector-tag,
+.hljs-selector-pseudo {
+ /* prettylights-syntax-entity-tag */
+ color: #7ee787
+}
+.hljs-subst {
+ /* prettylights-syntax-storage-modifier-import */
+ color: #c9d1d9
+}
+.hljs-section {
+ /* prettylights-syntax-markup-heading */
+ color: #1f6feb;
+ font-weight: bold
+}
+.hljs-bullet {
+ /* prettylights-syntax-markup-list */
+ color: #f2cc60
+}
+.hljs-emphasis {
+ /* prettylights-syntax-markup-italic */
+ color: #c9d1d9;
+ font-style: italic
+}
+.hljs-strong {
+ /* prettylights-syntax-markup-bold */
+ color: #c9d1d9;
+ font-weight: bold
+}
+.hljs-addition {
+ /* prettylights-syntax-markup-inserted */
+ color: #aff5b4;
+ background-color: #033a16
+}
+.hljs-deletion {
+ /* prettylights-syntax-markup-deleted */
+ color: #ffdcd7;
+ background-color: #67060c
+}
+.hljs-char.escape_,
+.hljs-link,
+.hljs-params,
+.hljs-property,
+.hljs-punctuation,
+.hljs-tag {
+ /* purposely ignored */
+
+}
\ No newline at end of file
diff --git a/app/src/main/assets/www/highlight/styles/github.css b/app/src/main/assets/www/highlight/styles/github.css
new file mode 100644
index 000000000..49a2a0540
--- /dev/null
+++ b/app/src/main/assets/www/highlight/styles/github.css
@@ -0,0 +1,122 @@
+pre code.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 1em
+}
+code.hljs {
+ padding: 3px 5px
+}
+* {
+ margin: 0;
+ padding: 0;
+}
+/*!
+ Theme: GitHub
+ Description: Light theme as seen on github.com
+ Author: github.com
+ Maintainer: @Hirse
+ Updated: 2021-05-15
+
+ Outdated base version: https://github.com/primer/github-syntax-light
+ Current colors taken from GitHub's CSS
+*/
+.hljs {
+ color: #24292e;
+ background: #ffffff
+}
+.hljs-doctag,
+.hljs-keyword,
+.hljs-meta .hljs-keyword,
+.hljs-template-tag,
+.hljs-template-variable,
+.hljs-type,
+.hljs-variable.language_ {
+ /* prettylights-syntax-keyword */
+ color: #d73a49
+}
+.hljs-title,
+.hljs-title.class_,
+.hljs-title.class_.inherited__,
+.hljs-title.function_ {
+ /* prettylights-syntax-entity */
+ color: #6f42c1
+}
+.hljs-attr,
+.hljs-attribute,
+.hljs-literal,
+.hljs-meta,
+.hljs-number,
+.hljs-operator,
+.hljs-variable,
+.hljs-selector-attr,
+.hljs-selector-class,
+.hljs-selector-id {
+ /* prettylights-syntax-constant */
+ color: #005cc5
+}
+.hljs-regexp,
+.hljs-string,
+.hljs-meta .hljs-string {
+ /* prettylights-syntax-string */
+ color: #032f62
+}
+.hljs-built_in,
+.hljs-symbol {
+ /* prettylights-syntax-variable */
+ color: #e36209
+}
+.hljs-comment,
+.hljs-code,
+.hljs-formula {
+ /* prettylights-syntax-comment */
+ color: #6a737d
+}
+.hljs-name,
+.hljs-quote,
+.hljs-selector-tag,
+.hljs-selector-pseudo {
+ /* prettylights-syntax-entity-tag */
+ color: #22863a
+}
+.hljs-subst {
+ /* prettylights-syntax-storage-modifier-import */
+ color: #24292e
+}
+.hljs-section {
+ /* prettylights-syntax-markup-heading */
+ color: #005cc5;
+ font-weight: bold
+}
+.hljs-bullet {
+ /* prettylights-syntax-markup-list */
+ color: #735c0f
+}
+.hljs-emphasis {
+ /* prettylights-syntax-markup-italic */
+ color: #24292e;
+ font-style: italic
+}
+.hljs-strong {
+ /* prettylights-syntax-markup-bold */
+ color: #24292e;
+ font-weight: bold
+}
+.hljs-addition {
+ /* prettylights-syntax-markup-inserted */
+ color: #22863a;
+ background-color: #f0fff4
+}
+.hljs-deletion {
+ /* prettylights-syntax-markup-deleted */
+ color: #b31d28;
+ background-color: #ffeef0
+}
+.hljs-char.escape_,
+.hljs-link,
+.hljs-params,
+.hljs-property,
+.hljs-punctuation,
+.hljs-tag {
+ /* purposely ignored */
+
+}
\ No newline at end of file
diff --git a/app/src/main/assets/www/highlight/styles/info.css b/app/src/main/assets/www/highlight/styles/info.css
new file mode 100644
index 000000000..39383a5d7
--- /dev/null
+++ b/app/src/main/assets/www/highlight/styles/info.css
@@ -0,0 +1,68 @@
+html {
+ -webkit-text-size-adjust: 100%;
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
+}
+
+body {
+ line-height: 1.5;
+ padding: 1em;
+ word-wrap: break-word;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ margin-top: 24px;
+ margin-bottom: 16px;
+ font-weight: 600;
+ line-height: 1.25;
+}
+
+h1 {
+ margin-top: 6px;
+ padding-bottom: .3em;
+ font-size: 2em;
+ border-bottom: 1px solid hsla(210,18%,87%,1);
+}
+
+h2 {
+ padding-bottom: .3em;
+ font-size: 1.5em;
+ border-bottom: 1px solid hsla(210,18%,87%,1);
+}
+
+ol, ul {
+ margin: 0;
+ padding: 0;
+ margin-left: 1.5rem;
+ margin-bottom: 1rem;
+ list-style-position: outside;
+ line-height: 1.6;
+}
+
+a {
+ color: #0E0EFF;
+}
+
+code {
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
+ padding: 0.2em 0.4em;
+ margin: 0;
+ font-size: 85%;
+ background-color: rgba(99,110,123,0.1);
+ border-radius: 6px;
+}
+
+/* Dark Mode */
+@media (prefers-color-scheme: dark) {
+ html {
+ background-color: #1F1F24;
+ color: #FFFFFF;
+ }
+
+ code {
+ background-color: rgba(110,118,129,0.4);
+ }
+
+ a {
+ color: #5482FF;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/MainActivity.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/MainActivity.kt
new file mode 100644
index 000000000..e48103c5f
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/MainActivity.kt
@@ -0,0 +1,43 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import com.esri.arcgismaps.kotlin.sampleviewer.navigation.NavGraph
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ setContent {
+
+ SampleAppTheme {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ NavGraph()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/SampleViewerApplication.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/SampleViewerApplication.kt
new file mode 100644
index 000000000..b7405d9e2
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/SampleViewerApplication.kt
@@ -0,0 +1,18 @@
+package com.esri.arcgismaps.kotlin.sampleviewer
+
+import android.app.Application
+import com.esri.arcgismaps.kotlin.sampleviewer.model.DefaultSampleInfoRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class SampleViewerApplication : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ CoroutineScope(Dispatchers.IO).launch {
+ // Load the repository once at app launch
+ DefaultSampleInfoRepository.load(applicationContext)
+ }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Category.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Category.kt
new file mode 100644
index 000000000..5aa187103
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Category.kt
@@ -0,0 +1,93 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+
+/**
+ * Represents a sample category.
+ */
+data class Category(
+ val title: SampleCategory,
+ val icon: Int,
+ val backgroundImage: Int
+) {
+ companion object {
+ val SAMPLE_CATEGORIES = listOf(
+ Category(
+ SampleCategory.ANALYSIS,
+ R.drawable.ic_analysis,
+ R.drawable.analysis_background,
+ ),
+ Category(
+ SampleCategory.AUGMENTED_REALITY,
+ R.drawable.ic_augmented_reality,
+ R.drawable.augmented_reality_background,
+ ),
+ Category(
+ SampleCategory.CLOUD_AND_PORTAL,
+ R.drawable.ic_cloud,
+ R.drawable.cloud_background,
+ ),
+ Category(
+ SampleCategory.EDIT_AND_MANAGE_DATA,
+ R.drawable.ic_manage_data,
+ R.drawable.manage_data_background,
+ ),
+ Category(
+ SampleCategory.LAYERS,
+ R.drawable.ic_layers,
+ R.drawable.layers_background,
+ ),
+ Category(
+ SampleCategory.MAPS,
+ R.drawable.ic_map,
+ R.drawable.maps_and_scenes_background,
+ ),
+ Category(
+ SampleCategory.ROUTING_AND_LOGISTICS,
+ R.drawable.ic_routing_and_logistics,
+ R.drawable.routing_and_logistics_background,
+ ),
+ Category(
+ SampleCategory.SCENES,
+ R.drawable.ic_scenes,
+ R.drawable.scenes_background,
+ ),
+ Category(
+ SampleCategory.SEARCH_AND_QUERY,
+ R.drawable.ic_search_and_query,
+ R.drawable.search_and_query_background,
+ ),
+ Category(
+ SampleCategory.UTILITY_NETWORKS,
+ R.drawable.ic_utility,
+ R.drawable.utility_background,
+ ),
+ Category(
+ SampleCategory.VISUALIZATION,
+ R.drawable.ic_visualization,
+ R.drawable.visualization_background,
+ ),
+ Category(
+ SampleCategory.FAVORITES,
+ R.drawable.ic_favorite_selected,
+ R.drawable.maps_and_scenes_background,
+ ),
+ )
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/CodeFile.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/CodeFile.kt
new file mode 100644
index 000000000..5ee355fd7
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/CodeFile.kt
@@ -0,0 +1,28 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import kotlinx.serialization.Serializable
+
+/**
+ * Represents a single code file in a sample.
+ */
+@Serializable
+data class CodeFile(
+ val name: String,
+ val code: String
+)
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt
new file mode 100644
index 000000000..559983f9d
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt
@@ -0,0 +1,120 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import android.content.Context
+import android.util.Log
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadActivityPath
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadReadMe
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadScreenshot
+import com.esri.arcgismaps.kotlin.sampleviewer.model.room.AppDatabase
+import com.esri.arcgismaps.kotlin.sampleviewer.model.room.Converters
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.serialization.json.Json
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * The single source of truth for app wide data. It reads the sample metadata to create as list of
+ * [Sample] objects and populates the database used for search.
+ * It also provides functions to get samples by category, name, or all samples.
+ */
+object DefaultSampleInfoRepository : SampleInfoRepository {
+
+ private val isInitialized = AtomicBoolean(false)
+
+ private val json = Json { ignoreUnknownKeys = true }
+
+ private val sampleList = mutableListOf()
+
+ /**
+ * Load the sample metadata from the metadata folder in the assets directory and updates sampleList
+ * of [Sample] objects.
+ */
+ suspend fun load(context: Context) {
+ if (isInitialized.compareAndSet(false, true)) {
+ // Iterate through the metadata folder for all metadata files
+
+ context.assets.list("samples")?.forEach { samplePath ->
+ // Get this metadata files as a string
+ context.assets.open("samples/$samplePath/README.metadata.json").use { inputStream ->
+ val metadataJsonString = inputStream.bufferedReader().use { it.readText() }
+ try {
+ val metadata = json.decodeFromString(metadataJsonString)
+
+ // Create and add a new sample metadata data class object to the list
+ val sample = Sample(
+ name = metadata.title,
+ codeFiles = Sample.loadCodeFiles(
+ context = context,
+ sampleName = metadata.title
+ ),
+ url = "https://developers.arcgis.com/kotlin/sample-code/" +
+ metadata.title.replace(" ", "-").lowercase(),
+ readMe = loadReadMe(
+ context = context,
+ sampleName = samplePath
+ ),
+ screenshotURL = loadScreenshot(
+ sampleName = metadata.title,
+ imageArray = metadata.imagePaths
+ ),
+ mainActivity = loadActivityPath(
+ codePaths = metadata.codePaths
+ ),
+ metadata = metadata,
+ )
+ // Add the new sample to the list
+ sampleList.add(sample)
+ } catch (e: Exception) {
+ Log.e(
+ DefaultSampleInfoRepository::class.simpleName,
+ "Exception at $samplePath: " + e.printStackTrace()
+ )
+ }
+ }
+ }
+ withContext(Dispatchers.IO) {
+ // Populates the Room SQL database
+ populateDatabase(context)
+ }
+ }
+ }
+
+ /**
+ * Populates the Room SQL database with all samples and sample info
+ */
+ private suspend fun populateDatabase(context: Context) {
+ val sampleEntities = sampleList.map { Converters().convertToEntity(sample = it) }
+ AppDatabase.getDatabase(context).clearAllTables()
+ AppDatabase.getDatabase(context).sampleDao().insertAll(samples = sampleEntities)
+ }
+
+ /**
+ * Get a sample by its name. Either the formal name or title.
+ */
+ override fun getSampleByName(sampleName: String): Sample {
+ return sampleList.first { it.metadata.formalName == sampleName || it.metadata.title == sampleName }
+ }
+
+ /**
+ * Get a list of samples for the given [SampleCategory].
+ */
+ override fun getSamplesInCategory(sampleCategory: SampleCategory): List {
+ return sampleList.filter { it.metadata.sampleCategory == sampleCategory }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Sample.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Sample.kt
new file mode 100644
index 000000000..45cc14542
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Sample.kt
@@ -0,0 +1,165 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.toolkit.authentication.signOut
+import kotlinx.serialization.Serializable
+
+/**
+ * Holds information about a sample.
+ */
+@Serializable
+data class Sample(
+ val name: String,
+ val mainActivity: String,
+ val codeFiles: List,
+ val readMe: String,
+ val screenshotURL: String,
+ val url: String,
+ val metadata: SampleMetadata,
+ val isFavorite: Boolean = false,
+ var score: Double = 0.0
+) {
+ companion object {
+ val PREVIEW_INSTANCE = Sample(
+ name = "Analyze hotspots",
+ codeFiles = listOf(CodeFile("", "")),
+ url = "",
+ readMe = "",
+ screenshotURL = "",
+ metadata = SampleMetadata(
+ description = "",
+ formalName = "Analyze hotspots",
+ ignore = false,
+ imagePaths = listOf(""),
+ keywords = listOf(""),
+ relevantApis = listOf(""),
+ codePaths = listOf(""),
+ sampleCategory = SampleCategory.ANALYSIS,
+ title = "Analyze hotspots"
+ ),
+ isFavorite = false,
+ mainActivity = ""
+ )
+
+ /**
+ * Returns a list of [CodeFile] objects for the given sample name.
+ */
+ fun loadCodeFiles(context: Context, sampleName: String): List {
+ // List of code files to be populated
+ val codeFiles = mutableListOf()
+ // Code file folders stored in assets directory as kebab case
+ val sampleNameKebabCase = sampleName.replace(" ", "-").lowercase()
+ val sampleAssetFiles = context.assets.list("samples/$sampleNameKebabCase/")
+ // Get the code files from sub-directories (components/, screens/)
+ sampleAssetFiles?.forEach { sampleAssetFile ->
+ if (sampleAssetFile.contains(".kt")) {
+ val codeString = context.assets.open(
+ /* fileName = */ "samples/$sampleNameKebabCase/$sampleAssetFile"
+ ).bufferedReader().use { it.readText() }
+ codeFiles.add(
+ CodeFile(
+ name = sampleAssetFile,
+ code = codeString
+ )
+ )
+ }
+ }
+ return codeFiles
+ }
+
+ /**
+ * Returns the readme for a given sample name.
+ */
+ fun loadReadMe(context: Context, sampleName: String): String {
+ // Get this metadata files as a string
+ context.assets.open("samples/$sampleName/README.md").use { inputStream ->
+ val readMeString = inputStream.bufferedReader().use { it.readText() }
+ // Remove screenshot markdown text from the README
+ return readMeString.lines().filterNot { it.contains("![") }.joinToString("\n")
+ }
+ }
+
+ /**
+ * Returns the screenshot URL for a given sample name.
+ */
+ fun loadScreenshot(
+ sampleName: String, imageArray: List,
+ ): String {
+ // Assuming imageArray will always have one image.
+ // Otherwise, function should be modified to return list of URLs for each image
+ val modifiedJsonSampleName = sampleName.replace(" ", "-").lowercase()
+ val imageFileName = imageArray.first().toString().replace("\"", "")
+ return "https://raw.githubusercontent.com/Esri/arcgis-maps-sdk-kotlin-samples/v.next/samples/$modifiedJsonSampleName/$imageFileName"
+ }
+
+ /**
+ * Return's a path to DownloadActivity if one exists, otherwise returns the path to
+ * MainActivity.
+ */
+ fun loadActivityPath(codePaths: List): String {
+ // Return a path to DownloadActivity if one exists
+ codePaths.find { it.contains("DownloadActivity.kt") }?.let { samplePath ->
+ val activityPath = samplePath
+ .substring(14, samplePath.indexOf("."))
+ .replace("/".toRegex(), ".")
+ .replace("\\", "")
+ return activityPath
+ }
+
+ // Otherwise return the path the MainActivity
+ codePaths.find { it.contains("MainActivity.kt") }.apply {
+ val samplePath = this.toString()
+ val activityPath = samplePath
+ .substring(14, samplePath.indexOf("."))
+ .replace("/".toRegex(), ".")
+ .replace("\\", "")
+ return activityPath
+ }
+ }
+ }
+}
+
+/**
+ * Starts the sample activity.
+ */
+suspend fun Sample.start(context: Context) {
+ // Revoke previously configured ArcGISEnvironment settings like ApiKeys/OAuth tokens/Credentials
+ ArcGISEnvironment.authenticationManager.signOut()
+ ArcGISEnvironment.apiKey = null
+ // Obtain and launch the sample activity
+ val className = Class.forName(mainActivity) as Class<*>
+ val sampleLauncherActivity = context.getActivityOrNull() ?: return
+ sampleLauncherActivity.startActivity(Intent(sampleLauncherActivity, className))
+}
+
+/**
+ * Returns the activity from the context.
+ */
+fun Context.getActivityOrNull(): Activity? {
+ var context = this
+ while (context is ContextWrapper) {
+ if (context is Activity) return context
+ context = context.baseContext
+ }
+ return null
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleCategory.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleCategory.kt
new file mode 100644
index 000000000..477e1dec4
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleCategory.kt
@@ -0,0 +1,73 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+
+/**
+ * Holds the enum of each Sample Category.
+ */
+@Serializable
+enum class SampleCategory(val text: String) {
+ ANALYSIS("Analysis"),
+ AUGMENTED_REALITY("Augmented Reality"),
+ CLOUD_AND_PORTAL("Cloud and Portal"),
+ LAYERS("Layers"),
+ EDIT_AND_MANAGE_DATA("Edit and Manage Data"),
+ MAPS("Maps"),
+ SCENES("Scenes"),
+ ROUTING_AND_LOGISTICS("Routing and Logistics"),
+ UTILITY_NETWORKS("Utility Networks"),
+ SEARCH_AND_QUERY("Search and Query"),
+ VISUALIZATION("Visualization"),
+ FAVORITES("Favorites");
+
+ /**
+ * Return string of category enum.
+ */
+ override fun toString(): String {
+ return text
+ }
+
+ /**
+ * Return the enum of the category string.
+ */
+ companion object {
+ fun toEnum(categoryString: String): SampleCategory {
+ return entries.firstOrNull {it.text == categoryString}!!
+ }
+ }
+}
+
+object SampleCategorySerializer : KSerializer {
+ override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SampleCategory", PrimitiveKind.STRING)
+
+ override fun deserialize(decoder: Decoder): SampleCategory {
+ val value = decoder.decodeString()
+ return SampleCategory.toEnum(value)
+ }
+
+ override fun serialize(encoder: Encoder, value: SampleCategory) {
+ encoder.encodeString(value.text)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt
new file mode 100644
index 000000000..fe5a4ae06
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleInfoRepository.kt
@@ -0,0 +1,27 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+/**
+ * A repository interface to fetch sample information.
+ */
+interface SampleInfoRepository {
+
+ fun getSamplesInCategory(sampleCategory: SampleCategory): List
+
+ fun getSampleByName(sampleName: String): Sample
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleMetadata.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleMetadata.kt
new file mode 100644
index 000000000..81c607cfc
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/SampleMetadata.kt
@@ -0,0 +1,37 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+/**
+ * A data class to hold detailed information about the [Sample]
+ */
+@Serializable
+data class SampleMetadata(
+ @Serializable(with = SampleCategorySerializer::class)
+ @SerialName("category") val sampleCategory: SampleCategory,
+ val description: String,
+ @SerialName("formal_name") val formalName: String,
+ val ignore: Boolean?,
+ @SerialName("images") val imagePaths: List,
+ val keywords: List,
+ @SerialName("relevant_apis") val relevantApis: List,
+ @SerialName("snippets") val codePaths: List,
+ val title: String
+)
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/AppDatabase.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/AppDatabase.kt
new file mode 100644
index 000000000..cea8b9e0a
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/AppDatabase.kt
@@ -0,0 +1,53 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model.room
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.esri.arcgismaps.kotlin.sampleviewer.BuildConfig
+
+/**
+ * [AppDatabase] inherits from Room and makes an instance of [SampleDao]
+ */
+@Database(
+ entities = [SampleEntity::class],
+ version = BuildConfig.VERSION_CODE,
+ exportSchema = false
+)
+abstract class AppDatabase : RoomDatabase() {
+ abstract fun sampleDao(): SampleDao
+
+ companion object {
+
+ @Volatile
+ private var INSTANCE: AppDatabase? = null
+
+ fun getDatabase(context: Context): AppDatabase {
+ return INSTANCE ?: synchronized(this) {
+ val instance = Room.databaseBuilder(
+ context.applicationContext,
+ AppDatabase::class.java,
+ "arcgis_maps_kotlin_samples_database"
+ ).build()
+ INSTANCE = instance
+ instance
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/Converters.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/Converters.kt
new file mode 100644
index 000000000..5edd21c07
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/Converters.kt
@@ -0,0 +1,63 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model.room
+
+import androidx.room.TypeConverter
+import com.esri.arcgismaps.kotlin.sampleviewer.model.CodeFile
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+
+/**
+ * Room cannot convert complex data types like [CodeFile] or [List],
+ * thus, this class helps to convert them into JSON [String] format
+ */
+class Converters {
+
+ private val json = Json { encodeDefaults = true }
+
+ @TypeConverter
+ fun fromCodeFileList(codeFiles: List): String {
+ return codeFiles.let { json.encodeToString(it) }
+ }
+
+ @TypeConverter
+ fun toCodeFileList(jsonString: String): List {
+ return jsonString.let { json.decodeFromString(it) }
+ }
+
+ @TypeConverter
+ fun fromRelevantApiList(relevantApi: List): String {
+ return relevantApi.let { json.encodeToString(it) }
+ }
+
+ @TypeConverter
+ fun toRelevantApiList(relevantApiJsonString: String): List {
+ return relevantApiJsonString.let { json.decodeFromString(it) }
+ }
+
+ // Converts Sample to SampleEntity in order to select relevant fields
+ fun convertToEntity(sample: Sample): SampleEntity {
+ return SampleEntity(
+ sampleName = sample.name,
+ sampleCodeFile = sample.codeFiles,
+ sampleReadMe = sample.readMe,
+ sampleRelevantApi = sample.metadata.relevantApis
+ )
+ }
+
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/OkapiBM25.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/OkapiBM25.kt
new file mode 100644
index 000000000..84a730813
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/OkapiBM25.kt
@@ -0,0 +1,80 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 Pablo Pallocchi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the “Software”), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model.room
+
+import kotlin.math.log
+
+class OkapiBM25 {
+
+ companion object {
+ /**
+ * Calculates the OkapiBM25 score for a given column in an FTS4 table.
+ */
+ fun score(matchInfo: Array, column: Int, b: Double = 0.75, k1: Double = 1.2): Double {
+
+ val pOffset = 0
+ val cOffset = 1
+ val nOffset = 2
+ val aOffset = 3
+
+ // the number of matchable phrases in the query
+ val termCount = matchInfo[pOffset]
+ // the number of user defined columns in the FTS table
+ val colCount = matchInfo[cOffset]
+
+ val lOffset = aOffset + colCount
+ val xOffset = lOffset + colCount
+
+ // the number of rows in the FTS4 table
+ val totalDocs = matchInfo[nOffset].toDouble()
+ val avgLength = matchInfo[aOffset + column].toDouble()
+ val docLength = matchInfo[lOffset + column].toDouble()
+
+ var score = 0.0
+
+ for (i in 0 until termCount) {
+
+ val currentX = xOffset + (3 * (column + i * colCount))
+
+ // in the current row, the number of times the phrase appears in the column
+ val termFrequency = matchInfo[currentX].toDouble()
+ // the total number of rows in the FTS table for which the column contains at least one instance of the phrase.
+ val docsWithTerm = matchInfo[currentX + 2].toDouble()
+
+ val p = totalDocs - docsWithTerm + 0.5
+ val q = docsWithTerm + 0.5
+ val idf = log(p, q)
+
+ val r = termFrequency * (k1 + 1)
+ val s = b * (docLength / avgLength)
+ val t = termFrequency + (k1 * (1 - b + s))
+ val rightSide = r / t
+
+ score += (idf * rightSide)
+ }
+
+ return score
+ }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleDao.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleDao.kt
new file mode 100644
index 000000000..8ba7a5421
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleDao.kt
@@ -0,0 +1,57 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model.room
+
+import android.database.Cursor
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+
+/**
+ * Holds all operations that uses Room
+ */
+@Dao
+interface SampleDao {
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertAll(samples: List)
+
+ // In support of ranked search.
+ @Query(
+ """SELECT name, codeFile, readMe, relevantAPIs, MATCHINFO(samplesDB, 'pcnalx') FROM samplesDB
+ WHERE samplesDB MATCH :query"""
+ )
+ fun matchInfoSearch(query: String): Cursor
+
+ // Offers samples as search suggestions.
+ @Query(
+ """SELECT rowid, name, codeFile, readMe, relevantAPIs FROM samplesDB
+ WHERE LOWER(name) LIKE LOWER('%' || :searchQuery || '%' )
+ OR LOWER(readMe) LIKE LOWER('%' || :searchQuery || '%' )
+ OR LOWER(codeFile) LIKE LOWER('%' || :searchQuery || '%' )
+ OR LOWER(relevantAPIs) LIKE LOWER('%' || :searchQuery || '%' )"""
+ )
+ fun getFilteredSamples(searchQuery: String): List
+
+ // Offers relevant APIs as search suggestions.
+ @Query(
+ """SELECT rowid, name, codeFile, readMe, relevantAPIs FROM samplesDB
+ WHERE LOWER(relevantAPIs) LIKE LOWER('%' || :searchQuery || '%' )"""
+ )
+ fun getFilteredRelevantAPIs(searchQuery: String): List
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleEntity.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleEntity.kt
new file mode 100644
index 000000000..ac867c2fe
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/room/SampleEntity.kt
@@ -0,0 +1,40 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.model.room
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Fts4
+import androidx.room.PrimaryKey
+import androidx.room.TypeConverters
+import com.esri.arcgismaps.kotlin.sampleviewer.model.CodeFile
+
+/**
+ * Define the structure of the Database records. The first column and the primary key is an integer
+ * rowid, which is a requirement of FTS4 tables. The other columns are the fields of the Sample
+ * class.
+ */
+@Fts4
+@Entity(tableName = "samplesDB")
+@TypeConverters(Converters::class)
+data class SampleEntity(
+ @PrimaryKey @ColumnInfo(name = "rowid") val id: Int = 0,
+ @ColumnInfo(name = "name") val sampleName: String,
+ @ColumnInfo(name = "codeFile") val sampleCodeFile: List,
+ @ColumnInfo(name = "readMe") val sampleReadMe: String,
+ @ColumnInfo(name = "relevantAPIs") val sampleRelevantApi: List,
+)
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/navigation/NavGraph.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/navigation/NavGraph.kt
new file mode 100644
index 000000000..f01611e20
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/navigation/NavGraph.kt
@@ -0,0 +1,199 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.navigation
+
+import android.content.Context
+import android.util.Log
+import android.widget.Toast
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import com.esri.arcgismaps.kotlin.sampleviewer.model.DefaultSampleInfoRepository
+import com.esri.arcgismaps.kotlin.sampleviewer.model.SampleCategory
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.about.AboutScreen
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.codePager.SampleInfoScreen
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.home.HomeCategoryScreen
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.sampleList.SampleListScreen
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.search.SearchResults
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.search.SearchScreen
+
+/**
+ * A composable function to host the navigation system.
+ */
+@Composable
+internal fun NavGraph() {
+ val context = LocalContext.current
+ val navController = rememberNavController()
+ NavHost(
+ navController = navController,
+ startDestination = Routes.HOME_SCREEN,
+ ) {
+
+ composable(Routes.HOME_SCREEN) {
+ HomeCategoryScreen(
+ onNavigateToSearch = { navController.navigate(Routes.SEARCH_SCREEN) },
+ onNavigateToAbout = { navController.navigate(Routes.ABOUT_SCREEN) },
+ onNavigateToCategory = { navController.navigate(Routes.createCategoryRoute(it)) }
+ )
+ }
+
+ composable(Routes.ABOUT_SCREEN) {
+ AboutScreen(onBackPressed = { navController.pop() })
+ }
+
+ composable(
+ route = Routes.CATEGORY_SAMPLE_LIST_ROUTE,
+ arguments = listOf(navArgument("category") { type = NavType.StringType })
+ ) { backStackEntry ->
+ val categoryNavEntry = backStackEntry.arguments?.getString("category")
+ if (!categoryNavEntry.isNullOrEmpty())
+ SampleListScreen(
+ categoryNavEntry = categoryNavEntry,
+ onNavigateToInfo = { optionPosition, sample ->
+ navController.navigate(
+ Routes.createSampleInfoRoute(optionPosition, sample.name)
+ )
+ },
+ onBackPressed = { navController.pop() }
+ )
+ else {
+ navController.displayError("categoryNavEntry is null/empty", context)
+ }
+ }
+
+ composable(
+ route = Routes.SAMPLE_INFO_ROUTE,
+ arguments = listOf(
+ navArgument("optionPosition") { type = NavType.IntType },
+ navArgument("sampleName") { type = NavType.StringType }
+ )
+ ) { backStackEntry ->
+ val optionPositionNavEntry = backStackEntry.arguments?.getInt("optionPosition")
+ val sampleNameNavEntry = backStackEntry.arguments?.getString("sampleName")
+
+ if (optionPositionNavEntry != null && !sampleNameNavEntry.isNullOrEmpty()) {
+ val sampleNavEntry = DefaultSampleInfoRepository.getSampleByName(sampleNameNavEntry)
+ SampleInfoScreen(
+ sample = sampleNavEntry,
+ optionPosition = optionPositionNavEntry,
+ onBackPressed = { navController.pop() }
+ )
+ } else if (optionPositionNavEntry == null) {
+ navController.displayError("optionPositionNavEntry is null", context)
+ } else {
+ navController.displayError("sampleNameNavEntry is null/empty", context)
+ }
+ }
+
+ composable(Routes.SEARCH_SCREEN) {
+ SearchScreen(
+ onNavigateToSearchResults = {
+ navController.navigate(Routes.createSearchResultsRoute(it))
+ },
+ onBackPressed = { navController.pop() }
+ )
+ }
+
+ composable(
+ route = Routes.SEARCH_RESULTS_ROUTE,
+ arguments = listOf(navArgument("query") { type = NavType.StringType })
+ ) { backStackEntry ->
+ val queryNavEntry = backStackEntry.arguments?.getString("query")
+ if (!queryNavEntry.isNullOrEmpty()) {
+ SearchResults(
+ searchQuery = queryNavEntry,
+ navigateToInfo = { optionPosition, sample ->
+ navController.navigate(
+ Routes.createSampleInfoRoute(
+ optionPosition,
+ sample.name
+ )
+ )
+ },
+ onBackPressed = { navController.pop() }
+ )
+ } else {
+ navController.displayError("queryNavEntry is null/empty", context)
+ }
+ }
+ }
+}
+
+
+/**
+ * Displays a Toast and a Log for the given [message] on route errors,
+ * then reset navigation to home screen.
+ */
+private fun NavHostController.displayError(message: String, context: Context) {
+ val exceptionTag = "InvalidRouteError"
+ Toast.makeText(context, "$exceptionTag: $message", Toast.LENGTH_SHORT).show()
+ Log.e(exceptionTag, message)
+ navigateToHome()
+}
+
+/**
+ * Attempts to pop the controller's back stack. Checks if screen was navigated to
+ * another destination, or navigate to home if no items in stack.
+ */
+private fun NavHostController.pop() {
+ if (!popBackStack()) {
+ navigateToHome()
+ }
+}
+
+/**
+ * Navigates to the home screen, clearing the back stack and restoring the state.
+ */
+private fun NavHostController.navigateToHome() {
+ navigate(Routes.HOME_SCREEN) {
+ popUpTo(graph.findStartDestination().id)
+ launchSingleTop = true
+ }
+}
+
+/**
+ * Navigation Routes for the application.
+ */
+private object Routes {
+ private const val SAMPLE_LIST = "Sample List"
+ private const val SAMPLE_INFO = "Sample Info"
+ private const val SEARCH_RESULTS = "Search Results"
+ const val HOME_SCREEN = "Sample Categories"
+ const val ABOUT_SCREEN = "About"
+ const val SEARCH_SCREEN = "Search"
+ const val CATEGORY_SAMPLE_LIST_ROUTE = "$SAMPLE_LIST/{category}"
+ const val SAMPLE_INFO_ROUTE = "$SAMPLE_INFO/{optionPosition}/{sampleName}"
+ const val SEARCH_RESULTS_ROUTE = "$SEARCH_RESULTS/{query}"
+
+ fun createCategoryRoute(category: SampleCategory): String {
+ return "$SAMPLE_LIST/${category}"
+ }
+
+ fun createSearchResultsRoute(query: String): String {
+ return "$SEARCH_RESULTS/$query"
+ }
+
+ fun createSampleInfoRoute(optionPosition: Int, sampleName: String): String {
+ return "$SAMPLE_INFO/$optionPosition/$sampleName"
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/CategoryCard.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/CategoryCard.kt
new file mode 100644
index 000000000..7c228ff3f
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/CategoryCard.kt
@@ -0,0 +1,133 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ElevatedCard
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Category
+import com.esri.arcgismaps.kotlin.sampleviewer.model.SampleCategory
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+/**
+ * A composable used in the HomeCategoryScreen to display the category cards.
+ */
+@Composable
+fun CategoryCard(
+ category: Category,
+ onClick: () -> Unit
+) {
+ ElevatedCard(
+ modifier = Modifier
+ .size(width = 175.dp, height = 175.dp)
+ .clickable { onClick() },
+ elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
+ shape = MaterialTheme.shapes.medium
+ ) {
+ Box {
+ CategoryBackground(category)
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically)
+ ) {
+ CategoryIconBackground(category)
+ Text(
+ modifier = Modifier.wrapContentSize(Alignment.Center),
+ text = category.title.text,
+ color = Color.White,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleMedium
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun CategoryIconBackground(item: Category) {
+ Box(
+ Modifier
+ .background(
+ color = Color.Black.copy(alpha = 0.8f),
+ shape = CircleShape
+ )
+ .padding(8.dp)
+ ) {
+ Icon(
+ modifier = Modifier
+ .size(30.dp),
+ painter = painterResource(item.icon),
+ contentDescription = "Category Icon",
+ tint = Color.White
+ )
+ }
+}
+
+@Composable
+private fun CategoryBackground(item: Category) {
+ Image(
+ modifier = Modifier.fillMaxSize(),
+ painter = painterResource(item.backgroundImage),
+ contentDescription = item.title.text,
+ contentScale = ContentScale.Crop
+ )
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.Black.copy(alpha = 0.6f))
+ )
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+fun PreviewCategoryCard() {
+ SampleAppTheme {
+ CategoryCard(
+ Category(
+ title = SampleCategory.ANALYSIS,
+ icon = R.drawable.ic_analysis,
+ backgroundImage = R.drawable.analysis_background
+ ),
+ ) { }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleCard.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleCard.kt
new file mode 100644
index 000000000..abcd19efe
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleCard.kt
@@ -0,0 +1,204 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material.icons.outlined.Info
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.model.start
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.sampleList.DropdownItemData
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import kotlinx.coroutines.launch
+
+/**
+ * Row to display Sample information and its contents
+ */
+@Composable
+fun SampleRow(
+ sample: Sample,
+ dropdownSampleItems: List
+) {
+ val context = LocalContext.current
+ val scope = rememberCoroutineScope()
+ Column(Modifier.clickable { scope.launch { sample.start(context) } }) {
+ TitleAndIcons(sample, dropdownSampleItems)
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun TitleAndIcons(
+ sample: Sample,
+ dropdownSampleItems: List
+) {
+ var expandedMenu by rememberSaveable { mutableStateOf(false) }
+ var expandedDescription by rememberSaveable { mutableStateOf(false) }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(MaterialTheme.colorScheme.surfaceContainer)
+ .padding(start = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .padding(vertical = 16.dp)
+ ) {
+ Text(
+ text = sample.name,
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ ExpandedDescriptionAnimation(expandedDescription, sample)
+ }
+
+ IconButton(onClick = { expandedDescription = !expandedDescription }) {
+ Icon(
+ imageVector = if (expandedDescription) Icons.Filled.Info else Icons.Outlined.Info,
+ contentDescription = "Sample Info",
+ tint = MaterialTheme.colorScheme.primary
+ )
+ }
+
+ Column {
+ IconButton(onClick = { expandedMenu = !expandedMenu }) {
+ Icon(
+ imageVector = Icons.Filled.MoreVert,
+ contentDescription = "More options",
+ tint = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ DropdownMenu(
+ modifier = Modifier
+ .height(IntrinsicSize.Max)
+ .background(MaterialTheme.colorScheme.surfaceContainer),
+ expanded = expandedMenu,
+ onDismissRequest = { expandedMenu = false }
+ ) {
+ dropdownSampleItems.forEach { option ->
+ DropdownMenuItem(
+ text = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(
+ space = 8.dp,
+ alignment = Alignment.CenterHorizontally
+ )
+ ) {
+ Icon(
+ painter = painterResource(id = option.icon),
+ contentDescription = "${option.title} Icon",
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ Text(
+ text = option.title,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ },
+ onClick = {
+ expandedMenu = option.title.lowercase().contains("favorite")
+ option.onClick()
+ },
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ExpandedDescriptionAnimation(
+ expandedDescription: Boolean,
+ sample: Sample
+) {
+ AnimatedVisibility(
+ visible = expandedDescription,
+ enter = expandVertically(),
+ exit = shrinkVertically()
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text(
+ text = sample.metadata.description,
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium,
+ color = Color.Gray
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+fun PreviewSampleRow() {
+ SampleAppTheme {
+ val dropdownItemData = listOf()
+ SampleRow(
+ sample = Sample.PREVIEW_INSTANCE,
+ dropdownSampleItems = dropdownItemData
+ )
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleViewerTopAppBar.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleViewerTopAppBar.kt
new file mode 100644
index 000000000..1d3b72c9b
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleViewerTopAppBar.kt
@@ -0,0 +1,72 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+/**
+ * The common [TopAppBar] used to display screen title and the back button for all screens.
+ */
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+fun SampleViewerTopAppBar(title: String, onBackPressed: () -> Unit) {
+ TopAppBar(
+ title = {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.titleLarge
+ )
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
+ ),
+ navigationIcon = {
+ IconButton(onClick = onBackPressed) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
+ contentDescription = stringResource(R.string.backButton)
+ )
+ }
+ }
+ )
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+fun PreviewSampleViewerTopAppBar() {
+ SampleAppTheme {
+ SampleViewerTopAppBar(title = "Sample Viewer title", onBackPressed = {})
+ }
+}
+
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleWebView.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleWebView.kt
new file mode 100644
index 000000000..ded309cb0
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/components/SampleWebView.kt
@@ -0,0 +1,181 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.components
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.webkit.WebView
+import androidx.compose.foundation.background
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalInspectionMode
+import androidx.compose.ui.viewinterop.AndroidView
+
+/**
+ * WebViewer to display formatted .kt code files.
+ */
+@Composable
+fun CodeView(code: String) {
+ if (LocalInspectionMode.current) {
+ return // if composition is composed inside a preview component
+ }
+
+ val isDeviceInDarkMode = isSystemInDarkTheme()
+ AndroidView(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background),
+ factory = { ctx -> createWebView(ctx) },
+ update = { webView ->
+ webView.loadDataWithBaseURL(
+ /* baseUrl = */ "file:///android_asset/www/highlight/",
+ /* data = */ formatWebViewHTMLContent(
+ rawFileContents = code,
+ webViewType = WebViewType.CODE_VIEW,
+ isDeviceInDarkMode = isDeviceInDarkMode
+ ),
+ /* mimeType = */ "text/html",
+ /* encoding = */ "utf-8",
+ /* historyUrl = */ null
+ )
+ }
+ )
+}
+
+/**
+ * WebViewer to display README.md files.
+ */
+@Composable
+fun ReadmeView(
+ markdownText: String
+) {
+ if (LocalInspectionMode.current) {
+ return // if composition is composed inside a preview component
+ }
+
+ val isDeviceInDarkMode = isSystemInDarkTheme()
+ AndroidView(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background),
+ factory = { ctx ->
+ createWebView(ctx).apply {
+ loadDataWithBaseURL(
+ /* baseUrl = */ "file:///android_asset/www/highlight/",
+ /* data = */ formatWebViewHTMLContent(
+ rawFileContents = markdownText,
+ webViewType = WebViewType.README_VIEW,
+ isDeviceInDarkMode = isDeviceInDarkMode
+ ),
+ /* mimeType = */ "text/html",
+ /* encoding = */ "utf-8",
+ /* historyUrl = */ null
+ )
+ }
+ }
+ )
+}
+
+/**
+ * Set up the HTML [String] to be displayed based on the given [rawFileContents]
+ * given the [webViewType].
+ */
+fun formatWebViewHTMLContent(
+ rawFileContents: String,
+ webViewType: WebViewType,
+ isDeviceInDarkMode: Boolean
+): String {
+ val webViewHTML = if (webViewType == WebViewType.README_VIEW) {
+ readmeHTML.replace("\\(content)", rawFileContents)
+ } else {
+ if (!isDeviceInDarkMode) {
+ codeViewHTML
+ .replace("github-dark", "github")
+ .replace("\\(content)", rawFileContents)
+ } else {
+ codeViewHTML.replace("\\(content)", rawFileContents)
+ }
+ }
+ return webViewHTML
+}
+
+/**
+ * Create the [WebView] using the default settings to load the static html.
+ */
+@SuppressLint("SetJavaScriptEnabled")
+fun createWebView(context: Context): WebView {
+ return WebView(context).apply {
+ settings.apply {
+ javaScriptEnabled = true
+ domStorageEnabled = true
+ }
+ alpha = 0.99f // TODO: Weirdly, without this the screen crashes on popBackStack (#4632)
+ }
+}
+
+enum class WebViewType {
+ README_VIEW,
+ CODE_VIEW
+}
+
+val codeViewHTML: String = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \(content)
+
+
+""".trimIndent()
+
+val readmeHTML: String = """
+
+
+
+
+
+
+
+
+
+ \(content)
+
+
+
+
+
+""".trimIndent()
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/about/AboutScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/about/AboutScreen.kt
new file mode 100644
index 000000000..d79852566
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/about/AboutScreen.kt
@@ -0,0 +1,511 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.about
+
+import android.content.Intent
+import android.content.res.Configuration
+import android.net.Uri
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.BasicAlertDialog
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.esri.arcgismaps.kotlin.sampleviewer.BuildConfig
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.SampleViewerTopAppBar
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+/**
+ * Showcase information about the application.
+ */
+@Composable
+fun AboutScreen(onBackPressed: () -> Unit) {
+ Scaffold(
+ modifier = Modifier
+ .fillMaxSize(),
+ topBar = { SampleViewerTopAppBar(title = "About", onBackPressed) },
+ containerColor = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground
+ ) { innerPadding ->
+ AboutContent(Modifier.padding(innerPadding))
+ }
+}
+
+@Composable
+fun AboutContent(modifier: Modifier) {
+ Column(
+ modifier
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Column(
+ modifier = Modifier.padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ TitleAndCopyrightSection()
+ VersionsSection()
+ PoweredBySection()
+ EsriCommunitySection()
+ GithubSection()
+ ApiDetailsSection()
+ }
+ }
+}
+
+@Composable
+private fun AboutIcon() {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ val size = (LocalConfiguration.current.screenWidthDp * 0.20).dp
+ Image(
+ modifier = Modifier.size(size),
+ painter = painterResource(com.esri.arcgismaps.sample.sampleslib.R.drawable.arcgis_maps_sdks_64),
+ contentDescription = stringResource(R.string.sdk_sample_icon_description)
+ )
+ }
+}
+
+@Composable
+private fun TitleAndCopyrightSection() {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ AboutIcon()
+ Text(
+ modifier = Modifier.align(Alignment.CenterHorizontally),
+ text = stringResource(R.string.about_title),
+ textAlign = TextAlign.Center,
+ fontWeight = FontWeight.Bold,
+ style = MaterialTheme.typography.titleMedium
+ )
+ Text(
+ modifier = Modifier.align(Alignment.CenterHorizontally),
+ text = stringResource(R.string.copyright_text),
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+}
+
+@Composable
+private fun VersionsSection() {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.surfaceContainer, RoundedCornerShape(8.dp))
+ .padding(16.dp),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ text = stringResource(R.string.app_version),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.labelLarge,
+ )
+ Text(
+ text = BuildConfig.VERSION_CODE.toString(),
+ textAlign = TextAlign.End,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ text = stringResource(R.string.SDK_version),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.labelLarge
+ )
+ Text(
+ text = BuildConfig.ARCGIS_VERSION,
+ textAlign = TextAlign.End,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+
+}
+
+@Composable
+private fun PoweredBySection() {
+ var isAcknowledgementsDialogVisible by rememberSaveable { mutableStateOf(false) }
+ val context = LocalContext.current
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.surfaceContainer, RoundedCornerShape(8.dp))
+ .padding(start = 16.dp, top = 16.dp, bottom = 8.dp),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text(
+ text = stringResource(R.string.powered_by),
+ fontWeight = FontWeight.Bold,
+ style = MaterialTheme.typography.titleSmall
+ )
+ }
+ Column(modifier = Modifier.padding(start = 16.dp, top = 8.dp)) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://github.com/Esri/arcgis-runtime-toolkit-android")
+ )
+ context.startActivity(intent)
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.ArcGIS_Maps_SDK_Toolkit),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .padding(8.dp)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://developers.arcgis.com/kotlin/")
+ )
+ context.startActivity(intent)
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.ArcGIS_Maps_SDK_Kotlin),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .padding(8.dp)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { isAcknowledgementsDialogVisible = true },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = "Acknowledgements",
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp) // Size of the clickable area
+ .padding(8.dp) // Padding inside the Box
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ }
+ if (isAcknowledgementsDialogVisible) {
+ AcknowledgementsDialog {
+ isAcknowledgementsDialogVisible = false
+ }
+ }
+ }
+}
+
+@Composable
+private fun EsriCommunitySection() {
+ val context = LocalContext.current
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(RoundedCornerShape(8.dp))
+ .background(MaterialTheme.colorScheme.surfaceContainer)
+ .clickable {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://community.esri.com/t5/kotlin-maps-sdk-questions/bd-p/kotlin-maps-sdk-questions")
+ )
+ context.startActivity(intent)
+ }
+ .padding(start = 16.dp, top = 16.dp, bottom = 8.dp),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text(
+ text = stringResource(R.string.browse_and_discuss),
+ fontWeight = FontWeight.Bold,
+ style = MaterialTheme.typography.titleSmall
+ )
+ }
+ Column(modifier = Modifier.padding(start = 16.dp)) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.esri_community),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .padding(8.dp)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun AcknowledgementsDialog(onDismissRequest: () -> Unit) {
+ BasicAlertDialog(
+ modifier = Modifier.clip(RoundedCornerShape(8.dp)),
+ onDismissRequest = onDismissRequest
+ ) {
+ Column(
+ modifier = Modifier
+ .background(MaterialTheme.colorScheme.background)
+ .padding(24.dp)
+ .clip(RoundedCornerShape(8.dp)),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ val context = LocalContext.current
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "License Acknowledgements",
+ style = MaterialTheme.typography.headlineSmall,
+ textAlign = TextAlign.Center
+
+ )
+ OutlinedButton(onClick = {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://github.com/showdownjs/showdown/blob/master/LICENSE")
+ )
+ context.startActivity(intent)
+ }) {
+ Text(text = "Showdown")
+ }
+ OutlinedButton(onClick = {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://github.com/highlightjs/highlight.js/blob/main/LICENSE")
+ )
+ context.startActivity(intent)
+ }) {
+ Text(text = "Highlight.js")
+ }
+ }
+ }
+}
+
+@Composable
+private fun GithubSection() {
+ val context = LocalContext.current
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(RoundedCornerShape(8.dp))
+ .background(MaterialTheme.colorScheme.surfaceContainer)
+ .clickable {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://github.com/Esri/arcgis-maps-sdk-kotlin-samples")
+ )
+ context.startActivity(intent)
+ }
+ .padding(start = 16.dp, top = 16.dp, bottom = 8.dp),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text(
+ text = stringResource(R.string.github_issue),
+ fontWeight = FontWeight.Bold,
+ style = MaterialTheme.typography.titleSmall
+ )
+ }
+ Column(modifier = Modifier.padding(start = 16.dp)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.github_repo),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .padding(8.dp)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ApiDetailsSection() {
+ val context = LocalContext.current
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(RoundedCornerShape(8.dp))
+ .background(MaterialTheme.colorScheme.surfaceContainer)
+ .clickable {
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("https://developers.arcgis.com/kotlin/api-reference/")
+ )
+ context.startActivity(intent)
+ }
+ .padding(start = 16.dp, top = 16.dp, bottom = 8.dp),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Text(
+ text = stringResource(R.string.API_details),
+ fontWeight = FontWeight.Bold,
+ style = MaterialTheme.typography.titleSmall
+ )
+ }
+ Column(modifier = Modifier.padding(start = 16.dp)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.API_ref),
+ textAlign = TextAlign.Start,
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .padding(8.dp)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.keyboard_arrow_right),
+ contentDescription = stringResource(R.string.forward_button)
+ )
+ }
+ }
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+@Composable
+fun PreviewAboutScreen() {
+ SampleAppTheme { AboutScreen {} }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/codePager/SampleInfoScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/codePager/SampleInfoScreen.kt
new file mode 100644
index 000000000..a4e319876
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/codePager/SampleInfoScreen.kt
@@ -0,0 +1,214 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.codePager
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ArrowDropDown
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import coil3.compose.AsyncImage
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.CodeView
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.ReadmeView
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.SampleViewerTopAppBar
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+/**
+ * Shows both README and Code for each sample.
+ */
+@Composable
+fun SampleInfoScreen(
+ sample: Sample,
+ optionPosition: Int,
+ onBackPressed: () -> Unit
+) {
+ val codePagerTitles = mutableListOf()
+ codePagerTitles.add("README.md")
+ sample.codeFiles.forEach {
+ codePagerTitles.add(it.name)
+ }
+ Scaffold(
+ topBar = { SampleViewerTopAppBar(title = sample.name, onBackPressed) },
+ containerColor = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground,
+ modifier = Modifier
+ .fillMaxSize(),
+ ) { innerPadding ->
+ Surface(
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(shape = MaterialTheme.shapes.extraLarge)
+ .padding(innerPadding)
+ ) {
+ CodePageView(codePagerTitles, sample, optionPosition)
+ }
+ }
+}
+
+@Composable
+fun CodePagerBar(selectedFileIndex: Int, fileList: List, onFileClicked: (Int) -> Unit) {
+ var expanded by rememberSaveable { mutableStateOf(false) }
+ Box(modifier = Modifier
+ .clickable { expanded = true }
+ .fillMaxWidth()) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(12.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ CodePageRow(
+ title = fileList[selectedFileIndex],
+ iconId = getIconId(fileList[selectedFileIndex])
+ )
+ Icon(
+ imageVector = Icons.Outlined.ArrowDropDown,
+ contentDescription = "Dropdown icon",
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ }
+ DropdownMenu(
+ modifier = Modifier.background(MaterialTheme.colorScheme.surface),
+ expanded = expanded,
+ onDismissRequest = { expanded = false }) {
+ fileList.forEachIndexed { index, file ->
+ DropdownMenuItem(modifier = Modifier.background(MaterialTheme.colorScheme.surface),
+ text = {
+ CodePageRow(title = file, iconId = getIconId(file))
+ }, onClick = {
+ onFileClicked(index)
+ expanded = false
+ })
+ }
+ }
+ }
+}
+
+@Composable
+fun CodePageRow(title: String, iconId: Int) {
+ Row(
+ modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start
+ ) {
+ Icon(
+ painter = painterResource(id = iconId),
+ contentDescription = "File icon",
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = title,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+}
+
+@Composable
+private fun CodePageView(
+ codePagerTitles: List,
+ sample: Sample,
+ optionPosition: Int
+) {
+ var selectedFileIndex by remember { mutableIntStateOf(optionPosition) }
+ Column {
+ CodePagerBar(selectedFileIndex, codePagerTitles, onFileClicked = {
+ selectedFileIndex = it
+ })
+ if (codePagerTitles[selectedFileIndex].contains(".md")) {
+ Column(
+ modifier = Modifier
+ .verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ val markdownText by remember { mutableStateOf(sample.readMe) }
+ val screenshotURL by remember { mutableStateOf(sample.screenshotURL) }
+ AsyncImage(
+ modifier = Modifier
+ .height(200.dp)
+ .width(350.dp)
+ .padding(8.dp),
+ model = screenshotURL,
+ contentDescription = "Sample screenshot"
+ )
+ ReadmeView(markdownText = markdownText)
+ }
+ } else {
+ CodeView(
+ code = sample.codeFiles
+ .find { it.name == codePagerTitles[selectedFileIndex] }?.code ?: ""
+ )
+ }
+ }
+}
+
+@Composable
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+fun PreviewSampleInfoScreen() {
+ SampleAppTheme {
+ SampleInfoScreen(
+ sample = Sample.PREVIEW_INSTANCE,
+ optionPosition = 0,
+ onBackPressed = {}
+ )
+ }
+}
+
+fun getIconId(selectedFile: String): Int {
+ return when (selectedFile.lowercase().contains(".kt")) {
+ true -> R.drawable.ic_kotlin
+ else -> R.drawable.ic_readme
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/home/HomeCategoryScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/home/HomeCategoryScreen.kt
new file mode 100644
index 000000000..7587b586d
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/home/HomeCategoryScreen.kt
@@ -0,0 +1,207 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.home
+
+import android.content.res.Configuration
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
+import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.outlined.Info
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FabPosition
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.times
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Category
+import com.esri.arcgismaps.kotlin.sampleviewer.model.SampleCategory
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.CategoryCard
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.stylegraphicswithsymbols.BuildConfig
+
+/**
+ * The main SampleViewer app screen which showcases the list all sample categories,
+ * saved favorites, and an app wide searching interface.
+ */
+@Composable
+fun HomeCategoryScreen(
+ onNavigateToAbout: () -> Unit,
+ onNavigateToSearch: () -> Unit,
+ onNavigateToCategory: (SampleCategory) -> Unit,
+) {
+ val config = LocalConfiguration.current
+ val layoutSpacing by remember { mutableStateOf(0.03f * config.screenWidthDp.dp) }
+ val isVisible = rememberSaveable { mutableStateOf(true) }
+ val nestedScrollConnection = remember {
+ object : NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ // Hide FAB
+ if (available.y < -1) {
+ isVisible.value = false
+ }
+ // Show FAB
+ if (available.y > 1) {
+ isVisible.value = true
+ }
+ return Offset.Zero
+ }
+ }
+ }
+ Scaffold(
+ modifier = Modifier.fillMaxSize(),
+ topBar = { HomeCategoryTopAppBar(onNavigateToAbout) },
+ floatingActionButton = { SearchFloatingActionButton(isVisible, onNavigateToSearch) },
+ floatingActionButtonPosition = FabPosition.End,
+ containerColor = MaterialTheme.colorScheme.background,
+ contentColor = MaterialTheme.colorScheme.onBackground
+ ) { innerPadding ->
+ Box(
+ modifier = Modifier
+ .padding(innerPadding)
+ .nestedScroll(nestedScrollConnection)
+ ) {
+ LazyVerticalStaggeredGrid(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(layoutSpacing),
+ columns = StaggeredGridCells.Adaptive(150.dp),
+ horizontalArrangement = Arrangement.spacedBy(layoutSpacing),
+ verticalItemSpacing = layoutSpacing
+ ) {
+ items(Category.SAMPLE_CATEGORIES.size) { index ->
+ val category = Category.SAMPLE_CATEGORIES[index]
+ CategoryCard(category) { onNavigateToCategory(category.title) }
+ }
+ }
+ CheckAccessToken()
+ }
+ }
+}
+
+/**
+ * Display a dialog if the default access token placeholder is still set.
+ * This is required for samples to access Esri location services, including basemaps, routing,
+ * and geocoding, requires authentication using either an API Key or an ArcGIS identity.
+ *
+ * @see Accessing Esri location services
+ *
+ */
+@Composable
+fun CheckAccessToken() {
+ var isDialogDisplayed by remember {
+ mutableStateOf(BuildConfig.ACCESS_TOKEN == "DEFAULT_ACCESS_TOKEN")
+ }
+ if (isDialogDisplayed) {
+ MessageDialog(
+ title = "Access token",
+ description = "Add an ACCESS_TOKEN to the project's local.properties",
+ onDismissRequest = { isDialogDisplayed = false }
+ )
+ }
+}
+
+@Composable
+private fun SearchFloatingActionButton(
+ isVisible: MutableState,
+ navigateToSearch: () -> Unit
+) {
+ AnimatedVisibility(
+ visible = isVisible.value,
+ enter = slideInVertically(initialOffsetY = { it * 2 }),
+ exit = slideOutVertically(targetOffsetY = { it * 2 }),
+ ) {
+ FloatingActionButton(
+ onClick = navigateToSearch,
+ shape = CircleShape,
+ containerColor = MaterialTheme.colorScheme.secondaryContainer,
+ contentColor = MaterialTheme.colorScheme.onSecondaryContainer
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Search,
+ contentDescription = stringResource(R.string.search)
+ )
+ }
+ }
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun HomeCategoryTopAppBar(navigateToAboutScreen: () -> Unit) {
+ TopAppBar(
+ title = {
+ Text(text = stringResource(R.string.home_screen_title))
+ },
+ colors = TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
+ ),
+ actions = {
+ IconButton(onClick = navigateToAboutScreen) {
+ Icon(
+ imageVector = Icons.Outlined.Info,
+ contentDescription = stringResource(R.string.about_section)
+ )
+ }
+ }
+ )
+}
+
+@Composable
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
+fun PreviewHomeCategoryScreen() {
+ SampleAppTheme {
+ HomeCategoryScreen(
+ onNavigateToCategory = {},
+ onNavigateToSearch = {},
+ onNavigateToAbout = {}
+ )
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/sampleList/SampleListScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/sampleList/SampleListScreen.kt
new file mode 100644
index 000000000..11e793b7d
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/sampleList/SampleListScreen.kt
@@ -0,0 +1,199 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.sampleList
+
+import android.content.Intent
+import android.net.Uri
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.model.DefaultSampleInfoRepository
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.model.SampleCategory
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.SampleRow
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.SampleViewerTopAppBar
+import com.esri.arcgismaps.kotlin.sampleviewer.viewmodels.FavoritesViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.util.Locale
+
+/**
+ * Shows the list of samples.
+ */
+@Composable
+fun SampleListScreen(
+ categoryNavEntry: String,
+ onNavigateToInfo: (Int, Sample) -> Unit,
+ onBackPressed: () -> Unit
+) {
+ val viewModel: FavoritesViewModel = viewModel()
+ val favoriteSamplesFlow = remember { viewModel.getFavorites() }
+ val favoriteSamples by favoriteSamplesFlow.collectAsState(initial = emptyList())
+ val category = SampleCategory.toEnum(categoryNavEntry)
+ val samplesList = DefaultSampleInfoRepository.getSamplesInCategory(category)
+
+ Scaffold(
+ topBar = { SampleViewerTopAppBar(title = category.text, onBackPressed) },
+ modifier = Modifier
+ .fillMaxSize(),
+ ) { innerPadding ->
+ Column(modifier = Modifier.padding(innerPadding)) {
+ if (samplesList.isEmpty() && category != SampleCategory.FAVORITES) {
+ EmptySampleListScreen(stringResource(R.string.upcoming_samples_text))
+ } else if (category == SampleCategory.FAVORITES) {
+ FavoriteItemsListScreen(favoriteSamples, onNavigateToInfo)
+ } else {
+ ListOfSamplesScreen(samplesList, onNavigateToInfo)
+ }
+ }
+ }
+}
+
+@Composable
+fun ListOfSamplesScreen(
+ samples: List,
+ navigateToInfo: (Int, Sample) -> Unit,
+) {
+ val favoritesViewModel: FavoritesViewModel = viewModel()
+ val favoriteSamples by favoritesViewModel.getFavorites().collectAsState(initial = emptyList())
+ val scope = rememberCoroutineScope()
+ val context = LocalContext.current
+ Box(
+ modifier = Modifier
+ .wrapContentSize()
+ .padding(16.dp)
+ .clip(RoundedCornerShape(16.dp))
+ .background(MaterialTheme.colorScheme.surfaceContainer)
+ ) {
+ LazyColumn(modifier = Modifier.animateContentSize()) {
+ itemsIndexed(samples) { index, sample ->
+ val isFavorite = favoriteSamples.contains(sample)
+ val readMePosition = 0
+ val codeFilePosition = 1
+
+ val dropdownSampleItems = listOf(
+ DropdownItemData(
+ title = "README",
+ icon = R.drawable.ic_readme,
+ onClick = { navigateToInfo(readMePosition, sample) }
+ ),
+ DropdownItemData(
+ title = "Code",
+ icon = R.drawable.ic_kotlin,
+ onClick = { navigateToInfo(codeFilePosition, sample) }
+ ),
+ DropdownItemData(
+ title = "Website",
+ icon = R.drawable.ic_link,
+ onClick = {
+ val url = "https://developers.arcgis.com/kotlin/sample-code/" +
+ sample.metadata.title
+ .replace(" ", "-")
+ .lowercase(Locale.getDefault())
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ context.startActivity(intent)
+ }
+ ),
+ DropdownItemData(
+ title = if (isFavorite) "Unfavorite" else "Favorite",
+ icon = if (isFavorite) R.drawable.ic_favorite_selected else R.drawable.ic_favorite_unselected,
+ onClick = {
+ scope.launch(Dispatchers.IO) {
+ if (isFavorite) favoritesViewModel.removeFavorite(sample)
+ else favoritesViewModel.addFavorite(sample)
+ }
+ }
+ )
+ )
+
+ SampleRow(
+ sample = sample,
+ dropdownSampleItems = dropdownSampleItems
+ )
+
+ // Add divider if not the last item
+ if (index < samples.size - 1) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun EmptySampleListScreen(emptyMessage: String) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Text(
+ text = emptyMessage,
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center
+ )
+ }
+}
+
+@Composable
+private fun FavoriteItemsListScreen(
+ favoriteSamples: List,
+ navigateToInfo: (Int, Sample) -> Unit
+) {
+ if (favoriteSamples.isNotEmpty()) {
+ ListOfSamplesScreen(
+ samples = favoriteSamples,
+ navigateToInfo = navigateToInfo
+ )
+ } else {
+ EmptySampleListScreen(stringResource(R.string.no_favorites_text))
+ }
+}
+
+data class DropdownItemData(
+ val title: String,
+ val icon: Int,
+ val onClick: () -> Unit
+)
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchResultsScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchResultsScreen.kt
new file mode 100644
index 000000000..d50be1588
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchResultsScreen.kt
@@ -0,0 +1,56 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.search
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.components.SampleViewerTopAppBar
+import com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.sampleList.ListOfSamplesScreen
+import com.esri.arcgismaps.kotlin.sampleviewer.viewmodels.SampleSearchViewModel
+
+/**
+ * Shows search results based on valid query searches.
+ */
+@Composable
+fun SearchResults(
+ searchQuery: String,
+ navigateToInfo: (Int, Sample) -> Unit,
+ onBackPressed: () -> Unit
+) {
+ val searchViewModel: SampleSearchViewModel = viewModel()
+ searchViewModel.rankedSearch(searchQuery)
+ Scaffold(
+ topBar = { SampleViewerTopAppBar(title = searchQuery, onBackPressed) }) { innerPadding ->
+ Column(
+ modifier = Modifier.padding(innerPadding)
+ ) {
+ // List of samples results ranked with using the searchQuery
+ val rankedSearchResults by searchViewModel.rankedSearchResults.collectAsState()
+ ListOfSamplesScreen(
+ samples = rankedSearchResults,
+ navigateToInfo = navigateToInfo
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchSuggestionsScreen.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchSuggestionsScreen.kt
new file mode 100644
index 000000000..960dccbbb
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/ui/screens/search/SearchSuggestionsScreen.kt
@@ -0,0 +1,320 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.ui.screens.search
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
+import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.BiasAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.platform.LocalWindowInfo
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.esri.arcgismaps.kotlin.sampleviewer.R
+import com.esri.arcgismaps.kotlin.sampleviewer.model.DefaultSampleInfoRepository
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.model.start
+import com.esri.arcgismaps.kotlin.sampleviewer.viewmodels.SampleSearchViewModel
+import kotlinx.coroutines.launch
+
+/**
+ * Allows functionality to search samples to get two types of categories: Samples and Relevant APIs attached to Sample Readmes
+ */
+@Composable
+fun SearchScreen(onNavigateToSearchResults: (String) -> Unit, onBackPressed: () -> Unit) {
+ val context = LocalContext.current
+ val focusManager = LocalFocusManager.current
+ val scope = rememberCoroutineScope()
+ val viewModel: SampleSearchViewModel = viewModel()
+ var searchQuery by remember { mutableStateOf("") }
+ val keyboardController = LocalSoftwareKeyboardController.current
+ // Get the ordered search suggestions of samples or relevantAPIs
+ val searchSuggestions: List> by viewModel.searchSuggestions.collectAsState()
+
+ Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
+ Column(modifier = Modifier.padding(innerPadding)) {
+ SampleViewerSearchBar(
+ searchQuery = searchQuery,
+ onValueChange = {
+ searchQuery = it
+ viewModel.suggestionSearch(searchQuery)
+ },
+ onClear = {
+ searchQuery = ""
+ focusManager.clearFocus()
+ },
+ onExit = {
+ keyboardController?.hide()
+ onBackPressed()
+ },
+ onSearch = {
+ if (searchQuery.trim().isNotEmpty()) {
+ keyboardController?.hide()
+ // Open results screen with list view.
+ onNavigateToSearchResults(searchQuery)
+ }
+
+ }
+ )
+ SearchSuggestionsList(
+ searchQuery = searchQuery,
+ searchSuggestions = searchSuggestions,
+ onSampleSelected = {
+ scope.launch {
+ it.start(context)
+ }
+ },
+ onRelevantAPISelected = onNavigateToSearchResults
+ )
+ }
+ }
+}
+
+@Composable
+fun SearchSuggestionsList(
+ searchQuery: String,
+ searchSuggestions: List>,
+ onSampleSelected: (Sample) -> Unit,
+ onRelevantAPISelected: (String) -> Unit
+
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+
+ AnimatedVisibility(
+ visible = searchQuery.isEmpty(),
+ enter = fadeIn(), exit = fadeOut(),
+ ) {
+ EmptySearchQueryMessage()
+ }
+
+ AnimatedVisibility(
+ visible = searchQuery.isNotEmpty(),
+ enter = fadeIn(), exit = fadeOut()
+ ) {
+ // Show dynamic search results as search query changes
+ LazyColumn(
+ Modifier
+ .fillMaxSize()
+ .animateContentSize()
+ ) {
+ items(
+ items = searchSuggestions,
+ key = { suggestion -> suggestion }
+ ) { suggestion ->
+ if (suggestion.second) { // Sample Suggestion
+ val sample = DefaultSampleInfoRepository.getSampleByName(suggestion.first)
+ Row(
+ modifier = Modifier
+ .animateItem()
+ .clickable { onSampleSelected(sample) }
+ ) {
+ SampleListItem(sample.name)
+ }
+ } else { // RelevantAPI Suggestion
+ val relevantApi = suggestion.first
+ Row(
+ modifier = Modifier
+ .animateItem()
+ .clickable { onRelevantAPISelected(relevantApi) }
+ ) {
+ RelevantAPIListItem(relevantApi)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun SampleViewerSearchBar(
+ searchQuery: String,
+ onValueChange: (String) -> Unit,
+ onExit: () -> Unit,
+ onClear: () -> Unit,
+ onSearch: () -> Unit
+) {
+ val windowInfo = LocalWindowInfo.current
+ val focusRequester = remember { FocusRequester() }
+ // Opens keyboard and focuses on search when the screen comes in focus
+ LaunchedEffect(windowInfo) {
+ snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused ->
+ if (isWindowFocused) {
+ focusRequester.requestFocus()
+ }
+ }
+ }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 24.dp, horizontal = 12.dp)
+ .focusRequester(focusRequester),
+ value = searchQuery,
+ onValueChange = onValueChange,
+ maxLines = 1,
+ singleLine = true,
+ shape = RoundedCornerShape(50.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant,
+ focusedBorderColor = MaterialTheme.colorScheme.secondary,
+ focusedContainerColor = MaterialTheme.colorScheme.background,
+ unfocusedContainerColor = MaterialTheme.colorScheme.background,
+ ),
+ placeholder = {
+ Text(
+ text = "Search...",
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ },
+ leadingIcon = {
+ IconButton(onClick = { onExit() }) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = stringResource(id = R.string.backButton),
+ tint = MaterialTheme.colorScheme.primary
+ )
+ }
+ },
+ trailingIcon = {
+ IconButton(onClick = { onClear() }) {
+ Icon(
+ imageVector = Icons.Default.Clear,
+ contentDescription = stringResource(id = R.string.clearButton),
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ }
+ },
+ keyboardOptions = KeyboardOptions(
+ imeAction = androidx.compose.ui.text.input.ImeAction.Search,
+ keyboardType = KeyboardType.Text
+ ),
+ keyboardActions = KeyboardActions(
+ onSearch = { onSearch() }
+ )
+ )
+}
+
+@Composable
+private fun RelevantAPIListItem(relevantApi: String) {
+ ListItem(
+ leadingContent = {
+ Icon(
+ imageVector = Icons.Default.Search,
+ contentDescription = stringResource(id = R.string.search),
+ modifier = Modifier.size(32.dp)
+ )
+ },
+ headlineContent = {
+ Text(
+ text = relevantApi,
+ fontSize = MaterialTheme.typography.titleSmall.fontSize,
+ )
+ },
+ )
+}
+
+@Composable
+private fun SampleListItem(sampleName: String) {
+ ListItem(
+ leadingContent = {
+ Image(
+ painter = painterResource(id = com.esri.arcgismaps.sample.sampleslib.R.drawable.arcgis_maps_sdks_64),
+ contentDescription = stringResource(id = R.string.sdk_sample_icon_description),
+ modifier = Modifier.size(32.dp)
+ )
+ },
+ headlineContent = {
+ Text(
+ text = sampleName,
+ fontSize = MaterialTheme.typography.titleSmall.fontSize,
+ )
+ },
+ trailingContent = {
+ Icon(
+ Icons.AutoMirrored.Filled.KeyboardArrowRight,
+ contentDescription = stringResource(id = R.string.forward_button),
+ tint = MaterialTheme.colorScheme.primary
+ )
+ }
+ )
+}
+
+@Composable
+private fun EmptySearchQueryMessage() {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = BiasAlignment(0f, -0.5f)
+ ) {
+ Text(
+ text = "Search for sample name, feature or an API",
+ textAlign = TextAlign.Center,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+}
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/FavoritesViewModel.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/FavoritesViewModel.kt
new file mode 100644
index 000000000..bd39cb10f
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/FavoritesViewModel.kt
@@ -0,0 +1,89 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.viewmodels
+
+import android.app.Application
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import androidx.lifecycle.AndroidViewModel
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+
+/**
+ * ViewModel to handle list of favorite samples
+ */
+class FavoritesViewModel(private val application: Application) : AndroidViewModel(application) {
+ private val json = Json { ignoreUnknownKeys = true }
+
+ /**
+ * Retrieve list of favorite Samples using Flow to allow UI to update reactively on change
+ */
+ fun getFavorites(): Flow> = application.applicationContext.dataStore.data
+ .map { preferences ->
+ val jsonString = preferences[FAVORITES_KEY] ?: "[]"
+ // Convert JSON string into list of Sample objects
+ json.decodeFromString(jsonString)
+ }
+
+ /**
+ * Save list of favorite Samples
+ */
+ private suspend fun saveFavorite(favorites: List) {
+ // Convert list of Sample objects into JSON string
+ val jsonString = json.encodeToString(favorites)
+ application.applicationContext.dataStore.edit { preferences ->
+ // Store JSON string to DataStore using FAVORITES KEY
+ preferences[FAVORITES_KEY] = jsonString
+ }
+ }
+
+ /**
+ * Add a new favorite sample to the list of favorite samples
+ */
+ suspend fun addFavorite(favorite: Sample) {
+ val currentFavorites = getFavorites().first()
+ val updatedFavorites = currentFavorites.toMutableList().apply {
+ add(favorite)
+ }
+ saveFavorite(updatedFavorites)
+ }
+
+ /**
+ * Remove an existing favorite sample from the list of favorite samples
+ */
+ suspend fun removeFavorite(favorite: Sample) {
+ val currentFavorites = getFavorites().first()
+ val updatedFavorites = currentFavorites.toMutableList().apply {
+ remove(favorite)
+ }
+ saveFavorite(updatedFavorites)
+ }
+
+ companion object {
+ private const val PREFS_NAME = "favorite_samples_pref"
+ private val FAVORITES_KEY = stringPreferencesKey("favorite_samples_key")
+ private val Context.dataStore: DataStore by preferencesDataStore(name = PREFS_NAME)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/SampleSearchViewModel.kt b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/SampleSearchViewModel.kt
new file mode 100644
index 000000000..08a42cda2
--- /dev/null
+++ b/app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/viewmodels/SampleSearchViewModel.kt
@@ -0,0 +1,123 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.kotlin.sampleviewer.viewmodels
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.esri.arcgismaps.kotlin.sampleviewer.model.DefaultSampleInfoRepository
+import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample
+import com.esri.arcgismaps.kotlin.sampleviewer.model.room.AppDatabase
+import com.esri.arcgismaps.kotlin.sampleviewer.model.room.OkapiBM25
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+
+/**
+ * Viewmodel to handle search logic such as finding relevant API keywords and samples
+ */
+class SampleSearchViewModel(private val application: Application) : AndroidViewModel(application) {
+
+ var rankedSearchResults = MutableStateFlow>(mutableListOf())
+ private set
+
+ var searchSuggestions = MutableStateFlow>>(emptyList())
+ private set
+
+ // Keep track of the scope where DB is queried, so it can be cancelled
+ // on subsequent searchQuery.
+ private var databaseQueryJob: Job? = null
+
+ /**
+ * Used to search through samples and list relevant Samples and API searches
+ */
+ fun suggestionSearch(searchQuery: String) {
+ viewModelScope.launch {
+ val database = AppDatabase.getDatabase(application).sampleDao()
+ // If the dao is querying, cancel and query on a new scope.
+ databaseQueryJob?.cancelAndJoin()
+ databaseQueryJob = launch(Dispatchers.IO) {
+ val filteredSamples = database.getFilteredSamples(searchQuery)
+ val filteredAPIs = database.getFilteredRelevantAPIs(searchQuery).flatMap { sample ->
+ sample.sampleRelevantApi.filter { apiName ->
+ apiName.lowercase().contains(searchQuery.lowercase())
+ }
+ }.distinct()
+ // Combine filtered samples and APIs into pairs
+ searchSuggestions.value = (filteredSamples.map { sample ->
+ // true indicates it's a filtered sample
+ Pair(sample.sampleName, true)
+ } + filteredAPIs.map { apiName ->
+ // false indicates it's a filtered API
+ Pair(apiName, false)
+ }).sortedBy { it.first }
+ }
+ }
+ }
+
+ /**
+ * Performs a ranked search on the given [searchQuery]
+ */
+ fun rankedSearch(searchQuery: String) {
+ viewModelScope.launch(Dispatchers.IO) {
+ val database = AppDatabase.getDatabase(application).sampleDao()
+ val cursor = database.matchInfoSearch(searchQuery)
+
+ if (cursor.moveToFirst()) {
+ val results = mutableListOf()
+ do {
+ // Get a reference to the mapInfo blob.
+ val matchInfo = cursor.getBlob(4).toIntArray()
+ // Get the score for each column.
+ val totalScore = listOf(
+ OkapiBM25.score(matchInfo, 0),
+ OkapiBM25.score(matchInfo, 1),
+ OkapiBM25.score(matchInfo, 2),
+ OkapiBM25.score(matchInfo, 3)
+ ).average()
+ // Add the score to the sample.
+ val sample = DefaultSampleInfoRepository.getSampleByName(
+ sampleName = cursor.getString(0)
+ ).apply {
+ score = totalScore
+ }
+ results.add(sample)
+ } while (!cursor.isClosed && cursor.moveToNext())
+ cursor.close()
+ results.sortByDescending { it.score }
+
+ // update the ranked list with the sorted results
+ if (rankedSearchResults.value != results) {
+ rankedSearchResults.value = results
+ }
+ }
+ }
+ }
+}
+
+fun ByteArray.toIntArray(): Array {
+ val intBuf = ByteBuffer.wrap(this)
+ .order(ByteOrder.LITTLE_ENDIAN)
+ .asIntBuffer()
+ val array = IntArray(intBuf.remaining())
+ intBuf.get(array)
+ return array.toTypedArray()
+}
diff --git a/app/src/main/res/drawable/analysis_background.webp b/app/src/main/res/drawable/analysis_background.webp
new file mode 100644
index 000000000..159f34864
Binary files /dev/null and b/app/src/main/res/drawable/analysis_background.webp differ
diff --git a/app/src/main/res/drawable/augmented_reality_background.webp b/app/src/main/res/drawable/augmented_reality_background.webp
new file mode 100644
index 000000000..ce3e52199
Binary files /dev/null and b/app/src/main/res/drawable/augmented_reality_background.webp differ
diff --git a/app/src/main/res/drawable/cloud_background.webp b/app/src/main/res/drawable/cloud_background.webp
new file mode 100644
index 000000000..3dc089304
Binary files /dev/null and b/app/src/main/res/drawable/cloud_background.webp differ
diff --git a/app/src/main/res/drawable/ic_analysis.xml b/app/src/main/res/drawable/ic_analysis.xml
new file mode 100644
index 000000000..91bd175ef
--- /dev/null
+++ b/app/src/main/res/drawable/ic_analysis.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_augmented_reality.xml b/app/src/main/res/drawable/ic_augmented_reality.xml
new file mode 100644
index 000000000..4b160cc36
--- /dev/null
+++ b/app/src/main/res/drawable/ic_augmented_reality.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_cloud.xml
new file mode 100644
index 000000000..373a42c45
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cloud.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_favorite_selected.xml b/app/src/main/res/drawable/ic_favorite_selected.xml
new file mode 100644
index 000000000..d02332b5f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite_selected.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_favorite_unselected.xml b/app/src/main/res/drawable/ic_favorite_unselected.xml
new file mode 100644
index 000000000..452e488e0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_favorite_unselected.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_kotlin.xml b/app/src/main/res/drawable/ic_kotlin.xml
new file mode 100644
index 000000000..5d4df83c9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_kotlin.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_layers.xml b/app/src/main/res/drawable/ic_layers.xml
new file mode 100644
index 000000000..fe5382108
--- /dev/null
+++ b/app/src/main/res/drawable/ic_layers.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_link.xml b/app/src/main/res/drawable/ic_link.xml
new file mode 100644
index 000000000..1146d4cb8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_link.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_manage_data.xml b/app/src/main/res/drawable/ic_manage_data.xml
new file mode 100644
index 000000000..fd51e2115
--- /dev/null
+++ b/app/src/main/res/drawable/ic_manage_data.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_map.xml b/app/src/main/res/drawable/ic_map.xml
new file mode 100644
index 000000000..2eb37f32d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_map.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_readme.xml b/app/src/main/res/drawable/ic_readme.xml
new file mode 100644
index 000000000..302bb4af1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_readme.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_routing_and_logistics.xml b/app/src/main/res/drawable/ic_routing_and_logistics.xml
new file mode 100644
index 000000000..d0a241b34
--- /dev/null
+++ b/app/src/main/res/drawable/ic_routing_and_logistics.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_scenes.xml b/app/src/main/res/drawable/ic_scenes.xml
new file mode 100644
index 000000000..8c671d288
--- /dev/null
+++ b/app/src/main/res/drawable/ic_scenes.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_search_and_query.xml b/app/src/main/res/drawable/ic_search_and_query.xml
new file mode 100644
index 000000000..442a45e28
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search_and_query.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_utility.xml b/app/src/main/res/drawable/ic_utility.xml
new file mode 100644
index 000000000..1ac2e85f2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_utility.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_visualization.xml b/app/src/main/res/drawable/ic_visualization.xml
new file mode 100644
index 000000000..5837d8fdf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_visualization.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/keyboard_arrow_right.xml b/app/src/main/res/drawable/keyboard_arrow_right.xml
new file mode 100644
index 000000000..4904368f3
--- /dev/null
+++ b/app/src/main/res/drawable/keyboard_arrow_right.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/layers_background.webp b/app/src/main/res/drawable/layers_background.webp
new file mode 100644
index 000000000..46f858535
Binary files /dev/null and b/app/src/main/res/drawable/layers_background.webp differ
diff --git a/app/src/main/res/drawable/manage_data_background.webp b/app/src/main/res/drawable/manage_data_background.webp
new file mode 100644
index 000000000..bbae2d21f
Binary files /dev/null and b/app/src/main/res/drawable/manage_data_background.webp differ
diff --git a/app/src/main/res/drawable/maps_and_scenes_background.webp b/app/src/main/res/drawable/maps_and_scenes_background.webp
new file mode 100644
index 000000000..02585a0f8
Binary files /dev/null and b/app/src/main/res/drawable/maps_and_scenes_background.webp differ
diff --git a/app/src/main/res/drawable/routing_and_logistics_background.webp b/app/src/main/res/drawable/routing_and_logistics_background.webp
new file mode 100644
index 000000000..f456374f7
Binary files /dev/null and b/app/src/main/res/drawable/routing_and_logistics_background.webp differ
diff --git a/app/src/main/res/drawable/scenes_background.webp b/app/src/main/res/drawable/scenes_background.webp
new file mode 100644
index 000000000..8b4c62229
Binary files /dev/null and b/app/src/main/res/drawable/scenes_background.webp differ
diff --git a/app/src/main/res/drawable/search_and_query_background.webp b/app/src/main/res/drawable/search_and_query_background.webp
new file mode 100644
index 000000000..228cafdd1
Binary files /dev/null and b/app/src/main/res/drawable/search_and_query_background.webp differ
diff --git a/app/src/main/res/drawable/utility_background.webp b/app/src/main/res/drawable/utility_background.webp
new file mode 100644
index 000000000..4bc9f74a9
Binary files /dev/null and b/app/src/main/res/drawable/utility_background.webp differ
diff --git a/app/src/main/res/drawable/visualization_background.webp b/app/src/main/res/drawable/visualization_background.webp
new file mode 100644
index 000000000..8a565f271
Binary files /dev/null and b/app/src/main/res/drawable/visualization_background.webp differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1d6dc5d2d
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,26 @@
+
+ ArcGIS Maps Kotlin Samples
+ Search
+ About
+ App Version
+ SDK Version
+ Copyright © 2015–2024 Esri.\nAll Rights Reserved.
+ ArcGIS Maps SDK Sample Viewer
+ Powered By
+ ArcGIS Maps SDK Toolkit for Kotlin
+ ArcGIS Maps SDK for Kotlin
+ Forward button
+ Browse and discuss in the Esri Community
+ Esri Community
+ Log an issue in the Github repository
+ Github Repository
+ View details about the API
+ API Reference
+ Samples in this category will be added in a future release
+ No favorite samples added yet!
+ Back button
+ Clear button
+ Cancel
+ SDK sample icon
+ Sample Categories
+
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apply-dictionary-renderer-to-feature-layer/build.gradle.kts b/apply-dictionary-renderer-to-feature-layer/build.gradle.kts
deleted file mode 100644
index a37785d83..000000000
--- a/apply-dictionary-renderer-to-feature-layer/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/apply-dictionary-renderer-to-feature-layer/proguard-rules.pro b/apply-dictionary-renderer-to-feature-layer/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/apply-dictionary-renderer-to-feature-layer/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml b/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml
deleted file mode 100644
index 264aa518e..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt b/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt
deleted file mode 100644
index cbc6b58ec..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // A stylx file that incorporates the MIL-STD-2525D symbol dictionary
- "https://www.arcgis.com/home/item.html?id=c78b149a1d52414682c86a5feeb13d30",
- // A mobile geodatabase created from the ArcGIS for Defense Military Overlay template
- "https://www.arcgis.com/home/item.html?id=e0d41b4b409a49a5a7ba11939d8535dc"
- )
- )
- }
-}
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt b/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt
deleted file mode 100644
index 6c0b8d2d4..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.data.Geodatabase
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.symbology.DictionaryRenderer
-import com.arcgismaps.mapping.symbology.DictionarySymbolStyle
-import com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- mapView.map = map
-
- // locate the .stylx file in the device
- val styleFile = File(provisionPath, getString(R.string.mil2525d_stylx))
- // instantiate the dictionarySymbolStyle using the file path
- val dictionarySymbolStyle = DictionarySymbolStyle.createFromFile(styleFile.absolutePath)
-
- // locate the .geodatabase file in the device
- val geodatabaseFile = File(provisionPath, getString(R.string.militaryoverlay_geodatabase))
- // instantiate the geodatabase with the file path
- val geodatabase = Geodatabase(geodatabaseFile.path)
-
- lifecycleScope.launch {
- // load the dictionary symbol style
- dictionarySymbolStyle.load().getOrElse {
- return@launch showError("Error loading DictionarySymbolStyle: ${it.message}")
- }
-
- // load the geodatabase
- geodatabase.load().getOrElse {
- showError("Error loading Geodatabase: ${it.message}")
- }
-
- geodatabase.featureTables.forEach { geodatabaseFeatureTable ->
- // load each geodatabaseFeatureTable and create featureLayer from it
- geodatabaseFeatureTable.load().getOrElse {
- return@launch showError("Error loading GeodatabaseFeatureTable: ${it.message}")
- }
- val featureLayer = FeatureLayer.createWithFeatureTable(geodatabaseFeatureTable)
- featureLayer.load().getOrElse {
- return@launch showError("Error loading FeatureLayer: ${it.message}")
- }
- // add featureLayer to the map's operational layer
- mapView.map?.operationalLayers?.add(featureLayer)
-
- // create dictionaryRenderer using the dictionarySymbolStyle and apply it to the featureLayer's renderer
- val dictionaryRenderer = DictionaryRenderer(dictionarySymbolStyle)
- featureLayer.renderer = dictionaryRenderer
- // get the featureLayer's envelope to set the map viewpoint
- val extent = featureLayer.fullExtent
- ?: return@launch showError("Error retrieving extent of the feature layer")
- mapView.setViewpoint(Viewpoint(extent))
- }
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml b/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable/ic_launcher_background.xml b/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/apply-dictionary-renderer-to-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml b/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml
deleted file mode 100644
index 6458b63ef..000000000
--- a/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Apply dictionary renderer to feature layer
- /militaryoverlay.geodatabase
- /mil2525d.stylx
-
diff --git a/apply-function-to-raster-from-service/.gitignore b/apply-function-to-raster-from-service/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/apply-function-to-raster-from-service/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/apply-function-to-raster-from-service/build.gradle.kts b/apply-function-to-raster-from-service/build.gradle.kts
deleted file mode 100644
index 9d2c890ce..000000000
--- a/apply-function-to-raster-from-service/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.applyfunctiontorasterfromservice"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.applyfunctiontorasterfromservice"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/apply-function-to-raster-from-service/proguard-rules.pro b/apply-function-to-raster-from-service/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/apply-function-to-raster-from-service/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/apply-function-to-raster-from-service/src/main/AndroidManifest.xml b/apply-function-to-raster-from-service/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/apply-function-to-raster-from-service/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt b/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt
deleted file mode 100644
index 56bc6c5ce..000000000
--- a/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.applyfunctiontorasterfromservice
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.LoadStatus
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.layers.RasterLayer
-import com.arcgismaps.raster.ImageServiceRaster
-import com.arcgismaps.raster.Raster
-import com.arcgismaps.raster.RasterFunction
-import com.esri.arcgismaps.sample.applyfunctiontorasterfromservice.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val imageServiceRaster: ImageServiceRaster by lazy {
- ImageServiceRaster(getString(R.string.image_service_raster_url))
- }
-
- private val imageRasterLayer: RasterLayer by lazy {
- RasterLayer(imageServiceRaster)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a dark gray basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISDarkGray)
- mapView.map = map
-
- // add the imageRasterLayer to the map
- addImageRasterLayer()
-
- activityMainBinding.apply {
- rasterButton.setOnClickListener {
- // update the raster with simplified hillshade
- applyRasterFunction()
- resetButton.isEnabled = true
- rasterButton.isEnabled = false
- }
- resetButton.setOnClickListener {
- // reset map to back to the RasterLayer
- addImageRasterLayer()
- resetButton.isEnabled = false
- rasterButton.isEnabled = true
- }
- }
- }
-
- /**
- * Adds the image raster layer to the map and set's the viewpoint
- * to the image server raster's bounding geometry
- */
- private fun addImageRasterLayer() {
- // clear and add the imageRasterLayer to the map
- mapView.map?.operationalLayers?.apply {
- clear()
- add(imageRasterLayer)
- }
-
- // collect the load status of the RasterLayer
- lifecycleScope.launch {
- imageRasterLayer.loadStatus.collect { loadStatus ->
- if (loadStatus == LoadStatus.Loaded) {
- // get the center point of the image service raster
- val extentEnvelope = imageServiceRaster.serviceInfo?.fullExtent
- ?: return@collect showError("Error retrieving the ArcGISImageServiceInfo")
- // set the viewpoint of the map to the envelope
- mapView.setViewpointGeometry(extentEnvelope)
- } else if (loadStatus is LoadStatus.FailedToLoad) {
- showError("Error loading image raster layer: ${loadStatus.error.message}")
- }
- }
- }
- }
-
- /**
- * Create a hillshade layer using a custom JSON raster function.
- */
- private fun applyRasterFunction() {
- // create raster function from json string
- val rasterFunction = RasterFunction.fromJsonOrNull(getString(R.string.hillshade_simplified))
- ?: return showError("Error creating a raster function object from JSON")
-
- // get parameter name value pairs used by hillside
- val rasterFunctionArguments = rasterFunction.arguments
- ?: return showError("Raster function arguments is null")
-
- // get a list of raster names associated with the raster function
- val rasterNames = rasterFunctionArguments.rasterNames
- // check if raster function arguments contains raster variable names
- if(rasterNames.isNotEmpty()){
- // using the first raster variable name
- rasterFunctionArguments.setRaster(rasterNames[0], imageServiceRaster)
- // create raster as raster layer
- val hillshadeRaster = Raster.createWithRasterFunction(rasterFunction)
- val hillshadeLayer = RasterLayer(hillshadeRaster)
- // clear and add the layer to the map
- mapView.map?.operationalLayers?.add(hillshadeLayer)
- } else{
- showError("Raster function arguments does not contain raster variable names")
- }
-
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/apply-function-to-raster-from-service/src/main/res/drawable-v24/ic_launcher_foreground.xml b/apply-function-to-raster-from-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/apply-function-to-raster-from-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-function-to-raster-from-service/src/main/res/drawable/ic_launcher_background.xml b/apply-function-to-raster-from-service/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/apply-function-to-raster-from-service/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/apply-function-to-raster-from-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher.png b/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher.png b/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher_round.png b/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/apply-function-to-raster-from-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/apply-function-to-raster-from-service/src/main/res/values/strings.xml b/apply-function-to-raster-from-service/src/main/res/values/strings.xml
deleted file mode 100644
index 772070487..000000000
--- a/apply-function-to-raster-from-service/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
- Apply function to raster from service
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/NLCDLandCover2001/ImageServer
-
- {
- \"raster_function_arguments\":
- {
- \"z_factor\":{\"double\":25.0,\"type\":\"Raster_function_variable\"},
- \"slope_type\":{\"raster_slope_type\":\"none\",\"type\":\"Raster_function_variable\"},
- \"azimuth\":{\"double\":315,\"type\":\"Raster_function_variable\"},
- \"altitude\":{\"double\":45,\"type\":\"Raster_function_variable\"},
- \"type\":\"Raster_function_arguments\",
- \"raster\":{\"name\":\"raster\",\"is_raster\":true,\"type\":\"Raster_function_variable\"},
- \"nbits\":{\"int\":8,\"type\":\"Raster_function_variable\"}
- },
- \"raster_function\":{\"type\":\"Hillshade_function\"},
- \"type\":\"Raster_function_template\"
- }
-
- Apply Layer
- Reset
-
diff --git a/authenticate-with-oauth/.gitignore b/authenticate-with-oauth/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/authenticate-with-oauth/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/authenticate-with-oauth/build.gradle.kts b/authenticate-with-oauth/build.gradle.kts
deleted file mode 100644
index bf36a62a4..000000000
--- a/authenticate-with-oauth/build.gradle.kts
+++ /dev/null
@@ -1,55 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.authenticatewithoauth"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.authenticatewithoauth"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
- implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
-}
diff --git a/authenticate-with-oauth/proguard-rules.pro b/authenticate-with-oauth/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/authenticate-with-oauth/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/authenticate-with-oauth/src/main/AndroidManifest.xml b/authenticate-with-oauth/src/main/AndroidManifest.xml
deleted file mode 100644
index 83e5566cb..000000000
--- a/authenticate-with-oauth/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt b/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt
deleted file mode 100644
index 784fceabb..000000000
--- a/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.authenticatewithoauth
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import androidx.lifecycle.viewmodel.compose.viewModel
-import com.arcgismaps.toolkit.authentication.DialogAuthenticator
-import com.esri.arcgismaps.sample.authenticatewithoauth.components.MapViewModel
-import com.esri.arcgismaps.sample.authenticatewithoauth.screens.MainScreen
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContent {
- SampleAppTheme {
- AuthenticateWithOAuthApp()
- }
- }
- }
-
- @Composable
- private fun AuthenticateWithOAuthApp() {
-
- // create a ViewModel to handle interactions
- val mapViewModel: MapViewModel = viewModel()
-
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- // Displays appropriate Authentication UI when an authentication challenge is issued.
- // Because the authenticatorState has an oAuthUserConfiguration set, authentication
- // challenges will happen via OAuth.
- // Call the DialogAuthenticator composable function at the top level of your view
- // hierarchy, for example at the same level as MainScreen(). This ensures that
- // authentication handling is set up before any components of the ArcGIS Maps SDK that
- // may require authentication are used.
- DialogAuthenticator(authenticatorState = mapViewModel.authenticatorState)
- }
- }
-}
diff --git a/authenticate-with-oauth/src/main/res/drawable-v24/ic_launcher_foreground.xml b/authenticate-with-oauth/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/authenticate-with-oauth/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/authenticate-with-oauth/src/main/res/drawable/ic_launcher_background.xml b/authenticate-with-oauth/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/authenticate-with-oauth/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/authenticate-with-oauth/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher.png b/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher_round.png b/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher.png b/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher_round.png b/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher.png b/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher.png b/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/authenticate-with-oauth/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/authenticate-with-oauth/src/main/res/values/strings.xml b/authenticate-with-oauth/src/main/res/values/strings.xml
deleted file mode 100644
index cc50e7c7d..000000000
--- a/authenticate-with-oauth/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Authenticate with OAuth
-
diff --git a/browse-building-floors/.gitignore b/browse-building-floors/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/browse-building-floors/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/browse-building-floors/build.gradle.kts b/browse-building-floors/build.gradle.kts
deleted file mode 100644
index 970e24e42..000000000
--- a/browse-building-floors/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.browsebuildingfloors"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.browsebuildingfloors"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/browse-building-floors/proguard-rules.pro b/browse-building-floors/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/browse-building-floors/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/browse-building-floors/src/main/AndroidManifest.xml b/browse-building-floors/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/browse-building-floors/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt b/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt
deleted file mode 100644
index 66f296731..000000000
--- a/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt
+++ /dev/null
@@ -1,186 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.browsebuildingfloors
-
-import android.content.Context
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.TextView
-import androidx.annotation.LayoutRes
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.PortalItem
-import com.arcgismaps.mapping.floor.FloorLevel
-import com.arcgismaps.mapping.floor.FloorManager
-import com.arcgismaps.portal.Portal
-import com.esri.arcgismaps.sample.browsebuildingfloors.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val currentFloorTV by lazy {
- activityMainBinding.selectedFloorTV
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // load the portal and create a map from the portal item
- val portalItem = PortalItem(
- Portal("https://www.arcgis.com/"),
- "f133a698536f44c8884ad81f80b6cfc7"
- )
-
- // set the map to be displayed in the layout's MapView
- val map = ArcGISMap(portalItem)
- mapView.map = map
-
- lifecycleScope.launch {
- //load the portal item on the map
- map.load().getOrElse {
- showError("Error loading map" + it.message.toString())
- return@launch
- }
-
- // load the web map's floor manager
- val floorManager =
- map.floorManager ?: return@launch showError("Map is not floor-aware")
- floorManager.load().getOrElse {
- showError("Error loading floor manager" + it.message.toString())
- return@launch
- }
-
- // set up dropdown and initial floor level to ground floor
- initializeFloorDropdown(floorManager)
- }
- }
-
- /**
- * Set and update the floor dropdown. Shows the currently selected floor
- * and hides the other floors using [floorManager].
- */
- private fun initializeFloorDropdown(floorManager: FloorManager) {
- // enable the dropdown view
- activityMainBinding.dropdownMenu.isEnabled = true
-
- // Select the ground floor using `verticalOrder`.
- // The floor at index 0 might not have a vertical order of 0 if,
- // for example, the building starts with basements.
- // To select the ground floor, we can search for a level with a
- // `verticalOrder` of 0. You can also use level ID, number or name
- // to locate a floor.
- val firstFloorIndex = floorManager.levels.indexOf(
- floorManager.levels.first { it.verticalOrder == 0 }
- )
-
- currentFloorTV.apply {
- // set the displayed floor to the first floor
- setSelection(firstFloorIndex)
-
- // set the name of the first floor
- setText(floorManager.levels[firstFloorIndex].longName)
-
- // set the dropdown adapter for the floor selection
- setAdapter(
- FloorsAdapter(
- this@MainActivity,
- android.R.layout.simple_list_item_1,
- floorManager.levels
- )
- )
-
- // handle on dropdown item selected
- onItemClickListener =
- AdapterView.OnItemClickListener { _, _, position, _ ->
- // set all the floors to invisible to reset the floorManager
- floorManager.levels.forEach { floorLevel ->
- floorLevel.isVisible = false
- }
-
- // set the currently selected floor to be visible
- floorManager.levels[position].isVisible = true
-
- // set the floor name
- currentFloorTV.setText(floorManager.levels[position].longName)
- }
- }
- }
-
- /**
- * Adapter to display a list [floorLevels]
- */
- private class FloorsAdapter(
- context: Context,
- @LayoutRes private val layoutResourceId: Int,
- private val floorLevels: List
- ) : ArrayAdapter(context, layoutResourceId, floorLevels) {
-
- private val mLayoutInflater: LayoutInflater =
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
-
- override fun getCount(): Int {
- return floorLevels.size
- }
-
- override fun getItem(position: Int): FloorLevel {
- return floorLevels[position]
- }
-
- override fun getItemId(position: Int): Long {
- return position.toLong()
- }
-
- override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- // bind the view to the layout inflater
- val view = convertView ?: mLayoutInflater.inflate(layoutResourceId, parent, false)
- val dropdownItemTV = view.findViewById(android.R.id.text1)
-
- // bind the long name of the floor to it's respective text view
- dropdownItemTV.text = floorLevels[position].longName
- return view
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/browse-building-floors/src/main/res/drawable-v24/ic_launcher_foreground.xml b/browse-building-floors/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/browse-building-floors/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/browse-building-floors/src/main/res/drawable/ic_launcher_background.xml b/browse-building-floors/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/browse-building-floors/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/browse-building-floors/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher.png b/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher_round.png b/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher.png b/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher_round.png b/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher.png b/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher.png b/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/browse-building-floors/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/browse-building-floors/src/main/res/values/strings.xml b/browse-building-floors/src/main/res/values/strings.xml
deleted file mode 100644
index 70d0c6f2f..000000000
--- a/browse-building-floors/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Browse building floors
- Select the floor to display
-
diff --git a/build-logic/.gitignore b/build-logic/.gitignore
new file mode 100644
index 000000000..00fd4dddb
--- /dev/null
+++ b/build-logic/.gitignore
@@ -0,0 +1,2 @@
+/build
+/.gradle
\ No newline at end of file
diff --git a/add-3d-tiles-layer/.gitignore b/build-logic/convention/.gitignore
similarity index 100%
rename from add-3d-tiles-layer/.gitignore
rename to build-logic/convention/.gitignore
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
new file mode 100644
index 000000000..5934b0c57
--- /dev/null
+++ b/build-logic/convention/build.gradle.kts
@@ -0,0 +1,57 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ `kotlin-dsl`
+}
+
+group = "com.esri.arcgismaps.kotlin.build_logic.convention"
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+tasks.withType().configureEach {
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17.toString()
+ }
+}
+
+dependencies {
+ compileOnly(libs.android.gradlePlugin)
+ compileOnly(libs.android.tools.common)
+ compileOnly(libs.kotlin.gradlePlugin)
+ compileOnly(libs.ksp.gradlePlugin)
+}
+
+tasks {
+ validatePlugins {
+ enableStricterValidation = true
+ failOnWarning = true
+ }
+}
+
+gradlePlugin {
+ plugins {
+ register("androidApplicationComposing") {
+ id = "arcgismaps.android.application.compose"
+ implementationClass = "AndroidApplicationComposeConventionPlugin"
+ }
+ register("androidApplication") {
+ id = "arcgismaps.android.application"
+ implementationClass = "AndroidApplicationConventionPlugin"
+ }
+ register("androidLibraryCompose") {
+ id = "arcgismaps.android.library.compose"
+ implementationClass = "AndroidLibraryComposeConventionPlugin"
+ }
+ register("androidLibrary") {
+ id = "arcgismaps.android.library"
+ implementationClass = "AndroidLibraryConventionPlugin"
+ }
+ register("arcgismapsKotlinSample") {
+ id = "arcgismaps.kotlin.sample"
+ implementationClass = "ArcGISMapsKotlinSampleConventionPlugin"
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/AndroidManifest.xml b/build-logic/convention/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a5918e68a
--- /dev/null
+++ b/build-logic/convention/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt
new file mode 100644
index 000000000..06dc52b38
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidApplicationComposeConventionPlugin.kt
@@ -0,0 +1,19 @@
+import com.android.build.api.dsl.ApplicationExtension
+import com.esri.arcgismaps.kotlin.build_logic.convention.configureAndroidCompose
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+
+class AndroidApplicationComposeConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.application")
+ apply("org.jetbrains.kotlin.android")
+ apply("org.jetbrains.kotlin.plugin.compose")
+ }
+ val extension = extensions.getByType()
+ configureAndroidCompose(extension)
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt
new file mode 100644
index 000000000..67049682e
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidApplicationConventionPlugin.kt
@@ -0,0 +1,53 @@
+import com.android.build.api.dsl.ApplicationExtension
+import com.esri.arcgismaps.kotlin.build_logic.convention.configureKotlinAndroid
+import com.esri.arcgismaps.kotlin.build_logic.convention.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.get
+
+class AndroidApplicationConventionPlugin: Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.application")
+ apply("org.jetbrains.kotlin.android")
+ }
+
+ extensions.configure {
+ configureKotlinAndroid(this)
+ compileSdk = 35
+ defaultConfig {
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary = true
+ }
+ minSdk = libs.findVersion("minSdk").get().toString().toInt()
+ targetSdk = libs.findVersion("targetSdk").get().toString().toInt()
+ versionCode = libs.findVersion("versionCode").get().toString().toInt()
+ versionName = libs.findVersion("versionName").get().toString()
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+
+ // Add the custom assets directory to the app module's assets build.
+ sourceSets["main"].assets.srcDirs(layout.buildDirectory.dir("sampleAssets/"))
+ }
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt
new file mode 100644
index 000000000..c20d05e75
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt
@@ -0,0 +1,19 @@
+import com.android.build.api.dsl.LibraryExtension
+import com.esri.arcgismaps.kotlin.build_logic.convention.configureAndroidCompose
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
+
+class AndroidLibraryComposeConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.library")
+ apply("org.jetbrains.kotlin.android")
+ apply("org.jetbrains.kotlin.plugin.compose")
+ }
+ val extension = extensions.getByType()
+ configureAndroidCompose(extension)
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt
new file mode 100644
index 000000000..53d5fc774
--- /dev/null
+++ b/build-logic/convention/src/main/java/AndroidLibraryConventionPlugin.kt
@@ -0,0 +1,58 @@
+import com.android.build.gradle.LibraryExtension
+import com.esri.arcgismaps.kotlin.build_logic.convention.configureKotlinAndroid
+import com.esri.arcgismaps.kotlin.build_logic.convention.implementation
+import com.esri.arcgismaps.kotlin.build_logic.convention.libs
+import com.esri.arcgismaps.kotlin.build_logic.convention.testImplementation
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.kotlin
+
+class AndroidLibraryConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ with(pluginManager) {
+ apply("com.android.library")
+ apply("org.jetbrains.kotlin.android")
+ }
+
+ extensions.configure {
+ configureKotlinAndroid(this)
+ compileSdk = 35
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary = true
+ }
+ minSdk = libs.findVersion("minSdk").get().toString().toInt()
+ lint.targetSdk = libs.findVersion("targetSdk").get().toString().toInt()
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+ }
+
+ dependencies {
+ testImplementation(kotlin("test"))
+ // External libraries
+ implementation(libs.findLibrary("androidx-constraintlayout").get())
+ implementation(libs.findLibrary("androidx-appcompat").get())
+ implementation(libs.findLibrary("android-material").get())
+ }
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/ArcGISMapsKotlinSampleConventionPlugin.kt b/build-logic/convention/src/main/java/ArcGISMapsKotlinSampleConventionPlugin.kt
new file mode 100644
index 000000000..1307ca462
--- /dev/null
+++ b/build-logic/convention/src/main/java/ArcGISMapsKotlinSampleConventionPlugin.kt
@@ -0,0 +1,23 @@
+import com.esri.arcgismaps.kotlin.build_logic.convention.implementation
+import com.esri.arcgismaps.kotlin.build_logic.convention.libs
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.project
+
+class ArcGISMapsKotlinSampleConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ dependencies {
+ // ArcGIS Maps SDK for Kotlin
+ implementation(libs.findLibrary("arcgis-maps-kotlin").get())
+ // Get the Toolkit BOM
+ implementation(platform(libs.findLibrary("arcgis-maps-kotlin-toolkit-bom").get()))
+ // ArcGIS Maps SDK Toolkit GeoView Compose
+ implementation(libs.findLibrary("arcgis-maps-kotlin-toolkit-geoview-compose").get())
+ // Local project common samples library
+ implementation(project(":samples-lib"))
+ }
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt
new file mode 100644
index 000000000..feef379ec
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/AndroidCompose.kt
@@ -0,0 +1,37 @@
+package com.esri.arcgismaps.kotlin.build_logic.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+
+/**
+ * Extension to use compose configurations and dependencies
+ */
+internal fun Project.configureAndroidCompose(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ buildFeatures {
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = libs.findVersion("kotlinCompilerExt").get().toString()
+ }
+
+ dependencies {
+ val composeBom = libs.findLibrary("androidx-compose-bom").get()
+ implementation(platform(composeBom))
+ androidTestImplementation(platform(composeBom))
+ implementation(libs.findLibrary("androidx-activity-compose").get())
+ implementation(libs.findLibrary("androidx-compose-material3").get())
+ implementation(libs.findLibrary("androidx-lifecycle-viewmodel-compose").get())
+ implementation(libs.findLibrary("androidx-compose-ui-tooling-preview").get())
+ debugImplementation(libs.findLibrary("androidx-compose-ui-tooling").get())
+ debugImplementation(libs.findLibrary("androidx-compose-ui-test-manifest").get())
+ androidTestImplementation(libs.findLibrary("androidx-compose-ui-test").get())
+ androidTestImplementation(libs.findLibrary("androidx-compose-ui-test-junit4").get())
+
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/KotlinAndroid.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/KotlinAndroid.kt
new file mode 100644
index 000000000..1e6d69482
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/KotlinAndroid.kt
@@ -0,0 +1,56 @@
+package com.esri.arcgismaps.kotlin.build_logic.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/**
+ * Extension to use Android Kotlin configurations and dependencies
+ */
+internal fun Project.configureKotlinAndroid(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ compileSdk = libs.findVersion("targetSdk").get().toString().toInt()
+
+ defaultConfig {
+ buildConfigField("String", "ACCESS_TOKEN", project.properties["ACCESS_TOKEN"].toString())
+ minSdk = libs.findVersion("minSdk").get().toString().toInt()
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ }
+
+ configureKotlin()
+}
+
+/**
+ * Configure base Kotlin options
+ */
+private fun Project.configureKotlin() {
+ tasks.withType().configureEach {
+ compilerOptions {
+ jvmTarget.set(JvmTarget.JVM_17)
+ // Treat all Kotlin warnings as errors (disabled by default)
+ // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
+ val warningsAsErrors: String? by project
+ allWarningsAsErrors.set(warningsAsErrors.toBoolean())
+ freeCompilerArgs.set(
+ listOf(
+ "-opt-in=kotlin.RequiresOptIn",
+ // Enable experimental coroutines APIs, including Flow
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ "-opt-in=kotlinx.coroutines.FlowPreview",
+ "-Xcontext-receivers",
+ )
+ )
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/ProjectExtensions.kt b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/ProjectExtensions.kt
new file mode 100644
index 000000000..26af71d39
--- /dev/null
+++ b/build-logic/convention/src/main/java/com/esri/arcgismaps/kotlin/build_logic/convention/ProjectExtensions.kt
@@ -0,0 +1,28 @@
+package com.esri.arcgismaps.kotlin.build_logic.convention
+
+import org.gradle.api.Project
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.api.artifacts.dsl.DependencyHandler
+import org.gradle.kotlin.dsl.getByType
+
+fun DependencyHandler.testImplementation(dependencyNotation: Any): Dependency? =
+ add("testImplementation", dependencyNotation)
+
+fun DependencyHandler.testRuntimeOnly(dependencyNotation: Any): Dependency? =
+ add("testRuntimeOnly", dependencyNotation)
+
+fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? =
+ add("implementation", dependencyNotation)
+
+fun DependencyHandler.androidTestImplementation(dependencyNotation: Any): Dependency? =
+ add("androidTestImplementation", dependencyNotation)
+
+fun DependencyHandler.debugImplementation(dependencyNotation: Any): Dependency? =
+ add("debugImplementation", dependencyNotation)
+
+val Project.libs
+ get(): VersionCatalog = extensions
+ .getByType()
+ .named("libs")
diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties
new file mode 100644
index 000000000..6977b7191
--- /dev/null
+++ b/build-logic/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.parallel=true
+org.gradle.caching=true
+org.gradle.configureondemand=true
\ No newline at end of file
diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts
new file mode 100644
index 000000000..2907fbfb8
--- /dev/null
+++ b/build-logic/settings.gradle.kts
@@ -0,0 +1,14 @@
+dependencyResolutionManagement {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ versionCatalogs {
+ create("libs") {
+ from(files("../gradle/libs.versions.toml"))
+ }
+ }
+}
+
+rootProject.name = "build-logic"
+include(":convention")
diff --git a/build.gradle.kts b/build.gradle.kts
index dabae1534..7f92fb744 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,61 +1,27 @@
-import com.android.build.gradle.BaseExtension
-import java.net.URL
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath("com.android.tools.build:gradle:${libs.versions.gradleVersion.get()}")
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${libs.versions.kotlinVersion.get()}")
- }
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.jetbrains.kotlin.android) apply false
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.compose.compiler) apply false
+ alias(libs.plugins.gradle.secrets) apply false
+ alias(libs.plugins.ksp) apply false
+ alias(libs.plugins.kotlin.serialization) apply false
}
-subprojects {
- afterEvaluate {
- if (hasProperty("dependencies")) {
- dependencies {
- val implementation by configurations
- implementation(libs.androidx.appcompat)
- implementation(libs.stdlib.jdk8)
- implementation(libs.arcgis.maps.kotlin)
- implementation(libs.androidx.multidex)
- }
- }
-
- extensions.findByType(BaseExtension::class)?.let { android ->
- android.compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
- }
- android.defaultConfig {
- multiDexEnabled = true
- }
- android.packagingOptions {
- resources {
- excludes.add("META-INF/DEPENDENCIES")
- }
- }
- }
-
- tasks.withType {
- kotlinOptions {
- jvmTarget = "17"
- }
+buildscript {
+ // if a "build" property is set from the command line like: "-D build=100.X.X-XXXX"
+ if (System.getProperty("build") != null) {
+ // override versions in libs.versions.toml file
+ rootProject.extra.apply {
+ set("arcgisMapsKotlinVersion", System.getProperty("build"))
+ set("arcgisToolkitVersion", System.getProperty("build"))
+ set("versionName", System.getProperty("build"))
}
-
- }
-}
-
-tasks.register("apiKey") {
- doLast {
- val apiKeyFile = File("${System.getProperty("user.home")}/.gradle/gradle.properties")
- if (!apiKeyFile.exists()) {
- print("Go to the ${URL("https://links.esri.com/create-an-api-key")} to obtain a new API key access token. Ensure the following privileges are enabled: Basemaps, Geocoding, and Routing.")
- print("Add your API Key to ${System.getProperty("user.home")}\\.gradle\\gradle.properties.")
- val apiKeyFileContents = "API_KEY = "
- apiKeyFile.writeText(apiKeyFileContents)
+ } else {
+ // use versions in libs.versions.toml file
+ rootProject.extra.apply {
+ set("arcgisMapsKotlinVersion", libs.versions.arcgisMapsKotlinVersion.get())
}
}
-}
+}
\ No newline at end of file
diff --git a/change-camera-controller/.gitignore b/change-camera-controller/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/change-camera-controller/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/change-camera-controller/build.gradle.kts b/change-camera-controller/build.gradle.kts
deleted file mode 100644
index 3f1d4ea30..000000000
--- a/change-camera-controller/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.changecameracontroller"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.changecameracontroller"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/change-camera-controller/proguard-rules.pro b/change-camera-controller/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/change-camera-controller/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/change-camera-controller/src/main/AndroidManifest.xml b/change-camera-controller/src/main/AndroidManifest.xml
deleted file mode 100644
index 1bf01ae77..000000000
--- a/change-camera-controller/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt b/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt
deleted file mode 100644
index 8a028d9c3..000000000
--- a/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-package com.esri.arcgismaps.sample.changecameracontroller
-
-import android.os.Bundle
-import android.util.Log
-import android.widget.ArrayAdapter
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISScene
-import com.arcgismaps.mapping.ArcGISTiledElevationSource
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.symbology.ModelSceneSymbol
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.SurfacePlacement
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.OrbitLocationCameraController
-import com.arcgismaps.mapping.view.OrbitGeoElementCameraController
-import com.arcgismaps.mapping.view.GlobeCameraController
-import com.arcgismaps.mapping.view.Camera
-import com.esri.arcgismaps.sample.changecameracontroller.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.withContext
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.ensureActive
-import java.io.File
-import java.io.FileOutputStream
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val sceneView by lazy {
- activityMainBinding.sceneView
- }
-
- // options dropdown view for the camera controller types
- private val cameraControllerOptionsView by lazy {
- // create an array adapter data source using the list of camera controller modes
- val arrayAdapter = ArrayAdapter(
- this,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- CameraControllerMode.getValuesByDisplayName()
- )
- activityMainBinding.bottomListItems.apply {
- setAdapter(arrayAdapter)
- }
- }
-
- // list of available asset files
- private val assetFiles by lazy {
- resources.getStringArray(R.array.asset_files).toList()
- }
-
- // the graphic representing the airplane 3d model
- private val airplane3DGraphic by lazy {
- // location for the target graphic
- val point = Point(-109.937516, 38.456714, 5000.0, SpatialReference.wgs84())
- // create the graphic with the target location
- Graphic(point)
- }
-
- // camera controller which orbits the plane graphic
- private val orbitPlaneCameraController by lazy {
- // instantiate a new camera controller with a distance from airplane graphic
- OrbitGeoElementCameraController(airplane3DGraphic, 100.0).apply {
- // set a relative pitch to the target
- setCameraPitchOffset(3.0)
- // set a relative heading to the target
- setCameraHeadingOffset(150.0)
- }
- }
-
- // camera controller which orbits a target location
- private val orbitLocationCameraController by lazy {
- // target location for the camera controller
- val point = Point(-109.929589, 38.437304, 1700.0, SpatialReference.wgs84())
- // instantiate a new camera controller with a distance from the target
- OrbitLocationCameraController(point, 5000.0).apply {
- // set a relative pitch to the target
- setCameraPitchOffset(3.0)
- // set a relative heading to the target
- setCameraHeadingOffset(150.0)
- }
- }
-
- // camera controller for free roam navigation
- private val globeCameraController = GlobeCameraController()
-
- // camera looking at the Upheaval Dome crater in Utah
- private val defaultCamera = Camera(
- latitude = 38.459291,
- longitude = -109.937576,
- altitude = 5500.0,
- heading = 150.0,
- pitch = 20.0,
- roll = 0.0
- )
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(sceneView)
-
- // create and add a scene with an imagery basemap style
- val terrainScene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
- // add an elevation data source to the base surface
- baseSurface.elevationSources.add(
- ArcGISTiledElevationSource(getString(R.string.elevation_service_url))
- )
- }
-
- // graphics overlay for the scene to draw the 3d graphics on
- val graphicsOverlay = GraphicsOverlay().apply {
- // set the altitude values in the scene to be absolute
- sceneProperties.surfacePlacement = SurfacePlacement.Absolute
- // add the airplane 3d graphic to the graphics overlay
- graphics.add(airplane3DGraphic)
- }
-
- sceneView.apply {
- // set the scene to the SceneView
- scene = terrainScene
- // add the graphics overlay to the SceneView
- graphicsOverlays.add(graphicsOverlay)
- }
-
- lifecycleScope.launch {
- // if the map load failed show an error and return
- terrainScene.load().onFailure {
- showError("Failed to load the scene: ${it.message}")
- return@launch
- }
- // set the sceneView viewpoint to the default camera
- sceneView.setViewpointCamera(defaultCamera)
- // copy the airplane model assets to the cache directory if needed
- copyAssetsToCache(assetFiles, cacheDir, false)
- // load the airplane model file and update the the airplane3DGraphic
- loadModel(getString(R.string.bristol_model_file), airplane3DGraphic)
- }
-
- // set the click listener for the options dropdown view
- cameraControllerOptionsView.setOnItemClickListener { parent, _, position, _ ->
- // get the selected camera mode item
- val selectedItem = parent.getItemAtPosition(position) as String
- // get the CameraControllerMode from the selected item
- val mode = CameraControllerMode.getValue(selectedItem)
- // update the camera controller
- setCameraControllerMode(mode)
- }
- }
-
- /**
- * Loads a [ModelSceneSymbol] from the [filename] in [getCacheDir] and updates the [graphic].
- */
- private suspend fun loadModel(filename: String, graphic: Graphic) {
- val modelFile = File(cacheDir, filename)
- if (modelFile.exists()) {
- // create a new ModelSceneSymbol with the file
- val modelSceneSymbol = ModelSceneSymbol(modelFile.path).apply {
- heading = 45f
- }
- // if the symbol load failed show and error and return
- modelSceneSymbol.load().onFailure {
- return showError("Error loading airplane model: ${it.message}")
- }
- // update the graphic's symbol
- graphic.symbol = modelSceneSymbol
- } else {
- showError("Error loading airplane model: file does not exist.")
- }
- }
-
- /**
- * Updates the SceneView's camera controller based on the [mode] specified.
- */
- private fun setCameraControllerMode(mode: CameraControllerMode) {
- sceneView.cameraController = when (mode) {
- CameraControllerMode.OrbitPlane -> orbitPlaneCameraController
- CameraControllerMode.OrbitLocation -> orbitLocationCameraController
- CameraControllerMode.Globe -> globeCameraController
- }
- }
-
- /**
- * Copies the list of [assets] files from the assets folder to a given [cache] directory. This
- * suspending function runs on the [Dispatchers.IO] context. If [overwrite] is true, any assets
- * already in the [cache] directory are overwritten, otherwise copy is skipped.
- */
- private suspend fun copyAssetsToCache(
- assets: List,
- cache: File,
- overwrite: Boolean
- ) = withContext(Dispatchers.IO) {
- // get the AssetManager
- val assetManager = applicationContext.assets ?: return@withContext
- assets.forEach { assetName ->
- // check for cancellation before reading/writing the asset files
- ensureActive()
- try {
- // create an output file in the cache directory
- val outFile = File(cache, assetName)
- // if the output file doesn't exist or overwrite is enabled
- if (!outFile.exists() || overwrite) {
- // create an input stream to the asset
- assetManager.open(assetName).use { inputStream ->
- // create an file output stream to the output file
- FileOutputStream(outFile).use { outputStream ->
- // copy the input file stream to the output file stream
- inputStream.copyTo(outputStream)
- }
- Log.i(localClassName, "$assetName copied to cache.")
- }
- } else {
- Log.i(localClassName, "$assetName already in cache, skipping copy.")
- }
- } catch (exception: Exception) {
- showError("Error caching asset :${exception.message}")
- }
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(sceneView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-// enum to keep track of the selected camera controller mode set for the SceneView
-enum class CameraControllerMode(val displayName: String) {
- OrbitPlane("Orbit camera around a plane model"),
- OrbitLocation("Orbit camera around a crater"),
- Globe("Free pan around the globe");
-
- companion object {
- /**
- * Returns a List containing the [displayName] property of this enum type.
- * */
- fun getValuesByDisplayName(): List {
- return entries.map { cameraControllerMode ->
- cameraControllerMode.displayName
- }
- }
-
- /**
- * Returns the enum constant of this type with the specified [displayName] property.
- */
- fun getValue(displayName: String): CameraControllerMode {
- return entries.first {
- it.displayName == displayName
- }
- }
- }
-}
diff --git a/change-camera-controller/src/main/res/drawable-v24/ic_launcher_foreground.xml b/change-camera-controller/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/change-camera-controller/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-camera-controller/src/main/res/drawable/ic_launcher_background.xml b/change-camera-controller/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/change-camera-controller/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/change-camera-controller/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher.png b/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher_round.png b/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher.png b/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher_round.png b/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher.png b/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher.png b/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/change-camera-controller/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-camera-controller/src/main/res/values/strings.xml b/change-camera-controller/src/main/res/values/strings.xml
deleted file mode 100644
index 226b15171..000000000
--- a/change-camera-controller/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
- Change camera controller
- https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
-
- Bristol.dae
- Bristol.png
- Select camera controller
-
-
- - @string/bristol_model_file
- - @string/bristol_skin_file
-
-
diff --git a/change-viewpoint/.gitignore b/change-viewpoint/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/change-viewpoint/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/change-viewpoint/build.gradle.kts b/change-viewpoint/build.gradle.kts
deleted file mode 100644
index f269a21dd..000000000
--- a/change-viewpoint/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.changeviewpoint"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.changeviewpoint"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/change-viewpoint/proguard-rules.pro b/change-viewpoint/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/change-viewpoint/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/change-viewpoint/src/main/AndroidManifest.xml b/change-viewpoint/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/change-viewpoint/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt b/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt
deleted file mode 100644
index 977a1b517..000000000
--- a/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.changeviewpoint
-
-import android.os.Bundle
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.esri.arcgismaps.sample.changeviewpoint.databinding.ActivityMainBinding
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- private val viewpointScale = 5000.0
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // add the MapView to the lifecycle
- lifecycle.addObserver(mapView)
- // create and add a map with a imagery basemap style
- mapView.map = ArcGISMap(BasemapStyle.ArcGISImagery)
- // set the start point of the ViewPoint
- val startPoint = Point(-14093.0, 6711377.0, SpatialReference.webMercator())
- lifecycleScope.launch {
- // set viewpoint of map view to starting point and scale
- mapView.setViewpointCenter(startPoint, viewpointScale)
- }
- }
-
- fun onGeometryClicked(view: View) {
- // create a collection of points around Westminster
- val westminsterPolylineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
- addPoint(Point(-13823.0, 6710390.0))
- addPoint(Point(-13823.0, 6710150.0))
- addPoint(Point(-14680.0, 6710390.0))
- addPoint(Point(-14680.0, 6710150.0))
- }
- val geometry = westminsterPolylineBuilder.toGeometry()
- // set the map view's viewpoint to Westminster
- lifecycleScope.launch {
- mapView.setViewpointGeometry(geometry)
- }
- }
-
- fun onCenterClicked(view: View) {
- // create the Waterloo location point
- val waterlooPoint = Point(-12153.0, 6710527.0, SpatialReference.webMercator())
- // set the map view's viewpoint centered on Waterloo and scaled
- lifecycleScope.launch {
- mapView.setViewpointCenter(waterlooPoint, viewpointScale)
- }
- }
-
- fun onAnimateClicked(view: View) {
- // create the London location point
- val londonPoint = Point(-14093.0, 6711377.0, SpatialReference.webMercator())
- // create the viewpoint with the London point and scale
- val viewpoint = Viewpoint(londonPoint, viewpointScale)
- // set the map view's viewpoint to London with a seven second animation duration
- lifecycleScope.launch {
- mapView.setViewpointAnimated(viewpoint, 7f)
- }
- }
-}
diff --git a/change-viewpoint/src/main/res/drawable-v24/ic_launcher_foreground.xml b/change-viewpoint/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/change-viewpoint/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-viewpoint/src/main/res/drawable/ic_launcher_background.xml b/change-viewpoint/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/change-viewpoint/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/change-viewpoint/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher.png b/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher_round.png b/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher.png b/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher_round.png b/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher.png b/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher.png b/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/change-viewpoint/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/change-viewpoint/src/main/res/values/strings.xml b/change-viewpoint/src/main/res/values/strings.xml
deleted file mode 100644
index da035a932..000000000
--- a/change-viewpoint/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Change viewpoint
- Animate
- Center
- Geometry
-
diff --git a/clip-geometry/.gitignore b/clip-geometry/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/clip-geometry/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/clip-geometry/build.gradle.kts b/clip-geometry/build.gradle.kts
deleted file mode 100644
index 85a5f8e8e..000000000
--- a/clip-geometry/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.clipgeometry"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.clipgeometry"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/clip-geometry/proguard-rules.pro b/clip-geometry/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/clip-geometry/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/clip-geometry/src/main/AndroidManifest.xml b/clip-geometry/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/clip-geometry/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt b/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt
deleted file mode 100644
index b1e808551..000000000
--- a/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.clipgeometry
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.clipgeometry.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- // graphics overlay along the Colorado state border
- private val coloradoOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- // graphics overlay to contain the clipping envelopes
- private val envelopesOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- // get the MapView instance from the data binding
- val mapView = activityMainBinding.mapView
- lifecycle.addObserver(mapView)
-
- // create buttons to perform the clip and reset operations on the geometry
- val clipButton = activityMainBinding.clipButton
- val resetButton = activityMainBinding.resetButton
-
- // create and add a map with a topographic basemap style
- mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- // set the viewpoint of the MapView
- mapView.setViewpoint(Viewpoint(40.0, -106.0, 10000000.0))
-
- // create a graphics overlay to contain the geometry to clip
- mapView.graphicsOverlays.add(coloradoOverlay)
- val (coloradoGraphic, fillSymbol) = createGraphics(coloradoOverlay)
-
- // create a graphics overlay to contain the clipping envelopes
- mapView.graphicsOverlays.add(envelopesOverlay)
- createEnvelope(envelopesOverlay)
-
- // create a graphics overlay to contain the clipped areas
- val clipAreasOverlay = GraphicsOverlay()
- mapView.graphicsOverlays.add(clipAreasOverlay)
-
- clipButton.setOnClickListener {
- // disable button
- clipButton.isEnabled = false
- resetButton.isEnabled = true
- // for each envelope, clip the Colorado geometry and show the result,
- // replacing the original Colorado graphic
- coloradoGraphic.isVisible = false
- for (graphic in envelopesOverlay.graphics) {
- val geometry =
- coloradoGraphic.geometry?.let { coloradoGeometry ->
- GeometryEngine.clipOrNull(coloradoGeometry, graphic.geometry as Envelope)
- }
- val clippedGraphic = Graphic(geometry, fillSymbol)
- clipAreasOverlay.graphics.add(clippedGraphic)
- }
- }
-
- resetButton.setOnClickListener {
- // clear clipped graphic
- clipAreasOverlay.graphics.clear()
-
- // set the visibility of the colorado graphic to true
- coloradoGraphic.isVisible = true
-
- clipButton.isEnabled = true
- resetButton.isEnabled = false
- }
- }
-
- /**
- * Create colorado graphic.
- */
- private fun createGraphics(
- coloradoOverlay: GraphicsOverlay,
- ) : Pair{
- // create a blue graphic of Colorado
- val colorado = Envelope(
- Point(-11362327.128340, 5012861.290274),
- Point(-12138232.018408, 4441198.773776)
- )
- val fillSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.Solid,
- Color(R.color.transparentDarkBlue),
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
- )
- val coloradoGraphic = Graphic(colorado, fillSymbol)
- coloradoOverlay.graphics.add(coloradoGraphic)
- return Pair(coloradoGraphic, fillSymbol)
- }
-
- /**
- * Create three envelopes, outside, inside and intersecting colorado graphic.
- */
- private fun createEnvelope(
- envelopesOverlay: GraphicsOverlay,
- ) {
- // create a dotted red outline symbol
- val redOutline = SimpleLineSymbol(SimpleLineSymbolStyle.Dot, Color.red, 3f)
-
- // create a envelope outside Colorado
- val outsideEnvelope = Envelope(
- Point(-11858344.321294, 5147942.225174),
- Point(-12201990.219681, 5297071.577304)
- )
- val outside = Graphic(outsideEnvelope, redOutline)
- envelopesOverlay.graphics.add(outside)
-
- // create a envelope intersecting Colorado
- val intersectingEnvelope = Envelope(
- Point(-11962086.479298, 4566553.881363),
- Point(-12260345.183558, 4332053.378376)
- )
- val intersecting = Graphic(intersectingEnvelope, redOutline)
- envelopesOverlay.graphics.add(intersecting)
-
- // create a envelope inside Colorado
- val containedEnvelope = Envelope(
- Point(-11655182.595204, 4741618.772994),
- Point(-11431488.567009, 4593570.068343)
- )
- val contained = Graphic(containedEnvelope, redOutline)
- envelopesOverlay.graphics.add(contained)
- }
-}
diff --git a/clip-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml b/clip-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/clip-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/clip-geometry/src/main/res/drawable/ic_launcher_background.xml b/clip-geometry/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/clip-geometry/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/clip-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher.png b/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png b/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/clip-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher.png b/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png b/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/clip-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png b/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png b/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/clip-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/clip-geometry/src/main/res/values/strings.xml b/clip-geometry/src/main/res/values/strings.xml
deleted file mode 100644
index 31bbf2876..000000000
--- a/clip-geometry/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Clip geometry
- Clip geometry
- Reset
-
diff --git a/configure-basemap-style-parameters/.gitignore b/configure-basemap-style-parameters/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/configure-basemap-style-parameters/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/configure-basemap-style-parameters/build.gradle.kts b/configure-basemap-style-parameters/build.gradle.kts
deleted file mode 100644
index 579eb3b3c..000000000
--- a/configure-basemap-style-parameters/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.configurebasemapstyleparameters"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.configurebasemapstyleparameters"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/configure-basemap-style-parameters/proguard-rules.pro b/configure-basemap-style-parameters/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/configure-basemap-style-parameters/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/configure-basemap-style-parameters/src/main/AndroidManifest.xml b/configure-basemap-style-parameters/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/configure-basemap-style-parameters/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt b/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt
deleted file mode 100644
index 6d7d93b34..000000000
--- a/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.configurebasemapstyleparameters
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.configurebasemapstyleparameters.screens.MainScreen
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/configure-basemap-style-parameters/src/main/res/drawable-v24/ic_launcher_foreground.xml b/configure-basemap-style-parameters/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/configure-basemap-style-parameters/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-basemap-style-parameters/src/main/res/drawable/ic_launcher_background.xml b/configure-basemap-style-parameters/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/configure-basemap-style-parameters/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/configure-basemap-style-parameters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher.png b/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher_round.png b/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher.png b/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher_round.png b/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher.png b/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher.png b/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/configure-basemap-style-parameters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-basemap-style-parameters/src/main/res/values/strings.xml b/configure-basemap-style-parameters/src/main/res/values/strings.xml
deleted file mode 100644
index 47ffbeb8a..000000000
--- a/configure-basemap-style-parameters/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Configure basemap style parameters
-
diff --git a/configure-clusters/.gitignore b/configure-clusters/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/configure-clusters/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/configure-clusters/build.gradle.kts b/configure-clusters/build.gradle.kts
deleted file mode 100644
index adc8e1d61..000000000
--- a/configure-clusters/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.configureclusters"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.configureclusters"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/configure-clusters/proguard-rules.pro b/configure-clusters/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/configure-clusters/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/configure-clusters/src/main/AndroidManifest.xml b/configure-clusters/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/configure-clusters/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt b/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt
deleted file mode 100644
index 72577553c..000000000
--- a/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.configureclusters
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.configureclusters.screens.MainScreen
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ConfigureClustersApp()
- }
- }
- }
-
- @Composable
- private fun ConfigureClustersApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt b/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt
deleted file mode 100644
index 356c60036..000000000
--- a/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt
+++ /dev/null
@@ -1,394 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.configureclusters.screens
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Settings
-import androidx.compose.material.icons.rounded.Close
-import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExposedDropdownMenuBox
-import androidx.compose.material3.ExposedDropdownMenuDefaults
-import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.ModalBottomSheet
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SheetState
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
-import androidx.compose.material3.rememberModalBottomSheetState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.viewmodel.compose.viewModel
-import com.arcgismaps.toolkit.geoviewcompose.MapView
-import com.esri.arcgismaps.sample.configureclusters.components.MapViewModel
-import com.esri.arcgismaps.sample.sampleslib.components.BottomSheet
-import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleTypography
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import kotlin.math.roundToInt
-
-/**
- * Main screen layout for the sample app
- */
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun MainScreen(sampleName: String) {
- // create a ViewModel to handle MapView interactions
- val mapViewModel: MapViewModel = viewModel()
-
- val composableScope = rememberCoroutineScope()
-
- Scaffold(
- topBar = { SampleTopAppBar(title = sampleName) },
- content = {
- Box(
- modifier = Modifier
- .fillMaxSize()
- .padding(it),
- contentAlignment = Alignment.Center
- ) {
- var mapScale by remember { mutableIntStateOf(0) }
- MapView(
- modifier = Modifier
- .fillMaxSize(),
- mapViewProxy = mapViewModel.mapViewProxy,
- // identify on single tap
- onSingleTapConfirmed = { singleTapConfirmedEvent ->
- mapViewModel.identify(singleTapConfirmedEvent)
- },
- arcGISMap = mapViewModel.arcGISMap,
- // update the map scale in the UI on map scale change
- onMapScaleChanged = { currentMapScale ->
- if (!currentMapScale.isNaN()) {
- mapScale = currentMapScale.roundToInt()
- }
- },
- )
-
- val controlsBottomSheetState =
- rememberModalBottomSheetState(skipPartiallyExpanded = true)
- // show the "Show controls" button only when the bottom sheet is not visible
- if (!controlsBottomSheetState.isVisible) {
- FloatingActionButton(
- modifier = Modifier
- .align(Alignment.BottomEnd)
- .padding(bottom = 36.dp, end = 24.dp),
- onClick = {
- composableScope.launch {
- controlsBottomSheetState.show()
- }
- },
- ) {
- Icon(Icons.Filled.Settings, contentDescription = "Show controls")
- }
- }
- if (controlsBottomSheetState.isVisible) {
- ClusterControlsBottomSheet(
- composableScope = composableScope,
- controlsBottomSheetState = controlsBottomSheetState,
- showClusterLabels = mapViewModel.showClusterLabels,
- updateClusterLabelState = mapViewModel::updateShowClusterLabelState,
- clusterRadiusOptions = mapViewModel.clusterRadiusOptions,
- clusterRadius = mapViewModel.clusterRadius,
- updateClusterRadiusState = mapViewModel::updateClusterRadiusState,
- clusterMaxScaleOptions = mapViewModel.clusterMaxScaleOptions,
- clusterMaxScale = mapViewModel.clusterMaxScale,
- updateClusterMaxScaleState = mapViewModel::updateClusterMaxScaleState,
- mapScale = mapScale
- )
- }
- }
-
- // display a bottom sheet to show popup details
- BottomSheet(
- isVisible = mapViewModel.showPopUpContent,
- bottomSheetContent = {
- ClusterInfoContent(
- popUpTitle = mapViewModel.popUpTitle,
- popUpInfo = mapViewModel.popUpInfo,
- onDismiss = { mapViewModel.updateShowPopUpContentState(false) }
- )
- })
-
- }
- )
-}
-
-/**
- * Composable function to display the cluster controls bottom sheet.
- */
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun ClusterControlsBottomSheet(
- composableScope: CoroutineScope,
- controlsBottomSheetState: SheetState,
- showClusterLabels: Boolean,
- updateClusterLabelState: (Boolean) -> Unit,
- clusterRadiusOptions: List,
- clusterRadius: Int,
- updateClusterRadiusState: (Int) -> Unit,
- clusterMaxScaleOptions: List,
- clusterMaxScale: Int,
- updateClusterMaxScaleState: (Int) -> Unit,
- mapScale: Int,
-) {
- ModalBottomSheet(
- modifier = Modifier.wrapContentHeight(),
- sheetState = controlsBottomSheetState,
- onDismissRequest = {
- composableScope.launch {
- controlsBottomSheetState.hide()
- }
- }) {
- Column(
- Modifier
- .padding(12.dp)
- .navigationBarsPadding()) {
- Text(
- "Cluster labels visibility:",
- style = MaterialTheme.typography.titleMedium
- )
- Spacer(Modifier.size(8.dp))
- Row(
- Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text("Show labels")
- Switch(
- checked = showClusterLabels,
- onCheckedChange = { showClusterLabels ->
- updateClusterLabelState(
- showClusterLabels
- )
- }
- )
- }
- Spacer(Modifier.size(8.dp))
- Row(
- Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text("Current map scale:")
- Text("1:$mapScale")
- }
- HorizontalDivider(Modifier.padding(vertical = 12.dp, horizontal = 8.dp))
- Text(
- "Clustering properties:",
- style = MaterialTheme.typography.titleMedium
- )
- Spacer(Modifier.size(8.dp))
- ClusterRadiusControls(
- clusterRadiusOptions,
- clusterRadius,
- updateClusterRadiusState
- )
- Spacer(Modifier.size(8.dp))
- ClusterMaxScaleControls(
- clusterMaxScaleOptions,
- clusterMaxScale,
- updateClusterMaxScaleState
- )
- }
- }
-}
-
-/**
- * Composable function to display the cluster radius controls within the cluster controls bottom
- * sheet.
- */
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun ClusterRadiusControls(
- clusterRadiusOptions: List,
- clusterRadius: Int,
- updateClusterRadius: (Int) -> Unit
-) {
- Row(
- Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text(
- "Cluster radius",
- modifier = Modifier.padding(8.dp)
- )
- var expanded by rememberSaveable { mutableStateOf(false) }
- ExposedDropdownMenuBox(
- modifier = Modifier.width(150.dp),
- expanded = expanded,
- onExpandedChange = { expanded = !expanded }
- ) {
- TextField(
- value = clusterRadius.toString(),
- onValueChange = {},
- readOnly = true,
- trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
- modifier = Modifier.menuAnchor()
- )
- ExposedDropdownMenu(
- expanded = expanded,
- onDismissRequest = { expanded = false }
- ) {
- clusterRadiusOptions.forEachIndexed { index, clusterRadius ->
- DropdownMenuItem(
- text = { Text(clusterRadius.toString()) },
- onClick = {
- updateClusterRadius(index)
- expanded = false
- })
- // show a divider between dropdown menu options
- if (index < clusterRadiusOptions.lastIndex) {
- HorizontalDivider()
- }
- }
- }
- }
- }
-}
-
-/**
- * Composable function to display the cluster max scale controls within the cluster controls bottom
- * sheet.
- */
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun ClusterMaxScaleControls(
- clusterMaxScaleOptions: List,
- clusterMaxScale: Int,
- updateClusterMaxScale: (Int) -> Unit
-) {
- Row(
- Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text(
- "Cluster max scale",
- modifier = Modifier.padding(8.dp)
- )
- var expanded by rememberSaveable { mutableStateOf(false) }
- ExposedDropdownMenuBox(
- modifier = Modifier.width(150.dp),
- expanded = expanded,
- onExpandedChange = { expanded = !expanded }
- ) {
- TextField(
- value = clusterMaxScale.toString(),
- onValueChange = {},
- readOnly = true,
- trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
- modifier = Modifier.menuAnchor()
- )
- ExposedDropdownMenu(
- expanded = expanded,
- onDismissRequest = { expanded = false }
- ) {
- clusterMaxScaleOptions.forEachIndexed { index, clusterRadius ->
- DropdownMenuItem(
- text = { Text(clusterRadius.toString()) },
- onClick = {
- updateClusterMaxScale(index)
- expanded = false
- })
- // show a divider between dropdown menu options
- if (index < clusterMaxScaleOptions.lastIndex) {
- HorizontalDivider()
- }
- }
- }
- }
- }
-}
-
-/**
- * Composable function to display the cluster info content from the pop up within a bottom sheet.
- */
-@Composable
-private fun ClusterInfoContent(
- popUpTitle: String,
- popUpInfo: Map,
- onDismiss: () -> Unit
-) {
- Column(Modifier.background(MaterialTheme.colorScheme.background)) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 30.dp, vertical = 12.dp),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Text(
- text = popUpTitle.ifEmpty { "Cluster Info:" },
- style = SampleTypography.headlineSmall
- )
- IconButton(
- onClick = onDismiss
- ) {
- Icon(
- imageVector = Icons.Rounded.Close,
- contentDescription = "Close button"
- )
- }
- }
- popUpInfo.forEach {
- Row(
- Modifier
- .fillMaxWidth()
- .padding(
- horizontal = 30.dp,
- vertical = 8.dp
- ),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(text = "${it.key}:", style = MaterialTheme.typography.labelMedium)
- Text(text = "${it.value}")
- }
- }
- Spacer(modifier = Modifier.size(24.dp))
- }
-}
diff --git a/configure-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml b/configure-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/configure-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-clusters/src/main/res/drawable/ic_launcher_background.xml b/configure-clusters/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/configure-clusters/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/configure-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher.png b/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png b/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/configure-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher.png b/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png b/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/configure-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png b/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png b/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/configure-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/configure-clusters/src/main/res/values/strings.xml b/configure-clusters/src/main/res/values/strings.xml
deleted file mode 100644
index 11b17f645..000000000
--- a/configure-clusters/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Configure clusters
-
diff --git a/create-convex-hull-around-points/.gitignore b/create-convex-hull-around-points/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/create-convex-hull-around-points/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/create-convex-hull-around-points/build.gradle.kts b/create-convex-hull-around-points/build.gradle.kts
deleted file mode 100644
index a8be150df..000000000
--- a/create-convex-hull-around-points/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.createconvexhullaroundpoints"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.createconvexhullaroundpoints"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/create-convex-hull-around-points/proguard-rules.pro b/create-convex-hull-around-points/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/create-convex-hull-around-points/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/create-convex-hull-around-points/src/main/AndroidManifest.xml b/create-convex-hull-around-points/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/create-convex-hull-around-points/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt b/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt
deleted file mode 100644
index 7065e18fb..000000000
--- a/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.createconvexhullaroundpoints
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Multipoint
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.createconvexhullaroundpoints.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // setup binding for the MapView
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // action button that creates the canvas hull
- private val createButton by lazy {
- activityMainBinding.createButton
- }
-
- // action button to reset the map
- private val resetButton by lazy {
- activityMainBinding.resetButton
- }
-
- // a red marker symbol for points
- private val pointSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10f)
-
- // a blue line symbol
- private val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 3f)
-
- // a fill symbol with an empty fill for polygons
- private val fillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Null, Color.red, lineSymbol)
-
- // set up the point graphic with point symbol
- private val pointGraphic = Graphic(symbol = pointSymbol)
-
- // init the convex hull graphic
- private val convexHullGraphic = Graphic()
-
- // create a graphics overlay to draw all graphics
- private val graphicsOverlay = GraphicsOverlay()
-
- // list to store the selected map points
- private val inputPoints = mutableListOf()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // add point and convex hull graphics to the graphics overlay
- graphicsOverlay.graphics.addAll(listOf(pointGraphic, convexHullGraphic))
-
- // create and add a map with topographic basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
- // set a default initial point and scale
- initialViewpoint = Viewpoint(Point(34.77, -10.24), 20e7)
- }
-
- // configure map view assignments
- mapView.apply {
- this.map = map
- // add the graphics overlay to the mapview
- graphicsOverlays.add(graphicsOverlay)
- }
-
- lifecycleScope.launch {
- // if the map load fails show the error and return
- map.load().onFailure {
- return@launch showError("Error loading map")
- }
- // capture and collect when the user taps on the screen
- mapView.onSingleTapConfirmed.collect { event ->
- event.mapPoint?.let { point ->
- addMapPoint(point)
- }
- }
- }
-
- // add a click listener to create a convex hull
- createButton.setOnClickListener {
- // check if the pointGraphic's geometry is not null
- pointGraphic.geometry?.let { geometry ->
- createConvexHull(geometry)
- }
- }
-
- // add a click listener to reset the map
- resetButton.setOnClickListener {
- resetMap()
- }
- }
-
- /**
- * Adds the [point] to the map drawn as a Multipoint geometry
- */
- private fun addMapPoint(point: Point) {
- // add the new point to the points list
- inputPoints.add(point)
- // recreate the graphics geometry representing the input points
- pointGraphic.geometry = Multipoint(inputPoints)
- // enable all the action buttons, since we have at least one point drawn
- createButton.isEnabled = true
- resetButton.isEnabled = true
- }
-
- /**
- * Creates and draws a convex hull graphic on the map using [pointGeometry] points
- */
- private fun createConvexHull(pointGeometry: Geometry) {
- // normalize the geometry for panning beyond the meridian
- // and proceed if the resulting geometry is not null
- val normalizedPointGeometry = GeometryEngine.normalizeCentralMeridian(pointGeometry)
- ?: return showError("Error normalizing point geometry")
-
- // create a convex hull from the points and proceed if it's not null
- val convexHullGeometry = GeometryEngine.convexHullOrNull(normalizedPointGeometry)
-
- // the convex hull's geometry may be a point or polyline if the number of
- // points is less than 3, set its symbol accordingly
- convexHullGraphic.symbol = when (convexHullGeometry) {
- is Point -> {
- // set symbol to use the pointSymbol
- pointSymbol
- }
- is Polyline -> {
- // set symbol to use the lineSymbol
- lineSymbol
- }
- is Polygon -> {
- // set symbol to use the fillSymbol
- fillSymbol
- }
- else -> {
- showError("Unknown geometry for convex hull")
- null
- }
- }
- // update the convex hull graphics geometry
- convexHullGraphic.geometry = convexHullGeometry
- // disable the create button until new input points are created
- createButton.isEnabled = false
- }
-
-
- /**
- * Resets the map by clearing any drawn points, graphics and disables all buttons
- */
- private fun resetMap() {
- // remove all the selected points
- inputPoints.clear()
- // remove the geometry for the point graphic and convex hull graphics
- pointGraphic.geometry = null
- convexHullGraphic.geometry = null
- // disable the buttons
- resetButton.isEnabled = false
- createButton.isEnabled = false
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-/**
- * Simple extension property that represents a blue color
- */
-private val Color.Companion.blue
- get() = fromRgba(0, 0, 255)
diff --git a/create-convex-hull-around-points/src/main/res/drawable-v24/ic_launcher_foreground.xml b/create-convex-hull-around-points/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/create-convex-hull-around-points/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-convex-hull-around-points/src/main/res/drawable/ic_launcher_background.xml b/create-convex-hull-around-points/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/create-convex-hull-around-points/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-convex-hull-around-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher.png b/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher_round.png b/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher.png b/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher_round.png b/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher.png b/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher.png b/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/create-convex-hull-around-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-convex-hull-around-points/src/main/res/values/strings.xml b/create-convex-hull-around-points/src/main/res/values/strings.xml
deleted file mode 100644
index 1b2408f57..000000000
--- a/create-convex-hull-around-points/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Create convex hull around points
- Create Convex Hull
- Reset
-
diff --git a/create-mobile-geodatabase/.gitignore b/create-mobile-geodatabase/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/create-mobile-geodatabase/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/create-mobile-geodatabase/build.gradle.kts b/create-mobile-geodatabase/build.gradle.kts
deleted file mode 100644
index 9024d10be..000000000
--- a/create-mobile-geodatabase/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.createmobilegeodatabase"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.createmobilegeodatabase"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/create-mobile-geodatabase/proguard-rules.pro b/create-mobile-geodatabase/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/create-mobile-geodatabase/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/create-mobile-geodatabase/src/main/AndroidManifest.xml b/create-mobile-geodatabase/src/main/AndroidManifest.xml
deleted file mode 100644
index 643b2bcc9..000000000
--- a/create-mobile-geodatabase/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt b/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt
deleted file mode 100644
index 4610a1c55..000000000
--- a/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt
+++ /dev/null
@@ -1,297 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.createmobilegeodatabase
-
-import android.app.Dialog
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.FileProvider
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.LoadStatus
-import com.arcgismaps.data.FieldDescription
-import com.arcgismaps.data.FieldType
-import com.arcgismaps.data.Geodatabase
-import com.arcgismaps.data.GeodatabaseFeatureTable
-import com.arcgismaps.data.QueryParameters
-import com.arcgismaps.data.TableDescription
-import com.arcgismaps.geometry.GeometryType
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.TableLayoutBinding
-import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.TableRowBinding
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-import java.time.Instant
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val createButton: MaterialButton by lazy {
- activityMainBinding.createButton
- }
-
- private val viewTableButton: MaterialButton by lazy {
- activityMainBinding.viewTableButton
- }
-
- private val featureCountTextView: TextView by lazy {
- activityMainBinding.featureCount
- }
-
- // feature table created using mobile geodatabase and added to the MapView
- private var featureTable: GeodatabaseFeatureTable? = null
-
- // mobile geodatabase used to create and store
- // the feature attributes (LocationHistory.geodatabase)
- private var geodatabase: Geodatabase? = null
-
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect { tapConfirmedEvent ->
- tapConfirmedEvent.mapPoint?.let {
- // create a feature on where the user clicked
- addFeature(it)
- }
- }
- }
-
- // displays a dialog to show the attributes of each feature in a feature table
- viewTableButton.setOnClickListener {
- lifecycleScope.launch {
- displayTable()
- }
- }
-
- // opens a share-sheet with the "LocationHistory.geodatabase" file
- createButton.setOnClickListener {
- // close the mobile geodatabase before sharing
- geodatabase?.close()
-
- // get the URI of the geodatabase file using FileProvider
- val geodatabaseURI = FileProvider.getUriForFile(
- this,
- getString(R.string.provider_authority),
- File(geodatabase?.path.toString())
- )
-
- // set up the sharing intent with the geodatabase URI
- val geodatabaseIntent = Intent(Intent.ACTION_SEND).apply {
- type = "*/*"
- putExtra(Intent.EXTRA_STREAM, geodatabaseURI)
- }
-
- // open the Android share sheet
- startActivity(geodatabaseIntent)
- }
- }
-
-
- /**
- * Create and load a new geodatabase file with TableDescription fields
- */
- private fun createGeodatabase() {
- // define the path and name of the geodatabase file
- // note: the path defined must be non-empty, available,
- // allow read/write access, and end in ".geodatabase"
- val file = File(getExternalFilesDir(null)?.path, "/LocationHistory.geodatabase")
- if (file.exists()) {
- file.delete()
- }
- // close the existing geodatabase
- geodatabase?.close()
- lifecycleScope.launch {
- // create a geodatabase file at the file path
- Geodatabase.create(file.path).onSuccess { geodatabase ->
- // keep the instance of the new geodatabase for sharing
- this@MainActivity.geodatabase = geodatabase
- createGeodatabaseFeatureTable()
- }.onFailure {
- showError(it.message.toString())
- }
-
- }
- }
-
- /**
- * Create a new [featureTable] using a custom table description
- * and add the feature layer to the MapView.
- */
- private suspend fun createGeodatabaseFeatureTable() {
- // construct a table description which stores features as points on map
- val tableDescription =
- TableDescription(
- "LocationHistory",
- SpatialReference.wgs84(),
- GeometryType.Point
- )
- // set up the fields to the table,
- // Field.Type.OID is the primary key of the SQLite table
- // Field.Type.DATE is a date column used to store a Calendar date
- // FieldDescriptions can be a SHORT, INTEGER, GUID, FLOAT, DOUBLE, DATE, TEXT, OID, GLOBALID, BLOB, GEOMETRY, RASTER, or XML.
- tableDescription.fieldDescriptions.addAll(
- listOf(
- FieldDescription("oid", FieldType.Oid),
- FieldDescription("collection_timestamp", FieldType.Date)
- )
- )
-
- // set any properties not needed to false
- tableDescription.apply {
- hasAttachments = false
- hasM = false
- hasZ = false
- }
-
- // add a new table to the geodatabase by creating one from the tableDescription
- geodatabase?.createTable(tableDescription)?.onSuccess { featureTable ->
- // get the result of the loaded "LocationHistory" table
- this.featureTable = featureTable
- // create a feature layer for the map using the GeodatabaseFeatureTable
- val featureLayer = FeatureLayer.createWithFeatureTable(featureTable)
- mapView.map?.operationalLayers?.add(featureLayer)
- // display the current count of features in the FeatureTable
- featureCountTextView.text =
- "Number of features added: ${featureTable.numberOfFeatures}"
- }?.onFailure {
- showError(it.message.toString())
- }
- }
-
- /**
- * Create a feature with attributes on map click and add it to the [featureTable]
- * Also, updates the TotalFeatureCount on the screen
- */
- private fun addFeature(mapPoint: Point) {
- // set up the feature attributes
- val featureAttributes = mutableMapOf()
- featureAttributes["collection_timestamp"] = Instant.now()
-
- // create a new feature at the mapPoint
- val feature = featureTable?.createFeature(featureAttributes, mapPoint)
- ?: return showError("Error creating feature using attributes")
-
- lifecycleScope.launch {
- // add the feature to the feature table
- featureTable?.addFeature(feature)?.onSuccess {
- // feature added successfully, update count
- featureCountTextView.text =
- "Number of features added: ${featureTable?.numberOfFeatures}"
- // enable table button since at least 1 feature loaded on the GeodatabaseFeatureTable
- viewTableButton.isEnabled = true
- }?.onFailure {
- showError(it.message.toString())
- }
- }
- }
-
- /**
- * Displays a dialog with the table of features
- * added to the GeodatabaseFeatureTable [featureTable]
- */
- private suspend fun displayTable() {
- // query all the features loaded to the table
- featureTable?.queryFeatures(QueryParameters())?.onSuccess { queryResults ->
- // inflate the table layout
- val tableLayoutBinding = TableLayoutBinding.inflate(layoutInflater)
- // set up a dialog to be displayed
- Dialog(this).apply {
- setContentView(tableLayoutBinding.root)
- setCancelable(true)
- // grab the instance of the TableLayout
- val table = tableLayoutBinding.tableLayout
- // iterate through each feature to add to the TableLayout
- queryResults.forEach { feature ->
- // prepare the table row
- val tableRowBinding = TableRowBinding.inflate(layoutInflater).apply {
- oid.text = feature.attributes["oid"].toString()
- collectionTimestamp.text =
- (feature.attributes["collection_timestamp"] as Instant).toString()
- }
- // add the row to the TableLayout
- table.addView(tableRowBinding.root)
- }
- }.show()
- }?.onFailure {
- showError(it.message.toString())
- }
- }
-
- /**
- * Called on app launch or when Android share sheet is closed
- */
- private fun setMapView() {
- // create and add a map with a navigation night basemap style
- mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- mapView.setViewpoint(Viewpoint(41.5, -100.0, 100_000_000.0))
-
- lifecycleScope.launch {
- mapView.map?.loadStatus?.collect { loadStatus ->
- if (loadStatus == LoadStatus.Loaded) {
- // clear any feature layers displayed on the map
- mapView.map?.operationalLayers?.clear()
- // disable the button since no features are displayed
- viewTableButton.isEnabled = false
- // create a new geodatabase file to add features into the feature table
- createGeodatabase()
- } else if (loadStatus is LoadStatus.FailedToLoad) {
- showError("Error loading MapView: ${loadStatus.error.message}")
- }
- }
- }
- }
-
- override fun onResume() {
- super.onResume()
- // set up map view and create new geodatabase file
- // on every app launch or on share sheet close
- setMapView()
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/create-mobile-geodatabase/src/main/res/drawable-v24/ic_launcher_foreground.xml b/create-mobile-geodatabase/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/create-mobile-geodatabase/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-mobile-geodatabase/src/main/res/drawable/ic_launcher_background.xml b/create-mobile-geodatabase/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/create-mobile-geodatabase/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-mobile-geodatabase/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher.png b/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher_round.png b/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher.png b/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher_round.png b/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher.png b/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher.png b/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/create-mobile-geodatabase/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-mobile-geodatabase/src/main/res/values/strings.xml b/create-mobile-geodatabase/src/main/res/values/strings.xml
deleted file mode 100644
index db270ed92..000000000
--- a/create-mobile-geodatabase/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
- Create mobile geodatabase
- Number of features added:
- Create and share mobile geodatabase
- View table
- Attribute table loaded from the mobile geodatabase file. File can be loaded on ArcGIS Pro or ArcGIS Runtime
- OID
- Collection Timestamp
- com.esri.arcgismaps.sample.createmobilegeodatabase.provider
-
diff --git a/create-planar-and-geodetic-buffers/.gitignore b/create-planar-and-geodetic-buffers/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/create-planar-and-geodetic-buffers/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/create-planar-and-geodetic-buffers/build.gradle.kts b/create-planar-and-geodetic-buffers/build.gradle.kts
deleted file mode 100644
index c9038f54c..000000000
--- a/create-planar-and-geodetic-buffers/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.createplanarandgeodeticbuffers"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.createplanarandgeodeticbuffers"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/create-planar-and-geodetic-buffers/proguard-rules.pro b/create-planar-and-geodetic-buffers/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/create-planar-and-geodetic-buffers/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml b/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt b/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt
deleted file mode 100644
index 3950526b9..000000000
--- a/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt
+++ /dev/null
@@ -1,187 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.createplanarandgeodeticbuffers
-
-import android.os.Bundle
-import android.view.View
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.GeodeticCurveType
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.LinearUnit
-import com.arcgismaps.geometry.LinearUnitId
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleRenderer
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.createplanarandgeodeticbuffers.databinding.ActivityMainBinding
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.slider.Slider
-import kotlinx.coroutines.launch
-import java.util.Locale
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
-
- // get the views from the layout
- val mapView = activityMainBinding.mapView
- val optionsButton = activityMainBinding.optionsButton
- val clearButton = activityMainBinding.clearButton
- // add mapview to the lifecycle
- lifecycle.addObserver(mapView)
-
- // create a map with a topographic basemap
- mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
-
- // create a fill symbol for geodesic buffer polygons
- val geodesicOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 2F)
- val geodesicBufferFillSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.Solid, Color.green,
- geodesicOutlineSymbol
- )
-
- // create a graphics overlay to display geodesic polygons and set its renderer
- val geodesicGraphicsOverlay = GraphicsOverlay().apply {
- renderer = SimpleRenderer(geodesicBufferFillSymbol)
- opacity = 0.5f
- }
-
- // create a fill symbol for planar buffer polygons
- val planarOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 2F)
- val planarBufferFillSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.Solid, Color.red,
- planarOutlineSymbol
- )
-
- // create a graphics overlay to display planar polygons and set its renderer
- val planarGraphicsOverlay = GraphicsOverlay().apply {
- renderer = SimpleRenderer(planarBufferFillSymbol)
- opacity = 0.5f
- }
-
- // create a marker symbol for tap locations
- val tapSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Cross, Color.white, 14F)
-
- // create a graphics overlay to display tap locations for buffers and set its renderer
- val tapLocationsOverlay = GraphicsOverlay().apply {
- renderer = SimpleRenderer(tapSymbol)
- }
-
- // add overlays to the mapView
- mapView.graphicsOverlays.addAll(
- listOf(
- geodesicGraphicsOverlay,
- planarGraphicsOverlay,
- tapLocationsOverlay
- )
- )
-
- // set the default buffer distance in miles
- var bufferInMiles = 500f
-
- // create a buffer around the clicked location
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect { event ->
- // get map point tapped, return if null
- val mapPoint = event.mapPoint ?: return@collect
-
- // convert the input distance to meters, 1609.34 meters in one mile
- val bufferInMeters = bufferInMiles * 1609.34
-
- // create a planar buffer graphic around the input location at the specified distance
- val bufferGeometryPlanar = GeometryEngine.bufferOrNull(mapPoint, bufferInMeters)
- val planarBufferGraphic = Graphic(bufferGeometryPlanar)
-
- // create a geodesic buffer graphic using the same location and distance
- val bufferGeometryGeodesic =
- GeometryEngine.bufferGeodeticOrNull(
- mapPoint, bufferInMeters,
- LinearUnit(LinearUnitId.Meters), Double.NaN, GeodeticCurveType.Geodesic
- )
- val geodesicBufferGraphic = Graphic(bufferGeometryGeodesic)
-
- // create a graphic for the user tap location
- val locationGraphic = Graphic(mapPoint)
-
- // add the buffer polygons and tap location graphics to the appropriate graphic overlays
- planarGraphicsOverlay.graphics.add(planarBufferGraphic)
- geodesicGraphicsOverlay.graphics.add(geodesicBufferGraphic)
- tapLocationsOverlay.graphics.add(locationGraphic)
-
- // set button interaction
- clearButton.isEnabled = true
-
- }
- }
- // open the option dialog
- optionsButton.setOnClickListener {
- val optionsDialog: View = layoutInflater.inflate(R.layout.buffer_options_dialog, null)
- // set up the dialog builder and the title
- val dialogBuilder = MaterialAlertDialogBuilder(this)
- .setView(optionsDialog)
- .setPositiveButton("Set buffer", null)
-
- dialogBuilder.setTitle("Set buffer radius")
- // set up the dialog views
- val bufferValue = optionsDialog.findViewById(R.id.bufferValue)
- val bufferSlider = optionsDialog.findViewById(R.id.bufferInput)
- bufferSlider.value = bufferInMiles
-
- // set initial buffer text
- bufferValue.text = String.format(Locale.getDefault(),"%d miles", bufferSlider.value.toInt())
- bufferSlider.addOnChangeListener { _, value, _ ->
- // update buffer text value on slider change
- bufferValue.text = String.format(Locale.getDefault(),"%d miles", value.toInt())
- bufferInMiles = value
- }
-
- // display the dialog
- dialogBuilder.show()
- }
-
- // clear the graphics from the graphics overlays
- clearButton.setOnClickListener {
- planarGraphicsOverlay.graphics.clear()
- geodesicGraphicsOverlay.graphics.clear()
- tapLocationsOverlay.graphics.clear()
- // set button interaction
- clearButton.isEnabled = false
- }
- }
-}
diff --git a/create-planar-and-geodetic-buffers/src/main/res/drawable-v24/ic_launcher_foreground.xml b/create-planar-and-geodetic-buffers/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/create-planar-and-geodetic-buffers/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-planar-and-geodetic-buffers/src/main/res/drawable/ic_launcher_background.xml b/create-planar-and-geodetic-buffers/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/create-planar-and-geodetic-buffers/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/create-planar-and-geodetic-buffers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher_round.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher_round.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/create-planar-and-geodetic-buffers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml b/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml
deleted file mode 100644
index 18f848bf8..000000000
--- a/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- Create planar and geodetic buffers
- Specify the buffer in miles then tap on map
- Geodesic Buffer
- Planar Buffer
- 2000
- Clear all
- miles
-
diff --git a/cut-geometry/.gitignore b/cut-geometry/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/cut-geometry/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/cut-geometry/build.gradle.kts b/cut-geometry/build.gradle.kts
deleted file mode 100644
index 14ff2d0ac..000000000
--- a/cut-geometry/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.cutgeometry"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.cutgeometry"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/cut-geometry/proguard-rules.pro b/cut-geometry/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/cut-geometry/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/cut-geometry/src/main/AndroidManifest.xml b/cut-geometry/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/cut-geometry/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt b/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt
deleted file mode 100644
index 55fede4fb..000000000
--- a/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt
+++ /dev/null
@@ -1,203 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.cutgeometry
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.MapView
-import com.esri.arcgismaps.sample.cutgeometry.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- private val lakeSuperiorPolygon by lazy {
- PolygonBuilder(SpatialReference.webMercator()) {
- addPoint(Point(-10254374.668616, 5908345.076380))
- addPoint(Point(-10178382.525314, 5971402.386779))
- addPoint(Point(-10118558.923141, 6034459.697178))
- addPoint(Point(-9993252.729399, 6093474.872295))
- addPoint(Point(-9882498.222673, 6209888.368416))
- addPoint(Point(-9821057.766387, 6274562.532928))
- addPoint(Point(-9690092.583250, 6241417.023616))
- addPoint(Point(-9605207.742329, 6206654.660191))
- addPoint(Point(-9564786.389509, 6108834.986367))
- addPoint(Point(-9449989.747500, 6095091.726408))
- addPoint(Point(-9462116.153346, 6044160.821855))
- addPoint(Point(-9417652.665244, 5985145.646738))
- addPoint(Point(-9438671.768711, 5946341.148031))
- addPoint(Point(-9398250.415891, 5922088.336339))
- addPoint(Point(-9419269.519357, 5855797.317714))
- addPoint(Point(-9467775.142741, 5858222.598884))
- addPoint(Point(-9462924.580403, 5902686.086985))
- addPoint(Point(-9598740.325877, 5884092.264688))
- addPoint(Point(-9643203.813979, 5845287.765981))
- addPoint(Point(-9739406.633691, 5879241.702350))
- addPoint(Point(-9783061.694736, 5922896.763395))
- addPoint(Point(-9844502.151022, 5936640.023354))
- addPoint(Point(-9773360.570059, 6019099.583107))
- addPoint(Point(-9883306.649729, 5968977.105610))
- addPoint(Point(-9957681.938918, 5912387.211662))
- addPoint(Point(-10055501.612742, 5871965.858842))
- addPoint(Point(-10116942.069028, 5884092.264688))
- addPoint(Point(-10111283.079633, 5933406.315128))
- addPoint(Point(-10214761.742852, 5888134.399970))
- addPoint(Point(-10254374.668616, 5901877.659929))
- }.toGeometry()
- }
-
- private val borderPolyline by lazy {
- PolylineBuilder(SpatialReference.webMercator()) {
- addPoint(Point(-9981328.687124, 6111053.281447))
- addPoint(Point(-9946518.044066, 6102350.620682))
- addPoint(Point(-9872545.427566, 6152390.920079))
- addPoint(Point(-9838822.617103, 6157830.083057))
- addPoint(Point(-9446115.050097, 5927209.572793))
- addPoint(Point(-9430885.393759, 5876081.440801))
- addPoint(Point(-9415655.737420, 5860851.784463))
- }.toGeometry()
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- val mapView = activityMainBinding.mapView
- lifecycle.addObserver(mapView)
- val cutButton = activityMainBinding.cutButton
- val resetButton = activityMainBinding.resetButton
-
- // set the map to be displayed in this view
- mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
-
- // create a graphic overlay
- val graphicsOverlay = GraphicsOverlay()
- mapView.graphicsOverlays.add(graphicsOverlay)
-
- val (polygonGraphic, polylineGraphic) = createGraphics(graphicsOverlay, mapView)
-
- cutButton.setOnClickListener {
- // cut the graphic along the polyline to create 2 graphic parts
- polygonGraphic.geometry?.let { graphicGeometry ->
- val parts = GeometryEngine.tryCut(
- graphicGeometry,
- polylineGraphic.geometry as Polyline
- )
-
- // create graphics for the US and Canada sides
- val canadaSide = Graphic(
- parts[0], SimpleFillSymbol(
- SimpleFillSymbolStyle.BackwardDiagonal,
- Color.green, SimpleLineSymbol(SimpleLineSymbolStyle.Null, Color.blue, 0F)
- )
- )
- val usSide = Graphic(
- parts[1], SimpleFillSymbol(
- SimpleFillSymbolStyle.ForwardDiagonal,
- Color.yellow, SimpleLineSymbol(SimpleLineSymbolStyle.Null, Color.blue, 0F)
- )
- )
- // add the graphics to the graphics overlay
- graphicsOverlay.graphics.addAll(listOf(canadaSide, usSide))
-
- // swap button state
- cutButton.isEnabled = false
- resetButton.isEnabled = true
- }
- }
-
- resetButton.setOnClickListener {
- // clear existing graphics
- graphicsOverlay.graphics.clear()
-
- // recreate original graphics
- createGraphics(graphicsOverlay, mapView)
-
- // swap button state
- cutButton.isEnabled = true
- resetButton.isEnabled = false
- }
-
- }
-
- /**
- * Create polygon and polyline graphics.
- *
- * @return polygon and polyline graphics
- */
- private fun createGraphics(
- graphicsOverlay: GraphicsOverlay,
- mapView: MapView
- ): Pair {
- // create a blue polygon graphic to cut
- val polygonGraphic = Graphic(
- lakeSuperiorPolygon,
- SimpleFillSymbol(
- SimpleFillSymbolStyle.Solid, Color(R.color.transparentBlue),
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 2F)
- )
- )
- graphicsOverlay.graphics.add(polygonGraphic)
-
- // create a red polyline graphic to cut the polygon
- val polylineGraphic = Graphic(
- borderPolyline, SimpleLineSymbol(
- SimpleLineSymbolStyle.Dot,
- Color.red, 3F
- )
- )
- graphicsOverlay.graphics.add(polylineGraphic)
- // zoom to show the polygon graphic
- polygonGraphic.geometry?.let { graphicGeometry ->
- mapView.setViewpoint(Viewpoint(graphicGeometry))
- }
- return Pair(polygonGraphic, polylineGraphic)
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
- private val Color.Companion.yellow: Color
- get() {
- return fromRgba(255, 255, 0, 255)
- }
-}
diff --git a/cut-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml b/cut-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/cut-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/cut-geometry/src/main/res/drawable/ic_launcher_background.xml b/cut-geometry/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/cut-geometry/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/cut-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher.png b/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png b/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/cut-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher.png b/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png b/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/cut-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png b/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png b/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/cut-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/cut-geometry/src/main/res/values/strings.xml b/cut-geometry/src/main/res/values/strings.xml
deleted file mode 100644
index 93f6c7659..000000000
--- a/cut-geometry/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Cut geometry
- Cut geometry
- Reset
-
diff --git a/display-clusters/.gitignore b/display-clusters/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-clusters/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-clusters/build.gradle.kts b/display-clusters/build.gradle.kts
deleted file mode 100644
index ef83b8376..000000000
--- a/display-clusters/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displayclusters"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.displayclusters"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/display-clusters/proguard-rules.pro b/display-clusters/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-clusters/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-clusters/src/main/AndroidManifest.xml b/display-clusters/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/display-clusters/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt b/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt
deleted file mode 100644
index 1d507ca55..000000000
--- a/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displayclusters
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.displayclusters.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- FeatureReductionApp()
- }
- }
- }
-
- @Composable
- private fun FeatureReductionApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/display-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-clusters/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-clusters/src/main/res/drawable/ic_launcher_background.xml b/display-clusters/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-clusters/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-clusters/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-clusters/src/main/res/mipmap-hdpi/ic_launcher.png b/display-clusters/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-clusters/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-clusters/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-mdpi/ic_launcher.png b/display-clusters/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-clusters/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-clusters/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-clusters/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-clusters/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-clusters/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-clusters/src/main/res/values/strings.xml b/display-clusters/src/main/res/values/strings.xml
deleted file mode 100644
index d0352fc1e..000000000
--- a/display-clusters/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Display clusters
- https://www.arcgis.com/
-
diff --git a/display-composable-mapview/.gitignore b/display-composable-mapview/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-composable-mapview/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-composable-mapview/build.gradle.kts b/display-composable-mapview/build.gradle.kts
deleted file mode 100644
index 4a3921c15..000000000
--- a/display-composable-mapview/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displaycomposablemapview"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.displaycomposablemapview"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/display-composable-mapview/proguard-rules.pro b/display-composable-mapview/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-composable-mapview/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-composable-mapview/src/main/AndroidManifest.xml b/display-composable-mapview/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/display-composable-mapview/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt b/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt
deleted file mode 100644
index 7e214d9f6..000000000
--- a/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaycomposablemapview
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.ui.Modifier
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.toolkit.geoviewcompose.MapView
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- // create a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- MapView(
- modifier = Modifier.fillMaxSize(),
- arcGISMap = map
- )
- }
- }
- }
-}
diff --git a/display-composable-mapview/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-composable-mapview/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-composable-mapview/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-composable-mapview/src/main/res/drawable/ic_launcher_background.xml b/display-composable-mapview/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-composable-mapview/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-composable-mapview/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher.png b/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher.png b/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-composable-mapview/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-composable-mapview/src/main/res/values/strings.xml b/display-composable-mapview/src/main/res/values/strings.xml
deleted file mode 100644
index cddd6bc7b..000000000
--- a/display-composable-mapview/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Display composable mapView
-
diff --git a/display-device-location-with-nmea-data-sources/.gitignore b/display-device-location-with-nmea-data-sources/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-device-location-with-nmea-data-sources/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-device-location-with-nmea-data-sources/build.gradle.kts b/display-device-location-with-nmea-data-sources/build.gradle.kts
deleted file mode 100644
index 1c8d60d70..000000000
--- a/display-device-location-with-nmea-data-sources/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(project(":samples-lib"))
-}
diff --git a/display-device-location-with-nmea-data-sources/proguard-rules.pro b/display-device-location-with-nmea-data-sources/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-device-location-with-nmea-data-sources/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml b/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml
deleted file mode 100644
index 07ff67411..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt b/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt
deleted file mode 100644
index c63dc3b5f..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // ArcGIS Portal item containing the Redlands.nmea
- // which features a vehicle driving around southern Redlands, CA.
- "https://www.arcgis.com/home/item.html?id=d5bad9f4fee9483791e405880fb466da"
- )
-
- )
- }
-}
diff --git a/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt b/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt
deleted file mode 100644
index 2d8bfa7b2..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt
+++ /dev/null
@@ -1,277 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.location.LocationDataSourceStatus
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.location.NmeaGnssSystem
-import com.arcgismaps.location.NmeaLocationDataSource
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources.databinding.ActivityMainBinding
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-import java.nio.charset.StandardCharsets
-import java.util.*
-import kotlin.concurrent.timerTask
-
-class MainActivity : AppCompatActivity() {
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- // create a new NMEA location data source
- private val nmeaLocationDataSource: NmeaLocationDataSource =
- NmeaLocationDataSource(SpatialReference.wgs84())
-
- // create a timer to simulate a stream of NMEA data
- private var timer = Timer()
-
- // list of nmea location sentences
- private var nmeaSentences: List? = emptyList()
-
- // index of nmea location sentence
- private var locationIndex = 0
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val accuracyTV: TextView by lazy {
- activityMainBinding.accuracyTV
- }
-
- private val satelliteCountTV: TextView by lazy {
- activityMainBinding.satelliteCountTV
- }
-
- private val satelliteIDsTV: TextView by lazy {
- activityMainBinding.satelliteIDsTV
- }
-
- private val systemTypeTV: TextView by lazy {
- activityMainBinding.systemTypeTV
- }
-
- private val playPauseFAB: FloatingActionButton by lazy {
- activityMainBinding.playPauseFAB
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- mapView.map = map
-
- // set a viewpoint on the map view centered on Redlands, California
- mapView.setViewpoint(
- Viewpoint(
- Point(-117.191, 34.0306, SpatialReference.wgs84()), 100000.0
- )
- )
-
- mapView.locationDisplay.apply {
- // set the map view's location display to use the nmea location data source
- dataSource = nmeaLocationDataSource
- // set the map view to recenter on location changed events
- setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- }
-
- // disable map view interaction, the location display will automatically center on the mock device location
- mapView.interactionOptions.apply {
- isPanEnabled = false
- isZoomEnabled = false
- isRotateEnabled = false
- }
-
- // read nmea location sentences from file
- nmeaSentences = getNMEASentenceList()
- // collects the accuracy for each location change
- collectLocationChanges()
- // collects satellite changes and display satellite information
- collectSatelliteChanges()
- }
-
- /**
- * Reads NMEA location sentences from the .nmea file and
- * returns it as a [MutableList]
- */
- private fun getNMEASentenceList(): List? {
- val simulatedNmeaDataFile = File("$provisionPath/Redlands.nmea")
- if (!simulatedNmeaDataFile.exists()) {
- showError("NMEA file does not exist")
- return null
- }
- // create list of nmea location sentences
- var nmeaSentences: List = emptyList()
- // create a buffered reader using the .nmea file
- val bufferedReader = File(simulatedNmeaDataFile.path).bufferedReader()
- // read the nmea file contents using a buffered reader and store the mock data sentences in a list
- bufferedReader.useLines { bufferReaderLines ->
- // add carriage return for nmea location data source parser
- nmeaSentences = bufferReaderLines.map { it + "\n" }.toList()
- }
- return nmeaSentences
- }
-
- /**
- * Control the start/stop status of the NMEA location data source
- */
- fun playPauseClick(view: View) = lifecycleScope.launch {
- if (nmeaLocationDataSource.status.value != LocationDataSourceStatus.Started) {
- // initialize the location data source and prepare to begin receiving location updates when data is pushed
- // as updates are received, they will be displayed on the map
- nmeaLocationDataSource.start().onFailure {
- showError("NmeaLocationDataSource failed to start: ${it.message}")
- return@launch
- }
- // starts the NMEA mock data sentences
- nmeaSentences?.let { startNMEAMockData(it) }
- setButtonStatus(true)
- } else {
- // stop receiving and displaying location data
- nmeaLocationDataSource.stop()
- // cancel up the timer task
- timer.cancel()
- setButtonStatus(false)
- clearUI()
- }
- }
-
- /**
- * Initializes the location data source, reads the mock data NMEA sentences, and displays location updates from that file
- * on the location display. Data is pushed to the data source using a timeline to simulate live updates, as they would
- * appear if using real-time data from a GPS dongle
- */
-
- /**
- * Push the mock data NMEA sentences into the data source every 250 ms
- */
- private fun startNMEAMockData(nmeaSentences: List) {
- timer = Timer()
- timer.schedule(timerTask {
- // only push data when started
- if (nmeaLocationDataSource.status.value == LocationDataSourceStatus.Started)
- nmeaLocationDataSource.pushData(
- nmeaSentences[locationIndex++].toByteArray(StandardCharsets.UTF_8)
- )
- // reset the location index after the last data point is reached
- if (locationIndex == nmeaSentences.size) locationIndex = 0
- }, 250, 250)
- }
-
- /**
- * Sets the FAB button to "Start"/"Stop" based on [isShowingLocation]
- */
- private fun setButtonStatus(isShowingLocation: Boolean) = if (isShowingLocation) {
- playPauseFAB.setImageDrawable(
- AppCompatResources.getDrawable(
- this, R.drawable.ic_round_pause_24
- )
- )
- } else {
- playPauseFAB.setImageDrawable(
- AppCompatResources.getDrawable(
- this, R.drawable.ic_round_play_arrow_24
- )
- )
- }
-
- /**
- * Collects location changes of the NMEA location data source,
- * and displays the location accuracy
- */
- private fun collectLocationChanges() = lifecycleScope.launch {
- nmeaLocationDataSource.locationChanged.collect { nmeaLocation ->
- // convert from meters to foot
- val horizontalAccuracy = nmeaLocation.horizontalAccuracy * 3.28084
- val verticalAccuracy = nmeaLocation.verticalAccuracy * 3.28084
- accuracyTV.text =
- getString(R.string.accuracy) + "Horizontal-%.1fft, Vertical-%.1fft".format(
- horizontalAccuracy, verticalAccuracy
- )
- }
- }
-
- /**
- * Obtains NMEA satellite information from the NMEA location data source,
- * and displays satellite information on the app
- */
- private fun collectSatelliteChanges() = lifecycleScope.launch {
- nmeaLocationDataSource.satellitesChanged.collect { nmeaSatelliteInfoList ->
- // set the text of the satellite count label
- satelliteCountTV.text = getString(R.string.satellite_count) + nmeaSatelliteInfoList.size
- // get the system of the first satellite
- val satelliteSystems = when (nmeaSatelliteInfoList.first().system) {
- NmeaGnssSystem.Bds -> "BDS"
- NmeaGnssSystem.Galileo -> "Galileo"
- NmeaGnssSystem.Glonass -> "Glonass"
- NmeaGnssSystem.Gps -> "GPS"
- NmeaGnssSystem.NavIc -> "NavIc"
- NmeaGnssSystem.Qzss -> "Qzss"
- NmeaGnssSystem.Unknown -> "Unknown"
- }
- // get the satellite IDs from the info list
- val uniqueSatelliteIDs = nmeaSatelliteInfoList.map { it.id }
- // display the satellite system and id information
- systemTypeTV.text = getString(R.string.system) + satelliteSystems
- satelliteIDsTV.text = getString(R.string.satellite_ids) + uniqueSatelliteIDs
- }
- }
-
- /**
- * Clears out the info messages when LocationDataSource is paused.
- */
- private fun clearUI() {
- accuracyTV.text = getString(R.string.accuracy)
- satelliteCountTV.text = getString(R.string.satellite_count)
- satelliteIDsTV.text = getString(R.string.satellite_ids)
- systemTypeTV.text = getString(R.string.system)
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-device-location-with-nmea-data-sources/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_launcher_background.xml b/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-device-location-with-nmea-data-sources/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml b/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml
deleted file mode 100644
index a77258522..000000000
--- a/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
- Display device location with NMEA data sources
- Start/Stop button
- Accuracy:
- Satellite Count:
- System:
- Satellite IDs:
-
diff --git a/display-dimensions/.gitignore b/display-dimensions/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-dimensions/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-dimensions/README.md b/display-dimensions/README.md
deleted file mode 100644
index 8ec9d3164..000000000
--- a/display-dimensions/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Display dimensions
-
-Display dimension features from a mobile map package.
-
-![Image showing the Display Dimensions sample](display-dimensions.png)
-
-## Use case
-
-Dimensions show specific lengths or distances on a map. A dimension may indicate the length of a side of a building or land parcel, or the distance between two features, such as a fire hydrant and the corner of a building.
-
-## How to use the sample
-
-When the sample loads, it will automatically display the map containing dimension features from the mobile map package. The name of the dimension layer containing the dimension features is displayed in the controls box. Control the visibility of the dimension layer with the "Dimension Settings" button, and apply a definition expression to show dimensions of greater than or equal to 450m in length using the "Definition Expression" switch.
-
-## How it works
-
-1. Create a `MobileMapPackage` specifying the path to the .mmpk file.
-2. Load the mobile map package with `mobileMapPackage.load()`.
-3. After it successfully loads, get the first map from the mmpk and set it to the map view: `mapView.map = mobileMapPackage.maps[0]`.
-4. Loop through the map's layers to create a `DimensionLayer`.
-5. Control the dimension layer's visibility with `dimensionLayer.isVisible` and set a definition expression with `dimensionLayer.definitionExpression`.
-
-## Relevant API
-
-* DimensionLayer
-* MobileMapPackage
-
-## About the data
-
-This sample shows a subset of the Edinburgh, Scotland network of pylons, substations, and powerlines within an [Edinburgh Pylon Dimensions mobile map package](https://arcgis.com/home/item.html?id=f5ff6f5556a945bca87ca513b8729a1e), digitized from satellite imagery. Note the data is intended as illustrative of the network only.
-
-## Additional information
-
-Dimension layers can be taken offline from a feature service hosted on ArcGIS Enterprise 10.9 or later, using the [GeodatabaseSyncTask](https://developers.arcgis.com/java/api-reference/reference/com/esri/arcgisruntime/tasks/geodatabase/GeodatabaseSyncTask.html). Dimension layers are also supported in mobile map packages or mobile geodatabases created in ArcGIS Pro 2.9 or later.
-
-## Tags
-
-dimension, layer, mmpk, mobile map package, utility
diff --git a/display-dimensions/build.gradle.kts b/display-dimensions/build.gradle.kts
deleted file mode 100644
index 75ff23e82..000000000
--- a/display-dimensions/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displaydimensions"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.displaydimensions"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(project(":samples-lib"))
-}
diff --git a/display-dimensions/proguard-rules.pro b/display-dimensions/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-dimensions/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-dimensions/src/main/AndroidManifest.xml b/display-dimensions/src/main/AndroidManifest.xml
deleted file mode 100644
index a524252ff..000000000
--- a/display-dimensions/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt b/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt
deleted file mode 100644
index b757d083a..000000000
--- a/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.esri.arcgismaps.sample.displaydimensions
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // ArcGIS Portal item containing the mmpk file which is a section of the
- // high-voltage electricity transmission network around Edinburgh, Scotland.
- "https://www.arcgis.com/home/item.html?id=f5ff6f5556a945bca87ca513b8729a1e"
- )
- )
- }
-}
diff --git a/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt b/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt
deleted file mode 100644
index dca5f1be4..000000000
--- a/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaydimensions
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.MobileMapPackage
-import com.arcgismaps.mapping.layers.DimensionLayer
-import com.esri.arcgismaps.sample.displaydimensions.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.displaydimensions.databinding.DimensionsDialogLayoutBinding
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-
-class MainActivity : AppCompatActivity() {
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val optionsButton by lazy {
- activityMainBinding.optionsButton
- }
-
- // keep an instance of the MapView's dimension layer
- private var dimensionLayer: DimensionLayer? = null
-
- // track if the layer is enabled
- private var isDimensionLayerEnabled: Boolean = true
-
- // track if the custom definition is enabled
- private var isDefinitionEnabled: Boolean = false
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // check if the .mmpk file exits
- val mmpkFile = File(provisionPath, getString(R.string.file_name))
- if (!mmpkFile.exists()) return showError("Mobile map package file does not exist.")
-
- // create and load a mobile map package
- val mobileMapPackage = MobileMapPackage(mmpkFile.path)
-
- lifecycleScope.launch {
- // load the mobile map package
- mobileMapPackage.load().getOrElse {
- return@launch showError("Failed to load the mobile map package: ${it.message}")
- }
- // if the loaded mobile map package does not contain a map
- if (mobileMapPackage.maps.isEmpty()) {
- return@launch showError("Mobile map package does not contain a map")
- }
-
- // add the map from the mobile map package to the map view,
- // and set a min scale to maintain dimension readability
- mapView.map = mobileMapPackage.maps[0]
- mapView.map?.minScale = 35000.0
-
- // set the dimension layer within the map
- dimensionLayer = mapView.map?.operationalLayers?.firstOrNull { layer ->
- layer is DimensionLayer
- } as DimensionLayer
-
- }
-
- optionsButton.setOnClickListener {
- // inflate the dialog layout and get references to each of its components
- val dialogBinding = DimensionsDialogLayoutBinding.inflate(layoutInflater)
- dialogBinding.dimensionLayerSwitch.apply {
- isChecked = isDimensionLayerEnabled
- setOnCheckedChangeListener { _, isEnabled ->
- // set the visibility of the dimension layer
- dimensionLayer?.isVisible = isEnabled
- isDimensionLayerEnabled = isEnabled
- }
- }
- dialogBinding.definitionSwitch.apply {
- isChecked = isDefinitionEnabled
- setOnCheckedChangeListener { _, isEnabled ->
- // set a definition expression to show dimension lengths of
- // greater than or equal to 450m when the checkbox is selected,
- // or to reset the definition expression to show all
- // dimension lengths when unselected
- val defExpression = if (isEnabled) "DIMLENGTH >= 450" else ""
- dimensionLayer?.definitionExpression = defExpression
- isDefinitionEnabled = isEnabled
- }
- }
-
- // set up the dialog
- MaterialAlertDialogBuilder(this).apply {
- setView(dialogBinding.root)
- setTitle("${getString(R.string.settings)}: ${dimensionLayer?.name}")
- }.show()
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/display-dimensions/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-dimensions/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-dimensions/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-dimensions/src/main/res/drawable/ic_launcher_background.xml b/display-dimensions/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-dimensions/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-dimensions/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher.png b/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-dimensions/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher.png b/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-dimensions/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-dimensions/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-dimensions/src/main/res/values/strings.xml b/display-dimensions/src/main/res/values/strings.xml
deleted file mode 100644
index 4d80e1948..000000000
--- a/display-dimensions/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Display dimensions
- Dimension settings
- Dimension Layer
- Definition expression: Dimensions >= 450m
- Edinburgh_Pylon_Dimensions.mmpk
-
diff --git a/display-map-from-mobile-map-package/.gitignore b/display-map-from-mobile-map-package/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-map-from-mobile-map-package/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-map-from-mobile-map-package/build.gradle.kts b/display-map-from-mobile-map-package/build.gradle.kts
deleted file mode 100644
index deee869e3..000000000
--- a/display-map-from-mobile-map-package/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displaymapfrommobilemappackage"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.displaymapfrommobilemappackage"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/display-map-from-mobile-map-package/proguard-rules.pro b/display-map-from-mobile-map-package/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-map-from-mobile-map-package/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-map-from-mobile-map-package/src/main/AndroidManifest.xml b/display-map-from-mobile-map-package/src/main/AndroidManifest.xml
deleted file mode 100644
index a524252ff..000000000
--- a/display-map-from-mobile-map-package/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt b/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt
deleted file mode 100644
index fb390329c..000000000
--- a/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaymapfrommobilemappackage
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // ArcGIS Portal item containing the .mmpk mobile map package
- "https://www.arcgis.com/home/item.html?id=e1f3a7254cb845b09450f54937c16061"
- )
-
- )
- }
-}
diff --git a/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt b/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt
deleted file mode 100644
index 0844febe7..000000000
--- a/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaymapfrommobilemappackage
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.MobileMapPackage
-import com.esri.arcgismaps.sample.displaymapfrommobilemappackage.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-
-class MainActivity : AppCompatActivity() {
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- // create and add the MapView to the lifecycle
- val mapView = activityMainBinding.mapView
- lifecycle.addObserver(mapView)
-
- // get the file path of the (.mmpk) file
- val filePath = provisionPath + getString(R.string.yellowstone_mmpk)
-
- // create the mobile map package
- val mapPackage = MobileMapPackage(filePath)
-
- lifecycleScope.launch {
- // load the mobile map package
- mapPackage.load().getOrElse {
- showError(it.message.toString(), mapView)
- }
- // add the map from the mobile map package to the MapView
- mapView.map = mapPackage.maps.first()
- }
- }
-
- private fun showError(message: String, view: View) {
- Log.e(localClassName, message)
- Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/display-map-from-mobile-map-package/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-map-from-mobile-map-package/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-map-from-mobile-map-package/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map-from-mobile-map-package/src/main/res/drawable/ic_launcher_background.xml b/display-map-from-mobile-map-package/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-map-from-mobile-map-package/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-map-from-mobile-map-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher.png b/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher.png b/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-map-from-mobile-map-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map-from-mobile-map-package/src/main/res/values/strings.xml b/display-map-from-mobile-map-package/src/main/res/values/strings.xml
deleted file mode 100644
index bbf2930a2..000000000
--- a/display-map-from-mobile-map-package/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Display map from mobile map package
- /Yellowstone.mmpk
-
diff --git a/display-map/.gitignore b/display-map/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-map/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-map/build.gradle.kts b/display-map/build.gradle.kts
deleted file mode 100644
index a9fc7d09d..000000000
--- a/display-map/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displaymap"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.displaymap"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/display-map/proguard-rules.pro b/display-map/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-map/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-map/src/main/AndroidManifest.xml b/display-map/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/display-map/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt b/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt
deleted file mode 100644
index a8be48198..000000000
--- a/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displaymap
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.esri.arcgismaps.sample.displaymap.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- lifecycle.addObserver(activityMainBinding.mapView)
-
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- activityMainBinding.mapView.map = map
- }
-}
diff --git a/display-map/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map/src/main/res/drawable/ic_launcher_background.xml b/display-map/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-map/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-map/src/main/res/mipmap-hdpi/ic_launcher.png b/display-map/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-map/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-map/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-map/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-mdpi/ic_launcher.png b/display-map/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-map/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-map/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-map/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-map/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-map/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-map/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-map/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-map/src/main/res/values/strings.xml b/display-map/src/main/res/values/strings.xml
deleted file mode 100644
index 58953e88d..000000000
--- a/display-map/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Display map
-
diff --git a/display-scene-from-mobile-scene-package/.gitignore b/display-scene-from-mobile-scene-package/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-scene-from-mobile-scene-package/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-scene-from-mobile-scene-package/build.gradle.kts b/display-scene-from-mobile-scene-package/build.gradle.kts
deleted file mode 100644
index f9da5e2d2..000000000
--- a/display-scene-from-mobile-scene-package/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displayscenefrommobilescenepackage"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.displayscenefrommobilescenepackage"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/display-scene-from-mobile-scene-package/proguard-rules.pro b/display-scene-from-mobile-scene-package/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-scene-from-mobile-scene-package/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml b/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml
deleted file mode 100644
index a524252ff..000000000
--- a/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt b/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt
deleted file mode 100644
index f1b7093f5..000000000
--- a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // ArcGIS Portal item containing the .mspk mobile scene package
- "https://www.arcgis.com/home/item.html?id=7dd2f97bb007466ea939160d0de96a9d"
- )
- )
- }
-}
diff --git a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt b/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt
deleted file mode 100644
index 48a800e18..000000000
--- a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.screens.MainScreen
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- DisplaySceneFromMobileScenePackageApp()
- }
- }
- }
-
- @Composable
- private fun DisplaySceneFromMobileScenePackageApp() {
- Surface(color = MaterialTheme.colorScheme.background) {
- MainScreen(sampleName = getString(R.string.app_name))
- }
- }
-}
diff --git a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt b/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt
deleted file mode 100644
index 02d3f6c2c..000000000
--- a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.components
-
-import android.app.Application
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.lifecycle.AndroidViewModel
-import com.arcgismaps.mapping.ArcGISScene
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.MobileScenePackage
-import com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.R
-import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import java.io.File
-
-class SceneViewModel(
- private val application: Application,
- private val sampleCoroutineScope: CoroutineScope
-) : AndroidViewModel(application) {
-
- // create a base scene to be used to load the mobile scene package
- var scene by mutableStateOf(ArcGISScene(BasemapStyle.ArcGISStreets))
-
- // create a ViewModel to handle dialog interactions
- val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
-
- private val provisionPath: String by lazy {
- application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
- R.string.app_name
- )
- }
-
- init {
- createMobileScenePackage()
- }
-
- private fun createMobileScenePackage() {
- // get the file path of the (.mspk) file
- val filePath = provisionPath + application.getString(R.string.philadelphia_mspk)
-
- // create the mobile scene package
- val mobileScenePackage = MobileScenePackage(filePath)
-
- sampleCoroutineScope.launch {
- // load the mobile scene package
- mobileScenePackage.load().onSuccess {
- // update the mutable state holder with the first scene from the MobileScenePackage
- scene = mobileScenePackage.scenes.first()
- }.onFailure { error ->
- // show the message dialog and pass the error message to be displayed in the dialog
- messageDialogVM.showMessageDialog(error.message.toString(), error.cause.toString())
- }
- }
- }
-}
diff --git a/display-scene-from-mobile-scene-package/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-scene-from-mobile-scene-package/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-scene-from-mobile-scene-package/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene-from-mobile-scene-package/src/main/res/drawable/ic_launcher_background.xml b/display-scene-from-mobile-scene-package/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-scene-from-mobile-scene-package/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-scene-from-mobile-scene-package/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-scene-from-mobile-scene-package/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml b/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml
deleted file mode 100644
index 108b13b8f..000000000
--- a/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Display scene from mobile scene package
- /philadelphia.mspk
-
diff --git a/display-scene/.gitignore b/display-scene/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/display-scene/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/display-scene/build.gradle.kts b/display-scene/build.gradle.kts
deleted file mode 100644
index 69367856e..000000000
--- a/display-scene/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.displayscene"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.displayscene"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/display-scene/proguard-rules.pro b/display-scene/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/display-scene/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/display-scene/src/main/AndroidManifest.xml b/display-scene/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/display-scene/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt b/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt
deleted file mode 100644
index 51162a418..000000000
--- a/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.displayscene
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISScene
-import com.arcgismaps.mapping.ArcGISTiledElevationSource
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.view.Camera
-import com.esri.arcgismaps.sample.displayscene.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val sceneView by lazy {
- activityMainBinding.sceneView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(sceneView)
-
- // create an elevation source, and add this to the base surface of the scene
- val elevationSource = ArcGISTiledElevationSource(
- resources.getString(R.string.elevation_image_service)
- )
-
- // create a scene with a imagery basemap style
- val imageryScene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
- // add the elevation source to the base surface
- baseSurface.elevationSources.add(elevationSource)
- }
-
- // add a camera and initial camera position
- val camera = Camera(
- latitude = 28.4,
- longitude = 83.9,
- altitude = 10010.0,
- heading = 10.0,
- pitch = 80.0,
- roll = 0.0
- )
-
- // apply the scene to the sceneView and set its viewpoint
- sceneView.apply {
- scene = imageryScene
- setViewpointCamera(camera)
- }
- }
-}
diff --git a/display-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml b/display-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/display-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene/src/main/res/drawable/ic_launcher_background.xml b/display-scene/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/display-scene/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/display-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/display-scene/src/main/res/mipmap-hdpi/ic_launcher.png b/display-scene/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/display-scene/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png b/display-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/display-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-mdpi/ic_launcher.png b/display-scene/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/display-scene/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png b/display-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/display-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xhdpi/ic_launcher.png b/display-scene/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/display-scene/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/display-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/display-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png b/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/display-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/display-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/display-scene/src/main/res/values/strings.xml b/display-scene/src/main/res/values/strings.xml
deleted file mode 100644
index 068b43027..000000000
--- a/display-scene/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Display scene
- https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
-
diff --git a/download-vector-tiles-to-local-cache/.gitignore b/download-vector-tiles-to-local-cache/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/download-vector-tiles-to-local-cache/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/download-vector-tiles-to-local-cache/build.gradle.kts b/download-vector-tiles-to-local-cache/build.gradle.kts
deleted file mode 100644
index 8ae706194..000000000
--- a/download-vector-tiles-to-local-cache/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.downloadvectortilestolocalcache"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.downloadvectortilestolocalcache"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/download-vector-tiles-to-local-cache/proguard-rules.pro b/download-vector-tiles-to-local-cache/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/download-vector-tiles-to-local-cache/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml b/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt b/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt
deleted file mode 100644
index 38cb42dd3..000000000
--- a/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt
+++ /dev/null
@@ -1,329 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.downloadvectortilestolocalcache
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.Basemap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.ViewpointType
-import com.arcgismaps.mapping.layers.ArcGISVectorTiledLayer
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesJob
-import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesParameters
-import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesResult
-import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesTask
-import com.esri.arcgismaps.sample.downloadvectortilestolocalcache.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.downloadvectortilestolocalcache.databinding.ProgressDialogLayoutBinding
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-
-class MainActivity : AppCompatActivity() {
-
- private val downloadArea: Graphic = Graphic()
- private var hasCurrentJobCompleted: Boolean = true
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val previewMapView by lazy {
- activityMainBinding.previewMapView
- }
-
- private val exportVectorTilesButton: MaterialButton by lazy {
- activityMainBinding.exportVectorTilesButton
- }
-
- private val closePreviewButton: MaterialButton by lazy {
- activityMainBinding.closePreviewButton
- }
-
- // inflate the progress dialog
- private val dialogLayoutBinding by lazy {
- ProgressDialogLayoutBinding.inflate(layoutInflater)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // add mapView to the lifecycle
- lifecycle.addObserver(mapView)
- lifecycle.addObserver(previewMapView)
-
- // create a graphic to show a red outline square around the vector tiles to be downloaded
- downloadArea.symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2f)
-
- // create a graphics overlay and add the downloadArea graphic
- val graphicsOverlay = GraphicsOverlay(listOf(downloadArea))
-
- mapView.apply {
- // set the map to BasemapType navigation night
- map = ArcGISMap(BasemapStyle.ArcGISStreetsNight)
- // disable rotation
- interactionOptions.isRotateEnabled = false
- // set the viewpoint of the sample to ESRI Redlands, CA campus
- setViewpoint(Viewpoint(34.056295, -117.195800, 100000.0))
- // add the graphics overlay to the MapView
- graphicsOverlays.add(graphicsOverlay)
- }
-
- lifecycleScope.launch {
- mapView.map?.load()?.onSuccess {
- // enable the export tiles button
- exportVectorTilesButton.isEnabled = true
- // update the red square whenever the viewpoint changes
- mapView.viewpointChanged.collect {
- updateDownloadAreaGeometry()
- }
- }?.onFailure {
- showMessage("Error loading map")
- }
- }
- }
-
- /**
- * Sets up the ExportVectorTilesTask on export button click.
- * Then calls handleExportVectorTilesJob()
- */
- fun exportButtonClick(view: View) {
- // check that the layer from the basemap is a vector tiled layer
- val vectorTiledLayer =
- mapView.map?.basemap?.value?.baseLayers?.get(0) as ArcGISVectorTiledLayer
-
- lifecycleScope.launch {
- // update the download area's geometry using the current viewpoint
- updateDownloadAreaGeometry()
- // create a new export vector tiles task
- val exportVectorTilesTask = ExportVectorTilesTask(vectorTiledLayer.uri.toString())
- val geometry = downloadArea.geometry
- if (geometry == null) {
- showMessage("Error retrieving download area geometry")
- return@launch
- }
- // set the parameters of the export vector tiles task
- // using the geometry of the area to export and it's max scale
- val exportVectorTilesParametersResult = exportVectorTilesTask
- .createDefaultExportVectorTilesParameters(
- geometry,
- // set the max scale parameter to 10% of the map's scale so the
- // number of tiles exported are within the vector tiled layer's max tile export limit
- mapView.mapScale.value * 0.1
- )
-
- // get the loaded vector tile parameters
- val exportVectorTilesParameters = exportVectorTilesParametersResult.getOrElse {
- showMessage(it.message.toString())
- return@launch
- }
-
- if (hasCurrentJobCompleted) {
- // create a job to export vector tiles
- initiateExportTilesJob(
- exportVectorTilesParameters,
- exportVectorTilesTask
- )
- } else {
- showMessage("Previous job is cancelling asynchronously")
- }
- }
-
- }
-
- /**
- * Start the export vector tiles job using [exportVectorTilesTask] and the
- * [exportVectorTilesParameters]. The vector tile package is exported as "file.vtpk"
- */
- private fun initiateExportTilesJob(
- exportVectorTilesParameters: ExportVectorTilesParameters,
- exportVectorTilesTask: ExportVectorTilesTask
- ) {
- // create a .vtpk and directory in the app's cache for saving exported tiles
- val vtpkFile = File(externalCacheDir, "/StyleItemResources/myVectorTiles.vtpk")
- val resDir = File(externalCacheDir, "/StyleItemResources")
- resDir.deleteRecursively()
- resDir.mkdir()
-
- // create a job with the export vector tile parameters
- // and exports the vector tile package as "file.vtpk"
- val exportVectorTilesJob = exportVectorTilesTask.createExportVectorTilesJob(
- exportVectorTilesParameters,
- vtpkFile.absolutePath, resDir.absolutePath
- ).apply {
- // start the export vector tile cache job
- start()
- }
-
- // display the progress dialog
- val dialog = createProgressDialog(exportVectorTilesJob).show()
-
- // since job is now started, set to false
- hasCurrentJobCompleted = false
-
- // set the value of the job's progress
- with(lifecycleScope) {
- // collect the progress of the job
- launch {
- exportVectorTilesJob.progress.collect {
- val progress = exportVectorTilesJob.progress.value
- dialogLayoutBinding.progressBar.progress = progress
- dialogLayoutBinding.progressTextView.text = "$progress% completed"
- }
- }
- // display map if job succeeds
- launch {
- exportVectorTilesJob.result().onSuccess {
- // display the map preview using the result from the completed job
- showMapPreview(it)
- // set job is completed
- hasCurrentJobCompleted = true
- // display the path of the saved vector tiles
- showMessage(it.vectorTileCache?.path.toString())
- // dismiss loading dialog
- dialog.dismiss()
- }.onFailure {
- showMessage(it.message.toString())
- dialog.dismiss()
- hasCurrentJobCompleted = true
- }
- }
-
- }
- }
-
- /**
- * Updates the [downloadArea]'s geometry when called with viewpoint change
- * or when export tiles button is clicked.
- */
- private fun updateDownloadAreaGeometry() {
- // upper left corner of the downloaded tile cache area
- val minScreenPoint = ScreenCoordinate(150.0, 175.0)
- // lower right corner of the downloaded tile cache area
- val maxScreenPoint = ScreenCoordinate(
- mapView.width - 150.0,
- mapView.height - 250.0
- )
- // convert screen points to map points
- val minPoint = mapView.screenToLocation(minScreenPoint)
- val maxPoint = mapView.screenToLocation(maxScreenPoint)
- if (minPoint != null && maxPoint != null) {
- // use the points to define and return an envelope
- downloadArea.geometry = Envelope(minPoint, maxPoint)
- } else {
- showMessage("Error getting screen coordinate")
- }
- }
-
- /**
- * Create a progress dialog to track the progress of the [exportVectorTilesJob]
- */
- private fun createProgressDialog(exportVectorTilesJob: ExportVectorTilesJob): MaterialAlertDialogBuilder {
- return MaterialAlertDialogBuilder(this@MainActivity).apply {
- setTitle("Exporting vector tiles")
- setNegativeButton("Cancel job") { _, _ ->
- lifecycleScope.launch {
- // cancels the export job asynchronously
- exportVectorTilesJob.cancel().getOrElse {
- showMessage(it.message.toString())
- }
- // cancel is completed, so set to true
- hasCurrentJobCompleted = true
- }
- }
- // removes parent of the progressDialog layout, if previously assigned
- dialogLayoutBinding.root.parent?.let { parent ->
- (parent as ViewGroup).removeAllViews()
- }
- setCancelable(false)
- setView(dialogLayoutBinding.root)
- }
- }
-
- /**
- * Display the preview of the exported map using the
- * [vectorTilesResult] from the completed job
- */
- private fun showMapPreview(vectorTilesResult: ExportVectorTilesResult) {
- val vectorTileCache = vectorTilesResult.vectorTileCache
- if (vectorTileCache == null) {
- showMessage("Cannot find tile cache")
- return
- }
- // get the layer exported for the preview MapView
- val vectorTiledLayer = ArcGISVectorTiledLayer(
- vectorTileCache,
- vectorTilesResult.itemResourceCache
- )
-
- // control UI visibility
- previewMapVisibility(true)
-
- // set up the preview MapView
- previewMapView.apply {
- map = ArcGISMap(Basemap(vectorTiledLayer))
- mapView.getCurrentViewpoint(ViewpointType.CenterAndScale)?.let { setViewpoint(it) }
- }
- closePreviewButton.setOnClickListener {
- previewMapVisibility(false)
- }
-
- }
-
- /**
- * Controls the visibility of the preview map and the export buttons.
- */
- private fun previewMapVisibility(isVisible: Boolean) = if (isVisible) {
- exportVectorTilesButton.visibility = View.INVISIBLE
- closePreviewButton.visibility = View.VISIBLE
- previewMapView.visibility = View.VISIBLE
- } else {
- exportVectorTilesButton.visibility = View.VISIBLE
- closePreviewButton.visibility = View.INVISIBLE
- previewMapView.visibility = View.GONE
- }
-
- private fun showMessage(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/download-vector-tiles-to-local-cache/src/main/res/drawable-v24/ic_launcher_foreground.xml b/download-vector-tiles-to-local-cache/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/download-vector-tiles-to-local-cache/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/download-vector-tiles-to-local-cache/src/main/res/drawable/ic_launcher_background.xml b/download-vector-tiles-to-local-cache/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/download-vector-tiles-to-local-cache/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/download-vector-tiles-to-local-cache/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher_round.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher_round.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/download-vector-tiles-to-local-cache/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml b/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml
deleted file mode 100644
index 4848bd0b7..000000000
--- a/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Download vector tiles to local cache
- Download Vector Tiles
- Close vector tile preview
-
diff --git a/edit-and-sync-features-with-feature-service/.gitignore b/edit-and-sync-features-with-feature-service/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/edit-and-sync-features-with-feature-service/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/edit-and-sync-features-with-feature-service/build.gradle.kts b/edit-and-sync-features-with-feature-service/build.gradle.kts
deleted file mode 100644
index 6a9979dee..000000000
--- a/edit-and-sync-features-with-feature-service/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/edit-and-sync-features-with-feature-service/proguard-rules.pro b/edit-and-sync-features-with-feature-service/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/edit-and-sync-features-with-feature-service/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml b/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt b/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt
deleted file mode 100644
index a04ce5c32..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml b/edit-and-sync-features-with-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/drawable/ic_launcher_background.xml b/edit-and-sync-features-with-feature-service/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/edit-and-sync-features-with-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml b/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml
deleted file mode 100644
index ba00bfe78..000000000
--- a/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Edit and Sync Features with Feature Service
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/WildfireSync/FeatureServer
- Generate Geodatabase
- Sync Geodatabase
- wildfire_gdb.geodatabase
-
diff --git a/edit-feature-attachments/.gitignore b/edit-feature-attachments/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/edit-feature-attachments/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/edit-feature-attachments/build.gradle.kts b/edit-feature-attachments/build.gradle.kts
deleted file mode 100644
index 2656a685f..000000000
--- a/edit-feature-attachments/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.editfeatureattachments"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.editfeatureattachments"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(libs.androidx.constraintlayout)
- implementation(project(":samples-lib"))
-}
diff --git a/edit-feature-attachments/proguard-rules.pro b/edit-feature-attachments/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/edit-feature-attachments/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/edit-feature-attachments/src/main/AndroidManifest.xml b/edit-feature-attachments/src/main/AndroidManifest.xml
deleted file mode 100644
index c192b9230..000000000
--- a/edit-feature-attachments/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt b/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt
deleted file mode 100644
index 416ba0798..000000000
--- a/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt
+++ /dev/null
@@ -1,341 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.editfeatureattachments
-
-import android.content.Intent
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.drawable.BitmapDrawable
-import android.net.Uri
-import android.os.Bundle
-import android.provider.MediaStore
-import android.util.Log
-import android.view.ViewGroup
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AlertDialog
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.FileProvider
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.data.ArcGISFeature
-import com.arcgismaps.data.Attachment
-import com.arcgismaps.data.FeatureRequestMode
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.GeoElement
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.view.IdentifyLayerResult
-import com.esri.arcgismaps.sample.editfeatureattachments.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.editfeatureattachments.databinding.AttachmentEditSheetBinding
-import com.esri.arcgismaps.sample.editfeatureattachments.databinding.AttachmentLoadingDialogBinding
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-import java.io.FileOutputStream
-
-class MainActivity : AppCompatActivity() {
-
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val attachmentsSheetBinding by lazy {
- AttachmentEditSheetBinding.inflate(layoutInflater)
- }
-
- private val loadingDialogBinding by lazy {
- AttachmentLoadingDialogBinding.inflate(layoutInflater)
- }
-
- // load the Damage to Residential Buildings feature server
- private val serviceFeatureTable by lazy {
- ServiceFeatureTable(getString(R.string.sample_service_url)).apply {
- // set the feature request mode to request from the server as they are needed
- featureRequestMode = FeatureRequestMode.OnInteractionCache
- }
- }
-
- // registers the activity for an image data result from the default image picker
- private val activityResultLauncher =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- result.data?.data?.let { imageUri ->
- // add the image data as a feature attachment
- addFeatureAttachment(imageUri)
- }
- }
-
- // tracks the selected ArcGISFeature and it's attachments
- private var selectedArcGISFeature: ArcGISFeature? = null
-
- // tracks the instance of the bottom sheet
- private var bottomSheet: BottomSheetDialog? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create the feature layer using the service feature table
- val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
-
- // create and add a map with a streets basemap style
- val streetsMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
- operationalLayers.add(featureLayer)
- }
- // set the map and the viewpoint to the MapView
- mapView.apply {
- map = streetsMap
- setViewpoint(Viewpoint(40.0, -95.0, 1e8))
- }
-
- // identify feature selected on map tap
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect { tapConfirmedEvent ->
- // clear any previous selection
- featureLayer.clearSelection()
- // identify tapped feature
- val layerResult = mapView.identifyLayer(
- layer = featureLayer,
- screenCoordinate = tapConfirmedEvent.screenCoordinate,
- tolerance = 5.0,
- returnPopupsOnly = false,
- maximumResults = 1
- ).getOrElse { exception ->
- showError("Failed to select feature: ${exception.message}")
- } as IdentifyLayerResult
-
- // get a list of identified elements
- val resultGeoElements: List = layerResult.geoElements
- // check if a feature was identified
- if (resultGeoElements.isNotEmpty() && resultGeoElements.first() is ArcGISFeature) {
- // retrieve and set the currently selected feature
- val selectedFeature = resultGeoElements.first() as ArcGISFeature
- // highlight the currently selected feature
- featureLayer.selectFeature(selectedFeature)
-
- // show the bottom sheet layout
- createBottomSheet(selectedFeature)
-
- // keep track of the selected feature
- selectedArcGISFeature = selectedFeature
- }
- }
- }
- }
-
- /**
- * Creates and displays a bottom sheet to display and modify
- * the attachments of [selectedFeature]. Calls [AttachmentsBottomSheet] to
- * inflate bottom sheet and listen for interactions.
- */
- private suspend fun createBottomSheet(selectedFeature: ArcGISFeature) {
- // get the number of attachments
- val attachmentList = selectedFeature.fetchAttachments().getOrElse {
- return showError(it.message.toString())
- }
- // get the attribute "typdamage" of the selected feature
- val damageTypeAttribute = selectedFeature.attributes["typdamage"].toString()
-
- // creates a new BottomSheetDialog
- bottomSheet = AttachmentsBottomSheet(
- context = this@MainActivity,
- bottomSheetBinding = attachmentsSheetBinding,
- attachments = attachmentList,
- damageType = damageTypeAttribute
- )
- // set the content view to the root of the binding layout
- bottomSheet?.setContentView(attachmentsSheetBinding.root)
- // display the bottom sheet view
- bottomSheet?.show()
- }
-
- /**
- * Retrieves the [attachment] data in the form of a byte array, converts it
- * to a [BitmapDrawable], caches the bitmap as a png image, and open's the
- * attachment image in the default image viewer.
- */
- suspend fun fetchAttachment(attachment: Attachment) {
- // display loading dialog
- val dialog = createLoadingDialog("Fetching attachment data").show()
-
- // create folder /ArcGIS/Attachments in external storage
- val fileDir = File(getExternalFilesDir(null)?.path + "/Attachments")
- fileDir.mkdirs()
- // create the file with the attachment name
- val file = File(fileDir, attachment.name)
-
- // file provider URI
- val contentUri = FileProvider.getUriForFile(
- this, getString(R.string.provider_authority), file
- )
- // open the file in gallery
- val imageIntent = Intent().apply {
- flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- action = Intent.ACTION_VIEW
- setDataAndType(contentUri, "image/png")
- }
-
- // fetch the attachment data
- attachment.fetchData().onSuccess {
- // create a drawable from InputStream, then create the Bitmap
- val bitmapDrawable = BitmapDrawable(
- resources,
- BitmapFactory.decodeByteArray(it, 0, it.size)
- )
- // create a file output stream using the attachment file
- FileOutputStream(file).use { imageOutputStream ->
- // compress the bitmap to PNG format
- bitmapDrawable.bitmap.compress(Bitmap.CompressFormat.PNG, 90, imageOutputStream)
- // start activity using created intent
- startActivity(imageIntent)
- // dismiss dialog
- dialog.dismiss()
- }
- }.onFailure {
- // dismiss dialog
- dialog.dismiss()
- showError(it.message.toString())
- }
- }
-
- /**
- * Adds a new attachment to the [selectedArcGISFeature] using the [selectedImageUri]
- * and updates the changes with the feature service table
- */
- private fun addFeatureAttachment(selectedImageUri: Uri) {
- // display a loading dialog
- val dialog = createLoadingDialog("Adding feature attachment").show()
-
- // create an input stream at the selected URI
- contentResolver.openInputStream(selectedImageUri)?.use { imageInputStream ->
- // get the byte array of the image input stream
- val imageBytes: ByteArray = imageInputStream.readBytes()
- // create the attachment name with the current time
- val attachmentName = "attachment_${System.currentTimeMillis()}.png"
-
- lifecycleScope.launch {
- selectedArcGISFeature?.let { arcGISFeature ->
- // add the attachment to the selected feature
- arcGISFeature.addAttachment(
- name = attachmentName,
- contentType = "image/png",
- data = imageBytes
- ).onFailure {
- return@launch showError(it.message.toString())
- }
- // update the feature changes in the loaded service feature table
- serviceFeatureTable.updateFeature(arcGISFeature).getOrElse {
- return@launch showError(it.message.toString())
- }
- }
- applyServerEdits(dialog)
- }
- }
- }
-
- /**
- * Delete the [attachment] from the [selectedArcGISFeature] and update the changes
- * with the feature service table
- */
- fun deleteAttachment(attachment: Attachment) {
- lifecycleScope.launch {
- val dialog = createLoadingDialog("Deleting feature attachment").show()
- selectedArcGISFeature?.let { arcGISFeature ->
- // delete the attachment from the selected feature
- arcGISFeature.deleteAttachment(attachment).getOrElse {
- return@launch showError(it.message.toString())
- }
- // update the feature changes in the loaded service feature table
- serviceFeatureTable.updateFeature(arcGISFeature).getOrElse {
- return@launch showError(it.message.toString())
- }
- }
- // apply changes back to the server
- applyServerEdits(dialog)
- }
- }
-
- /**
- * Applies changes from a Service Feature Table to the server.
- * The [dialog] will be dismissed when changes are applied.
- */
- private suspend fun applyServerEdits(dialog: AlertDialog) {
- // close the bottom sheet, as it will be created
- // after service changes are made
- bottomSheet?.dismiss()
-
- // apply edits to the server
- val updatedServerResult = serviceFeatureTable.applyEdits()
- updatedServerResult.onSuccess { edits ->
- dialog.dismiss()
- // check that the feature table was successfully updated
- if (edits.isEmpty()) {
- return showError(getString(R.string.failure_edit_results))
- }
- // if the edits were made successfully, create the bottom sheet to display new changes.
- selectedArcGISFeature?.let { createBottomSheet(it) }
- }.onFailure {
- showError(it.message.toString())
- dialog.dismiss()
- }
- }
-
- /**
- * Opens the default Android image selector
- */
- fun selectAttachment() {
- val mediaIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
- activityResultLauncher.launch(mediaIntent)
- }
-
- /**
- * Creates a loading dialog with the [message]
- */
- private fun createLoadingDialog(message: String): MaterialAlertDialogBuilder {
- // build and return a new alert dialog
- return MaterialAlertDialogBuilder(this).apply {
- // set message
- setMessage(message)
- // allow it to be cancellable
- setCancelable(false)
- // removes parent of the progressDialog layout, if previously assigned
- loadingDialogBinding.root.parent?.let { parent ->
- (parent as ViewGroup).removeAllViews()
- }
- // set the loading dialog layout to this alert dialog
- setView(loadingDialogBinding.root)
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/edit-feature-attachments/src/main/res/drawable-v24/ic_launcher_foreground.xml b/edit-feature-attachments/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/edit-feature-attachments/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-feature-attachments/src/main/res/drawable/ic_launcher_background.xml b/edit-feature-attachments/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/edit-feature-attachments/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-feature-attachments/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher.png b/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher_round.png b/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher.png b/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher_round.png b/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher.png b/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher.png b/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/edit-feature-attachments/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-feature-attachments/src/main/res/values/strings.xml b/edit-feature-attachments/src/main/res/values/strings.xml
deleted file mode 100644
index 6e32b47c0..000000000
--- a/edit-feature-attachments/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
- Edit feature attachments
-
-
- Are you sure you want to delete the attachment?
- Yes
- No
- Server did not return edit results
-
-
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0
- attachment_click
- Edit attachments
- Done
- Add attachment
- com.esri.arcgismaps.sample.editfeatureattachments.provider
-
diff --git a/edit-features-using-feature-forms/.gitignore b/edit-features-using-feature-forms/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/edit-features-using-feature-forms/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/edit-features-using-feature-forms/README.md b/edit-features-using-feature-forms/README.md
deleted file mode 100644
index 834bea7de..000000000
--- a/edit-features-using-feature-forms/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Edit features using feature forms
-
-Display and edit feature attributes using feature forms
-
-![Image of edit features using feature forms](edit-features-using-feature-forms.png)
-
-## Use case
-
-Feature forms help enhance the accuracy, efficiency, and user experience of attribute editing in your application. Forms can be authored as part of the WebMap using [Field Maps Designer](https://www.arcgis.com/apps/fieldmaps/) or using Web Map Viewer. This allows for a simplified user experience to edit feature attribute data on the web-map.
-
-## How to use the sample
-
-Tap a feature on the feature form map to open a bottom sheet displaying the list of form elements. Select through the list of elements to view the coded value field groups and edit elements to update the field values. Tap the submit icon to commit the changes on the web map.
-
-## How it works
-
-1. Add a feature form enabled web-map to the MapView using `PortalItem` URL and itemID.
-2. When the map is tapped, perform an identity operation to check if the tapped location is an `ArcGISFeature` and the `FeatureLayer.featureFormDefinition` is not null, indicating the feature layer does have an associated feature form definition.
-3. Create a `FeatureForm()` object using the identified `ArcGISFeature` and the `FeatureLayer.featureFormDefinition`.
-4. On the screen within a bottom sheet, use the `FeatureForm` Toolkit component to display the feature form configuration by providing the created `featureForm` object.
-5. Optionally, you can add a `validationErrorVisibility` option to the `FeatureForm` Toolkit component that determines the behavior of when the validation errors are visible.
-6. Once edits are added to the form fields, check to verify that there are no validation errors using `featureForm.validationErrors`. The list will be empty if there are no errors.
-7. To commit edits on the service geodatabase:
- 1. Call `featureForm.finishEditing()` to save edits to the database.
- 2. Retrieve the backing service feature table's geodatabase using `(featureForm.feature.featureTable as? ServiceFeatureTable)?.serviceGeodatabase`.
- 3. Verify the service geodatabase can commit changes back to the service using `serviceGeodatabase.serviceInfo?.canUseServiceGeodatabaseApplyEdits`
- 4. If apply edits are allowed, call `serviceGeodatabase.applyEdits()` to apply local edits to the online service.
- 5. If edits are not allowed on the `ServiceGeodatabase`, then apply edits to the `ServiceFeatureTable` using `(featureForm.feature.featureTable as? ServiceFeatureTable)?.applyEdits()`
-
-## Relevant API
-
-* ArcGISFeature
-* FeatureForm
-* FeatureLayer
-* FieldFormElement
-* GroupFormElement
-* ServiceFeatureTable
-
-## Additional information
-
-This sample uses the FeatureForm and GeoViewCompose Toolkit modules to be able to implement a Composable MapView which displays a Composable FeatureForm UI.
-
-## Tags
-
-compose, edits, feature, featureforms, form, geoviewcompose, jetpack, toolkit
diff --git a/edit-features-using-feature-forms/build.gradle.kts b/edit-features-using-feature-forms/build.gradle.kts
deleted file mode 100644
index aa8e957b2..000000000
--- a/edit-features-using-feature-forms/build.gradle.kts
+++ /dev/null
@@ -1,55 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.editfeaturesusingfeatureforms"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.editfeaturesusingfeatureforms"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
- implementation(libs.arcgis.maps.kotlin.toolkit.featureforms)
-}
diff --git a/edit-features-using-feature-forms/proguard-rules.pro b/edit-features-using-feature-forms/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/edit-features-using-feature-forms/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/edit-features-using-feature-forms/src/main/AndroidManifest.xml b/edit-features-using-feature-forms/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/edit-features-using-feature-forms/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt b/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt
deleted file mode 100644
index ec7638add..000000000
--- a/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt
+++ /dev/null
@@ -1,239 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components
-
-import android.app.Application
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.arcgismaps.data.ArcGISFeature
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.exceptions.FeatureFormValidationException
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.PortalItem
-import com.arcgismaps.mapping.featureforms.FeatureForm
-import com.arcgismaps.mapping.featureforms.FeatureFormDefinition
-import com.arcgismaps.mapping.featureforms.FieldFormElement
-import com.arcgismaps.mapping.featureforms.FormElement
-import com.arcgismaps.mapping.featureforms.GroupFormElement
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.view.SingleTapConfirmedEvent
-import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
-import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.R
-import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
-
-/**
- * A view model for the MainScreen UI
- */
-class MapViewModel(application: Application) : AndroidViewModel(application) {
-
- val mapViewProxy = MapViewProxy()
-
- // feature forms enabled web-map showcasing places of interest with form fields
- private var portalItem = PortalItem(application.getString(R.string.feature_form_web_map))
-
- val map = ArcGISMap(portalItem)
-
- // keep track of the selected feature form
- private val _featureForm = MutableStateFlow(null)
- val featureForm: StateFlow = _featureForm.asStateFlow()
-
- // keep track of the list of validation errors
- private val _errors = MutableStateFlow>(listOf())
- val errors: StateFlow> = _errors.asStateFlow()
-
- // create a ViewModel to handle dialog interactions
- val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
-
- init {
- viewModelScope.launch {
- // load a map that has a FeatureFormDefinition on any of its layers
- map.load()
- }
- }
-
- /**
- * Apply attribute edits to the Geodatabase backing the ServiceFeatureTable
- * and refresh the local feature. Persisting changes to attributes is
- * not part of the FeatureForm API.
- *
- * @param onEditsCompleted Invoked when edits are applied successfully
- */
- fun applyEdits(onEditsCompleted: () -> Unit) {
- val featureForm = _featureForm.value
- ?: return messageDialogVM.showMessageDialog("Feature form state is not configured")
-
- // update the state flow with the list of validation errors found
- _errors.value = validateFormInputEdits(featureForm)
- // if there are no errors then update the feature
- if (_errors.value.isEmpty()) {
- val serviceFeatureTable = featureForm.feature.featureTable as? ServiceFeatureTable
- ?: return messageDialogVM.showMessageDialog("Cannot save feature edit without a ServiceFeatureTable")
-
- viewModelScope.launch {
- // commits changes of the edited feature to the database
- featureForm.finishEditing().onSuccess {
- serviceFeatureTable.serviceGeodatabase?.let { database ->
- if (database.serviceInfo?.canUseServiceGeodatabaseApplyEdits == true) {
- // applies all local edits in the tables to the service
- database.applyEdits().onFailure {
- return@onFailure messageDialogVM.showMessageDialog(
- title = it.message.toString(),
- description = it.cause.toString()
- )
- }
- } else {
- // uploads any changes to the local table to the feature service
- serviceFeatureTable.applyEdits().onFailure {
- return@onFailure messageDialogVM.showMessageDialog(
- title = it.message.toString(),
- description = it.cause.toString()
- )
- }
- }
- }
- // resets the attributes and geometry to the values in the data source
- featureForm.feature.refresh()
- // unselect the feature after the edits have been saved
- (featureForm.feature.featureTable?.layer as FeatureLayer).clearSelection()
- // dismiss dialog when edits are completed
- onEditsCompleted()
- }.onFailure {
- return@onFailure messageDialogVM.showMessageDialog(
- title = it.message.toString(),
- description = it.cause.toString()
- )
- }
- }
- }
- }
-
- /**
- * Performs validation checks on the given [featureForm] with local edits.
- * Return a list of [ErrorInfo] if errors are found, if not, empty list is returned.
- */
- private fun validateFormInputEdits(featureForm: FeatureForm): List {
- val errors = mutableListOf()
- // If an element is editable or derives its value from an arcade expression,
- // its errors must be corrected before submitting the form
- featureForm.validationErrors.value.forEach { entry ->
- entry.value.forEach { error ->
- featureForm.elements.getFormElement(entry.key)?.let { formElement ->
- if (formElement.isEditable.value || formElement.hasValueExpression) {
- errors.add(
- ErrorInfo(
- fieldName = formElement.label,
- error = error as FeatureFormValidationException
- )
- )
- }
- }
- }
- }
- return errors
- }
-
- /**
- * Cancels the commit by resetting the validation errors.
- */
- fun cancelCommit() {
- // reset the validation errors
- _errors.value = listOf()
- }
-
- /**
- * Discard edits and unselects feature from the layer
- */
- fun rollbackEdits() {
- // discard local edits to the feature form
- _featureForm.value?.discardEdits()
- // unselect the feature
- (_featureForm.value?.feature?.featureTable?.layer as FeatureLayer).clearSelection()
- // reset the validation errors
- _errors.value = listOf()
- }
-
- /**
- * Perform an identify the tapped [ArcGISFeature] and retrieve the
- * layer's [FeatureFormDefinition] to create the respective [FeatureForm]
- */
- fun onSingleTapConfirmed(singleTapEvent: SingleTapConfirmedEvent) {
- viewModelScope.launch {
- mapViewProxy.identifyLayers(
- screenCoordinate = singleTapEvent.screenCoordinate,
- tolerance = 22.dp,
- returnPopupsOnly = false
- ).onSuccess { results ->
- try {
- results.forEach { result ->
- result.geoElements.firstOrNull {
- it is ArcGISFeature && (it.featureTable?.layer as? FeatureLayer)?.featureFormDefinition != null
- }?.let {
- val feature = it as ArcGISFeature
- val layer = feature.featureTable!!.layer as FeatureLayer
- val featureForm = FeatureForm(feature, layer.featureFormDefinition!!)
- // select the feature
- layer.selectFeature(feature)
- // set the UI to an editing state with the FeatureForm
- _featureForm.value = featureForm
- }
- }
- } catch (e: Exception) {
- messageDialogVM.showMessageDialog(
- title = "Failed to create feature form for the feature",
- description = e.message.toString()
- )
- }
- }
- }
- }
-}
-
-/**
- * Returns the [FieldFormElement] with the given [fieldName] in the [FeatureForm]. If none exists
- * null is returned.
- */
-fun List.getFormElement(fieldName: String): FieldFormElement? {
- val fieldElements = filterIsInstance()
- val element = if (fieldElements.isNotEmpty()) {
- fieldElements.firstNotNullOfOrNull {
- if (it.fieldName == fieldName) it else null
- }
- } else {
- null
- }
-
- return element ?: run {
- val groupElements = filterIsInstance()
- if (groupElements.isNotEmpty()) {
- groupElements.firstNotNullOfOrNull {
- it.elements.getFormElement(fieldName)
- }
- } else {
- null
- }
- }
-}
-
-/**
- * Class that provides a validation error [error] for the field with name [fieldName].
- */
-data class ErrorInfo(val fieldName: String, val error: FeatureFormValidationException)
diff --git a/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt b/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt
deleted file mode 100644
index e2b815c40..000000000
--- a/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt
+++ /dev/null
@@ -1,354 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.screens
-
-import android.content.res.Configuration
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.ime
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Check
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Button
-import androidx.compose.material3.Card
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.ModalBottomSheet
-import androidx.compose.material3.OutlinedButton
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.material3.rememberModalBottomSheetState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import com.arcgismaps.toolkit.featureforms.FeatureForm
-import com.arcgismaps.toolkit.featureforms.ValidationErrorVisibility
-import com.arcgismaps.toolkit.featureforms.theme.FeatureFormDefaults
-import com.arcgismaps.toolkit.geoviewcompose.MapView
-import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.R
-import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components.ErrorInfo
-import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components.MapViewModel
-import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
-import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import kotlinx.coroutines.launch
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun MainScreen(mapViewModel: MapViewModel) {
-
- val scope = rememberCoroutineScope()
- var showBottomSheet by remember { mutableStateOf(false) }
- val sheetState = rememberModalBottomSheetState()
-
- // the feature form the currently selected feature
- val featureForm by mapViewModel.featureForm.collectAsState()
- // the validation errors found when the edits are applied
- val formValidationErrors by mapViewModel.errors.collectAsState()
-
- // boolean trackers for save and discard edits dialogs
- var showSaveEditsDialog by remember { mutableStateOf(false) }
- var showDiscardEditsDialog by remember { mutableStateOf(false) }
-
- Scaffold(
- modifier = Modifier.fillMaxSize(),
- topBar = { SampleTopAppBar(title = stringResource(R.string.app_name)) }
- ) { padding ->
- // display the composable map using the mapViewModel
- MapView(
- arcGISMap = mapViewModel.map,
- mapViewProxy = mapViewModel.mapViewProxy,
- modifier = Modifier
- .padding(padding)
- .fillMaxSize(),
- onSingleTapConfirmed = { mapViewModel.onSingleTapConfirmed(it) }
- )
-
- // update bottom sheet visibility when a feature is selected
- LaunchedEffect(featureForm) {
- showBottomSheet = featureForm != null
- }
-
- if (showBottomSheet && featureForm != null) {
- // display feature form bottom sheet
- ModalBottomSheet(
- onDismissRequest = {
- showBottomSheet = false
- showDiscardEditsDialog = true
- },
- sheetState = sheetState
- ) {
- // top bar to manage save or discard edits
- TopFormBar(
- onClose = { showDiscardEditsDialog = true },
- onSave = {
- showSaveEditsDialog = true
- mapViewModel.applyEdits {
- scope.launch {
- sheetState.hide()
- showBottomSheet = false
- showSaveEditsDialog = false
- }
- }
- })
- // display the selected feature form using the Toolkit component
- FeatureForm(
- featureForm = featureForm!!,
- modifier = Modifier
- .fillMaxSize()
- .padding(top = 20.dp)
- .navigationBarsPadding(),
- validationErrorVisibility = ValidationErrorVisibility.Automatic,
- colorScheme = FeatureFormDefaults.colorScheme(
- groupElementColors = FeatureFormDefaults.groupElementColors(
- outlineColor = MaterialTheme.colorScheme.secondary,
- labelColor = MaterialTheme.colorScheme.onSurface,
- supportingTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
- containerColor = MaterialTheme.colorScheme.surfaceContainer
- )
- ),
- typography = FeatureFormDefaults.typography(
- readOnlyFieldTypography = FeatureFormDefaults.readOnlyFieldTypography(
- labelStyle = MaterialTheme.typography.headlineSmall,
- textStyle = MaterialTheme.typography.bodyMedium,
- supportingTextStyle = MaterialTheme.typography.labelLarge
- )
- )
- )
- }
- }
- }
-
- if (showSaveEditsDialog && formValidationErrors.isNotEmpty() && showBottomSheet) {
- // validation errors found, cancel the commit and show validation errors
- ValidationErrorsDialog(errors = formValidationErrors) {
- showSaveEditsDialog = false
- mapViewModel.cancelCommit()
- }
- } else if (showSaveEditsDialog && showBottomSheet) {
- // no validation errors found, show dialog when commiting edits
- SaveFormDialog()
- }
-
- if (showDiscardEditsDialog) {
- DiscardEditsDialog(
- onConfirm = {
- mapViewModel.rollbackEdits()
- scope.launch {
- sheetState.hide()
- showDiscardEditsDialog = false
- showBottomSheet = false
- }
- },
- onCancel = {
- showDiscardEditsDialog = false
- showBottomSheet = true
- }
- )
- }
-
- // Display a MessageDialog with any error information
- mapViewModel.messageDialogVM.apply {
- if (dialogStatus) {
- MessageDialog(
- title = messageTitle,
- description = messageDescription,
- onDismissRequest = ::dismissDialog
- )
- }
- }
-}
-
-@Composable
-fun DiscardEditsDialog(onConfirm: () -> Unit, onCancel: () -> Unit) {
- AlertDialog(
- onDismissRequest = onCancel,
- confirmButton = {
- Button(onClick = onConfirm) {
- Text(text = stringResource(R.string.discard))
- }
- },
- dismissButton = {
- OutlinedButton(onClick = onCancel) {
- Text(text = stringResource(R.string.cancel))
- }
- },
- title = {
- Text(text = stringResource(R.string.discard_edits))
- },
- text = {
- Text(text = stringResource(R.string.all_changes_will_be_lost))
- }
- )
-}
-
-@Composable
-fun TopFormBar(
- onClose: () -> Unit = {},
- onSave: () -> Unit = {},
-) {
- Column {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 12.dp),
- horizontalArrangement = Arrangement.Absolute.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- IconButton(onClick = onClose) {
- Icon(
- imageVector = Icons.Default.Close,
- contentDescription = "Close Feature Editor"
- )
- }
- Text(
- text = "Edit feature",
- style = MaterialTheme.typography.titleMedium,
- textAlign = TextAlign.Center
- )
- IconButton(onClick = onSave) {
- Icon(
- imageVector = Icons.Default.Check,
- contentDescription = "Save Feature",
- tint = MaterialTheme.colorScheme.primary
- )
- }
- }
- HorizontalDivider()
- }
-}
-
-@Composable
-private fun SaveFormDialog() {
- // show a progress dialog when no errors are present
- Dialog(onDismissRequest = { /* cannot be dismissed */ }) {
- Card(modifier = Modifier.wrapContentSize()) {
- Column(
- modifier = Modifier.padding(25.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- ) {
- CircularProgressIndicator()
- Spacer(modifier = Modifier.height(10.dp))
- Text(text = "Saving..")
- }
- }
- }
-}
-
-@Composable
-private fun ValidationErrorsDialog(errors: List, onDismissRequest: () -> Unit) {
- // show all the validation errors in a dialog
- AlertDialog(
- onDismissRequest = onDismissRequest,
- modifier = Modifier.heightIn(max = 600.dp),
- confirmButton = {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center
- ) {
- Button(onClick = onDismissRequest) {
- Text(text = stringResource(R.string.view))
- }
- }
- },
- title = {
- Column {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = "Form Validation Errors",
- style = MaterialTheme.typography.titleLarge,
- textAlign = TextAlign.Center
- )
- }
- },
- text = {
- Card(modifier = Modifier.fillMaxWidth()) {
- Column(modifier = Modifier.padding(15.dp)) {
- Text(
- text = stringResource(R.string.attributes_failed, errors.count()),
- color = MaterialTheme.colorScheme.error
- )
- Spacer(modifier = Modifier.height(10.dp))
- LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) {
- items(errors.count()) { index ->
- Text(
- text = "${errors[index].fieldName}: ${errors[index].error::class.simpleName.toString()}",
- color = MaterialTheme.colorScheme.error
- )
- }
- }
- }
- }
- }
- )
-}
-
-@Preview
-@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-fun SavePreview() {
- SampleAppTheme { SaveFormDialog() }
-}
-
-@Preview(showBackground = true)
-@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-fun ValidationErrorsPreview() {
- SampleAppTheme { ValidationErrorsDialog(listOf()) { } }
-}
-
-@Preview(showBackground = true)
-@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-fun DiscardEditsDialogPreview() {
- SampleAppTheme { DiscardEditsDialog(onConfirm = {}) {} }
-}
-
-@Preview(showBackground = true)
-@Composable
-fun TopFormBarPreview() {
- SampleAppTheme { TopFormBar() }
-}
diff --git a/edit-features-using-feature-forms/src/main/res/drawable-v24/ic_launcher_foreground.xml b/edit-features-using-feature-forms/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/edit-features-using-feature-forms/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-features-using-feature-forms/src/main/res/drawable/ic_launcher_background.xml b/edit-features-using-feature-forms/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/edit-features-using-feature-forms/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/edit-features-using-feature-forms/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher.png b/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher_round.png b/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher.png b/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher_round.png b/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher.png b/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher.png b/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/edit-features-using-feature-forms/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/edit-features-using-feature-forms/src/main/res/values/strings.xml b/edit-features-using-feature-forms/src/main/res/values/strings.xml
deleted file mode 100644
index 1fc3c8787..000000000
--- a/edit-features-using-feature-forms/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
- Edit features using feature forms
- https://www.arcgis.com/home/item.html?id=516e4d6aeb4c495c87c41e11274c767f
- Cancel
- %1$s attributes failed
- View
- Discard
- Discard Edits?
- All changes made within the form will be lost.
-
diff --git a/find-address-with-reverse-geocode/.gitignore b/find-address-with-reverse-geocode/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-address-with-reverse-geocode/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-address-with-reverse-geocode/build.gradle.kts b/find-address-with-reverse-geocode/build.gradle.kts
deleted file mode 100644
index 861468512..000000000
--- a/find-address-with-reverse-geocode/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findaddresswithreversegeocode"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.findaddresswithreversegeocode"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.android.material)
- implementation(libs.androidx.constraintlayout)
- implementation(project(":samples-lib"))
-}
diff --git a/find-address-with-reverse-geocode/proguard-rules.pro b/find-address-with-reverse-geocode/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-address-with-reverse-geocode/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-address-with-reverse-geocode/src/main/AndroidManifest.xml b/find-address-with-reverse-geocode/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/find-address-with-reverse-geocode/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt b/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt
deleted file mode 100644
index 0b5bf0930..000000000
--- a/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findaddresswithreversegeocode
-
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.tasks.geocode.LocatorTask
-import com.esri.arcgismaps.sample.findaddresswithreversegeocode.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // service url to be provided to the LocatorTask (geocoder)
- private val geocodeServer =
- "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer"
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // create a MapView using binding
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // display the street of the tapped location
- private val titleTV by lazy {
- activityMainBinding.titleTV
- }
-
- // display the metro area of the tapped location
- private val descriptionTV by lazy {
- activityMainBinding.descriptionTV
- }
-
- // set the pin graphic for tapped location
- private val pinSymbol by lazy {
- createPinSymbol()
- }
-
- // create a graphics overlay
- private val graphicsOverlay = GraphicsOverlay()
-
- // locator task to provide geocoding services
- private val locatorTask = LocatorTask(geocodeServer)
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- mapView.apply {
- // add a map with a imagery basemap style
- map = ArcGISMap(BasemapStyle.ArcGISImagery)
- // add a graphics overlay to the map for showing where the user tapped
- graphicsOverlays.add(graphicsOverlay)
- // set initial viewpoint
- setViewpoint(Viewpoint(34.058, -117.195, 5e4))
- }
-
- lifecycleScope.launch {
- // load geocode locator task
- locatorTask.load().onSuccess {
- // locator task loaded, look for geo view tapped
- mapView.onSingleTapConfirmed.collect { event ->
- event.mapPoint?.let { mapPoint -> geoViewTapped(mapPoint) }
- }
- }.onFailure {
- showError(it.message.toString())
- }
- }
- }
-
- /**
- * Displays a pin of the tapped location using [mapPoint]
- * and finds address with reverse geocode
- */
- private suspend fun geoViewTapped(mapPoint: Point) {
- // create graphic for tapped point
- val pinGraphic = Graphic(mapPoint, pinSymbol)
- graphicsOverlay.graphics.apply {
- // clear existing graphics
- clear()
- // add the pin graphic
- add(pinGraphic)
- }
- // normalize the geometry - needed if the user crosses the international date line.
- val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
- // reverse geocode to get address
- locatorTask.reverseGeocode(normalizedPoint).onSuccess { addresses ->
- // get the first result
- val address = addresses.firstOrNull()
- if (address == null) {
- showError("Could not find address at tapped point")
- return@onSuccess
- }
- // use the street and region for the title
- val title = address.attributes["Address"].toString()
- // use the metro area for the description details
- val description = "${address.attributes["City"]} " +
- "${address.attributes["Region"]} " +
- "${address.attributes["CountryCode"]}"
- // set the strings to the text views
- titleTV.text = title
- descriptionTV.text = description
- }.onFailure {
- showError(it.message.toString())
- }
- }
-
- /**
- * Create a picture marker symbol to represent a pin at the tapped location
- */
- private fun createPinSymbol(): PictureMarkerSymbol {
- // get pin drawable
- val pinDrawable = ContextCompat.getDrawable(
- this,
- R.drawable.baseline_location_pin_red_48
- )
- //add a graphic for the tapped point
- val pinSymbol = PictureMarkerSymbol.createWithImage(
- pinDrawable as BitmapDrawable
- )
- pinSymbol.apply {
- // resize the dimensions of the symbol
- width = 50f
- height = 50f
- // the image is a pin so offset the image so that the pinpoint
- // is on the point rather than the image's true center
- leaderOffsetX = 30f
- offsetY = 25f
- }
- return pinSymbol
- }
-
- private fun showError(errorMessage: String) {
- Log.e(localClassName, errorMessage)
- Snackbar.make(mapView, errorMessage, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/find-address-with-reverse-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-address-with-reverse-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-address-with-reverse-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-address-with-reverse-geocode/src/main/res/drawable/ic_launcher_background.xml b/find-address-with-reverse-geocode/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-address-with-reverse-geocode/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-address-with-reverse-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher.png b/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher.png b/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-address-with-reverse-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-address-with-reverse-geocode/src/main/res/values/strings.xml b/find-address-with-reverse-geocode/src/main/res/values/strings.xml
deleted file mode 100644
index c3ef88ed3..000000000
--- a/find-address-with-reverse-geocode/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Find address with reverse geocode
- Tap on map to find address
-
diff --git a/find-closest-facility-from-point/.gitignore b/find-closest-facility-from-point/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-closest-facility-from-point/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-closest-facility-from-point/build.gradle.kts b/find-closest-facility-from-point/build.gradle.kts
deleted file mode 100644
index ca5d27c24..000000000
--- a/find-closest-facility-from-point/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findclosestfacilityfrompoint"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.findclosestfacilityfrompoint"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/find-closest-facility-from-point/proguard-rules.pro b/find-closest-facility-from-point/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-closest-facility-from-point/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-closest-facility-from-point/src/main/AndroidManifest.xml b/find-closest-facility-from-point/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/find-closest-facility-from-point/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt b/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt
deleted file mode 100644
index 1c92443c9..000000000
--- a/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findclosestfacilityfrompoint
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.findclosestfacilityfrompoint.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/find-closest-facility-from-point/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-closest-facility-from-point/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-closest-facility-from-point/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-closest-facility-from-point/src/main/res/drawable/ic_launcher_background.xml b/find-closest-facility-from-point/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-closest-facility-from-point/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-closest-facility-from-point/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher.png b/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher.png b/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-closest-facility-from-point/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-closest-facility-from-point/src/main/res/values/strings.xml b/find-closest-facility-from-point/src/main/res/values/strings.xml
deleted file mode 100644
index aa5a437ed..000000000
--- a/find-closest-facility-from-point/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Find closest facility from point
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility
- https://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png
-
diff --git a/find-nearest-vertex/.gitignore b/find-nearest-vertex/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-nearest-vertex/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-nearest-vertex/build.gradle.kts b/find-nearest-vertex/build.gradle.kts
deleted file mode 100644
index f64dc37c1..000000000
--- a/find-nearest-vertex/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findnearestvertex"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.findnearestvertex"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/find-nearest-vertex/proguard-rules.pro b/find-nearest-vertex/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-nearest-vertex/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-nearest-vertex/src/main/AndroidManifest.xml b/find-nearest-vertex/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/find-nearest-vertex/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt b/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt
deleted file mode 100644
index ce139e643..000000000
--- a/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt
+++ /dev/null
@@ -1,192 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findnearestvertex
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.portal.Portal
-import com.arcgismaps.mapping.PortalItem
-import com.esri.arcgismaps.sample.findnearestvertex.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val distanceLayout: ConstraintLayout by lazy {
- activityMainBinding.distanceLayout
- }
-
- private val vertexDistanceTextView: TextView by lazy {
- activityMainBinding.vertexDistanceTextView
- }
-
- private val coordinateDistanceTextView: TextView by lazy {
- activityMainBinding.coordinateDistanceTextView
- }
-
- // California zone 5 (ftUS) state plane coordinate system.
- private val statePlaneCaliforniaZone5SpatialReference = SpatialReference(2229)
-
- // create graphics with symbols for tapped location, nearest coordinate, and nearest vertex
- private val tappedLocationGraphic =
- Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.X, Color.magenta, 15f))
-
- // create graphic symbol of the nearest coordinate
- private val nearestCoordinateGraphic =
- Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Diamond, Color.red, 10f))
-
- // create graphic symbol of the nearest vertex
- private val nearestVertexGraphic =
- Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.blue, 15f))
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a polygon geometry
- val polygon = PolygonBuilder(statePlaneCaliforniaZone5SpatialReference) {
- addPoint(Point(6627416.41469281, 1804532.53233782))
- addPoint(Point(6669147.89779046, 2479145.16609522))
- addPoint(Point(7265673.02678292, 2484254.50442408))
- addPoint(Point(7676192.55880379, 2001458.66365744))
- }.toGeometry()
- // set up the outline and fill color of the polygon
- val polygonOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
- val polygonFillSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.ForwardDiagonal,
- Color.green,
- polygonOutlineSymbol
- )
- // create a polygon graphic
- val polygonGraphic = Graphic(polygon, polygonFillSymbol)
- // create a graphics overlay to show the polygon, tapped location, and nearest vertex/coordinate
- val graphicsOverlay = GraphicsOverlay(
- listOf(
- polygonGraphic,
- tappedLocationGraphic,
- nearestCoordinateGraphic,
- nearestVertexGraphic
- )
- )
-
- // create a map using the portal item
- val map = ArcGISMap(statePlaneCaliforniaZone5SpatialReference)
- val portal = Portal("https://arcgisruntime.maps.arcgis.com")
- val portalItem = PortalItem(portal, "99fd67933e754a1181cc755146be21ca")
- val usStatesGeneralizedLayer = FeatureLayer.createWithItemAndLayerId(portalItem, 0)
- // and add the feature layer to the map's operational layers
- map.operationalLayers.add(usStatesGeneralizedLayer)
- // add the map to the map view
- mapView.map = map
- // add the graphics overlay to the map view
- mapView.graphicsOverlays.add(graphicsOverlay)
- lifecycleScope.launch {
- // check if map has loaded
- map.load().onSuccess {
- // zoom to the polygon's extent
- mapView.setViewpointGeometry(polygon.extent, 100.0)
- // get point on map tapped
- mapView.onSingleTapConfirmed.collect { event ->
- // find nearest vertex on map tapped
- event.mapPoint?.let { findNearestVertex(it, polygon) }
- }
- }.onFailure {
- showError("Error loading map")
- }
- }
- }
-
- /**
- * Finds the nearest vertex from [mapPoint] from the [polygon]
- */
- private fun findNearestVertex(mapPoint: Point, polygon: Polygon) {
- // show where the user clicked
- tappedLocationGraphic.geometry = mapPoint
- // use the geometry engine to get the nearest vertex
- val nearestVertexResult =
- GeometryEngine.nearestVertex(polygon, mapPoint)
- // set the nearest vertex graphic's geometry to the nearest vertex
- nearestVertexGraphic.geometry = nearestVertexResult?.coordinate
- // use the geometry engine to get the nearest coordinate
- val nearestCoordinateResult =
- GeometryEngine.nearestCoordinate(polygon, mapPoint)
- // set the nearest coordinate graphic's geometry to the nearest coordinate
- nearestCoordinateGraphic.geometry = nearestCoordinateResult?.coordinate
- // show the distances to the nearest vertex and nearest coordinate
- distanceLayout.visibility = View.VISIBLE
- // convert distance to miles
- val vertexDistance = ((nearestVertexResult?.distance)?.div(5280.0))?.toInt()
- val coordinateDistance = ((nearestCoordinateResult?.distance)?.div(5280.0))?.toInt()
- // set the distance to the text views
- vertexDistanceTextView.text = getString(R.string.nearest_vertex, vertexDistance)
- coordinateDistanceTextView.text =
- getString(R.string.nearest_coordinate, coordinateDistance)
-
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
- private val Color.Companion.magenta: Color
- get() {
- return fromRgba(255, 0, 255, 255)
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/find-nearest-vertex/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-nearest-vertex/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-nearest-vertex/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-nearest-vertex/src/main/res/drawable/ic_launcher_background.xml b/find-nearest-vertex/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-nearest-vertex/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-nearest-vertex/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher.png b/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher.png b/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-nearest-vertex/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-nearest-vertex/src/main/res/values/strings.xml b/find-nearest-vertex/src/main/res/values/strings.xml
deleted file mode 100644
index f2662bb43..000000000
--- a/find-nearest-vertex/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Find nearest vertex
- Vertex distance: %1$d mi
- "Coordinate distance: %1$d mi"
- Tap on the map to find vertex
-
diff --git a/find-route-around-barriers/.gitignore b/find-route-around-barriers/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-route-around-barriers/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-route-around-barriers/build.gradle.kts b/find-route-around-barriers/build.gradle.kts
deleted file mode 100644
index 9abbceb6d..000000000
--- a/find-route-around-barriers/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findroutearoundbarriers"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.findroutearoundbarriers"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/find-route-around-barriers/proguard-rules.pro b/find-route-around-barriers/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-route-around-barriers/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-route-around-barriers/src/main/AndroidManifest.xml b/find-route-around-barriers/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/find-route-around-barriers/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt b/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt
deleted file mode 100644
index 95c9bfaf9..000000000
--- a/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt
+++ /dev/null
@@ -1,462 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findroutearoundbarriers
-
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.view.ViewGroup
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.ImageView
-import android.widget.ListView
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.CompositeSymbol
-import com.arcgismaps.mapping.symbology.HorizontalAlignment
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.arcgismaps.mapping.symbology.VerticalAlignment
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.tasks.networkanalysis.DirectionManeuver
-import com.arcgismaps.tasks.networkanalysis.PolygonBarrier
-import com.arcgismaps.tasks.networkanalysis.RouteParameters
-import com.arcgismaps.tasks.networkanalysis.RouteTask
-import com.arcgismaps.tasks.networkanalysis.Stop
-import com.esri.arcgismaps.sample.findroutearoundbarriers.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.findroutearoundbarriers.databinding.OptionsDialogBinding
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // show the options dialog
- private val optionsDialogBinding by lazy {
- OptionsDialogBinding.inflate(layoutInflater)
- }
-
- // set up the dialog UI views
- private val findBestSequenceSwitch by lazy {
- optionsDialogBinding.findBestSequenceSwitch
- }
- private val firstStopSwitch by lazy {
- optionsDialogBinding.firstStopSwitch
- }
- private val lastStopSwitch by lazy {
- optionsDialogBinding.lastStopSwitch
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val mainContainer: ConstraintLayout by lazy {
- activityMainBinding.mainContainer
- }
-
- private val addStopsButton: MaterialButton by lazy {
- activityMainBinding.addStopsButton
- }
-
- private val addBarriersButton: MaterialButton by lazy {
- activityMainBinding.addBarriersButton
- }
-
- private val resetButton by lazy {
- activityMainBinding.resetButton
- }
-
- private val optionsButton by lazy {
- activityMainBinding.optionsButton
- }
-
- private val directionsButton by lazy {
- activityMainBinding.directionsButton
- }
-
- private val bottomSheet by lazy {
- activityMainBinding.directionSheet.directionSheetLayout
- }
-
- private val header: ConstraintLayout by lazy {
- activityMainBinding.directionSheet.header
- }
-
- private val imageView: ImageView by lazy {
- activityMainBinding.directionSheet.imageView
- }
-
- private val cancelTV: TextView by lazy {
- activityMainBinding.directionSheet.cancelTv
- }
-
- private val directionsLV: ListView by lazy {
- activityMainBinding.directionSheet.directionsLV
- }
-
- private val stopList by lazy { mutableListOf() }
-
- private val barriersList by lazy { mutableListOf() }
-
- private val directionsList by lazy { mutableListOf() }
-
- private val stopsOverlay by lazy { GraphicsOverlay() }
-
- private val barriersOverlay by lazy { GraphicsOverlay() }
-
- private val routeOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- private val barrierSymbol by lazy {
- SimpleFillSymbol(SimpleFillSymbolStyle.DiagonalCross, Color.red, null)
- }
-
- // create route task from San Diego service
- private val routeTask by lazy {
- RouteTask(getString(R.string.routing_service_url))
- }
-
- private var routeParameters: RouteParameters? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // some parts of the API require an Android Context to properly interact with Android system
- // features, such as LocationProvider and application resources
- ArcGISEnvironment.applicationContext = applicationContext
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- mapView.apply {
- map = ArcGISMap(BasemapStyle.ArcGISStreets)
- setViewpoint(Viewpoint(32.7270, -117.1750, 40000.0))
- graphicsOverlays.addAll(listOf(stopsOverlay, barriersOverlay, routeOverlay))
- }
-
- // set an on touch listener on the map view
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect { event ->
- // add stop or barriers graphics to overlay
- event.mapPoint?.let { mapPoint -> addStopOrBarrier(mapPoint) }
- resetButton.isEnabled = true
- }
- }
-
- // coroutine scope to use the default parameters for the route calculation
- lifecycleScope.launch {
- routeTask.load().onSuccess {
- routeParameters = routeTask.createDefaultParameters().getOrThrow().apply {
- returnStops = true
- returnDirections = true
- }
- }.onFailure {
- showError(it.message.toString())
- }
- }
-
- // make a clear button to reset the stops and routes
- resetButton.setOnClickListener {
- // clear stops from route parameters and stops list
- routeParameters?.clearStops()
- stopList.clear()
- // clear barriers from route parameters and barriers list
- routeParameters?.clearPolygonBarriers()
- barriersList.clear()
- // clear the directions list
- directionsList.clear()
- // clear all graphics overlays
- mapView.graphicsOverlays.forEach { it.graphics.clear() }
- resetButton.isEnabled = false
- }
-
- // display the options dialog having the route finding parameters
- optionsButton.setOnClickListener {
- displayOptionsDialog()
- }
-
- // display the bottom sheet with directions when the button is clicked
- directionsButton.setOnClickListener {
- if (directionsList.isEmpty()) return@setOnClickListener showError("Add stops on map to find route")
- setupBottomSheet(directionsList)
- }
-
- // hide the bottom sheet and make the map view span the whole screen
- bottomSheet.visibility = View.INVISIBLE
- (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
- }
-
- /**
- * Create options dialog with the route finding parameters to reorder stops to find the optimized route
- */
- private fun displayOptionsDialog() {
- // removes parent of the progressDialog layout, if previously assigned
- optionsDialogBinding.root.parent?.let { parent ->
- (parent as ViewGroup).removeAllViews()
- }
-
- // set up the dialog builder
- MaterialAlertDialogBuilder(this).apply {
- setView(optionsDialogBinding.root)
- show()
- }
-
- // set the best sequence toggle state
- findBestSequenceSwitch.isChecked = routeParameters?.findBestSequence ?: false
-
- // solve route on each state change
- findBestSequenceSwitch.setOnCheckedChangeListener { _, _ ->
- // update route params if the switch is toggled
- routeParameters?.findBestSequence = findBestSequenceSwitch.isChecked
- createAndDisplayRoute()
-
- // if best sequence switch is enabled, then enable the options
- if (findBestSequenceSwitch.isChecked) {
- firstStopSwitch.isEnabled = true
- lastStopSwitch.isEnabled = true
-
- } else {
- firstStopSwitch.apply {
- isChecked = false
- isEnabled = false
- }
- lastStopSwitch.apply {
- isChecked = false
- isEnabled = false
- }
- }
- }
- firstStopSwitch.setOnCheckedChangeListener { _, _ ->
- routeParameters?.preserveFirstStop = firstStopSwitch.isChecked
- createAndDisplayRoute()
- }
- lastStopSwitch.setOnCheckedChangeListener { _, _ ->
- routeParameters?.preserveLastStop = lastStopSwitch.isChecked
- createAndDisplayRoute()
- }
- }
-
- /**
- * Add a stop or a barrier at the selected [mapPoint] to the correct graphics
- * overlay depending on which button is currently checked.
- */
- private fun addStopOrBarrier(mapPoint: Point) {
- if (addStopsButton.isChecked) {
- // normalize the geometry - needed if the user crosses the international date line.
- val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
- // use the mapPoint to create a stop
- val stop = Stop(Point(normalizedPoint.x, normalizedPoint.y, mapPoint.spatialReference))
- // add the new stop to the list of stops
- stopList.add(stop)
- // create a marker symbol and graphics, and add the graphics to the graphics overlay
- stopsOverlay.graphics.add(Graphic(mapPoint, createStopSymbol(stopList.size)))
- } else if (addBarriersButton.isChecked) {
- // create a buffered polygon around the clicked point
- val barrierBufferPolygon = GeometryEngine.bufferOrNull(mapPoint, 200.0)
- ?: return showError("Error creating buffer polygon")
- // create a polygon barrier for the routing task, and add it to the list of barriers
- barriersList.add(PolygonBarrier(barrierBufferPolygon))
- barriersOverlay.graphics.add(Graphic(barrierBufferPolygon, barrierSymbol))
- }
- // solve the route once the graphics are created
- createAndDisplayRoute()
- }
-
- /**
- * Create route parameters and a route task from them. Display the route result geometry as a
- * graphic and call showDirectionsInBottomSheet which shows directions in a list view.
- */
- private fun createAndDisplayRoute() = lifecycleScope.launch {
-
- // clear the previous route from the graphics overlay, if it exists
- routeOverlay.graphics.clear()
- // clear the directions list from the directions list view, if they exist
- directionsList.clear()
-
- val routeParameters = routeParameters ?: return@launch
-
- if (stopList.size <= 1) return@launch
-
- routeParameters.apply {
- // add the existing stops and barriers to the route parameters
- setStops(stopList)
- setPolygonBarriers(barriersList)
- }
-
- // solve the route task
- val routeResults = routeParameters.let { routeTask.solveRoute(it) }
-
- routeResults.onSuccess { routeResult ->
- // get the first solved route
- val firstRoute = routeResult.routes[0]
-
- // create Graphic for route
- val graphic = Graphic(
- firstRoute.routeGeometry,
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 3f)
- )
- routeOverlay.graphics.add(graphic)
- // get the direction text for each maneuver and add them to the list to display
- directionsList.addAll(firstRoute.directionManeuvers)
- }.onFailure {
- showError("No route solution. ${it.message}")
- }
-
- }
-
- /** Creates a bottom sheet to display a list of direction maneuvers.
- * [directions] a list of DirectionManeuver which represents the route
- */
- private fun setupBottomSheet(directions: List) {
- val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet).apply {
- // expand the bottom sheet, and ensure it is displayed on the screen when collapsed
- state = BottomSheetBehavior.STATE_EXPANDED
- peekHeight = header.height
- // animate the arrow when the bottom sheet slides
- addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
- override fun onSlide(bottomSheet: View, slideOffset: Float) {
- imageView.rotation = slideOffset * 180f
- }
-
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- imageView.rotation = when (newState) {
- BottomSheetBehavior.STATE_EXPANDED -> 180f
- else -> imageView.rotation
- }
- }
- })
- }
-
- bottomSheet.apply {
- visibility = View.VISIBLE
- // expand or collapse the bottom sheet when the header is clicked
- header.setOnClickListener {
- bottomSheetBehavior.state = when (bottomSheetBehavior.state) {
- BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED
- else -> BottomSheetBehavior.STATE_COLLAPSED
- }
-
- }
- // rotate the arrow so it starts off in the correct rotation
- imageView.rotation = 180f
-
- directionsLV.apply {
- // set the adapter for the list view
- adapter = ArrayAdapter(
- this@MainActivity,
- android.R.layout.simple_list_item_1,
- directions.map { it.directionText }
- )
-
- // when the user taps a maneuver, set the viewpoint to that portion of the route
- onItemClickListener =
- AdapterView.OnItemClickListener { _, _, position, _ ->
- // remove any graphics that are not the original (blue) route graphic
- if (routeOverlay.graphics.size > 1) {
- routeOverlay.graphics.removeAt(routeOverlay.graphics.size - 1)
- }
- // set the viewpoint to the selected maneuver
- val geometry = directionsList[position].geometry
- geometry?.let { mapView.setViewpoint(Viewpoint(it.extent, 20.0)) }
- // create a graphic with a symbol for the maneuver and add it to the graphics overlay
- val selectedRouteSymbol = SimpleLineSymbol(
- SimpleLineSymbolStyle.Solid,
- Color.green, 3f
- )
- routeOverlay.graphics.add(Graphic(geometry, selectedRouteSymbol))
- // collapse the bottom sheet
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
- }
- }
- // hide the bottom sheet when cancel button is clicked
- cancelTV.setOnClickListener {
- bottomSheet.visibility = View.INVISIBLE
- }
- }
-
- }
-
- /**
- * Create a composite symbol consisting of a pin graphic overlaid with a particular [stopNumber].
- * Returns a [CompositeSymbol] consisting of the pin graphic overlaid with the stop number
- */
- private fun createStopSymbol(stopNumber: Int): CompositeSymbol {
- // create black stop number TextSymbol
- val stopNumberSymbol = TextSymbol(
- stopNumber.toString(),
- Color.black,
- 12f,
- HorizontalAlignment.Center,
- VerticalAlignment.Bottom
- ).apply {
- offsetY = 4f
- }
-
- // create a new picture marker from a pin drawable
- val pinSymbol = PictureMarkerSymbol.createWithImage(
- ContextCompat.getDrawable(
- this,
- R.drawable.pin_symbol
- ) as BitmapDrawable
- ).apply {
- // set the scale of the symbol
- width = 24f
- height = 24f
- // set in pin "drop" to be offset to the point on map
- offsetY = 10f
- }
-
- // create a composite symbol and add the picture marker symbol and text symbol
- val compositeSymbol = CompositeSymbol()
- compositeSymbol.symbols.addAll(listOf(pinSymbol, stopNumberSymbol))
-
- return compositeSymbol
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/find-route-around-barriers/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-route-around-barriers/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-route-around-barriers/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_launcher_background.xml b/find-route-around-barriers/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-route-around-barriers/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route-around-barriers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher.png b/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher.png b/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-route-around-barriers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-around-barriers/src/main/res/values/strings.xml b/find-route-around-barriers/src/main/res/values/strings.xml
deleted file mode 100644
index f097deb73..000000000
--- a/find-route-around-barriers/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
- Find route around barriers
- Reset
- Stops
- Barriers
- Directions
- Collapse
- Cancel
- Find the best sequence
- Preserve last stop
- Preserve first stop
- https://sampleserver7.arcgisonline.com/server/rest/services/NetworkAnalysis/SanDiego/NAServer/Route
- Reset button
- Options button
- Directions button
- Collapse directions
-
diff --git a/find-route-in-transport-network/.gitignore b/find-route-in-transport-network/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-route-in-transport-network/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-route-in-transport-network/build.gradle.kts b/find-route-in-transport-network/build.gradle.kts
deleted file mode 100644
index ebc955632..000000000
--- a/find-route-in-transport-network/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findrouteintransportnetwork"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.findrouteintransportnetwork"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(project(":samples-lib"))
-}
diff --git a/find-route-in-transport-network/proguard-rules.pro b/find-route-in-transport-network/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-route-in-transport-network/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-route-in-transport-network/src/main/AndroidManifest.xml b/find-route-in-transport-network/src/main/AndroidManifest.xml
deleted file mode 100644
index 556b670d3..000000000
--- a/find-route-in-transport-network/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt b/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt
deleted file mode 100644
index 3a6720693..000000000
--- a/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.esri.arcgismaps.sample.findrouteintransportnetwork
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- //A zip file containing an offline routing network and .tpkx basemap
- "https://arcgisruntime.maps.arcgis.com/home/item.html?id=df193653ed39449195af0c9725701dca"
- )
-
- )
- }
-}
diff --git a/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt b/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt
deleted file mode 100644
index a82f50e6d..000000000
--- a/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt
+++ /dev/null
@@ -1,316 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findrouteintransportnetwork
-
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.Basemap
-import com.arcgismaps.mapping.layers.ArcGISTiledLayer
-import com.arcgismaps.mapping.layers.TileCache
-import com.arcgismaps.mapping.symbology.CompositeSymbol
-import com.arcgismaps.mapping.symbology.HorizontalAlignment
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.arcgismaps.mapping.symbology.VerticalAlignment
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.tasks.networkanalysis.RouteParameters
-import com.arcgismaps.tasks.networkanalysis.RouteTask
-import com.arcgismaps.tasks.networkanalysis.Stop
-import com.esri.arcgismaps.sample.findrouteintransportnetwork.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-import kotlin.math.roundToInt
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val toggleButtons by lazy {
- activityMainBinding.toggleButtons
- }
-
- private val clearButton by lazy {
- activityMainBinding.clearButton
- }
-
- private val distanceTimeTextView by lazy {
- activityMainBinding.distanceTimeTextView
- }
-
- private val stopsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
- private val routeOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- private val envelope = Envelope(
- Point(-1.3045e7, 3.87e6, 0.0, SpatialReference.webMercator()),
- Point(-1.3025e7, 3.84e6, 0.0, SpatialReference.webMercator())
- )
-
- // create a route task to calculate routes
- private var routeTask: RouteTask? = null
-
- private var routeParameters: RouteParameters? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // some parts of the API require an Android Context to properly interact with Android system
- // features, such as LocationProvider and application resources
- ArcGISEnvironment.applicationContext = applicationContext
- lifecycle.addObserver(mapView)
-
- // create a tile cache from the .tpkx file
- val tileCache = TileCache(provisionPath + getString(R.string.tpkx_path))
- val tiledLayer = ArcGISTiledLayer(tileCache)
- // make a basemap with the tiled layer and add it to the mapview as an ArcGISMap
- mapView.map = ArcGISMap(Basemap(tiledLayer))
-
- // add the graphics overlays to the map view
- mapView.graphicsOverlays.addAll(listOf(routeOverlay, stopsOverlay))
-
- // create a route task using the geodatabase file
- val geodatabaseFile = File(provisionPath + getString(R.string.geodatabase_path))
- routeTask = RouteTask(geodatabaseFile.path, "Streets_ND")
-
- // load the route task
- lifecycleScope.launch {
- routeTask?.load()?.onFailure {
- showError(it.message.toString())
- }?.onSuccess {
- // use the default parameters for the route calculation
- routeParameters = routeTask?.createDefaultParameters()?.getOrThrow()
- }
- }
-
- toggleButtons.addOnButtonCheckedListener { _, checkedId, isChecked ->
- if (isChecked) {
- when (checkedId) {
- R.id.fastestButton -> {
- // calculate fastest route
- routeParameters?.travelMode =
- routeTask?.getRouteTaskInfo()?.travelModes?.get(0)
-
- // update route based on selection
- updateRoute()
- }
- R.id.shortestButton -> {
- // calculate shortest route
- routeParameters?.travelMode =
- routeTask?.getRouteTaskInfo()?.travelModes?.get(1)
-
- // update route based on selection
- updateRoute()
- }
- }
- }
- }
-
- // make a clear button to reset the stops and routes
- clearButton.setOnClickListener {
- stopsOverlay.graphics.clear()
- routeOverlay.graphics.clear()
- clearButton.isEnabled = false
- distanceTimeTextView.text = getString(R.string.tap_on_map_to_create_a_transport_network)
- }
-
- // set up the touch listeners on the map view
- setUpMapView()
- }
-
- /**
- * Sets up the viewpoint and onSingleTapConfirmed for the mapView.
- * For single taps, graphics will be selected.
- * */
- private fun setUpMapView() {
- with(lifecycleScope) {
- // set the viewpoint of the MapView
- launch {
- mapView.setViewpointGeometry(envelope)
- }
-
- // add graphic at the tapped coordinate
- launch {
- mapView.onSingleTapConfirmed.collect { tapEvent ->
- val screenCoordinate = tapEvent.screenCoordinate
- addOrSelectGraphic(screenCoordinate)
- clearButton.isEnabled = true
- }
- }
- }
- }
-
- /**
- * Updates the calculated route using the
- * stops on the map by calling routeTask.solveRoute().
- * Creates a graphic to display the route.
- * */
- private fun updateRoute() = lifecycleScope.launch {
- // get a list of stops from the graphics currently on the graphics overlay.
- val stops = stopsOverlay.graphics.map {
- Stop(it.geometry as Point)
- }
-
- // do not calculate a route if there is only one stop
- if (stops.size <= 1) return@launch
-
- routeParameters?.setStops(stops)
-
- // solve the route
- val results = routeParameters?.let { routeTask?.solveRoute(it) }
- if (results != null) {
- results.onFailure {
- showError("No route solution. ${it.message}")
- routeOverlay.graphics.clear()
- }.onSuccess { routeResult ->
- // get the first solved route result
- val route = routeResult.routes[0]
-
- // create graphic for route
- val graphic = Graphic(
- route.routeGeometry, SimpleLineSymbol(
- SimpleLineSymbolStyle.Solid,
- Color.black, 3F
- )
- )
- routeOverlay.graphics.clear()
- routeOverlay.graphics.add(graphic)
-
- // set distance-time text
- val travelTime = route.travelTime.roundToInt()
- val travelDistance = "%.2f".format(
- route.totalLength * 0.000621371192 // convert meters to miles and round 2 decimals
- )
- distanceTimeTextView.text = String.format("$travelTime min ($travelDistance mi)")
- }
- }
- }
-
- /**
- * Selects a graphic if there is one at the
- * provided [screenCoordinate] or, if there is
- * none, creates a new graphic.
- * */
- private suspend fun addOrSelectGraphic(screenCoordinate: ScreenCoordinate) {
- // identify the selected graphic
- val result =
- mapView.identifyGraphicsOverlay(stopsOverlay, screenCoordinate, 10.0, false)
-
- result.onFailure {
- showError(it.message.toString())
- }.onSuccess { identifyGraphicsOverlayResult ->
- val graphics = identifyGraphicsOverlayResult.graphics
-
- // unselect everything
- if (stopsOverlay.selectedGraphics.isNotEmpty()) {
- stopsOverlay.unselectGraphics(stopsOverlay.selectedGraphics)
- }
-
- // if the user tapped on something, select it
- if (graphics.isNotEmpty()) {
- val firstGraphic = graphics[0]
- firstGraphic.isSelected = true
- } else { // there is no graphic at this location
- val locationPoint = mapView.screenToLocation(screenCoordinate)
- // check if tapped location is within the envelope
- if (GeometryEngine.within(locationPoint as Geometry, envelope))
- // make a new graphic at the tapped location
- createStopSymbol(stopsOverlay.graphics.size + 1, locationPoint)
- else
- showError("Tapped location is outside the transport network")
- }
- }
- }
-
- /**
- * Creates a composite symbol to represent a numbered stop.
- * The [stopNumber] is the ordinal number of this stop and the
- * symbol will be placed at the [locationPoint].
- */
- private fun createStopSymbol(stopNumber: Int, locationPoint: Point?) {
- // create a orange pin PictureMarkerSymbol
- val pinSymbol = PictureMarkerSymbol.createWithImage(
- ContextCompat.getDrawable(
- this,
- R.drawable.pin_symbol
- ) as BitmapDrawable
- ).apply {
- // set the scale of the symbol
- width = 24f
- height = 24f
- // set in pin "drop" to be offset to the point on map
- offsetY = 10f
- }
-
- // create black stop number TextSymbol
- val stopNumberSymbol = TextSymbol(
- stopNumber.toString(),
- Color.black,
- 12f,
- HorizontalAlignment.Center,
- VerticalAlignment.Bottom
- ).apply {
- offsetY = 4f
- }
-
- // create a composite symbol and add the picture marker symbol and text symbol
- val compositeSymbol = CompositeSymbol()
- compositeSymbol.symbols.addAll(listOf(pinSymbol, stopNumberSymbol))
-
- // create a graphic to add to the overlay and update the route
- val graphic = Graphic(locationPoint, compositeSymbol)
- stopsOverlay.graphics.add(graphic)
-
- updateRoute()
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/find-route-in-transport-network/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-route-in-transport-network/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-route-in-transport-network/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-in-transport-network/src/main/res/drawable/ic_launcher_background.xml b/find-route-in-transport-network/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-route-in-transport-network/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route-in-transport-network/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher.png b/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher.png b/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-route-in-transport-network/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route-in-transport-network/src/main/res/values/strings.xml b/find-route-in-transport-network/src/main/res/values/strings.xml
deleted file mode 100644
index ae71b2ed1..000000000
--- a/find-route-in-transport-network/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- Find route in transport network
- /streetmap_SD.tpkx
- /sandiego.geodatabase
- Tap on map to create a transport network
- Shortest
- Fastest
- Clear
-
diff --git a/find-route/.gitignore b/find-route/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/find-route/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/find-route/build.gradle.kts b/find-route/build.gradle.kts
deleted file mode 100644
index 66701dce3..000000000
--- a/find-route/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.findroute"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.findroute"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.androidx.coordinatorlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/find-route/proguard-rules.pro b/find-route/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/find-route/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/find-route/src/main/AndroidManifest.xml b/find-route/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/find-route/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt b/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt
deleted file mode 100644
index 56fcf38dc..000000000
--- a/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt
+++ /dev/null
@@ -1,296 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.findroute
-
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.ListView
-import android.widget.ProgressBar
-import androidx.appcompat.app.AppCompatActivity
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.MapView
-import com.arcgismaps.tasks.networkanalysis.DirectionManeuver
-import com.arcgismaps.tasks.networkanalysis.RouteResult
-import com.arcgismaps.tasks.networkanalysis.RouteTask
-import com.arcgismaps.tasks.networkanalysis.Stop
-import com.esri.arcgismaps.sample.findroute.databinding.ActivityMainBinding
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- private val mapView: MapView by lazy {
- activityMainBinding.mapView
- }
-
- private val mainContainer: ConstraintLayout by lazy {
- activityMainBinding.mainContainer
- }
-
- private val mainProgressBar: ProgressBar by lazy {
- activityMainBinding.mainProgressBar
- }
-
- private val directionFab: FloatingActionButton by lazy {
- activityMainBinding.directionFab
- }
-
- private val bottomSheet: LinearLayout by lazy {
- activityMainBinding.bottomSheet.bottomSheetLayout
- }
-
- private val header: ConstraintLayout by lazy {
- activityMainBinding.bottomSheet.header
- }
-
- private val imageView: ImageView by lazy {
- activityMainBinding.bottomSheet.imageView
- }
-
- private val directionsListView: ListView by lazy {
- activityMainBinding.bottomSheet.directionsListView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(activityMainBinding.mapView)
-
- mapView.apply {
- // set the map to a new map with the navigation base map
- map = ArcGISMap(BasemapStyle.ArcGISNavigation)
- // set initial viewpoint to San Diego
- setViewpoint(Viewpoint(32.7157, -117.1611, 200000.0))
- mapView.graphicsOverlays.add(graphicsOverlay)
- }
-
- // create the symbols for the route
- setupSymbols()
-
- // hide the bottom sheet and make the map view span the whole screen
- bottomSheet.visibility = View.INVISIBLE
- (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
-
- // solve the route and display the bottom sheet when the FAB is clicked
- directionFab.setOnClickListener { lifecycleScope.launch { solveRoute() } }
- }
-
- /**
- * Solves the route using a Route Task, populates the navigation drawer with the directions,
- * and displays a graphic of the route on the map.
- */
- private suspend fun solveRoute() {
- // set the applicationContext as it is required with RouteTask
- ArcGISEnvironment.applicationContext = applicationContext
- // create a route task instance
- val routeTask =
- RouteTask(
- "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"
- )
-
- // show the progress bar
- mainProgressBar.visibility = View.VISIBLE
- routeTask.createDefaultParameters().onSuccess { routeParams ->
- // create stops
- val stops = listOf(
- Stop(Point(-117.1508, 32.7411, SpatialReference.wgs84())),
- Stop(Point(-117.1555, 32.7033, SpatialReference.wgs84()))
- )
-
- routeParams.apply {
- setStops(stops)
- // set return directions as true to return turn-by-turn directions in the route's directionManeuvers
- returnDirections = true
- }
-
- // solve the route
- val routeResult = routeTask.solveRoute(routeParams).getOrElse {
- showError(it.message.toString())
- } as RouteResult
- val route = routeResult.routes[0]
- // create a simple line symbol for the route
- val routeSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 5f)
-
- // create a graphic for the route and add it to the graphics overlay
- graphicsOverlay.graphics.add(Graphic(route.routeGeometry, routeSymbol))
- // get the list of direction maneuvers and display it
- // NOTE: to get turn-by-turn directions the route parameters
- // must have the isReturnDirections parameter set to true.
- val directions = route.directionManeuvers
- setupBottomSheet(directions)
-
- // when the route is solved, hide the FAB and the progress bar
- directionFab.visibility = View.GONE
- mainProgressBar.visibility = View.GONE
- }.onFailure {
- showError(it.message.toString())
- mainProgressBar.visibility = View.GONE
- }
- }
-
- /** Creates a bottom sheet to display a list of direction maneuvers.
- * [directions] a list of DirectionManeuver which represents the route
- */
- private fun setupBottomSheet(directions: List) {
- // create a bottom sheet behavior from the bottom sheet view in the main layout
- val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet).apply {
- // expand the bottom sheet, and ensure it is displayed on the screen when collapsed
- state = BottomSheetBehavior.STATE_EXPANDED
- peekHeight = header.height
- // animate the arrow when the bottom sheet slides
- addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
- override fun onSlide(bottomSheet: View, slideOffset: Float) {
- imageView.rotation = slideOffset * 180f
- }
-
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- imageView.rotation = when (newState) {
- BottomSheetBehavior.STATE_EXPANDED -> 180f
- else -> imageView.rotation
- }
- }
- })
- }
-
- bottomSheet.apply {
- visibility = View.VISIBLE
- // expand or collapse the bottom sheet when the header is clicked
- header.setOnClickListener {
- bottomSheetBehavior.state = when (bottomSheetBehavior.state) {
- BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED
- else -> BottomSheetBehavior.STATE_COLLAPSED
- }
- }
- // rotate the arrow so it starts off in the correct rotation
- imageView.rotation = 180f
- }
-
- directionsListView.apply {
- // Set the adapter for the list view
- adapter = ArrayAdapter(
- this@MainActivity,
- android.R.layout.simple_list_item_1,
- directions.map { it.directionText }
- )
- // when the user taps a maneuver, set the viewpoint to that portion of the route
- onItemClickListener =
- AdapterView.OnItemClickListener { _, _, position, _ ->
- directions[position].geometry?.let { geometry ->
- // set the viewpoint to the selected maneuver
- mapView.setViewpoint(
- Viewpoint(geometry.extent, 20.0)
- )
- // create a graphic with a symbol for the maneuver and add it to the graphics overlay
- val selectedRouteSymbol = SimpleLineSymbol(
- SimpleLineSymbolStyle.Solid,
- Color.green, 5f
- )
- graphicsOverlay.graphics.add(Graphic(geometry, selectedRouteSymbol))
- // collapse the bottom sheet
- bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
- }
- }
- }
-
- // shrink the map view so it is not hidden under the bottom sheet header
- (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin =
- header.height
- }
-
- /**
- * Set up the source, destination and route symbols.
- */
- private fun setupSymbols() {
- val startDrawable =
- ContextCompat.getDrawable(this, R.drawable.ic_source) as BitmapDrawable
- val pinSourceSymbol = PictureMarkerSymbol.createWithImage(startDrawable).apply {
- // make the graphic smaller
- width = 30f
- height = 30f
- offsetY = 20f
- }
- // create a point for the new graphic
- val sourcePoint = Point(
- -117.1508, 32.7411, SpatialReference.wgs84()
- )
- // create a graphic and it to the graphics overlay
- graphicsOverlay.graphics.add(Graphic(sourcePoint, pinSourceSymbol))
-
- val endDrawable =
- ContextCompat.getDrawable(this, R.drawable.ic_destination) as BitmapDrawable
-
- endDrawable.let {
- val pinDestinationSymbol =
- PictureMarkerSymbol.createWithImage(endDrawable).apply {
- // make the graphic smaller
- width = 30f
- height = 30f
- offsetY = 20f
- }
- // create a point for the new graphic
- val destinationPoint = Point(-117.1555, 32.7033, SpatialReference.wgs84())
- // create a graphic and add it to the graphics overlay
- graphicsOverlay.graphics.add(Graphic(destinationPoint, pinDestinationSymbol))
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
-}
diff --git a/find-route/src/main/res/drawable-v24/ic_launcher_foreground.xml b/find-route/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/find-route/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route/src/main/res/drawable/ic_launcher_background.xml b/find-route/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/find-route/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/find-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/find-route/src/main/res/mipmap-hdpi/ic_launcher.png b/find-route/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/find-route/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-hdpi/ic_launcher_round.png b/find-route/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/find-route/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-mdpi/ic_launcher.png b/find-route/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/find-route/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-mdpi/ic_launcher_round.png b/find-route/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/find-route/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xhdpi/ic_launcher.png b/find-route/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/find-route/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/find-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/find-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xxhdpi/ic_launcher.png b/find-route/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/find-route/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/find-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/find-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/find-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/find-route/src/main/res/values/strings.xml b/find-route/src/main/res/values/strings.xml
deleted file mode 100644
index 1a3d16fd8..000000000
--- a/find-route/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Find route
- Directions
- Collapse directions
- Display Directions button
-
diff --git a/generate-geodatabase-replica-from-feature-service/.gitignore b/generate-geodatabase-replica-from-feature-service/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/generate-geodatabase-replica-from-feature-service/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/generate-geodatabase-replica-from-feature-service/build.gradle.kts b/generate-geodatabase-replica-from-feature-service/build.gradle.kts
deleted file mode 100644
index 636a317e7..000000000
--- a/generate-geodatabase-replica-from-feature-service/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/generate-geodatabase-replica-from-feature-service/proguard-rules.pro b/generate-geodatabase-replica-from-feature-service/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/generate-geodatabase-replica-from-feature-service/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml b/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt b/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt
deleted file mode 100644
index af8178b2c..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice
-
-import android.os.Bundle
-import android.util.Log
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.data.Geodatabase
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.tasks.geodatabase.GenerateGeodatabaseJob
-import com.arcgismaps.tasks.geodatabase.GeodatabaseSyncTask
-import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.databinding.GenerateGeodatabaseDialogLayoutBinding
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // setup data binding for the mapview
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // starts the geodatabase replica process
- private val generateButton by lazy {
- activityMainBinding.generateButton
- }
-
- private val resetButton by lazy {
- activityMainBinding.resetButton
- }
-
- // shows the geodatabase loading progress
- private val progressDialog by lazy {
- GenerateGeodatabaseDialogLayoutBinding.inflate(layoutInflater)
- }
-
- // local file path to the geodatabase
- private val geodatabaseFilePath by lazy {
- getExternalFilesDir(null)?.path + getString(R.string.portland_trees_geodatabase_file)
- }
-
- // keep track of the geodatabase replica generated by the feature service
- private var geodatabase: Geodatabase? = null
-
- private val downloadArea: Graphic = Graphic()
-
- // create a Trees FeatureLayer using the first layer of the ServiceFeatureTable
- private val featureLayer: FeatureLayer by lazy {
- FeatureLayer.createWithFeatureTable(
- featureTable = ServiceFeatureTable(
- uri = application.getString(R.string.feature_server_url) + "/0"
- )
- )
- }
-
- // creates a graphic overlay
- private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a Topographic basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
- operationalLayers.add(featureLayer)
- }
- // set the max map extents to that of the feature service
- // representing portland area
- map.maxExtent = Envelope(
- -13687689.2185849,
- 5687273.88331375,
- -13622795.3756647,
- 5727520.22085841,
- spatialReference = SpatialReference.webMercator()
- )
- // configure mapview assignments
- mapView.apply {
- this.map = map
- // add the graphics overlay to display the boundary
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // create a geodatabase sync task with the feature service url
- // This feature service shows a web map of portland street trees,
- // their attributes, as well as related inspection information
- val geodatabaseSyncTask = GeodatabaseSyncTask(getString(R.string.feature_server_url))
-
- // set the button's onClickListener
- generateButton.setOnClickListener {
- // start the geodatabase generation process
- generateGeodatabase(geodatabaseSyncTask, map, downloadArea.geometry?.extent)
- }
-
- resetButton.setOnClickListener {
- // clear any layers already on the map
- map.operationalLayers.clear()
- // clear all symbols drawn
- graphicsOverlay.graphics.clear()
- // add the download boundary
- graphicsOverlay.graphics.add(downloadArea)
- // add back the feature layer
- map.operationalLayers.add(featureLayer)
- // close the current geodatabase, if a replica was already generated
- geodatabase?.close()
- // show generate button
- generateButton.isEnabled = true
- resetButton.isEnabled = false
- }
-
- lifecycleScope.launch {
- // show the error and return if map load failed
- map.load().onFailure {
- showError("Unable to load map")
- return@launch
- }
-
- geodatabaseSyncTask.load().onFailure {
- // if the metadata load fails, show the error and return
- showError("Failed to fetch geodatabase metadata")
- return@launch
- }
-
- // show download area once map is loaded
- updateDownloadArea()
-
- // enable the generate button since the task is now loaded
- generateButton.isEnabled = true
-
- // create a symbol to show a box around the extent we want to download
- downloadArea.symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2F)
- // add the graphic to the graphics overlay when it is created
- graphicsOverlay.graphics.add(downloadArea)
- // update the download area on viewpoint change
- mapView.viewpointChanged.collect {
- updateDownloadArea()
- }
- }
- }
-
- /**
- * Displays a red border on the map to signify the [downloadArea]
- */
- private fun updateDownloadArea() {
- // define screen area to create replica
- val minScreenPoint = ScreenCoordinate(200.0, 200.0)
- val maxScreenPoint = ScreenCoordinate(
- mapView.measuredWidth - 200.0,
- mapView.measuredHeight - 200.0
- )
- // convert screen points to map points
- val minPoint = mapView.screenToLocation(minScreenPoint)
- val maxPoint = mapView.screenToLocation(maxScreenPoint)
- // use the points to define and return an envelope
- if (minPoint != null && maxPoint != null) {
- val envelope = Envelope(minPoint, maxPoint)
- downloadArea.geometry = envelope
- }
- }
-
- /**
- * Starts a [geodatabaseSyncTask] with the given [map] and [extents],
- * runs a GenerateGeodatabaseJob and saves the geodatabase file into local storage
- */
- private fun generateGeodatabase(
- geodatabaseSyncTask: GeodatabaseSyncTask,
- map: ArcGISMap,
- extents: Envelope?
- ) {
- if (extents == null) {
- return showError("Download area extent is null")
- }
-
- lifecycleScope.launch {
- // create generate geodatabase parameters for the selected extents
- val defaultParameters =
- geodatabaseSyncTask.createDefaultGenerateGeodatabaseParameters(extents).getOrElse {
- // show the error and return if the task fails
- showError("Error creating geodatabase parameters")
- return@launch
- }.apply {
- // set the parameters to only create a replica of the Trees (0) layer
- layerOptions.removeIf { layerOptions ->
- layerOptions.layerId != 0L
- }
- }
-
- // set return attachments option to false
- // indicates if any attachments are added to the geodatabase from the feature service
- defaultParameters.returnAttachments = false
- // create a generate geodatabase job
- geodatabaseSyncTask.createGenerateGeodatabaseJob(defaultParameters, geodatabaseFilePath)
- .run {
- // create a dialog to show the jobs progress
- val materialDialogBuilder = createProgressDialog(this)
-
- // show the dialog and obtain a reference to it
- val jobProgressDialog = materialDialogBuilder.show()
-
- // launch a progress collector to display progress
- launch {
- progress.collect { value ->
- // update the progress bar and progress text
- progressDialog.progressBar.progress = value
- progressDialog.progressTextView.text = value.toString()
- }
- }
- // start the generateGeodatabase job
- start()
- // if the job completed successfully, get the geodatabase from the result
- val geodatabase = result().getOrElse {
- // show an error and return if job failed
- showError("Error fetching geodatabase: ${it.message}")
- // dismiss the dialog
- jobProgressDialog.dismiss()
- return@launch
- }
-
- // load and display the geodatabase
- loadGeodatabase(geodatabase, map)
- // dismiss the dialog view
- jobProgressDialog.dismiss()
- // unregister since we are not syncing
- geodatabaseSyncTask.unregisterGeodatabase(geodatabase)
- // show reset button as the task is now complete
- generateButton.isEnabled = false
- resetButton.isEnabled = true
- }
- }
- }
-
- /**
- * Loads the [replicaGeodatabase] and renders the feature layers on to the [map]
- */
- private suspend fun loadGeodatabase(replicaGeodatabase: Geodatabase, map: ArcGISMap) {
- // clear any layers already on the map
- map.operationalLayers.clear()
- // clear all symbols drawn
- graphicsOverlay.graphics.clear()
-
- // load the geodatabase
- replicaGeodatabase.load().onFailure {
- // if the load failed, show the error and return
- showError("Error loading geodatabase")
- return
- }
-
- // add all of the geodatabase feature tables to the map as feature layers
- map.operationalLayers += replicaGeodatabase.featureTables.map { featureTable ->
- FeatureLayer.createWithFeatureTable(featureTable)
- }
-
- // keep track of the geodatabase to close it before generating a new replica
- geodatabase = replicaGeodatabase
- }
-
- /**
- * Creates a new alert dialog using the progressDialog and provides
- * GenerateGeodatabaseJob cancellation on dialog cancellation
- *
- * @param generateGeodatabaseJob the job to cancel
- *
- * @return returns an alert dialog
- */
- private fun createProgressDialog(generateGeodatabaseJob: GenerateGeodatabaseJob): MaterialAlertDialogBuilder {
- // build and return a new alert dialog
- return MaterialAlertDialogBuilder(this).apply {
- // setting it title
- setTitle(getString(R.string.dialog_title))
- // allow it to be cancellable
- setCancelable(false)
- // sets negative button configuration
- setNegativeButton("Cancel") { _, _ ->
- // cancels the generateGeodatabaseJob
- lifecycleScope.launch {
- generateGeodatabaseJob.cancel()
- }
- }
- // removes parent of the progressDialog layout, if previously assigned
- progressDialog.root.parent?.let { parent ->
- (parent as ViewGroup).removeAllViews()
- }
- // set the progressDialog Layout to this alert dialog
- setView(progressDialog.root)
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml b/generate-geodatabase-replica-from-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/drawable/ic_launcher_background.xml b/generate-geodatabase-replica-from-feature-service/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/generate-geodatabase-replica-from-feature-service/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml b/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml
deleted file mode 100644
index 6699c6345..000000000
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
- Generate geodatabase replica from feature service
- https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/Mobile_Data_Collection_WFL1/FeatureServer
- Generate
- Fetching result
- /portland_trees_gdb.geodatabase
- Reset map
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/.gitignore b/generate-offline-map-using-android-jetpack-workmanager/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts b/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts
deleted file mode 100644
index 8a759e9a6..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts
+++ /dev/null
@@ -1,40 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
- implementation(libs.androidx.work.runtime.ktx)
- implementation(libs.androidx.lifecycle.livedata.ktx)
-}
diff --git a/generate-offline-map-using-android-jetpack-workmanager/proguard-rules.pro b/generate-offline-map-using-android-jetpack-workmanager/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml
deleted file mode 100644
index 58bd69d5e..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt b/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt
deleted file mode 100644
index 4e66328db..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager
-
-import android.Manifest.permission.POST_NOTIFICATIONS
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.asFlow
-import androidx.lifecycle.lifecycleScope
-import androidx.work.ExistingWorkPolicy
-import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.OutOfQuotaPolicy
-import androidx.work.WorkInfo
-import androidx.work.WorkManager
-import androidx.work.workDataOf
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.MobileMapPackage
-import com.arcgismaps.mapping.PortalItem
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.portal.Portal
-import com.arcgismaps.tasks.offlinemaptask.GenerateOfflineMapJob
-import com.arcgismaps.tasks.offlinemaptask.GenerateOfflineMapParameters
-import com.arcgismaps.tasks.offlinemaptask.OfflineMapTask
-import com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager.databinding.OfflineJobProgressDialogLayoutBinding
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-import kotlin.random.Random
-
-// data parameter keys for the WorkManager
-// key for the NotificationId parameter
-const val notificationIdParameter = "NotificationId"
-
-// key for the json job file path
-const val jobParameter = "JsonJobPath"
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val takeMapOfflineButton by lazy {
- activityMainBinding.takeMapOfflineButton
- }
-
- private val resetMapButton by lazy {
- activityMainBinding.resetButton
- }
-
- // instance of the WorkManager
- private val workManager by lazy {
- WorkManager.getInstance(this)
- }
-
- // file path to store the offline map package
- private val offlineMapPath by lazy {
- getExternalFilesDir(null)?.path + getString(R.string.offlineMapFile)
- }
-
- // shows the offline map job loading progress
- private val progressLayout by lazy {
- OfflineJobProgressDialogLayoutBinding.inflate(layoutInflater)
- }
-
- // alert dialog view for the progress layout
- private val progressDialog by lazy {
- createProgressDialog().create()
- }
-
- // used to uniquely identify the work request so that only one worker is active at a time
- // also allows us to query and observe work progress
- private val uniqueWorkName = "ArcgisMaps.Sample.OfflineMapJob.Worker"
-
- // create a graphic overlay
- private val graphicsOverlay = GraphicsOverlay()
-
- // represents bounds of the downloadable area of the map
- private val downloadArea = Graphic(
- symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2F)
- )
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // request notifications permission
- requestNotificationPermission()
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // set up the portal item to take offline
- setUpMapView()
-
- // clear the preview map and display the Portal Item
- resetMapButton.setOnClickListener {
- // enable offline button
- takeMapOfflineButton.isEnabled = true
- resetMapButton.isEnabled = false
- // clear graphic overlays
- graphicsOverlay.graphics.clear()
- mapView.graphicsOverlays.clear()
-
- // set up the portal item to take offline
- setUpMapView()
- }
- }
-
- /**
- * Sets up a portal item and displays map area to take offline
- */
- private fun setUpMapView() {
- // create a portal item with the itemId of the web map
- val portal = Portal(getString(R.string.portal_url))
- val portalItem = PortalItem(portal, getString(R.string.item_id))
-
- // add the graphic to the graphics overlay when it is created
- graphicsOverlay.graphics.add(downloadArea)
- // create and add a map with with portal item
- val map = ArcGISMap(portalItem)
- // apply mapview assignments
- mapView.apply {
- this.map = map
- graphicsOverlays.add(graphicsOverlay)
- }
-
- lifecycleScope.launch {
- map.load().onFailure {
- // show an error and return if the map load failed
- showMessage("Error loading map: ${it.message}")
- return@launch
- }
-
- // enable the take map offline button only after the map is loaded
- takeMapOfflineButton.isEnabled = true
-
- // get the Control Valve layer from the map's operational layers
- val operationalLayer =
- map.operationalLayers.firstOrNull { layer ->
- layer.name == "Control Valve"
- } ?: return@launch showMessage("Error finding Control Valve layer")
-
- // limit the map scale to the layer's scale
- map.maxScale = operationalLayer.maxScale ?: 0.0
- map.minScale = operationalLayer.minScale ?: 0.0
-
- mapView.viewpointChanged.collect {
- // upper left corner of the area to take offline
- val minScreenPoint = ScreenCoordinate(200.0, 200.0)
- // lower right corner of the downloaded area
- val maxScreenPoint = ScreenCoordinate(
- mapView.width - 200.0,
- mapView.height - 200.0
- )
- // convert screen points to map points
- val minPoint = mapView.screenToLocation(minScreenPoint) ?: return@collect
- val maxPoint = mapView.screenToLocation(maxScreenPoint) ?: return@collect
- // use the points to define and set an envelope for the downloadArea graphic
- val envelope = Envelope(minPoint, maxPoint)
- downloadArea.geometry = envelope
- }
- }
-
- // set onclick listener for the takeMapOfflineButton
- takeMapOfflineButton.setOnClickListener {
- // if the downloadArea's geometry is not null
- downloadArea.geometry?.let { geometry ->
- // create an OfflineMapJob
- val offlineMapJob = createOfflineMapJob(map, geometry)
- // start the OfflineMapJob
- startOfflineMapJob(offlineMapJob)
- // show the progress dialog
- progressDialog.show()
- // disable the button
- takeMapOfflineButton.isEnabled = false
- }
- }
-
- // start observing the worker's progress and status
- observeWorkStatus()
- }
-
- /**
- * Creates and returns a new GenerateOfflineMapJob for the [map] and its [areaOfInterest]
- */
- private fun createOfflineMapJob(
- map: ArcGISMap,
- areaOfInterest: Geometry
- ): GenerateOfflineMapJob {
- // check and delete if the offline map package file already exists
- File(offlineMapPath).deleteRecursively()
- // specify the min scale and max scale as parameters
- val maxScale = map.maxScale ?: 0.0
- var minScale = map.minScale ?: 0.0
- // minScale must always be larger than maxScale
- if (minScale <= maxScale) {
- minScale = maxScale + 1
- }
- // set the offline map parameters
- val generateOfflineMapParameters = GenerateOfflineMapParameters(
- areaOfInterest,
- minScale,
- maxScale
- ).apply {
- // set job to cancel on any errors
- continueOnErrors = false
- }
- // create an offline map task with the map
- val offlineMapTask = OfflineMapTask(map)
- // create an offline map job with the download directory path and parameters and
- // return the job
- return offlineMapTask.createGenerateOfflineMapJob(
- generateOfflineMapParameters,
- offlineMapPath
- )
- }
-
- /**
- * Starts the [offlineMapJob] using OfflineJobWorker with WorkManager. The [offlineMapJob] is
- * serialized into a json file and the uri is passed to the OfflineJobWorker, since WorkManager
- * enforces a MAX_DATA_BYTES for the WorkRequest's data
- */
- private fun startOfflineMapJob(offlineMapJob: GenerateOfflineMapJob) {
- // create a temporary file path to save the offlineMapJob json file
- val offlineJobJsonPath = getExternalFilesDir(null)?.path +
- getString(R.string.offlineJobJsonFile)
-
- // create the json file
- val offlineJobJsonFile = File(offlineJobJsonPath)
- // serialize the offlineMapJob into the file
- offlineJobJsonFile.writeText(offlineMapJob.toJson())
-
- // create a non-zero notification id for the OfflineJobWorker
- // this id will be used to post or update any progress/status notifications
- val notificationId = Random.Default.nextInt(1, 100)
-
- // create a one-time work request with an instance of OfflineJobWorker
- val workRequest = OneTimeWorkRequestBuilder()
- // run it as an expedited work
- .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
- // add the input data
- .setInputData(
- // add the notificationId and the json file path as a key/value pair
- workDataOf(
- notificationIdParameter to notificationId,
- jobParameter to offlineJobJsonFile.absolutePath
- )
- ).build()
-
- // enqueue the work request to run as a unique work with the uniqueWorkName, so that
- // only one instance of OfflineJobWorker is running at any time
- // if any new work request with the uniqueWorkName is enqueued, it replaces any existing
- // ones that are active
- workManager.enqueueUniqueWork(uniqueWorkName, ExistingWorkPolicy.REPLACE, workRequest)
- }
-
- /**
- * Starts observing any running or completed OfflineJobWorker work requests by capturing the
- * LiveData as a flow. The flow starts receiving updates when the activity is in started
- * or resumed state. This allows the application to capture immediate progress when
- * in foreground and latest progress when the app resumes or restarts.
- */
- private fun observeWorkStatus() {
- // get the livedata observer of the unique work as a flow
- val liveDataFlow = workManager.getWorkInfosForUniqueWorkLiveData(uniqueWorkName).asFlow()
-
- lifecycleScope.launch {
- // collect the live data flow to get the latest work info list
- liveDataFlow.collect { workInfoList ->
- if (workInfoList.isNotEmpty()) {
- // fetch the first work info as we only ever run one work request at any time
- val workInfo = workInfoList[0]
- // check the current state of the work request
- when (workInfo.state) {
- // if work completed successfully
- WorkInfo.State.SUCCEEDED -> {
- // load and display the offline map
- displayOfflineMap()
- // dismiss the progress dialog
- if (progressDialog.isShowing) {
- progressDialog.dismiss()
- }
- }
- // if the work failed or was cancelled
- WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> {
- // show an error message based on if it was cancelled or failed
- if (workInfo.state == WorkInfo.State.FAILED) {
- showMessage("Error generating offline map")
- } else {
- showMessage("Cancelled offline map generation")
- }
- // dismiss the progress dialog
- if (progressDialog.isShowing) {
- progressDialog.dismiss()
- }
- // enable the takeMapOfflineButton
- takeMapOfflineButton.isEnabled = true
- // this removes the completed WorkInfo from the WorkManager's database
- // otherwise, the observer will emit the WorkInfo on every launch
- // until WorkManager auto-prunes
- workManager.pruneWork()
- }
- // if the work is currently in progress
- WorkInfo.State.RUNNING -> {
- // get the current progress value
- val value = workInfo.progress.getInt("Progress", 0)
- // update the progress bar and progress text
- progressLayout.progressBar.progress = value
- progressLayout.progressTextView.text = "$value%"
- // shows the progress dialog if the app is relaunched and the
- // dialog is not visible
- if (!progressDialog.isShowing) {
- progressDialog.show()
- }
- }
- else -> { /* don't have to handle other states */
- }
- }
- }
- }
- }
- }
-
- /**
- * Loads the offline map package into the mapView
- */
- private fun displayOfflineMap() {
- lifecycleScope.launch {
- // check if the offline map package file exists
- if (File(offlineMapPath).exists()) {
- // load it as a MobileMapPackage
- val mapPackage = MobileMapPackage(offlineMapPath)
- mapPackage.load().onFailure {
- // if the load fails, show an error and return
- showMessage("Error loading map package: ${it.message}")
- return@launch
- }
- // add the map from the mobile map package to the MapView
- mapView.map = mapPackage.maps.first()
- // clear all the drawn graphics
- graphicsOverlay.graphics.clear()
- // disable the button to take the map offline once the offline map is showing
- takeMapOfflineButton.isEnabled = false
- resetMapButton.isEnabled = true
- // this removes the completed WorkInfo from the WorkManager's database
- // otherwise, the observer will emit the WorkInfo on every launch
- // until WorkManager auto-prunes
- workManager.pruneWork()
- // display the offline map loaded message
- showMessage("Loaded offline map. Map saved at: $offlineMapPath")
- } else {
- showMessage("Offline map does not exists at path: $offlineMapPath")
- }
- }
- }
-
- /**
- * Creates a progress dialog to show the OfflineMapJob worker progress. It cancels all the
- * running workers when the dialog is cancelled
- */
- private fun createProgressDialog(): MaterialAlertDialogBuilder {
- // build and return a new alert dialog
- return MaterialAlertDialogBuilder(this).apply {
- // set it title
- setTitle(getString(R.string.dialog_title))
- // allow it to be cancellable
- setCancelable(false)
- // set negative button configuration
- setNegativeButton("Cancel") { _, _ ->
- // cancel all the running work
- workManager.cancelAllWork()
- }
- // removes parent of the progressDialog layout, if previously assigned
- progressLayout.root.parent?.let { parent ->
- (parent as ViewGroup).removeAllViews()
- }
- // set the progressDialog Layout to this alert dialog
- setView(progressLayout.root)
- }
- }
-
- /**
- * Request Post Notifications permission for API level 33+
- * https://developer.android.com/develop/ui/views/notifications/notification-permission
- */
- private fun requestNotificationPermission() {
- // request notification permission only for android versions >= 33
- if (Build.VERSION.SDK_INT >= 33) {
- // check if push notifications permission is granted
- val permissionCheckPostNotifications =
- ContextCompat.checkSelfPermission(this@MainActivity, POST_NOTIFICATIONS) ==
- PackageManager.PERMISSION_GRANTED
-
- // if permission is not already granted, request permission from the user
- if (!permissionCheckPostNotifications) {
- ActivityCompat.requestPermissions(
- this@MainActivity,
- arrayOf(POST_NOTIFICATIONS),
- 2
- )
- }
- }
- }
-
- /**
- * Handle the permissions request response.
- */
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) {
- Snackbar.make(
- mapView,
- "Notification permissions required to show progress!",
- Snackbar.LENGTH_LONG
- ).show()
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- // dismiss the dialog when the activity is destroyed
- progressDialog.dismiss()
- }
-
- private fun showMessage(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable-v24/ic_launcher_foreground.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable/ic_launcher_background.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher_round.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher_round.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml b/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml
deleted file mode 100644
index 03c1fd310..000000000
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
- Generate offline map using android jetpack workmanager
- https://www.arcgis.com
- acc027394bc84c2fb04d1ed317aac674
- Generating offline map..
- /offlineMap
- /offlineJobJson
- Offline Map Job Notifications
- Shows notifications for offline map job progress
- ArcGIS Maps Sample: Offline Map Download
- NotificationAction
- Take Map Offline
- Reset Map
-
diff --git a/generate-offline-map/.gitignore b/generate-offline-map/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/generate-offline-map/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/generate-offline-map/build.gradle.kts b/generate-offline-map/build.gradle.kts
deleted file mode 100644
index 14b01ecb9..000000000
--- a/generate-offline-map/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.generateofflinemap"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.generateofflinemap"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/generate-offline-map/proguard-rules.pro b/generate-offline-map/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/generate-offline-map/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/generate-offline-map/src/main/AndroidManifest.xml b/generate-offline-map/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/generate-offline-map/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt b/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt
deleted file mode 100644
index 56e8d6bb0..000000000
--- a/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.generateofflinemap
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.generateofflinemap.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/generate-offline-map/src/main/res/drawable-v24/ic_launcher_foreground.xml b/generate-offline-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/generate-offline-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map/src/main/res/drawable/ic_launcher_background.xml b/generate-offline-map/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/generate-offline-map/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/generate-offline-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher.png b/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher_round.png b/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher.png b/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher_round.png b/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher.png b/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher.png b/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/generate-offline-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/generate-offline-map/src/main/res/values/strings.xml b/generate-offline-map/src/main/res/values/strings.xml
deleted file mode 100644
index 282a115ca..000000000
--- a/generate-offline-map/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Generate offline map
- https://www.arcgis.com
- acc027394bc84c2fb04d1ed317aac674
- Take Map Offline
- Reset Map
-
diff --git a/geocode-offline/.gitignore b/geocode-offline/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/geocode-offline/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/geocode-offline/build.gradle.kts b/geocode-offline/build.gradle.kts
deleted file mode 100644
index e76341bff..000000000
--- a/geocode-offline/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.geocodeoffline"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.geocodeoffline"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(project(":samples-lib"))
-}
diff --git a/geocode-offline/proguard-rules.pro b/geocode-offline/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/geocode-offline/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/geocode-offline/src/main/AndroidManifest.xml b/geocode-offline/src/main/AndroidManifest.xml
deleted file mode 100644
index 8eaea2ef4..000000000
--- a/geocode-offline/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt b/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt
deleted file mode 100644
index 16b0545df..000000000
--- a/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.esri.arcgismaps.sample.geocodeoffline
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // A .tpkx Tile Package file covering the San Diego, CA, USA area
- "https://www.arcgis.com/home/item.html?id=22c3083d4fa74e3e9b25adfc9f8c0496",
- // San Diego Locator Offline Dataset
- "https://www.arcgis.com/home/item.html?id=3424d442ebe54f3cbf34462382d3aebe"
- )
- )
- }
-}
diff --git a/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt b/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt
deleted file mode 100644
index 3649a6c30..000000000
--- a/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt
+++ /dev/null
@@ -1,269 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.geocodeoffline
-
-import android.database.MatrixCursor
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.provider.BaseColumns
-import android.util.Log
-import android.view.Menu
-import android.widget.AutoCompleteTextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.SearchView
-import androidx.core.content.ContextCompat
-import androidx.cursoradapter.widget.SimpleCursorAdapter
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.Basemap
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.ArcGISTiledLayer
-import com.arcgismaps.mapping.layers.TileCache
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.tasks.geocode.GeocodeParameters
-import com.arcgismaps.tasks.geocode.GeocodeResult
-import com.arcgismaps.tasks.geocode.LocatorTask
-import com.esri.arcgismaps.sample.geocodeoffline.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.io.File
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // display the metro area of the tapped location
- private val descriptionTV by lazy {
- activityMainBinding.descriptionTV
- }
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- // create a picture marker symbol
- private val pinSymbol: PictureMarkerSymbol by lazy {
- createPinSymbol()
- }
-
- // geocode parameters used to perform a search
- private val geocodeParameters: GeocodeParameters by lazy {
- GeocodeParameters().apply {
- // get all attributes
- resultAttributeNames.add("*")
- // get only the closest result
- maxResults = 1
- }
- }
-
- // locator task to provide geocoding services
- private val locatorTask: LocatorTask by lazy {
- LocatorTask(File(provisionPath, getString(R.string.san_diego_loc)).path)
- }
-
- // create a graphics overlay
- private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // load the tile cache from local storage
- val tileCache = TileCache("$provisionPath/streetmap_SD.tpkx")
- // create a tiled layer and add it to as the base map
- val tiledLayer = ArcGISTiledLayer(tileCache)
- mapView.apply {
- map = ArcGISMap(Basemap(tiledLayer))
- // set map initial viewpoint
- map?.initialViewpoint = Viewpoint(32.72, -117.155, 120000.0)
- // add a graphics overlay to the map view
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // load geocode locator task
- lifecycleScope.launch {
- locatorTask.load().onSuccess {
- mapView.onSingleTapConfirmed.collect { event ->
- // find address with reverse geocode using the tapped location
- event.mapPoint?.let { mapPoint -> findAddressReverseGeocode(mapPoint) }
- }
- }.onFailure {
- showError(it.message.toString())
- }
- }
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu, menu)
- val search = menu.findItem(R.id.appSearchBar)
- // set up address search view and listeners
- setupAddressSearchView(search.actionView as SearchView)
- return super.onCreateOptionsMenu(menu)
- }
-
- /**
- * Sets up the address SearchView and uses MatrixCursor to
- * show suggestions to the user as text is entered.
- */
- private fun setupAddressSearchView(addressSearchView: SearchView) {
- // disable threshold to show results from single character
- val autoCompleteTextViewID = resources.getIdentifier("search_src_text", "id", packageName)
- addressSearchView.findViewById(autoCompleteTextViewID).threshold = 0
-
- // get the list of pre-made suggestions
- val suggestions = resources.getStringArray(R.array.suggestion_items)
- // set up parameters for searching with MatrixCursor
- val columnNames = arrayOf(BaseColumns._ID, "address")
- val suggestionsCursor = MatrixCursor(columnNames)
- // add each address suggestion to a new row
- suggestions.forEachIndexed { i, s -> suggestionsCursor.addRow(arrayOf(i, s)) }
-
- // column names for the adapter to look at when mapping data
- val cols = arrayOf("address")
- // ids that show where data should be assigned in the layout
- val to = intArrayOf(R.id.suggestionAddress)
- // define SimpleCursorAdapter
- val suggestionsAdapter = SimpleCursorAdapter(
- this@MainActivity,
- R.layout.suggestion_address, suggestionsCursor, cols, to, 0
- )
-
- addressSearchView.suggestionsAdapter = suggestionsAdapter
- // handle an address suggestion being chosen
- addressSearchView.setOnSuggestionListener(object : SearchView.OnSuggestionListener {
- override fun onSuggestionSelect(position: Int): Boolean {
- return false
- }
-
- override fun onSuggestionClick(position: Int): Boolean {
- // geocode the typed address
- addressSearchView.setQuery(suggestions[position], true)
- return true
- }
- })
-
- // geocode the searched address on submit
- addressSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
- override fun onQueryTextSubmit(address: String): Boolean {
- geocodeAddress(address)
- addressSearchView.clearFocus()
- return true
- }
-
- override fun onQueryTextChange(newText: String?) = true
- })
- }
-
- /**
- * Use the locator task to geocode the given address.
- *
- * @param address as a string to geocode
- */
- private fun geocodeAddress(address: String) = lifecycleScope.launch {
- // clear graphics on map before displaying search results
- graphicsOverlay.graphics.clear()
- // load the locator task
- locatorTask.load().getOrThrow()
- // run the locatorTask geocode task, passing in the address
- val geocodeResults = locatorTask.geocode(address, geocodeParameters).getOrThrow()
- geocodeResults.ifEmpty {
- // no address found in geocode so return
- showError("No address found for $address")
- return@launch
- }
- // display address found in geocode
- displaySearchResultOnMap(geocodeResults)
- }
-
- /**
- * Get the reverse geocode result from the [mapPoint]
- */
- private suspend fun findAddressReverseGeocode(mapPoint: Point) {
- // normalize the geometry - needed if the user crosses the international date line.
- val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
- locatorTask.reverseGeocode(normalizedPoint).onSuccess { geocodeResults ->
- // no address found in geocode so return
- if (geocodeResults.isEmpty()) {
- showError("Could not find address at tapped point")
- return@onSuccess
- }
- displaySearchResultOnMap(geocodeResults)
- }.onFailure {
- showError(it.message.toString())
- }
- }
-
- /**
- * Turn the first address from [geocodeResultList] into a point marker and adds it to the graphic overlay of the map.
- */
- private fun displaySearchResultOnMap(geocodeResultList: List) {
- // clear graphics overlay of existing graphics
- graphicsOverlay.graphics.clear()
-
- // create graphic object
- val resultLocationGraphic = Graphic(
- geocodeResultList[0].displayLocation,
- geocodeResultList[0].attributes, pinSymbol
- )
- graphicsOverlay.graphics.add(resultLocationGraphic)
- descriptionTV.text = geocodeResultList[0].label
-
- // get the envelop to set the viewpoint
- val envelope = graphicsOverlay.extent ?: return showError("Geocode result extent is null")
- // animate viewpoint to geocode result's extent
- lifecycleScope.launch {
- mapView.setViewpointGeometry(envelope, 25.0)
- }
- }
-
- /**
- * Creates a picture marker symbol from the pin icon.
- */
- private fun createPinSymbol(): PictureMarkerSymbol {
- val pinDrawable = ContextCompat.getDrawable(this, R.drawable.pin) as BitmapDrawable
- val pinSymbol = PictureMarkerSymbol.createWithImage(pinDrawable)
- pinSymbol.apply {
- // resize the dimensions of the symbol
- width = 18f
- height = 65f
- }
- return pinSymbol
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/geocode-offline/src/main/res/drawable-v24/ic_launcher_foreground.xml b/geocode-offline/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/geocode-offline/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/geocode-offline/src/main/res/drawable/ic_launcher_background.xml b/geocode-offline/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/geocode-offline/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/geocode-offline/src/main/res/menu/menu.xml b/geocode-offline/src/main/res/menu/menu.xml
deleted file mode 100644
index 777703c57..000000000
--- a/geocode-offline/src/main/res/menu/menu.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/geocode-offline/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher.png b/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher_round.png b/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/geocode-offline/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher.png b/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher_round.png b/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/geocode-offline/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher.png b/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher.png b/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/geocode-offline/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/geocode-offline/src/main/res/values/strings.xml b/geocode-offline/src/main/res/values/strings.xml
deleted file mode 100644
index cf943df5b..000000000
--- a/geocode-offline/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
- Geocode offline
- Search for an address or tap on the map
- /SanDiego_StreetAddress.loc
- Enter address
-
- - 910 N Harbor Dr, San Diego, CA 92101
- - 2920 Zoo Dr, San Diego, CA 92101
- - 111 W Harbor Dr, San Diego, CA 92101
- - 868 4th Ave, San Diego, CA 92101
- - 750 A St, San Diego, CA 92101
-
-
diff --git a/gradle-plugins/build.gradle.kts b/gradle-plugins/build.gradle.kts
new file mode 100644
index 000000000..e00110e4a
--- /dev/null
+++ b/gradle-plugins/build.gradle.kts
@@ -0,0 +1,23 @@
+repositories {
+ google()
+ mavenCentral()
+}
+
+plugins {
+ `kotlin-dsl`
+}
+
+gradlePlugin {
+ plugins {
+ create("copyCodeFiles") {
+ id = "com.arcgismaps.sampleFiles.copy"
+ version = "1.0"
+ implementationClass = "com.arcgismaps.CopySampleFilesTask"
+ }
+ create("copyScreenshots") {
+ id = "com.arcgismaps.screenshots.copy"
+ version = "1.0"
+ implementationClass = "com.arcgismaps.CopyScreenshotsTask"
+ }
+ }
+}
diff --git a/gradle-plugins/settings.gradle.kts b/gradle-plugins/settings.gradle.kts
new file mode 100644
index 000000000..014e161ed
--- /dev/null
+++ b/gradle-plugins/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "gradle-plugins"
diff --git a/gradle-plugins/src/main/kotlin/com/arcgismaps/CopySampleFilesTask.kt b/gradle-plugins/src/main/kotlin/com/arcgismaps/CopySampleFilesTask.kt
new file mode 100644
index 000000000..7988ac585
--- /dev/null
+++ b/gradle-plugins/src/main/kotlin/com/arcgismaps/CopySampleFilesTask.kt
@@ -0,0 +1,47 @@
+package com.arcgismaps
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.Copy
+import org.gradle.kotlin.dsl.register
+
+/**
+ * Copy md, kt, and json files to a directory (with the name of the associated sample)
+ * into the app's build assets directory.
+ */
+class CopySampleFilesTask : Plugin {
+ override fun apply(project: Project) {
+ project.tasks.register("copyCodeFiles") {
+ description = """
+ Copies sample files for the given sample directory if a code file doesn't
+ already exist or a newer version is present.
+ """.trimIndent()
+
+ // Define the input files
+ val inputFiles: FileCollection = project.fileTree("${project.rootDir.path}/samples/") {
+ exclude("**/build/")
+ include("**/*.md", "**/*.kt", "**/*.metadata.json")
+ }
+
+ // Set input files
+ inputs.files(inputFiles)
+
+ // Define the output directory
+ val outputDir = project.file("${project.rootDir.path}/app/build/sampleAssets/samples/")
+ outputs.dir(outputDir)
+
+ from(inputFiles) {
+ eachFile {
+ path = path.substringBefore("/") + "/" + name
+ includeEmptyDirs = false
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ }
+ }
+
+ // Place in the assets codeFiles folder of the app.
+ into(outputDir)
+ }
+ }
+}
\ No newline at end of file
diff --git a/gradle-plugins/src/main/kotlin/com/arcgismaps/CopyScreenshotsTask.kt b/gradle-plugins/src/main/kotlin/com/arcgismaps/CopyScreenshotsTask.kt
new file mode 100644
index 000000000..90909d1dc
--- /dev/null
+++ b/gradle-plugins/src/main/kotlin/com/arcgismaps/CopyScreenshotsTask.kt
@@ -0,0 +1,59 @@
+package com.arcgismaps
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.tasks.Copy
+import org.gradle.kotlin.dsl.register
+import java.awt.image.BufferedImage
+import javax.imageio.ImageIO
+
+
+/**
+ * Copy screenshot files to a directory (with the name of the associated sample)
+ * into the app's build assets directory.
+ */
+class CopyScreenshotsTask : Plugin {
+ override fun apply(project: Project) {
+ project.tasks.register("copyScreenshots") {
+ description = """
+ Copies screenshot screenshots from all the given sample directories. Note:
+ Screenshots must be in .png format.
+ """.trimIndent()
+
+ // Define input and output directories
+ val inputDir = project.file("${project.rootDir.path}/samples/")
+ val outputDir = project.file("${project.rootDir.path}/app/build/sampleAssets/samples/")
+
+ from(inputDir) {
+ // Include all screenshots.
+ exclude("**/build/")
+ exclude("**/mipmap*/**")
+ exclude("**/res/")
+ include("**/*.png")
+ eachFile {
+ // Prepend the kebab style sample directory to each file path.
+ path = path.substringBefore("/") + "/" + name.replace("-", "_")
+ includeEmptyDirs = false
+ // Don't overwrite existing files with the same name.
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ }
+ }
+
+ into(outputDir)
+
+ doLast {
+ outputDir.listFiles()?.forEach { sampleFolder ->
+ sampleFolder.listFiles()?.filter { it.name.endsWith(".png") }?.forEach {
+ val inputImage = ImageIO.read(it)
+ val outputImage = BufferedImage(350, 200, inputImage.type)
+ val g2d = outputImage.createGraphics()
+ g2d.drawImage(inputImage, 0, 0, 350, 200, null)
+ g2d.dispose()
+ ImageIO.write(outputImage, "png", it)
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 1b9f5f090..ed515f7b7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,19 +1,26 @@
# Project-wide Gradle settings.
-
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
# enable daemon
org.gradle.daemon=true
-
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
org.gradle.parallel=true
-
-# enable configure on demand
-org.gradle.configureondemand=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
-android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.enableJetifier=true
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9f36c0774..0992da2b6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,71 +1,117 @@
[versions]
+
# ArcGIS Maps SDK for Kotlin version
-arcgisMapsKotlinVersion = "200.5.0"
-# SDK versions
-compileSdk = "34"
-minSdk = "26"
-targetSdk = "34"
-versionCode = "2005000"
-versionName = "200.5.0"
-# Kotlin versions
-kotlinVersion = "1.9.23"
-ktxLifecycle = "2.7.0"
-ktxFragmentsExt = "1.6.2"
-ktxActivityExt = "1.9.0"
-ktxAndroidCore = "1.13.0"
+arcgisMapsKotlinVersion = "200.6.0"
+
+### Android versions
+androidGradlePlugin = "8.7.1"
+lifecycle = "2.8.4"
+androidTools = "31.5.2"
+appcompat = "1.7.0"
+constraintLayoutVersion = "2.1.4"
+kotlinxSerializationJson = "1.6.0"
+accompanistSystemuicontroller = "0.32.0"
+workVersion = "2.9.1"
+datastorePreferences = "1.1.1"
+roomVersion = "2.6.1"
+
+### Kotlin versions
+kotlinVersion = "2.0.0"
+coreKtx = "1.13.1"
+# TODO is this needed: ktx-activity = { group = "androidx.activity", name = "activity-ktx", version.ref = "ktxActivity" }
+ktxActivity = "1.9.1"
+ksp = "2.0.0-1.0.21"
kotlinCompilerExt = "1.5.12"
-# Compose versions
-composeActivityVersion = "1.9.0"
-composeBOM = "2024.04.01"
-# Library versions
-appcompatVersion = "1.6.1"
commonsIoVersion = "2.15.1"
-constraintLayoutVersion = "2.1.4"
-coordinateLayoutVersion = "1.2.0"
-jsonSerialization = "1.6.3"
-workVersion = "2.9.0"
-multidexVersion = "2.0.1"
-materialVersion = "1.11.0"
-androidBrowserVersion = "1.8.0"
-androidxWindow = "1.2.0"
-# Plugin versions
-gradleVersion = "8.3.2"
-[plugins]
-android-application = { id = "com.android.application", version.ref = "gradleVersion" }
-android-library = { id = "com.android.library", version.ref = "gradleVersion" }
-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }
-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinVersion" }
+### Compose verions
+composeBom = "2024.10.00"
+activityCompose = "1.9.3"
+material = "1.12.0"
+navigationCompose = "2.8.3"
+
+### Testing versions
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+
+### Application Verions
+versionCode = "2006000"
+versionName = "200.6.0"
+minSdk = "26"
+targetSdk = "35"
+### Third party libraries
[libraries]
-android-material = { group = "com.google.android.material", name = "material", version.ref = "materialVersion"}
-androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompatVersion"}
-androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "composeActivityVersion"}
-androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "ktxActivityExt"}
-androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidBrowserVersion"}
-androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBOM"}
-androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+
+### Android libs
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
+androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIoVersion" }
+android-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintLayoutVersion" }
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
+androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workVersion" }
+androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
+androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "roomVersion" }
+androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomVersion" }
+androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomVersion" }
+
+### Compose libs
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
-androidx-window = { group = "androidx.window", name = "window", version.ref = "androidxWindow" }
-androidx-window-core = { group = "androidx.window", name = "window-core", version.ref = "androidxWindow" }
-androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintLayoutVersion"}
-androidx-coordinatorlayout = { group = "androidx.coordinatorlayout", name = "coordinatorlayout", version.ref = "coordinateLayoutVersion"}
-androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktxAndroidCore"}
-androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "ktxFragmentsExt" }
-androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "ktxLifecycle"}
-androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "ktxLifecycle"}
-androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "ktxLifecycle"}
-androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "ktxLifecycle"}
-androidx-multidex = { group = "androidx.multidex", name = "multidex", version.ref = "multidexVersion"}
-androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workVersion" }
-arcgis-maps-kotlin = { group = "com.esri", name = "arcgis-maps-kotlin", version.ref = "arcgisMapsKotlinVersion"}
-arcgis-maps-kotlin-toolkit-bom = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-bom", version.ref = "arcgisMapsKotlinVersion"}
+androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
+androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test" }
+androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
+
+### ArcGIS Maps SDK for Kotlin libs
+arcgis-maps-kotlin = { group = "com.esri", name = "arcgis-maps-kotlin", version.ref = "arcgisMapsKotlinVersion" }
+arcgis-maps-kotlin-toolkit-bom = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-bom", version.ref = "arcgisMapsKotlinVersion" }
arcgis-maps-kotlin-toolkit-authentication = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-authentication" }
arcgis-maps-kotlin-toolkit-geoview-compose = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-geoview-compose" }
arcgis-maps-kotlin-toolkit-featureforms = { group = "com.esri", name = "arcgis-maps-kotlin-toolkit-featureforms" }
-commons-io = { group = "commons-io", name = "commons-io", version.ref = "commonsIoVersion" }
-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "jsonSerialization" }
-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlinVersion"}
+
+### Third party libraries
+coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version = "3.0.0-rc01" }
+coil-network-http = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version = "3.0.0-rc01" }
+
+### Testing libs
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+
+# Dependencies of the included build-logic
+android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
+kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlinVersion" }
+ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
+android-tools-common = { group = "com.android.tools", name = "common", version.ref = "androidTools" }
+
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
+jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }
+android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
+compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinVersion" }
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinVersion" }
+gradle-secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version = "2.0.1" }
+
+# Plugins defined by the project build-logic
+arcgismaps-android-application = { id = "arcgismaps.android.application", version = "unspecified" }
+arcgismaps-android-application-compose = { id = "arcgismaps.android.application.compose", version = "unspecified" }
+arcgismaps-android-library-compose = { id = "arcgismaps.android.library.compose", version = "unspecified" }
+arcgismaps-android-library = { id = "arcgismaps.android.library", version = "unspecified" }
+arcgismaps-kotlin-sample = { id = "arcgismaps.kotlin.sample", version = "unspecified" }
+sample-files-copy = { id = "com.arcgismaps.sampleFiles.copy", version = "unspecified" }
+screenshots-copy = { id = "com.arcgismaps.screenshots.copy", version = "unspecified" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9bcc5593d..043f02227 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Apr 16 15:17:41 PDT 2024
+#Mon Nov 04 09:26:16 PST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew.bat b/gradlew.bat
index 107acd32c..ac1b06f93 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,89 +1,89 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/identify-layer-features/.gitignore b/identify-layer-features/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/identify-layer-features/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/identify-layer-features/build.gradle.kts b/identify-layer-features/build.gradle.kts
deleted file mode 100644
index fc1bbbf5e..000000000
--- a/identify-layer-features/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.identifylayerfeatures"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.identifylayerfeatures"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/identify-layer-features/proguard-rules.pro b/identify-layer-features/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/identify-layer-features/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/identify-layer-features/src/main/AndroidManifest.xml b/identify-layer-features/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/identify-layer-features/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt
deleted file mode 100644
index ac4844fa4..000000000
--- a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.identifylayerfeatures
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.identifylayerfeatures.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- IdentifyLayerFeaturesApp()
- }
- }
- }
-
- @Composable
- private fun IdentifyLayerFeaturesApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml b/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/identify-layer-features/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml b/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/identify-layer-features/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/identify-layer-features/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/identify-layer-features/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/identify-layer-features/src/main/res/values/strings.xml b/identify-layer-features/src/main/res/values/strings.xml
deleted file mode 100644
index 5a6b8c777..000000000
--- a/identify-layer-features/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- Identify layer features
-
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0
-
-
- https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer
-
-
diff --git a/manage-operational-layers/.gitignore b/manage-operational-layers/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/manage-operational-layers/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/manage-operational-layers/build.gradle.kts b/manage-operational-layers/build.gradle.kts
deleted file mode 100644
index 0ca9e0a7f..000000000
--- a/manage-operational-layers/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.manageoperationallayers"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.manageoperationallayers"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/manage-operational-layers/proguard-rules.pro b/manage-operational-layers/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/manage-operational-layers/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/manage-operational-layers/src/main/AndroidManifest.xml b/manage-operational-layers/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/manage-operational-layers/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt b/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt
deleted file mode 100644
index d97c91028..000000000
--- a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.manageoperationallayers
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.manageoperationallayers.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ManageOperationalLayersApp()
- }
- }
- }
-
- @Composable
- private fun ManageOperationalLayersApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/manage-operational-layers/src/main/res/drawable-v24/ic_launcher_foreground.xml b/manage-operational-layers/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/manage-operational-layers/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/manage-operational-layers/src/main/res/drawable/ic_launcher_background.xml b/manage-operational-layers/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/manage-operational-layers/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/manage-operational-layers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher.png b/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher_round.png b/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher.png b/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher_round.png b/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher.png b/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher.png b/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/manage-operational-layers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/manage-operational-layers/src/main/res/values/strings.xml b/manage-operational-layers/src/main/res/values/strings.xml
deleted file mode 100644
index 939a98883..000000000
--- a/manage-operational-layers/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Manage operational layers
- https://sampleserver5.arcgisonline.com/arcgis/rest/services/Elevation/WorldElevations/MapServer
- https://sampleserver5.arcgisonline.com/arcgis/rest/services/Census/MapServer
- https://sampleserver5.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer
-
diff --git a/navigate-route/.gitignore b/navigate-route/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/navigate-route/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/navigate-route/build.gradle.kts b/navigate-route/build.gradle.kts
deleted file mode 100644
index 0b646c585..000000000
--- a/navigate-route/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.navigateroute"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.navigateroute"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/navigate-route/proguard-rules.pro b/navigate-route/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/navigate-route/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/navigate-route/src/main/AndroidManifest.xml b/navigate-route/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/navigate-route/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt b/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt
deleted file mode 100644
index be304284a..000000000
--- a/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt
+++ /dev/null
@@ -1,401 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.navigateroute
-
-import android.os.Bundle
-import android.speech.tts.TextToSpeech
-import android.text.format.DateUtils
-import android.util.Log
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.location.RouteTrackerLocationDataSource
-import com.arcgismaps.location.SimulatedLocationDataSource
-import com.arcgismaps.location.SimulationParameters
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.navigation.DestinationStatus
-import com.arcgismaps.navigation.RouteTracker
-import com.arcgismaps.navigation.TrackingStatus
-import com.arcgismaps.tasks.networkanalysis.RouteResult
-import com.arcgismaps.tasks.networkanalysis.RouteTask
-import com.arcgismaps.tasks.networkanalysis.Stop
-import com.esri.arcgismaps.sample.navigateroute.databinding.ActivityMainBinding
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.cancelAndJoin
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
-import java.time.Instant
-import java.util.concurrent.atomic.AtomicBoolean
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val resetNavigationButton: MaterialButton by lazy {
- activityMainBinding.resetNavigationButton
- }
-
- private val recenterButton: MaterialButton by lazy {
- activityMainBinding.recenterButton
- }
-
- private val distanceRemainingTextView: TextView by lazy {
- activityMainBinding.distanceRemainingTextView
- }
-
- private val timeRemainingTextView: TextView by lazy {
- activityMainBinding.timeRemainingTextView
- }
-
- private val nextDirectionTextView: TextView by lazy {
- activityMainBinding.nextDirectionTextView
- }
-
- private val nextStopTextView: TextView by lazy {
- activityMainBinding.nextStopTextView
- }
-
- /**
- * Destination list of stops for the RouteParameters
- */
- private val routeStops by lazy {
- listOf(
- // San Diego Convention Center
- Stop(Point(-117.160386, 32.706608, SpatialReference.wgs84())),
- // USS San Diego Memorial
- Stop(Point(-117.173034, 32.712327, SpatialReference.wgs84())),
- // RH Fleet Aerospace Museum
- Stop(Point(-117.147230, 32.730467, SpatialReference.wgs84()))
- )
- }
-
- // instance of the route ahead polyline
- private var routeAheadGraphic: Graphic = Graphic()
-
- // instance of the route traveled polyline
- private var routeTraveledGraphic: Graphic = Graphic()
-
- // instance of the MapView's graphic overlay
- private val graphicsOverlay = GraphicsOverlay()
-
- // boolean to check if Android text-speech is initialized
- private var isTextToSpeechInitialized = AtomicBoolean(false)
-
- // instance of Android text-speech
- private var textToSpeech: TextToSpeech? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // some parts of the API require an Android Context to properly interact with Android system
- // features, such as LocationProvider and application resources
- ArcGISEnvironment.applicationContext = applicationContext
-
- lifecycle.addObserver(mapView)
-
- // create and add a map with a streets basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISStreets)
- mapView.map = map
-
- // create a graphics overlay to hold our route graphics
- mapView.graphicsOverlays.add(graphicsOverlay)
-
- // create text-to-speech to replay navigation voice guidance
- textToSpeech = TextToSpeech(this) { status ->
- if (status != TextToSpeech.ERROR) {
- textToSpeech?.language = resources.configuration.locales[0]
- isTextToSpeechInitialized.set(true)
- }
- }
-
- // generate a route with directions and stops for navigation
- val routeTask = RouteTask(getString(R.string.routing_service_url))
-
- // create the default parameters and solve route
- lifecycleScope.launch {
- // load and set the route parameters
- val routeParameters = routeTask.createDefaultParameters().getOrElse {
- return@launch showError("Error creating default parameters:${it.message}")
- }.apply {
- setStops(routeStops)
- returnDirections = true
- returnStops = true
- returnRoutes = true
- }
-
- // get the solved route result
- val routeResult = routeTask.solveRoute(routeParameters).getOrElse {
- return@launch showError("Error solving route:${it.message}")
- }
-
- // get the route geometry from the route result
- val routeGeometry = routeResult.routes[0].routeGeometry
-
- // set the map view view point to show the whole route
- if (routeGeometry?.extent != null) {
- mapView.setViewpoint(Viewpoint(routeGeometry.extent))
- } else {
- return@launch showError("Route geometry extent is null.")
- }
-
- // start navigating on app launch
- startNavigation(routeResult)
- }
-
- // wire up recenter button
- recenterButton.setOnClickListener {
- mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
- recenterButton.isEnabled = false
- }
- }
-
- /**
- * Start the navigation along the provided route using the [routeResult] and
- * collects updates in the location using the MapView's location display.
- * */
- private fun startNavigation(routeResult: RouteResult) {
- // get the route's geometry from the route result
- val routeGeometry: Polyline = routeResult.routes[0].routeGeometry
- ?: return showError("Route is missing geometry")
-
- // create the graphics using the route's geometry
- createRouteGraphics(routeGeometry)
-
- // set up a simulated location data source which simulates movement along the route
- val simulationParameters = SimulationParameters(
- Instant.now(),
- velocity = 35.0,
- horizontalAccuracy = 5.0,
- verticalAccuracy = 5.0
- )
-
- // create the simulated data source using the geometry and parameters
- val simulatedLocationDataSource = SimulatedLocationDataSource(
- routeGeometry, simulationParameters
- )
-
- // set up a RouteTracker for navigation along the calculated route
- val routeTracker = RouteTracker(
- routeResult,
- routeIndex = 0,
- skipCoincidentStops = true
- ).apply {
- setSpeechEngineReadyCallback {
- isTextToSpeechInitialized.get() && textToSpeech?.isSpeaking == false
- }
- }
- // plays the direction voice guidance
- lifecycleScope.launch {
- updateVoiceGuidance(routeTracker)
- }
-
- // create a route tracker location data source to snap the location display to the route
- val routeTrackerLocationDataSource = RouteTrackerLocationDataSource(
- routeTracker,
- simulatedLocationDataSource
- )
-
- // get the map view's location display and set it up
- val locationDisplay = mapView.locationDisplay.also {
- // set the simulated location data source as the location data source for this app
- it.dataSource = routeTrackerLocationDataSource
- it.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
- }
-
- // start the LocationDisplay, which starts the
- // RouteTrackerLocationDataSource and SimulatedLocationDataSource
- lifecycleScope.launch {
- locationDisplay.dataSource.start().getOrElse {
- showError("Error starting LocationDataSource: ${it.message} ")
- }
- // set the text for first destination
- nextStopTextView.text = resources.getStringArray(R.array.stop_message)[0]
- }
-
- // listen for changes in location
- val locationDisplayJob = lifecycleScope.launch {
- locationDisplay.location.collect {
- // get the route's tracking status
- val trackingStatus = routeTracker.trackingStatus.value ?: return@collect
-
- // displays the remaining and traversed route
- updateRouteGraphics(trackingStatus)
-
- // display route status and directions info
- displayRouteInfo(routeTracker, trackingStatus)
- }
- }
-
- // listen if user navigates the map view away from the
- // location display, activate the recenter button
- val autoPanModeJob = lifecycleScope.launch {
- locationDisplay.autoPanMode.filter { it == LocationDisplayAutoPanMode.Off }
- .collect { recenterButton.isEnabled = true }
- }
-
- // reset the navigation if button is clicked
- resetNavigationButton.setOnClickListener {
- lifecycleScope.launch {
- if (locationDisplayJob.isActive) {
- // stop location data sources
- locationDisplay.dataSource.stop()
- // cancel the coroutine jobs
- locationDisplayJob.cancelAndJoin()
- autoPanModeJob.cancelAndJoin()
- // start navigation again
- startNavigation(routeResult)
- }
- }
- }
- }
-
- /**
- * Uses Android's [textToSpeech] to speak to say the latest
- * voice guidance from the [routeTracker] out loud.
- */
- private suspend fun updateVoiceGuidance(routeTracker: RouteTracker) {
- // listen for new voice guidance events
- routeTracker.newVoiceGuidance.collect { voiceGuidance ->
- // use Android's text to speech to speak the voice guidance
- textToSpeech?.speak(voiceGuidance.text, TextToSpeech.QUEUE_FLUSH, null, null)
- // set next direction text
- nextDirectionTextView.text = getString(R.string.next_direction, voiceGuidance.text)
- }
- }
-
- /**
- * Displays the route distance and time information using [trackingStatus], and
- * switches destinations using [routeTracker]. When final destination is reached,
- * the location data source is stopped.
- */
- private suspend fun displayRouteInfo(
- routeTracker: RouteTracker,
- trackingStatus: TrackingStatus
- ) {
- // get remaining distance information
- val remainingDistance = trackingStatus.destinationProgress.remainingDistance
- // convert remaining minutes to hours:minutes:seconds
- val remainingTimeString =
- DateUtils.formatElapsedTime((trackingStatus.destinationProgress.remainingTime * 60).toLong())
-
- // update text views
- distanceRemainingTextView.text = getString(
- R.string.distance_remaining,
- remainingDistance.displayText,
- remainingDistance.displayTextUnits.abbreviation
- )
- timeRemainingTextView.text = getString(R.string.time_remaining, remainingTimeString)
-
- // if a destination has been reached
- if (trackingStatus.destinationStatus == DestinationStatus.Reached) {
- // if there are more destinations to visit. Greater than 1 because the start point is considered a "stop"
- if (trackingStatus.remainingDestinationCount > 1) {
- // switch to the next destination
- routeTracker.switchToNextDestination().getOrElse {
- return showError("Error retrieving next destination: ${it.message}")
- }
- // set second stop message
- nextStopTextView.text = resources.getStringArray(R.array.stop_message)[1]
- } else {
- // the final destination has been reached,
- // stop the location data source
- mapView.locationDisplay.dataSource.stop()
- // set last stop message
- nextStopTextView.text = resources.getStringArray(R.array.stop_message)[2]
- }
- }
- }
-
- /**
- * Update the remaining and traveled route graphics using [trackingStatus]
- */
- private fun updateRouteGraphics(trackingStatus: TrackingStatus) {
- trackingStatus.routeProgress.let {
- // set geometries for the route ahead and the remaining route
- routeAheadGraphic.geometry = it.remainingGeometry
- routeTraveledGraphic.geometry = it.traversedGeometry
- }
- }
-
- /**
- * Initialize and add route travel graphics to the map using [routeGeometry]
- */
- private fun createRouteGraphics(routeGeometry: Polyline) {
- // clear any graphics from the current graphics overlay
- graphicsOverlay.graphics.clear()
-
- // create a graphic (with a dashed line symbol) to represent the route
- routeAheadGraphic = Graphic(
- routeGeometry,
- SimpleLineSymbol(
- SimpleLineSymbolStyle.Dash,
- Color(getColor(com.esri.arcgismaps.sample.sampleslib.R.color.colorPrimary)),
- 5f
- )
- )
-
- // create a graphic (solid) to represent the route that's been traveled (initially empty)
- routeTraveledGraphic = Graphic(
- routeGeometry,
- SimpleLineSymbol(
- SimpleLineSymbolStyle.Solid,
- Color.red,
- 5f
- )
- )
-
- // add the graphics to the mapView's graphics overlays
- mapView.graphicsOverlays[0].graphics.addAll(
- listOf(
- routeAheadGraphic,
- routeTraveledGraphic
- )
- )
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
diff --git a/navigate-route/src/main/res/drawable-v24/ic_launcher_foreground.xml b/navigate-route/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/navigate-route/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/navigate-route/src/main/res/drawable/ic_launcher_background.xml b/navigate-route/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/navigate-route/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/navigate-route/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/navigate-route/src/main/res/mipmap-hdpi/ic_launcher.png b/navigate-route/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/navigate-route/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-hdpi/ic_launcher_round.png b/navigate-route/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/navigate-route/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-mdpi/ic_launcher.png b/navigate-route/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/navigate-route/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-mdpi/ic_launcher_round.png b/navigate-route/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/navigate-route/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher.png b/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/navigate-route/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher.png b/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/navigate-route/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/navigate-route/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/navigate-route/src/main/res/values/strings.xml b/navigate-route/src/main/res/values/strings.xml
deleted file mode 100644
index 9992920d8..000000000
--- a/navigate-route/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
- Navigate route
- https://sampleserver7.arcgisonline.com/server/rest/services/NetworkAnalysis/SanDiego/NAServer/Route
- Distance remaining: %s %s
- Time remaining: %s
- Next direction: %s
- Reset Navigation
- Recenter
-
- - Navigating to the first stop, the USS San Diego Memorial.
- - Navigating to the second stop, the Fleet Science Center.
- - Arrived at the final destination.
-
-
diff --git a/play-kml-tour/.gitignore b/play-kml-tour/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/play-kml-tour/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/play-kml-tour/build.gradle.kts b/play-kml-tour/build.gradle.kts
deleted file mode 100644
index acb69f7fe..000000000
--- a/play-kml-tour/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.playkmltour"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.playkmltour"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(libs.androidx.appcompat)
- implementation(project(":samples-lib"))
-}
diff --git a/play-kml-tour/proguard-rules.pro b/play-kml-tour/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/play-kml-tour/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/play-kml-tour/src/main/AndroidManifest.xml b/play-kml-tour/src/main/AndroidManifest.xml
deleted file mode 100644
index 07ff67411..000000000
--- a/play-kml-tour/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt b/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt
deleted file mode 100644
index 9619f5308..000000000
--- a/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.esri.arcgismaps.sample.playkmltour
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- // ArcGIS Portal item containing the .kmz markup tour file
- // of Esri HQ and some of the global offices
- "https://arcgisruntime.maps.arcgis.com/home/item.html?id=f10b1d37fdd645c9bc9b189fb546307c"
- )
- )
- }
-}
diff --git a/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt b/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt
deleted file mode 100644
index 4406f09ac..000000000
--- a/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt
+++ /dev/null
@@ -1,249 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.playkmltour
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISScene
-import com.arcgismaps.mapping.ArcGISTiledElevationSource
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Surface
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.ViewpointType
-import com.arcgismaps.mapping.kml.KmlContainer
-import com.arcgismaps.mapping.kml.KmlDataset
-import com.arcgismaps.mapping.kml.KmlNode
-import com.arcgismaps.mapping.kml.KmlTour
-import com.arcgismaps.mapping.kml.KmlTourController
-import com.arcgismaps.mapping.kml.KmlTourStatus
-import com.arcgismaps.mapping.layers.KmlLayer
-import com.esri.arcgismaps.sample.playkmltour.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import java.io.File
-import kotlin.math.roundToInt
-
-
-class MainActivity : AppCompatActivity() {
-
- private val provisionPath: String by lazy {
- getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.app_name)
- }
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val sceneView by lazy {
- activityMainBinding.sceneView
- }
-
- private val playPauseButton by lazy {
- activityMainBinding.playPauseButton
- }
-
- private val resetTourButton by lazy {
- activityMainBinding.resetTourButton
- }
-
- private val tourStatusTV by lazy {
- activityMainBinding.tourStatusTV
- }
-
- private val tourProgressBar by lazy {
- activityMainBinding.tourProgressBar
- }
-
- private var initialViewpoint: Viewpoint? = null
-
- private val kmlTourController = KmlTourController()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(sceneView)
-
-
- // add elevation data
- val surface = Surface().apply {
- elevationSources.add(ArcGISTiledElevationSource(getString(R.string.world_terrain_service)))
- }
-
- // create a scene and set the surface
- sceneView.scene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
- baseSurface = surface
- }
-
- // add a KML layer from a KML dataset with a KML tour
- val kmlDataset = KmlDataset(provisionPath + getString(R.string.kml_tour_path))
- val kmlLayer = KmlLayer(kmlDataset)
-
- // add the layer to the scene view's operational layers
- sceneView.scene?.operationalLayers?.add(kmlLayer)
-
- // load the KML layer
- lifecycleScope.launch {
- kmlLayer.load().onFailure {
- showError(it.message.toString())
- }.onSuccess {
- // get the first loaded KML tour
- val kmlTour = findFirstKMLTour(kmlDataset.rootNodes)
- if (kmlTour == null) {
- showError("Cannot find KML tour in dataset")
- return@onSuccess
- }
-
- // collect changes in KML tour status
- collectKmlTourStatus(kmlTour)
-
- // set the KML tour to the controller
- kmlTourController.tour = kmlTour
- }
- }
-
- resetTourButton.setOnClickListener {
- // set tour to the initial viewpoint
- initialViewpoint?.let { sceneView.setViewpoint(it) }
- // reset tour controller
- kmlTourController.reset()
- }
-
- playPauseButton.setOnClickListener {
- // button was clicked when tour was playing
- if (kmlTourController.tour?.status?.value == KmlTourStatus.Playing)
- // pause KML tour
- kmlTourController.pause()
- else
- // play KML tour
- kmlTourController.play()
- }
- }
-
- /**
- * Recursively searches for the first KML tour in a list of [kmlNodes].
- * Returns the first [KmlTour], or null if there are no tours.
- */
- private fun findFirstKMLTour(kmlNodes: List): KmlTour? {
- kmlNodes.forEach { node ->
- if (node is KmlTour)
- return node
- else if (node is KmlContainer)
- return findFirstKMLTour(node.childNodes)
- }
- return null
- }
-
- /**
- * Collects KmlTourStatus events from the [kmlTour] and then calls
- * showKmlTourStatus()
- */
- private fun collectKmlTourStatus(kmlTour: KmlTour) = lifecycleScope.launch {
- kmlTour.status.collect { kmlTourStatus ->
- when (kmlTourStatus) {
- KmlTourStatus.Completed -> {
- showKmlTourStatus("Completed", isResetEnabled = false, isPlayingTour = false)
- }
- KmlTourStatus.Initialized -> {
- showKmlTourStatus("Initialized", isResetEnabled = false, isPlayingTour = false)
- }
- KmlTourStatus.Paused -> {
- showKmlTourStatus("Paused", isResetEnabled = true, isPlayingTour = false)
- }
- KmlTourStatus.Playing -> {
- showKmlTourStatus("Playing", isResetEnabled = true, isPlayingTour = true)
- // set the tour's initial viewpoint
- if (initialViewpoint == null) {
- initialViewpoint = sceneView.getCurrentViewpoint(
- ViewpointType.BoundingGeometry
- )
- }
- }
- else -> {}
- }
- }
- }
-
- /**
- * Displays the KML tour status using the [kmlTourStatus], display [resetTourButton]
- * if [isResetEnabled] and set [playPauseButton] based on [isPlayingTour].
- */
- private fun showKmlTourStatus(
- kmlTourStatus: String,
- isResetEnabled: Boolean,
- isPlayingTour: Boolean
- ) {
- // set the KML tour status
- tourStatusTV.text = String.format("Tour status: %s", kmlTourStatus)
-
- // enable the buttons
- resetTourButton.isEnabled = isResetEnabled
- playPauseButton.isEnabled = true
-
- // show pause button if true
- if (isPlayingTour) {
- playPauseButton.apply {
- // set button icon
- icon = AppCompatResources.getDrawable(
- this@MainActivity,
- R.drawable.ic_round_pause_24
- )
- // set button text
- text = getText(R.string.pause)
- }
- } else { // show play button if false
- playPauseButton.apply {
- // set button icon
- icon = AppCompatResources.getDrawable(
- this@MainActivity,
- R.drawable.ic_round_play_arrow_24
- )
- // set button text
- text = getString(R.string.play)
- }
- }
-
- // get progress of tour every second
- lifecycleScope.launch {
- // run as long as KML tour status is "Playing"
- while (kmlTourStatus == "Playing") {
- // get percentage of current position over total duration
- val tourProgressInt = ((kmlTourController.currentPosition.value * 100.0)
- / (kmlTourController.totalDuration.value)).roundToInt()
- tourProgressBar.progress = tourProgressInt
- // set a second delay
- delay(1000)
- }
- }
-
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(sceneView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/play-kml-tour/src/main/res/drawable-v24/ic_launcher_foreground.xml b/play-kml-tour/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/play-kml-tour/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/play-kml-tour/src/main/res/drawable/ic_launcher_background.xml b/play-kml-tour/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/play-kml-tour/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/play-kml-tour/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher.png b/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher_round.png b/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher.png b/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher_round.png b/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher.png b/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher.png b/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/play-kml-tour/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/play-kml-tour/src/main/res/values/strings.xml b/play-kml-tour/src/main/res/values/strings.xml
deleted file mode 100644
index 2d8919e93..000000000
--- a/play-kml-tour/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
- Play KML tour
- https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
- /Esri_tour.kmz
- Tour status:
- Reset tour
- Play tour
- Pause tour
-
diff --git a/project-geometry/.gitignore b/project-geometry/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/project-geometry/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/project-geometry/build.gradle.kts b/project-geometry/build.gradle.kts
deleted file mode 100644
index 6e4f41c03..000000000
--- a/project-geometry/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.projectgeometry"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.projectgeometry"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/project-geometry/proguard-rules.pro b/project-geometry/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/project-geometry/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/project-geometry/src/main/AndroidManifest.xml b/project-geometry/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/project-geometry/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt b/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt
deleted file mode 100644
index 31b59c282..000000000
--- a/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.projectgeometry
-
-import android.graphics.BitmapFactory
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.*
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.MapView
-import com.esri.arcgismaps.sample.projectgeometry.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.util.Locale
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // setup the data binding for the MapView
- private val mapView: MapView by lazy {
- activityMainBinding.mapView
- }
-
- // shows the projection information as a TextView
- private val infoTextView: TextView by lazy {
- activityMainBinding.infoTextView
- }
-
- // setup the red pin marker image as bitmap drawable
- private val markerDrawable: BitmapDrawable by lazy {
- // load the bitmap from resources and create a drawable
- val bitmap = BitmapFactory.decodeResource(resources, R.drawable.pin_symbol)
- BitmapDrawable(resources, bitmap)
- }
-
- // setup the red pin marker as a Graphic
- private val markerGraphic: Graphic by lazy {
- // creates a symbol from the marker drawable
- val markerSymbol = PictureMarkerSymbol.createWithImage(markerDrawable).apply {
- // resize the symbol into a smaller size
- width = 30f
- height = 30f
- // offset in +y axis so the marker spawned
- // is right on the touch point
- offsetY = 25f
- }
- // create the graphic from the symbol
- Graphic(symbol = markerSymbol)
- }
-
- // creates a graphic overlay
- private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- // configure mapView assignments
- mapView.apply {
- this.map = map
- // add our marker overlay to the graphics overlay
- graphicsOverlay.graphics.add(markerGraphic)
- // add the graphics overlay to display marker graphics
- graphicsOverlays.add(graphicsOverlay)
- // set the default viewpoint to Redlands,CA
- setViewpoint(Viewpoint(34.058, -117.195, 5e4))
- }
-
- lifecycleScope.launch {
- // check if the map has loaded successfully
- map.load().onSuccess {
- // capture and collect when the user taps on the screen
- mapView.onSingleTapConfirmed.collect { event ->
- event.mapPoint?.let { point -> onGeoViewTapped(point) }
- }
- }.onFailure {
- // if map load failed, show the error
- showError("Error Loading Map", mapView)
- }
- }
- }
-
- /**
- * Handles the SingleTapEvent by drawing a marker, re-centering the mapView to the marker
- * and performs a Spatial reference transformation of the tapped Location
- * using GeometryEngine and displays the result.
- */
- private suspend fun onGeoViewTapped(point: Point) {
- // update the marker location to where the user tapped on the map
- markerGraphic.geometry = point
- // set mapview to recenter to the tapped location
- mapView.setViewpointGeometry(point.extent)
- // project the web mercator location into a WGS84
- val projectedPoint = GeometryEngine.projectOrNull(point, SpatialReference.wgs84())
- // build and display the projection result as a string
- infoTextView.text = getString(
- R.string.projection_info_text,
- point.toDisplayFormat(),
- projectedPoint?.toDisplayFormat()
- )
- }
-
- /**
- * Displays an error onscreen
- */
- private fun showError(message: String, view: View) {
- Log.e(localClassName, message)
- Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-/**
- * Extension function for the Point type that returns
- * a float-precision formatted string suitable for display
- */
-private fun Point.toDisplayFormat() =
- "${String.format(Locale.getDefault(),"%.5f", x)}, ${String.format(Locale.getDefault(),"%.5f", y)}"
diff --git a/project-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml b/project-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/project-geometry/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/project-geometry/src/main/res/drawable/ic_launcher_background.xml b/project-geometry/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/project-geometry/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/project-geometry/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/project-geometry/src/main/res/mipmap-hdpi/ic_launcher.png b/project-geometry/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/project-geometry/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png b/project-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/project-geometry/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-mdpi/ic_launcher.png b/project-geometry/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/project-geometry/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png b/project-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/project-geometry/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png b/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/project-geometry/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png b/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/project-geometry/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/project-geometry/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/project-geometry/src/main/res/values/strings.xml b/project-geometry/src/main/res/values/strings.xml
deleted file mode 100644
index 384a91a47..000000000
--- a/project-geometry/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Project geometry
- Coordinates
- Tap to begin
- Original: %1s\nProjected: %2s
-
diff --git a/query-feature-table/.gitignore b/query-feature-table/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/query-feature-table/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/query-feature-table/build.gradle.kts b/query-feature-table/build.gradle.kts
deleted file mode 100644
index dcb034a7f..000000000
--- a/query-feature-table/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.queryfeaturetable"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.queryfeaturetable"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/query-feature-table/proguard-rules.pro b/query-feature-table/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/query-feature-table/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/query-feature-table/src/main/AndroidManifest.xml b/query-feature-table/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/query-feature-table/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt b/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt
deleted file mode 100644
index c1938f4b4..000000000
--- a/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.queryfeaturetable
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.queryfeaturetable.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- QueryFeatureTableApp()
- }
- }
- }
-
- @Composable
- private fun QueryFeatureTableApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/query-feature-table/src/main/res/drawable-v24/ic_launcher_foreground.xml b/query-feature-table/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/query-feature-table/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-feature-table/src/main/res/drawable/ic_launcher_background.xml b/query-feature-table/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/query-feature-table/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/query-feature-table/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher.png b/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher_round.png b/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/query-feature-table/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher.png b/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher_round.png b/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/query-feature-table/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher.png b/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher.png b/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/query-feature-table/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-feature-table/src/main/res/values/strings.xml b/query-feature-table/src/main/res/values/strings.xml
deleted file mode 100644
index a67aa2905..000000000
--- a/query-feature-table/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Query feature table
- https://services.arcgis.com/jIL9msH9OI208GCb/arcgis/rest/services/USA_Daytime_Population_2016/FeatureServer/0
-
diff --git a/query-features-with-arcade-expression/.gitignore b/query-features-with-arcade-expression/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/query-features-with-arcade-expression/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/query-features-with-arcade-expression/build.gradle.kts b/query-features-with-arcade-expression/build.gradle.kts
deleted file mode 100644
index d524e8465..000000000
--- a/query-features-with-arcade-expression/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/query-features-with-arcade-expression/proguard-rules.pro b/query-features-with-arcade-expression/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/query-features-with-arcade-expression/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/query-features-with-arcade-expression/src/main/AndroidManifest.xml b/query-features-with-arcade-expression/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/query-features-with-arcade-expression/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt b/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt
deleted file mode 100644
index b24e9ee7b..000000000
--- a/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression
-
-import android.graphics.BitmapFactory
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.arcade.ArcadeEvaluator
-import com.arcgismaps.arcade.ArcadeExpression
-import com.arcgismaps.arcade.ArcadeProfile
-import com.arcgismaps.data.ArcGISFeature
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.layers.Layer
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.portal.Portal
-import com.arcgismaps.mapping.PortalItem
-import com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val infoTextView by lazy {
- activityMainBinding.infoTextView
- }
-
- // progress indicator
- private val progressBar by lazy {
- activityMainBinding.progressBar
- }
-
- // setup the red pin marker image as a bitmap drawable
- private val markerDrawable: BitmapDrawable by lazy {
- // load the bitmap from resources and create a drawable
- val bitmap = BitmapFactory.decodeResource(resources, R.drawable.map_pin_symbol)
- BitmapDrawable(resources, bitmap)
- }
-
- // setup the red pin marker as a Graphic
- private val markerGraphic: Graphic by lazy {
- // creates a symbol from the marker drawable
- val markerSymbol = PictureMarkerSymbol.createWithImage(markerDrawable).apply {
- // resize the symbol into a smaller size
- width = 30f
- height = 30f
- // offset in +y axis so the marker spawned is right on the touch point
- offsetY = 25f
- }
- // create the graphic from the symbol
- Graphic(symbol = markerSymbol)
- }
-
- // create a graphic overlay
- private val graphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a portal item with the itemId of the web map
- val portal = Portal("https://www.arcgis.com/")
- val portalItem = PortalItem(portal, "539d93de54c7422f88f69bfac2aebf7d")
- // create and add a map with with portal item
- val map = ArcGISMap(portalItem)
- // add the marker graphic to the graphics overlay
- graphicsOverlay.graphics.add(markerGraphic)
- mapView.apply {
- this.map = map
- // add the graphics overlay to the MapView
- graphicsOverlays.add(graphicsOverlay)
- }
-
- lifecycleScope.launch {
- // show an error and return if the map load failed
- map.load().onFailure {
- return@launch showError("Error loading map:${it.message}")
- }
-
- // get the RPD Beats layer from the map's operational layers
- val policeBeatsLayer = map.operationalLayers.firstOrNull { layer ->
- layer.id == "RPD_Reorg_9254"
- } ?: return@launch showError("Error finding RPD Beats layer")
-
- // capture and collect when the user taps on the screen
- mapView.onSingleTapConfirmed.collect { event ->
- // update the marker location to where the user tapped on the map
- event.mapPoint?.let { point ->
- markerGraphic.geometry = point
- mapView.setViewpointCenter(point)
- }
- // evaluate an Arcade expression on the tapped screen coordinate
- evaluateArcadeExpression(event.screenCoordinate, map, policeBeatsLayer)
- }
- }
- }
-
- /**
- * Evaluates an Arcade expression that returns crime in the last 60 days at the tapped
- * [screenCoordinate] on the [map] with the [policeBeatsLayer] and displays the result
- * in a textview
- */
- private suspend fun evaluateArcadeExpression(
- screenCoordinate: ScreenCoordinate,
- map: ArcGISMap,
- policeBeatsLayer: Layer
- ) {
- // show the progress indicator as the Arcade evaluation can take time to complete
- progressBar.visibility = View.VISIBLE
- // identify the layer and its elements based on the position tapped on the mapView and
- // get the result
- val result = mapView.identifyLayer(
- layer = policeBeatsLayer,
- screenCoordinate = screenCoordinate,
- tolerance = 12.0,
- returnPopupsOnly = false
- )
- // get the result as an IdentifyLayerResult
- val identifyLayerResult = result.getOrElse { error ->
- // if the identifyLayer operation failed show an error and return
- showError("Error identifying layer:${error.message}")
- // reset the text view to show its default text
- infoTextView.text = getString(R.string.tap_to_begin)
- // dismiss the progress indicator
- progressBar.visibility = View.GONE
- return
- }
- // if there are no geoElements identified
- if (identifyLayerResult.geoElements.isEmpty()) {
- // since the layer is a feature layer, display that no features were found
- infoTextView.text = getString(R.string.no_features_found)
- // dismiss the progress indicator
- progressBar.visibility = View.GONE
- return
- }
- // get the first identified GeoElement as an ArcGISFeature
- val identifiedFeature = identifyLayerResult.geoElements.first() as ArcGISFeature
- // create a string containing the Arcade expression
- val expressionValue =
- "var crimes = FeatureSetByName(\$map, 'Crime in the last 60 days');\n" +
- "return Count(Intersects(\$feature, crimes));"
- // create an ArcadeExpression using the string expression
- val arcadeExpression = ArcadeExpression(expressionValue)
- // create an ArcadeEvaluator with the ArcadeExpression and an ArcadeProfile
- val arcadeEvaluator = ArcadeEvaluator(arcadeExpression, ArcadeProfile.FormCalculation)
- // create a map of profile variables with the feature and map as key value pairs
- val profileVariables = mapOf("\$feature" to identifiedFeature, "\$map" to map)
- // evaluate using the previously set profile variables and get the result
- val evaluationResult = arcadeEvaluator.evaluate(profileVariables)
- // get the result as an ArcadeEvaluationResult
- val arcadeEvaluationResult = evaluationResult.getOrElse { error ->
- // if the evaluation failed show an error and return
- showError("Error evaluating Arcade expression:${error.message}")
- // reset the text view to show its default text
- infoTextView.text = getString(R.string.tap_to_begin)
- // dismiss the progress indicator
- progressBar.visibility = View.GONE
- return
- }
- // get the crimes count from the arcadeEvaluationResult as a numerical double value
- val crimesCount = arcadeEvaluationResult.result as Double
- // display this result in a textview
- infoTextView.text = getString(R.string.crime_info_text, crimesCount.toInt())
- // hide the progress indicator
- progressBar.visibility = View.GONE
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/query-features-with-arcade-expression/src/main/res/drawable-v24/ic_launcher_foreground.xml b/query-features-with-arcade-expression/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/query-features-with-arcade-expression/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-features-with-arcade-expression/src/main/res/drawable/ic_launcher_background.xml b/query-features-with-arcade-expression/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/query-features-with-arcade-expression/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/query-features-with-arcade-expression/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher.png b/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher_round.png b/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher.png b/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher_round.png b/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher.png b/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher.png b/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/query-features-with-arcade-expression/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/query-features-with-arcade-expression/src/main/res/values/strings.xml b/query-features-with-arcade-expression/src/main/res/values/strings.xml
deleted file mode 100644
index 4a1c44c8e..000000000
--- a/query-features-with-arcade-expression/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
- Query features with arcade expression
- Tap to begin
- Crime in the last 60 days: %1d
- No features found
-
diff --git a/render-multilayer-symbols/.gitignore b/render-multilayer-symbols/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/render-multilayer-symbols/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/render-multilayer-symbols/build.gradle.kts b/render-multilayer-symbols/build.gradle.kts
deleted file mode 100644
index c85164204..000000000
--- a/render-multilayer-symbols/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.rendermultilayersymbols"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.rendermultilayersymbols"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/render-multilayer-symbols/proguard-rules.pro b/render-multilayer-symbols/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/render-multilayer-symbols/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/render-multilayer-symbols/src/main/AndroidManifest.xml b/render-multilayer-symbols/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/render-multilayer-symbols/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt b/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt
deleted file mode 100644
index c3e4f1d29..000000000
--- a/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt
+++ /dev/null
@@ -1,518 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.rendermultilayersymbols
-
-import android.graphics.BitmapFactory
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.symbology.DashGeometricEffect
-import com.arcgismaps.mapping.symbology.HatchFillSymbolLayer
-import com.arcgismaps.mapping.symbology.HorizontalAlignment
-import com.arcgismaps.mapping.symbology.MultilayerPointSymbol
-import com.arcgismaps.mapping.symbology.MultilayerPolygonSymbol
-import com.arcgismaps.mapping.symbology.MultilayerPolylineSymbol
-import com.arcgismaps.mapping.symbology.MultilayerSymbol
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbolLayer
-import com.arcgismaps.mapping.symbology.SolidFillSymbolLayer
-import com.arcgismaps.mapping.symbology.SolidStrokeSymbolLayer
-import com.arcgismaps.mapping.symbology.StrokeSymbolLayerCapStyle
-import com.arcgismaps.mapping.symbology.SymbolAnchor
-import com.arcgismaps.mapping.symbology.SymbolAnchorPlacementMode
-import com.arcgismaps.mapping.symbology.SymbolLayer
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.arcgismaps.mapping.symbology.VectorMarkerSymbolElement
-import com.arcgismaps.mapping.symbology.VectorMarkerSymbolLayer
-import com.arcgismaps.mapping.symbology.VerticalAlignment
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.rendermultilayersymbols.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
-private val Color.Companion.yellow: Color
- get() {
- return fromRgba(255, 255, 0, 255)
- }
-
-private val Color.Companion.magenta: Color
- get() {
- return fromRgba(255, 0, 255, 255)
- }
-
-// define offset used to keep a consistent distance between symbols in the same column
-private const val OFFSET = 20.0
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // define the graphics overlay to add the multilayer symbols
- private var graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- mapView.apply {
- // set the map to be displayed in the layout's map view
- mapView.map = ArcGISMap(BasemapStyle.ArcGISLightGray)
- // add the graphic overlay to the map view
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // create labels to go above each category of graphic
- addTextGraphics()
-
- // create picture marker symbols, from URI or local cache
- addImageGraphics()
-
- // add graphics with simple vector marker symbol elements (MultilayerPoint Simple Markers on app UI)
- val solidFillSymbolLayer = SolidFillSymbolLayer(Color.red)
- val multilayerPolygonSymbol = MultilayerPolygonSymbol(listOf(solidFillSymbolLayer))
- val solidStrokeSymbolLayer =
- SolidStrokeSymbolLayer(1.0, Color.red, listOf(DashGeometricEffect()))
- val multilayerPolylineSymbol = MultilayerPolylineSymbol(listOf(solidStrokeSymbolLayer))
-
- // define vector element for a diamond, triangle and cross
- val diamondGeometry =
- Geometry.fromJsonOrNull("{\"rings\":[[[0.0,2.5],[2.5,0.0],[0.0,-2.5],[-2.5,0.0],[0.0,2.5]]]}")
- val triangleGeometry =
- Geometry.fromJsonOrNull("{\"rings\":[[[0.0,5.0],[5,-5.0],[-5,-5.0],[0.0,5.0]]]}")
- val crossGeometry =
- Geometry.fromJsonOrNull("{\"paths\":[[[-1,1],[0,0],[1,-1]],[[1,1],[0,0],[-1,-1]]]}")
-
- if (diamondGeometry == null || triangleGeometry == null || crossGeometry == null) {
- showError("Error reading geometry from json")
- return
- }
-
- // add red diamond graphic
- addGraphicsWithVectorMarkerSymbolElements(
- multilayerPolygonSymbol, diamondGeometry, 0.0
- )
- // add red triangle graphic
- addGraphicsWithVectorMarkerSymbolElements(
- multilayerPolygonSymbol, triangleGeometry, OFFSET
- )
- // add red cross graphic
- addGraphicsWithVectorMarkerSymbolElements(
- multilayerPolylineSymbol, crossGeometry, 2 * OFFSET
- )
-
- // create line marker symbols with short dash dot dot
- addLineGraphicsWithMarkerSymbols(
- listOf(4.0, 6.0, 0.5, 6.0, 0.5, 6.0), 0.0
- )
- // create line marker symbol with short dash
- addLineGraphicsWithMarkerSymbols(
- listOf(4.0, 6.0), OFFSET
- )
- // create line marker symbol with dash dot dot
- addLineGraphicsWithMarkerSymbols(
- listOf(7.0, 9.0, 0.5, 9.0), 2 * OFFSET
- )
-
- // create polygon marker symbols
- // cross-hatched diagonal lines
- addPolygonGraphicsWithMarkerSymbols(
- listOf(-45.0, 45.0), 0.0
- )
- // hatched diagonal lines
- addPolygonGraphicsWithMarkerSymbols(
- listOf(-45.0), OFFSET
- )
- // hatched vertical lines
- addPolygonGraphicsWithMarkerSymbols(
- listOf(90.0), 2 * OFFSET
- )
-
- // define vector element for a hexagon which will be used as the basis of a complex point
- val complexPointGeometry =
- Geometry.fromJsonOrNull("{\"rings\":[[[-2.89,5.0],[2.89,5.0],[5.77,0.0],[2.89,-5.0],[-2.89,-5.0],[-5.77,0.0],[-2.89,5.0]]]}")
-
- // create the more complex multilayer graphics: a point, polygon, and polyline
- complexPointGeometry?.let { addComplexPoint(it) }
- addComplexPolygon()
- addComplexPolyline()
- }
-
- /**
- * Creates the label graphics to be displayed above each category of symbol,
- * and adds them to the graphics overlay.
- */
- private fun addTextGraphics() {
- graphicsOverlay.graphics.addAll(
- listOf(
- Graphic(
- Point(-150.0, 50.0, SpatialReference.wgs84()),
- getTextSymbol("MultilayerPoint\nSimple Markers")
- ), Graphic(
- Point(-80.0, 50.0, SpatialReference.wgs84()),
- getTextSymbol("MultilayerPoint\nPicture Markers")
- ), Graphic(
- Point(0.0, 50.0, SpatialReference.wgs84()),
- getTextSymbol("Multilayer\nPolyline")
- ), Graphic(
- Point(65.0, 50.0, SpatialReference.wgs84()),
- getTextSymbol("Multilayer\nPolygon")
- ), Graphic(
- Point(130.0, 50.0, SpatialReference.wgs84()),
- getTextSymbol("Multilayer\nComplex Symbols")
- )
- )
- )
- }
-
- /**
- * @return the TextSymbol with the [text] to be displayed on the map.
- */
- private fun getTextSymbol(text: String): TextSymbol {
- val textSymbol = TextSymbol(
- text, Color.black, 10F, HorizontalAlignment.Center, VerticalAlignment.Middle
- )
- // give the text symbol a white background
- textSymbol.backgroundColor = Color.white
- return textSymbol
- }
-
- /**
- * Create picture marker symbols from online URI and local cache.
- */
- private fun addImageGraphics() {
- // URI of image to display
- val blueTentImageURI = "https://static.arcgis.com/images/Symbols/OutdoorRecreation/Camping.png"
- // load the PictureMarkerSymbolLayer using the image URI
- val pictureMarkerFromUri = PictureMarkerSymbolLayer(blueTentImageURI)
- // add loaded layer to the map
- addGraphicFromPictureMarkerSymbolLayer(pictureMarkerFromUri, 0.0)
- // load blue pin from as a bitmap
- val bitmap = BitmapFactory.decodeResource(resources, R.drawable.blue_pin)
- // load the PictureMarkerSymbolLayer using the bitmap drawable
- val pictureMarkerFromCache = PictureMarkerSymbolLayer.createWithImage(BitmapDrawable(resources, bitmap))
- // add loaded layer to the map
- addGraphicFromPictureMarkerSymbolLayer(pictureMarkerFromCache, 40.0)
- }
-
- /**
- * Loads a picture marker symbol layer and after it has loaded, creates a new multilayer point symbol from it.
- * A graphic is created from the multilayer point symbol and added to the graphics overlay.
- *
- * The [pictureMarkerSymbolLayer] to be loaded.
- * The [offset] value used to keep a consistent distance between symbols in the same column.
- */
- private fun addGraphicFromPictureMarkerSymbolLayer(
- pictureMarkerSymbolLayer: PictureMarkerSymbolLayer, offset: Double
- ) = lifecycleScope.launch {
- // wait for the picture marker symbol layer to load and check it has loaded
- pictureMarkerSymbolLayer.load().getOrElse {
- showError("Picture marker symbol layer failed to load: ${it.message}")
- }
- // set the size of the layer and create a new multilayer point symbol from it
- pictureMarkerSymbolLayer.size = 40.0
- val multilayerPointSymbol = MultilayerPointSymbol(listOf(pictureMarkerSymbolLayer))
- // create location for the symbol
- val point = Point(-80.0, 20.0 - offset, SpatialReference.wgs84())
-
- // create graphic with the location and symbol and add it to the graphics overlay
- val graphic = Graphic(point, multilayerPointSymbol)
- graphicsOverlay.graphics.add(graphic)
- }
-
- /**
- * Adds new graphics constructed from multilayer point symbols.
- *
- * The [multilayerSymbol] to construct the vector marker symbol element with.
- * The input [geometry] for the vector marker symbol element.
- * [offset] the value used to keep a consistent distance between symbols in the same column.
- */
- private fun addGraphicsWithVectorMarkerSymbolElements(
- multilayerSymbol: MultilayerSymbol, geometry: Geometry, offset: Double
- ) {
- // define a vector element and create a new multilayer point symbol from it
- val vectorMarkerSymbolElement = VectorMarkerSymbolElement(geometry, multilayerSymbol)
- val vectorMarkerSymbolLayer = VectorMarkerSymbolLayer(listOf(vectorMarkerSymbolElement))
- val multilayerPointSymbol = MultilayerPointSymbol(listOf(vectorMarkerSymbolLayer))
-
- // create point graphic using the symbol and add it to the graphics overlay
- val graphic =
- Graphic(Point(-150.0, 20 - offset, SpatialReference.wgs84()), multilayerPointSymbol)
- graphicsOverlay.graphics.add(graphic)
- }
-
- /**
- * Adds new graphics constructed from multilayer polyline symbols.
- *
- * The pattern of [dashSpacing] dots/dashes used by the line and
- * [offset] the value used to keep a consistent distance between symbols in the same column.
- */
- private fun addLineGraphicsWithMarkerSymbols(dashSpacing: List, offset: Double) {
- // create a dash effect from the provided values
- val dashGeometricEffect = DashGeometricEffect(dashSpacing)
- // create stroke used by line symbols
- val solidStrokeSymbolLayer = SolidStrokeSymbolLayer(
- 3.0, Color.red, listOf(dashGeometricEffect)
- ).apply {
- capStyle = StrokeSymbolLayerCapStyle.Round
- }
- // create a polyline for the multilayer polyline symbol
- val polylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
- addPoint(Point(-30.0, 20 - offset))
- addPoint(Point(30.0, 20 - offset))
- }
- // create a multilayer polyline symbol from the solidStrokeSymbolLayer
- val multilayerPolylineSymbol = MultilayerPolylineSymbol(listOf(solidStrokeSymbolLayer))
- // create a polyline graphic with geometry using the symbol created above, and add it to the graphics overlay
- graphicsOverlay.graphics.add(
- Graphic(
- polylineBuilder.toGeometry(), multilayerPolylineSymbol
- )
- )
- }
-
- /**
- * Adds new graphics constructed from multilayer polygon symbols.
- *
- * Takes a list containing the [angles] at which to draw fill lines within the polygon and
- * [offset] the value used to keep a consistent distance between symbols in the same column.
- */
- private fun addPolygonGraphicsWithMarkerSymbols(angles: List, offset: Double) {
- val polygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
- addPoint(Point(60.0, 25 - offset))
- addPoint(Point(70.0, 25 - offset))
- addPoint(Point(70.0, 20 - offset))
- addPoint(Point(60.0, 20 - offset))
- }
-
- // create a stroke symbol layer to be used by patterns
- val strokeForHatches = SolidStrokeSymbolLayer(2.0, Color.red, listOf(DashGeometricEffect()))
-
- // create a stroke symbol layer to be used as an outline for aforementioned patterns
- val strokeForOutline =
- SolidStrokeSymbolLayer(1.0, Color.black, listOf(DashGeometricEffect()))
-
- // create a list to hold all necessary symbol layers - at least one for patterns and one for an outline at the end
- val symbolLayerList = mutableListOf()
-
- // for each angle, create a symbol layer using the pattern stroke, with hatched lines at the given angle
- for (i in angles.indices) {
- val hatchFillSymbolLayer = HatchFillSymbolLayer(
- MultilayerPolylineSymbol(listOf(strokeForHatches)), angles[i]
- )
- // define separation distance for lines and add them to the symbol layer list
- hatchFillSymbolLayer.separation = 9.0
- symbolLayerList.add(hatchFillSymbolLayer)
- }
-
- // assign the outline layer to the last element of the symbol layer list
- symbolLayerList.add(strokeForOutline)
- // create a multilayer polygon symbol from the symbol layer list
- val multilayerPolygonSymbol =
- MultilayerPolygonSymbol(symbolLayerList)
- // create a polygon graphic with geometry using the symbol created above, and add it to the graphics overlay
- val graphic = Graphic(polygonBuilder.toGeometry(), multilayerPolygonSymbol)
- graphicsOverlay.graphics.add(graphic)
- }
-
- /**
- * Creates a complex point from multiple symbol layers and a provided geometry.
- * @param complexPointGeometry a base geometry upon which other symbol layers are drawn.
- */
- private fun addComplexPoint(complexPointGeometry: Geometry) {
- // create marker layers for complex point
- val orangeSquareVectorMarkerLayer: VectorMarkerSymbolLayer =
- getLayerForComplexPoint(Color.cyan, Color.blue, 11.0)
- val blackSquareVectorMarkerLayer: VectorMarkerSymbolLayer =
- getLayerForComplexPoint(Color.black, Color.cyan, 6.0)
- val purpleSquareVectorMarkerLayer: VectorMarkerSymbolLayer = getLayerForComplexPoint(
- Color.transparent, Color.magenta, 14.0
- )
-
- // set anchors for marker layers
- orangeSquareVectorMarkerLayer.anchor =
- SymbolAnchor(-4.0, -6.0, SymbolAnchorPlacementMode.Absolute)
- blackSquareVectorMarkerLayer.anchor =
- SymbolAnchor(2.0, 1.0, SymbolAnchorPlacementMode.Absolute)
- purpleSquareVectorMarkerLayer.anchor =
- SymbolAnchor(4.0, 2.0, SymbolAnchorPlacementMode.Absolute)
-
- // create a yellow hexagon with a black outline
- val yellowFillLayer = SolidFillSymbolLayer(Color.yellow)
- val blackOutline = SolidStrokeSymbolLayer(2.0, Color.black, listOf(DashGeometricEffect()))
- val hexagonVectorElement = VectorMarkerSymbolElement(
- complexPointGeometry, MultilayerPolylineSymbol(listOf(yellowFillLayer, blackOutline))
- )
- val hexagonVectorMarkerLayer = VectorMarkerSymbolLayer(listOf(hexagonVectorElement)).apply {
- size = 35.0
- }
-
- // create the multilayer point symbol
- val multilayerPointSymbol = MultilayerPointSymbol(
- listOf(
- hexagonVectorMarkerLayer,
- orangeSquareVectorMarkerLayer,
- blackSquareVectorMarkerLayer,
- purpleSquareVectorMarkerLayer
- )
- )
-
- // create the multilayer point graphic using the symbols created above
- val complexPointGraphic =
- Graphic(Point(130.0, 20.0, SpatialReference.wgs84()), multilayerPointSymbol)
- graphicsOverlay.graphics.add(complexPointGraphic)
- }
-
- /**
- * Creates a symbol layer for use in the composition of a complex point
- * using [fillColor], [outlineColor] as colors and the [size] of the symbol
- * Then return a [VectorMarkerSymbolLayer] of the created symbol.
- */
- private fun getLayerForComplexPoint(
- fillColor: Color, outlineColor: Color, size: Double
- ): VectorMarkerSymbolLayer {
- // create the fill layer and outline
- val fillLayer = SolidFillSymbolLayer(fillColor)
- val outline = SolidStrokeSymbolLayer(
- 2.0, outlineColor, listOf(DashGeometricEffect())
- )
- // create a geometry from an envelope
- val geometry = Envelope(
- Point(-0.5, -0.5, SpatialReference.wgs84()), Point(0.5, 0.5, SpatialReference.wgs84())
- )
- //create a symbol element using the geometry, fill layer, and outline
- val vectorMarkerSymbolElement = VectorMarkerSymbolElement(
- geometry, MultilayerPolygonSymbol(listOf(fillLayer, outline))
- )
- // create a symbol layer containing just the above symbol element, set its size, and return it
- val vectorMarkerSymbolLayer =
- VectorMarkerSymbolLayer(listOf(vectorMarkerSymbolElement)).apply {
- this.size = size
- }
- return vectorMarkerSymbolLayer
- }
-
- /**
- * Adds a complex polygon generated with multiple symbol layers.
- */
- private fun addComplexPolygon() {
- // create the multilayer polygon symbol
- val multilayerPolygonSymbol = MultilayerPolygonSymbol(getLayersForComplexPolys(true))
- // create the polygon
- val polygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
- addPoint(Point(120.0, 0.0))
- addPoint(Point(140.0, 0.0))
- addPoint(Point(140.0, -10.0))
- addPoint(Point(120.0, -10.0))
- }
- // create a multilayer polygon graphic with geometry using the symbols
- // created above and add it to the graphics overlay
- graphicsOverlay.graphics.add(
- Graphic(
- polygonBuilder.toGeometry(), multilayerPolygonSymbol
- )
- )
- }
-
- /**
- * Adds a complex polyline generated with multiple symbol layers.
- */
- private fun addComplexPolyline() {
- // create the multilayer polyline symbol
- val multilayerPolylineSymbol = MultilayerPolylineSymbol(getLayersForComplexPolys(false))
- val polylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
- addPoint(Point(120.0, -25.0))
- addPoint(Point(140.0, -25.0))
- }
- // create the multilayer polyline graphic with geometry using the symbols created above and add it to the graphics overlay
- graphicsOverlay.graphics.add(
- Graphic(
- polylineBuilder.toGeometry(), multilayerPolylineSymbol
- )
- )
- }
-
- /**
- * Generates and returns the symbol layers used by the addComplexPolygon and addComplexPolyline methods.
- * [includeRedFill] indicates whether to include the red fill needed by the complex polygon.
- * @return a list of symbol layers including the necessary effects.
- */
- private fun getLayersForComplexPolys(includeRedFill: Boolean): List {
- // create a black dash effect
- val blackDashes = SolidStrokeSymbolLayer(
- 1.0, Color.black, listOf(DashGeometricEffect(listOf(5.0, 3.0)))
- ).apply {
- capStyle = StrokeSymbolLayerCapStyle.Square
- }
-
- // create a black outline
- val blackOutline = SolidStrokeSymbolLayer(
- 7.0, Color.black, listOf(DashGeometricEffect())
- ).apply {
- capStyle = StrokeSymbolLayerCapStyle.Round
- }
-
- // create a yellow stroke inside
- val yellowStroke = SolidStrokeSymbolLayer(
- 5.0, Color.yellow, listOf(DashGeometricEffect())
- ).apply {
- capStyle = StrokeSymbolLayerCapStyle.Round
- }
-
- return if (includeRedFill) {
- // create a red filling for the polygon
- val redFillLayer = SolidFillSymbolLayer(Color.red)
- listOf(redFillLayer, blackOutline, yellowStroke, blackDashes)
- } else {
- listOf(blackOutline, yellowStroke, blackDashes)
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/render-multilayer-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml b/render-multilayer-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/render-multilayer-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/render-multilayer-symbols/src/main/res/drawable/ic_launcher_background.xml b/render-multilayer-symbols/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/render-multilayer-symbols/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/render-multilayer-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher.png b/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png b/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher.png b/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png b/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png b/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png b/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/render-multilayer-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/render-multilayer-symbols/src/main/res/values/strings.xml b/render-multilayer-symbols/src/main/res/values/strings.xml
deleted file mode 100644
index 8edc79c1b..000000000
--- a/render-multilayer-symbols/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Render multilayer symbols
-
diff --git a/samples-lib/build.gradle.kts b/samples-lib/build.gradle.kts
index 5bf54b61f..f577f4811 100644
--- a/samples-lib/build.gradle.kts
+++ b/samples-lib/build.gradle.kts
@@ -1,36 +1,25 @@
plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.gradle.secrets)
}
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- minSdk = libs.versions.minSdk.get().toInt()
- consumerProguardFiles("consumer-rules.pro")
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
- }
- }
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+android {
+ namespace = "com.esri.arcgismaps.sample.sampleslib"
buildFeatures {
- dataBinding = true
buildConfig = true
- compose = true
- }
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
+ dataBinding = true
}
-
- namespace = "com.esri.arcgismaps.sample.sampleslib"
}
dependencies {
+ // Only module specific dependencies needed here
+
// lib dependencies from rootProject build.gradle
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.constraintlayout)
@@ -46,5 +35,7 @@ dependencies {
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.ui.tooling)
implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.arcgis.maps.kotlin)
}
+
diff --git a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/DownloaderActivity.kt b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/DownloaderActivity.kt
index 1f8591c1a..13d26bc31 100644
--- a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/DownloaderActivity.kt
+++ b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/DownloaderActivity.kt
@@ -102,7 +102,7 @@ abstract class DownloaderActivity : AppCompatActivity() {
* at the [destinationPath]. The downloadManager clears the directory if user chooses
* to re-download the provisioned data.
*/
- private suspend fun sampleDownloadManager(
+ private fun sampleDownloadManager(
provisionURLs: List,
destinationPath: String,
): Flow = flow {
diff --git a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/BottomSheet.kt b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/BottomSheet.kt
index fc88228ac..c05478008 100644
--- a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/BottomSheet.kt
+++ b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/BottomSheet.kt
@@ -22,7 +22,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
diff --git a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/JobLoadingDialog.kt b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/JobLoadingDialog.kt
index 258d7b980..f57ae457e 100644
--- a/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/JobLoadingDialog.kt
+++ b/samples-lib/src/main/java/com/esri/arcgismaps/sample/sampleslib/components/JobLoadingDialog.kt
@@ -84,7 +84,8 @@ fun JobLoadingDialog(
)
// row of progress indicator and percentage text.
Row(
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// set ease animation when progress state changes
val progressAnimation by animateFloatAsState(
@@ -96,13 +97,13 @@ fun JobLoadingDialog(
)
// create the linear progress indicator
LinearProgressIndicator(
+ modifier = Modifier.fillMaxWidth().weight(1f),
progress = { progressAnimation },
)
// progress percentage text
Text(
text = "$progress%",
- textAlign = TextAlign.Center,
- modifier = Modifier.fillMaxWidth()
+ textAlign = TextAlign.Center
)
}
diff --git a/samples-lib/src/main/res/drawable/arcgis_maps_sdks_64.xml b/samples-lib/src/main/res/drawable/arcgis_maps_sdks_64.xml
new file mode 100644
index 000000000..8390b45f6
--- /dev/null
+++ b/samples-lib/src/main/res/drawable/arcgis_maps_sdks_64.xml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples-lib/src/main/res/drawable/arcgis_sdk_foreground.xml b/samples-lib/src/main/res/drawable/arcgis_sdk_foreground.xml
new file mode 100644
index 000000000..25cbc9858
--- /dev/null
+++ b/samples-lib/src/main/res/drawable/arcgis_sdk_foreground.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk.xml b/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk.xml
new file mode 100644
index 000000000..8dcfe3274
--- /dev/null
+++ b/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk_round.xml b/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk_round.xml
new file mode 100644
index 000000000..8dcfe3274
--- /dev/null
+++ b/samples-lib/src/main/res/mipmap-anydpi-v26/arcgis_sdk_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk.webp b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk.webp
new file mode 100644
index 000000000..7d9d890bc
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk.webp differ
diff --git a/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_background.webp b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_background.webp
new file mode 100644
index 000000000..2844fe0a2
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_background.webp differ
diff --git a/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_round.webp b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_round.webp
new file mode 100644
index 000000000..7d9d890bc
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-hdpi/arcgis_sdk_round.webp differ
diff --git a/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk.webp b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk.webp
new file mode 100644
index 000000000..3af13249e
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk.webp differ
diff --git a/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_background.webp b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_background.webp
new file mode 100644
index 000000000..dc0fb4029
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_background.webp differ
diff --git a/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_round.webp b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_round.webp
new file mode 100644
index 000000000..3af13249e
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-mdpi/arcgis_sdk_round.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk.webp b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk.webp
new file mode 100644
index 000000000..cc4a2b5d9
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_background.webp b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_background.webp
new file mode 100644
index 000000000..af87f8dd0
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_background.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_round.webp b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_round.webp
new file mode 100644
index 000000000..cc4a2b5d9
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xhdpi/arcgis_sdk_round.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk.webp b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk.webp
new file mode 100644
index 000000000..f0e02de89
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_background.webp b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_background.webp
new file mode 100644
index 000000000..52f290a15
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_background.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_round.webp b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_round.webp
new file mode 100644
index 000000000..f0e02de89
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxhdpi/arcgis_sdk_round.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk.webp b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk.webp
new file mode 100644
index 000000000..457e9ebb9
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_background.webp b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_background.webp
new file mode 100644
index 000000000..9e1d3b862
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_background.webp differ
diff --git a/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_round.webp b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_round.webp
new file mode 100644
index 000000000..457e9ebb9
Binary files /dev/null and b/samples-lib/src/main/res/mipmap-xxxhdpi/arcgis_sdk_round.webp differ
diff --git a/samples-lib/src/main/res/values-v35/styles.xml b/samples-lib/src/main/res/values-v35/styles.xml
new file mode 100644
index 000000000..0e8d457ea
--- /dev/null
+++ b/samples-lib/src/main/res/values-v35/styles.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/samples-lib/src/main/res/values/styles.xml b/samples-lib/src/main/res/values/styles.xml
index 0e0431d19..263e655a2 100644
--- a/samples-lib/src/main/res/values/styles.xml
+++ b/samples-lib/src/main/res/values/styles.xml
@@ -59,7 +59,7 @@
-
+
diff --git a/add-3d-tiles-layer/README.md b/samples/add-3d-tiles-layer/README.md
similarity index 100%
rename from add-3d-tiles-layer/README.md
rename to samples/add-3d-tiles-layer/README.md
diff --git a/add-3d-tiles-layer/README.metadata.json b/samples/add-3d-tiles-layer/README.metadata.json
similarity index 100%
rename from add-3d-tiles-layer/README.metadata.json
rename to samples/add-3d-tiles-layer/README.metadata.json
diff --git a/add-3d-tiles-layer/add-3d-tiles-layer.png b/samples/add-3d-tiles-layer/add-3d-tiles-layer.png
similarity index 100%
rename from add-3d-tiles-layer/add-3d-tiles-layer.png
rename to samples/add-3d-tiles-layer/add-3d-tiles-layer.png
diff --git a/samples/add-3d-tiles-layer/build.gradle.kts b/samples/add-3d-tiles-layer/build.gradle.kts
new file mode 100644
index 000000000..cfa75385d
--- /dev/null
+++ b/samples/add-3d-tiles-layer/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.add3dtileslayer"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-3d-tiles-layer/src/main/AndroidManifest.xml b/samples/add-3d-tiles-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/add-3d-tiles-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/MainActivity.kt b/samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/MainActivity.kt
new file mode 100644
index 000000000..0d4416259
--- /dev/null
+++ b/samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.add3dtileslayer
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.add3dtileslayer.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ Add3DTilesLayerApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun Add3DTilesLayerApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.add_3d_tiles_layer_app_name)
+ )
+ }
+ }
+}
diff --git a/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/components/SceneViewModel.kt b/samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/components/SceneViewModel.kt
similarity index 100%
rename from add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/components/SceneViewModel.kt
rename to samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/components/SceneViewModel.kt
diff --git a/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/screens/MainScreen.kt b/samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/screens/MainScreen.kt
similarity index 100%
rename from add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/screens/MainScreen.kt
rename to samples/add-3d-tiles-layer/src/main/java/com/esri/arcgismaps/sample/add3dtileslayer/screens/MainScreen.kt
diff --git a/samples/add-3d-tiles-layer/src/main/res/values/strings.xml b/samples/add-3d-tiles-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..4560bf5ae
--- /dev/null
+++ b/samples/add-3d-tiles-layer/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Add 3D tiles layer
+
diff --git a/add-custom-dynamic-entity-data-source/README.md b/samples/add-custom-dynamic-entity-data-source/README.md
similarity index 100%
rename from add-custom-dynamic-entity-data-source/README.md
rename to samples/add-custom-dynamic-entity-data-source/README.md
diff --git a/samples/add-custom-dynamic-entity-data-source/README.metadata.json b/samples/add-custom-dynamic-entity-data-source/README.metadata.json
new file mode 100644
index 000000000..608ce0c6c
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/README.metadata.json
@@ -0,0 +1,46 @@
+{
+ "category": "Layers",
+ "description": "Create a custom dynamic entity data source and display it using a dynamic entity layer.",
+ "formal_name": "AddCustomDynamicEntityDataSource",
+ "ignore": false,
+ "images": [
+ "add-custom-dynamic-entity-data-source.png"
+ ],
+ "keywords": [
+ "callout",
+ "data",
+ "dynamic",
+ "entity",
+ "flow",
+ "label",
+ "labeling",
+ "live",
+ "real-time",
+ "stream",
+ "track",
+ "CustomDynamicEntityDataSource.EntityFeedProvider",
+ "DynamicEntity",
+ "DynamicEntityDataSource",
+ "DynamicEntityLayer",
+ "LabelDefinition",
+ "TrackDisplayProperties"
+ ],
+ "language": "kotlin",
+ "redirect_from": "",
+ "relevant_apis": [
+ "CustomDynamicEntityDataSource.EntityFeedProvider",
+ "DynamicEntity",
+ "DynamicEntityDataSource",
+ "DynamicEntityLayer",
+ "LabelDefinition",
+ "TrackDisplayProperties"
+ ],
+ "snippets": [
+ "src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/DownloadActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/MainActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/MapViewModel.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/screens/MainScreen.kt"
+ ],
+ "title": "Add custom dynamic entity data source"
+}
diff --git a/add-custom-dynamic-entity-data-source/add-custom-dynamic-entity-data-source.png b/samples/add-custom-dynamic-entity-data-source/add-custom-dynamic-entity-data-source.png
similarity index 100%
rename from add-custom-dynamic-entity-data-source/add-custom-dynamic-entity-data-source.png
rename to samples/add-custom-dynamic-entity-data-source/add-custom-dynamic-entity-data-source.png
diff --git a/samples/add-custom-dynamic-entity-data-source/build.gradle.kts b/samples/add-custom-dynamic-entity-data-source/build.gradle.kts
new file mode 100644
index 000000000..7df90b279
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addcustomdynamicentitydatasource"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.kotlinx.serialization.json)
+}
diff --git a/samples/add-custom-dynamic-entity-data-source/src/main/AndroidManifest.xml b/samples/add-custom-dynamic-entity-data-source/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..93166ac47
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/DownloadActivity.kt b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/DownloadActivity.kt
new file mode 100644
index 000000000..b97b352c6
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/DownloadActivity.kt
@@ -0,0 +1,37 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addcustomdynamicentitydatasource
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.add_custom_dynamic_entity_data_source_app_name),
+ // ArcGIS Portal item containing the json file of observations of marine vessels in
+ // the Pacific North West
+ listOf(
+ "https://www.arcgis.com/home/item.html?id=a8a942c228af4fac96baa78ad60f511f"
+ )
+ )
+ }
+}
diff --git a/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/MainActivity.kt b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/MainActivity.kt
new file mode 100644
index 000000000..128202b7e
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addcustomdynamicentitydatasource
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.addcustomdynamicentitydatasource.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.add_custom_dynamic_entity_data_source_app_name)
+ )
+ }
+ }
+}
diff --git a/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt
similarity index 98%
rename from add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt
rename to samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt
index e06e64edc..1fa8ce331 100644
--- a/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/CustomEntityFeedProvider.kt
@@ -45,7 +45,7 @@ import java.io.IOException
import kotlin.time.Duration
/**
- * Implements the [EntityFeedProvider] interface on the [CustomDynamicEntityDataSource] to provide
+ * Implements the [CustomDynamicEntityDataSource.EntityFeedProvider] interface on the [CustomDynamicEntityDataSource] to provide
* feed events from a JSON file for the custom dynamic entity data source. Uses a buffered reader to
* process lines in the file and emit feed events for each observation. Uses the [onConnect] to
* start reading the file and [onDisconnect] to cancel the coroutine job as required.
diff --git a/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/MapViewModel.kt b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/MapViewModel.kt
new file mode 100644
index 000000000..43ac14e54
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/components/MapViewModel.kt
@@ -0,0 +1,198 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addcustomdynamicentitydatasource.components
+
+import android.app.Application
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.arcgismaps.arcgisservices.LabelingPlacement
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.GeoElement
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.labeling.LabelDefinition
+import com.arcgismaps.mapping.labeling.SimpleLabelExpression
+import com.arcgismaps.mapping.layers.DynamicEntityLayer
+import com.arcgismaps.mapping.layers.Layer
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.view.SingleTapConfirmedEvent
+import com.arcgismaps.realtime.ConnectionStatus
+import com.arcgismaps.realtime.CustomDynamicEntityDataSource
+import com.arcgismaps.realtime.DynamicEntityObservation
+import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
+import com.esri.arcgismaps.sample.addcustomdynamicentitydatasource.R
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.time.Duration.Companion.milliseconds
+
+class MapViewModel(application: Application) : AndroidViewModel(application) {
+
+ // Keep track of connected status string state.
+ var connectionStatusString by mutableStateOf("")
+ private set
+
+ // Set connection status string in he UI.
+ private fun updateConnectionStatusString(connectionStatus: String) {
+ connectionStatusString = connectionStatus
+ }
+
+ private val provisionPath: String by lazy {
+ application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
+ R.string.add_custom_dynamic_entity_data_source_app_name
+ )
+ }
+
+ // Create a new custom feed provider that processes observations from a JSON file.
+ // This takes the path to the simulation file, field name that will be used as the entity id,
+ // and the delay between each observation that is processed.
+ // In this example we are using a json file as our custom data source.
+ // This field value should be a unique identifier for each entity.
+ // Adjusting the value for the delay will change the speed at which the entities and their
+ // observations are displayed.
+ private val feedProvider = CustomEntityFeedProvider(
+ fileName = "$provisionPath/AIS_MarineCadastre_SelectedVessels_CustomDataSource.jsonl",
+ entityIdField = "MMSI",
+ delayDuration = 10.milliseconds
+ )
+
+ private val dynamicEntityDataSource = CustomDynamicEntityDataSource(feedProvider).apply {
+ // Observe the connection status of the custom data source.
+ viewModelScope.launch {
+ connectionStatus.collect { connectionStatus ->
+ updateConnectionStatusString(
+ when (connectionStatus) {
+ is ConnectionStatus.Connected -> "Connected"
+ is ConnectionStatus.Disconnected -> "Disconnected"
+ is ConnectionStatus.Connecting -> "Connecting"
+ is ConnectionStatus.Failed -> "Failed"
+ }
+ )
+ }
+ }
+ }
+
+ // Create the dynamic entity layer using the custom data source.
+ private val dynamicEntityLayer = DynamicEntityLayer(dynamicEntityDataSource).apply {
+ trackDisplayProperties.apply {
+ // Set up the track display properties, these properties will be used to configure the appearance of the track line and previous observations.
+ showPreviousObservations = true
+ showTrackLine = true
+ maximumObservations = 20
+ }
+
+ // Define the label expression to be used, in this case we will use the "VesselName" for each of the dynamic entities.
+ val simpleLabelExpression = SimpleLabelExpression("[VesselName]")
+
+ // Set the text symbol color and size for the labels.
+ val labelSymbol = TextSymbol().apply {
+ color = com.arcgismaps.Color.red
+ size = 12.0F
+ }
+
+ // Add the label definition to the dynamic entity layer and enable labels.
+ labelDefinitions.add(LabelDefinition(simpleLabelExpression, labelSymbol).apply {
+ // Set the label position.
+ placement = LabelingPlacement.PointAboveCenter
+ })
+ labelsEnabled = true
+ }
+
+ val arcGISMap = ArcGISMap(BasemapStyle.ArcGISOceans).apply {
+ initialViewpoint = Viewpoint(47.984, -123.657, 3e6)
+ // Add the dynamic entity layer to the map.
+ operationalLayers.add(dynamicEntityLayer)
+ }
+
+ // Create a mapViewProxy that will be used to identify features in the MapView.
+ // This should also be passed to the composable MapView this mapViewProxy is associated with.
+ val mapViewProxy = MapViewProxy()
+
+ // create a ViewModel to handle dialog interactions
+ val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
+
+ fun dynamicEntityDataSourceConnect() =
+ viewModelScope.launch { dynamicEntityDataSource.connect() }
+
+ fun dynamicEntityDataSourceDisconnect() =
+ viewModelScope.launch { dynamicEntityDataSource.disconnect() }
+
+ // Keep track of the currently selected GeoElement.
+ var selectedGeoElement by mutableStateOf(null)
+ private set
+
+ // Keep track of the most recent observation string.
+ var observationString by mutableStateOf("")
+ private set
+
+ // Keep track of the Coroutine Scope where observations on being collected on, so that it can
+ // be cancelled on subsequent identifies.
+ private var observationsJob: Job? = null
+
+ /**
+ * Identifies the tapped screen coordinate in the provided [singleTapConfirmedEvent]
+ */
+ fun identify(singleTapConfirmedEvent: SingleTapConfirmedEvent) {
+ viewModelScope.launch {
+ // If collecting observations on a previous identify, now cancel and stop collecting.
+ observationsJob?.cancelAndJoin()
+ // identify the cluster in the feature layer on the tapped coordinate
+ mapViewProxy.identify(
+ dynamicEntityLayer as Layer,
+ screenCoordinate = singleTapConfirmedEvent.screenCoordinate,
+ tolerance = 12.dp,
+ maximumResults = 1
+ ).onSuccess { result ->
+ (result.geoElements.firstOrNull() as? DynamicEntityObservation)?.let { observation ->
+ // Set the identified dynamic entity, used to display the callout.
+ selectedGeoElement = observation.dynamicEntity
+ // Define a new CoroutineScope to collect observation events on.
+ observationsJob = launch(Dispatchers.IO) {
+ // Collect observation events and update the observation string accordingly.
+ observation.dynamicEntity?.dynamicEntityChangedEvent?.collect { dynamicEntityChangedInfo ->
+ // Parse the observation attributes, filter out empty values, and remove
+ // starting and ending {}s.
+ observationString = dynamicEntityChangedInfo
+ .receivedObservation?.attributes?.filter {
+ it.value.toString().isNotEmpty() && !it.key.contains("globalid")
+ }.toString()
+ .replaceFirst("{", " ")
+ .removeSuffix("}")
+ .replace(",", "\n")
+ }
+ }
+ // If no observation is found, set the selectedGeoElement to null.
+ } ?: run {
+ selectedGeoElement = null
+ observationString = "Waiting for a new observation ..."
+ }
+ }.onFailure { error ->
+ messageDialogVM.showMessageDialog(
+ title = "Error identifying results: ${error.message.toString()}",
+ description = error.cause.toString()
+ )
+ }
+ }
+ }
+}
diff --git a/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/screens/MainScreen.kt b/samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/screens/MainScreen.kt
similarity index 100%
rename from add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/screens/MainScreen.kt
rename to samples/add-custom-dynamic-entity-data-source/src/main/java/com/esri/arcgismaps/sample/addcustomdynamicentitydatasource/screens/MainScreen.kt
diff --git a/samples/add-custom-dynamic-entity-data-source/src/main/res/values/strings.xml b/samples/add-custom-dynamic-entity-data-source/src/main/res/values/strings.xml
new file mode 100644
index 000000000..25bdb0b58
--- /dev/null
+++ b/samples/add-custom-dynamic-entity-data-source/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Add custom dynamic entity data source
+ Connect
+ Disconnect
+
diff --git a/add-dynamic-entity-layer/README.md b/samples/add-dynamic-entity-layer/README.md
similarity index 100%
rename from add-dynamic-entity-layer/README.md
rename to samples/add-dynamic-entity-layer/README.md
diff --git a/add-dynamic-entity-layer/README.metadata.json b/samples/add-dynamic-entity-layer/README.metadata.json
similarity index 100%
rename from add-dynamic-entity-layer/README.metadata.json
rename to samples/add-dynamic-entity-layer/README.metadata.json
diff --git a/add-dynamic-entity-layer/add-dynamic-entity-layer.png b/samples/add-dynamic-entity-layer/add-dynamic-entity-layer.png
similarity index 100%
rename from add-dynamic-entity-layer/add-dynamic-entity-layer.png
rename to samples/add-dynamic-entity-layer/add-dynamic-entity-layer.png
diff --git a/samples/add-dynamic-entity-layer/build.gradle.kts b/samples/add-dynamic-entity-layer/build.gradle.kts
new file mode 100644
index 000000000..d6fb8994d
--- /dev/null
+++ b/samples/add-dynamic-entity-layer/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.adddynamicentitylayer"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-dynamic-entity-layer/src/main/AndroidManifest.xml b/samples/add-dynamic-entity-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/add-dynamic-entity-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/MainActivity.kt b/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/MainActivity.kt
new file mode 100644
index 000000000..79791e323
--- /dev/null
+++ b/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/MainActivity.kt
@@ -0,0 +1,54 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.adddynamicentitylayer
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.adddynamicentitylayer.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ setContent {
+ SampleAppTheme {
+ AddDynamicEntityLayerApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun AddDynamicEntityLayerApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.add_dynamic_entity_layer_app_name)
+ )
+ }
+ }
+}
diff --git a/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/BottomSheetContent.kt b/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/BottomSheetContent.kt
similarity index 100%
rename from add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/BottomSheetContent.kt
rename to samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/BottomSheetContent.kt
diff --git a/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/MapViewModel.kt b/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/MapViewModel.kt
similarity index 100%
rename from add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/MapViewModel.kt
rename to samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/components/MapViewModel.kt
diff --git a/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/screens/MainScreen.kt b/samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/screens/MainScreen.kt
similarity index 100%
rename from add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/screens/MainScreen.kt
rename to samples/add-dynamic-entity-layer/src/main/java/com/esri/arcgismaps/sample/adddynamicentitylayer/screens/MainScreen.kt
diff --git a/samples/add-dynamic-entity-layer/src/main/res/values/strings.xml b/samples/add-dynamic-entity-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..a67659318
--- /dev/null
+++ b/samples/add-dynamic-entity-layer/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Add dynamic entity layer
+ https://realtimegis2016.esri.com:6443/arcgis/rest/services/SandyVehicles/StreamServer
+
diff --git a/samples/add-enc-exchange-set/README.md b/samples/add-enc-exchange-set/README.md
new file mode 100644
index 000000000..6beff1793
--- /dev/null
+++ b/samples/add-enc-exchange-set/README.md
@@ -0,0 +1,41 @@
+# Add ENC exchange set
+
+Display nautical charts per the ENC specification.
+
+![Image showing the add ENC exchange set app](add-enc-exchange-set.png)
+
+## Use case
+
+The [ENC specification](https://docs.iho.int/iho_pubs/standard/S-57Ed3.1/20ApB1.pdf) describes how hydrographic data should be displayed digitally.
+
+An ENC exchange set is a catalog of data files which can be loaded as cells. The cells contain information on how symbols should be displayed in relation to one another, so as to represent information such as depth and obstacles accurately.
+
+## How to use the sample
+
+Run the sample and view the ENC data. Pan and zoom around the map. Take note of the high level of detail in the data and the smooth rendering of the layer.
+
+## How it works
+
+1. Specify the path to a local CATALOG.031 file to create an `EncExchangeSet`.
+2. After loading the exchange set, get the `EncDataset` objects in the exchange set with `EncExchangeSet.datasets`.
+3. Create an `EncCell` for each dataset. Then create an `EncLayer` for each cell.
+4. Add the ENC layer to a map's operational layers collection to display it.
+
+## Relevant API
+
+* EncCell
+* EncDataset
+* EncExchangeSet
+* EncLayer
+
+## Offline data
+
+This sample downloads the [ENC Exchange Set without updates](https://www.arcgis.com/home/item.html?id=9d2987a825c646468b3ce7512fb76e2d) and [Hydrography dataset resources](https://www.arcgis.com/home/item.html?id=5028bf3513ff4c38b28822d010a4937c) from ArcGIS Online.
+
+## Additional information
+
+This sample uses the GeoViewCompose Toolkit module to be able to implement a Composable SceneView.
+
+## Tags
+
+data, ENC, geoviewcompose, hydrographic, layers, maritime, nautical chart
diff --git a/samples/add-enc-exchange-set/README.metadata.json b/samples/add-enc-exchange-set/README.metadata.json
new file mode 100644
index 000000000..08e3401d6
--- /dev/null
+++ b/samples/add-enc-exchange-set/README.metadata.json
@@ -0,0 +1,42 @@
+{
+ "category": "Layers",
+ "description": "Display nautical charts per the ENC specification.",
+ "formal_name": "AddEncExchangeSet",
+ "ignore": false,
+ "images": [
+ "add-enc-exchange-set.png"
+ ],
+ "keywords": [
+ "ENC",
+ "data",
+ "geoviewcompose",
+ "hydrographic",
+ "layers",
+ "maritime",
+ "nautical chart",
+ "EncCell",
+ "EncDataset",
+ "EncExchangeSet",
+ "EncLayer"
+ ],
+ "language": "kotlin",
+ "provision_from": [
+ "https://www.arcgis.com/home/item.html?id=9d2987a825c646468b3ce7512fb76e2d",
+ "https://www.arcgis.com/home/item.html?id=5028bf3513ff4c38b28822d010a4937c"
+ ],
+ "provision_to": [],
+ "redirect_from": "",
+ "relevant_apis": [
+ "EncCell",
+ "EncDataset",
+ "EncExchangeSet",
+ "EncLayer"
+ ],
+ "snippets": [
+ "src/main/java/com/esri/arcgismaps/sample/addencexchangeset/MainActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addencexchangeset/DownloadActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addencexchangeset/components/MapViewModel.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addencexchangeset/screens/MainScreen.kt"
+ ],
+ "title": "Add ENC exchange set"
+}
diff --git a/samples/add-enc-exchange-set/add-enc-exchange-set.png b/samples/add-enc-exchange-set/add-enc-exchange-set.png
new file mode 100644
index 000000000..702244391
Binary files /dev/null and b/samples/add-enc-exchange-set/add-enc-exchange-set.png differ
diff --git a/samples/add-enc-exchange-set/build.gradle.kts b/samples/add-enc-exchange-set/build.gradle.kts
new file mode 100644
index 000000000..2a3b67fb2
--- /dev/null
+++ b/samples/add-enc-exchange-set/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addencexchangeset"
+ // For view based samples
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-enc-exchange-set/src/main/AndroidManifest.xml b/samples/add-enc-exchange-set/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..c5b999c3b
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/DownloadActivity.kt b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/DownloadActivity.kt
new file mode 100644
index 000000000..15223a552
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/DownloadActivity.kt
@@ -0,0 +1,38 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addencexchangeset
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.add_enc_exchange_set_app_name),
+ listOf(
+ // ArcGIS Portal item containing ENC hydrography resources
+ "https://www.arcgis.com/home/item.html?id=5028bf3513ff4c38b28822d010a4937c",
+ // ArcGIS Portal item containing the ENC dataset
+ "https://www.arcgis.com/home/item.html?id=9d2987a825c646468b3ce7512fb76e2d"
+ )
+ )
+ }
+}
diff --git a/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/MainActivity.kt b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/MainActivity.kt
new file mode 100644
index 000000000..c364856d7
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addencexchangeset
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.addencexchangeset.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.add_enc_exchange_set_app_name)
+ )
+ }
+ }
+}
diff --git a/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/components/MapViewModel.kt b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/components/MapViewModel.kt
new file mode 100644
index 000000000..36db2f063
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/components/MapViewModel.kt
@@ -0,0 +1,118 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addencexchangeset.components
+
+import android.app.Application
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.hydrography.EncCell
+import com.arcgismaps.hydrography.EncEnvironmentSettings
+import com.arcgismaps.hydrography.EncExchangeSet
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.EncLayer
+import com.esri.arcgismaps.sample.addencexchangeset.R
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MapViewModel(application: Application) : AndroidViewModel(application) {
+ private val provisionPath: String by lazy {
+ application.getExternalFilesDir(null)?.path.toString() +
+ File.separator +
+ application.getString(R.string.add_enc_exchange_set_app_name)
+ }
+
+ // Paths to ENC data and hydrology resources
+ private val encResourcesPath = provisionPath + application.getString(R.string.enc_res_dir)
+ private val encDataPath = provisionPath + application.getString(R.string.enc_data_dir)
+
+ // Create an ENC exchange set from the local ENC data
+ private val encExchangeSet = EncExchangeSet(listOf(encDataPath))
+ private val encEnvironmentSettings: EncEnvironmentSettings = EncEnvironmentSettings
+
+ // Create an empty map, to be updated once ENC data is loaded
+ var arcGISMap by mutableStateOf(ArcGISMap())
+
+ // Create a message dialog view model for handling error messages
+ val messageDialogVM = MessageDialogViewModel()
+
+ init {
+ // Provide ENC environment with location of ENC resources and configure SENC caching location
+ encEnvironmentSettings.resourcePath = encResourcesPath
+ encEnvironmentSettings.sencDataPath = application.externalCacheDir?.path
+
+ viewModelScope.launch {
+ encExchangeSet.load().onSuccess {
+
+ // Calculate the combined extent of all datasets in the exchange set
+ val exchangeSetExtent: Envelope? = encExchangeSet.extentOrNull()
+
+ // Set the map to the oceans basemap style, and viewpoint to the exchange set extent
+ arcGISMap = ArcGISMap(BasemapStyle.ArcGISOceans).apply {
+ exchangeSetExtent?.let {
+ initialViewpoint = Viewpoint(exchangeSetExtent)
+ }
+ }
+
+ encExchangeSet.datasets.forEach { encDataset ->
+ // Create a layer for each ENC dataset and add it to the map
+ val encCell = EncCell(encDataset)
+ val encLayer = EncLayer(encCell)
+ arcGISMap.operationalLayers.add(encLayer)
+
+ encLayer.load().onFailure { err ->
+ messageDialogVM.showMessageDialog(
+ "Error loading ENC layer",
+ err.message.toString()
+ )
+ }
+ }
+ }.onFailure { err ->
+ messageDialogVM.showMessageDialog(
+ "Error loading ENC exchange set",
+ err.message.toString()
+ )
+ }
+ }
+ }
+}
+
+/**
+ * Get the combined extent of every dataset in the exchange set.
+ */
+private fun EncExchangeSet.extentOrNull(): Envelope? {
+ var extent: Envelope? = null
+
+ datasets.forEach { dataset ->
+ if (extent == null) {
+ extent = dataset.extent
+ }
+
+ if (extent != null && dataset.extent != null) {
+ // Update the combined extent of the exchange set if geometry engine returns non-null
+ extent = GeometryEngine.combineExtentsOrNull(extent!!, dataset.extent!!) ?: extent
+ }
+ }
+ return extent
+}
diff --git a/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/screens/MainScreen.kt b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/screens/MainScreen.kt
new file mode 100644
index 000000000..8792041d0
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/java/com/esri/arcgismaps/sample/addencexchangeset/screens/MainScreen.kt
@@ -0,0 +1,58 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addencexchangeset.screens
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.addencexchangeset.components.MapViewModel
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+
+/**
+ * Main screen layout for the sample app
+ */
+@Composable
+fun MainScreen(sampleName: String) {
+ // create a ViewModel to handle MapView interactions
+ val mapViewModel: MapViewModel = viewModel()
+
+ Scaffold(
+ topBar = { SampleTopAppBar(title = sampleName) },
+ content = {
+ MapView(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it),
+ arcGISMap = mapViewModel.arcGISMap,
+ )
+ mapViewModel.messageDialogVM.apply {
+ if (dialogStatus) {
+ MessageDialog(
+ title = messageTitle,
+ description = messageDescription,
+ onDismissRequest = ::dismissDialog
+ )
+ }
+ }
+ }
+ )
+}
diff --git a/samples/add-enc-exchange-set/src/main/res/values/strings.xml b/samples/add-enc-exchange-set/src/main/res/values/strings.xml
new file mode 100644
index 000000000..759dc4a67
--- /dev/null
+++ b/samples/add-enc-exchange-set/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Add ENC exchange set
+ /hydrography
+ /ExchangeSetwithoutUpdates/ENC_ROOT/CATALOG.031
+
diff --git a/add-feature-layers/README.md b/samples/add-feature-layers/README.md
similarity index 100%
rename from add-feature-layers/README.md
rename to samples/add-feature-layers/README.md
diff --git a/add-feature-layers/README.metadata.json b/samples/add-feature-layers/README.metadata.json
similarity index 100%
rename from add-feature-layers/README.metadata.json
rename to samples/add-feature-layers/README.metadata.json
diff --git a/add-feature-layers/add-feature-layers.png b/samples/add-feature-layers/add-feature-layers.png
similarity index 100%
rename from add-feature-layers/add-feature-layers.png
rename to samples/add-feature-layers/add-feature-layers.png
diff --git a/samples/add-feature-layers/build.gradle.kts b/samples/add-feature-layers/build.gradle.kts
new file mode 100644
index 000000000..54758a0fb
--- /dev/null
+++ b/samples/add-feature-layers/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addfeaturelayers"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-feature-layers/src/main/AndroidManifest.xml b/samples/add-feature-layers/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b3ff1f67f
--- /dev/null
+++ b/samples/add-feature-layers/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/DownloadActivity.kt b/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/DownloadActivity.kt
new file mode 100644
index 000000000..56ac55e6f
--- /dev/null
+++ b/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/DownloadActivity.kt
@@ -0,0 +1,24 @@
+package com.esri.arcgismaps.sample.addfeaturelayers
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.add_feature_layers_app_name),
+ listOf(
+ // ArcGIS Portal item containing the .mmpk mobile map package
+ "https://www.arcgis.com/home/item.html?id=cb1b20748a9f4d128dad8a87244e3e37",
+ // ArcGIS Portal item containing shapefiles of Scottish Wildlife Trust reserve boundaries
+ "https://www.arcgis.com/home/item.html?id=15a7cbd3af1e47cfa5d2c6b93dc44fc2",
+ // GeoPackage AuroraCO.gpkg with datasets that cover Aurora Colorado
+ "https://www.arcgis.com/home/item.html?id=68ec42517cdd439e81b036210483e8e7"
+ )
+ )
+ }
+}
diff --git a/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/MainActivity.kt b/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/MainActivity.kt
new file mode 100644
index 000000000..87fef567b
--- /dev/null
+++ b/samples/add-feature-layers/src/main/java/com/esri/arcgismaps/sample/addfeaturelayers/MainActivity.kt
@@ -0,0 +1,248 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addfeaturelayers
+
+import android.os.Bundle
+import android.util.Log
+import android.widget.ArrayAdapter
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.data.GeoPackage
+import com.arcgismaps.data.Geodatabase
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.data.ShapefileFeatureTable
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.mapping.PortalItem
+import com.esri.arcgismaps.sample.addfeaturelayers.databinding.AddFeatureLayersActivityMainBinding
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: AddFeatureLayersActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.add_feature_layers_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.add_feature_layers_app_name)
+ }
+
+ // enum to keep track of the selected source to display the feature layer
+ enum class FeatureLayerSource(val menuPosition: Int) {
+ SERVICE_FEATURE_TABLE(0),
+ PORTAL_ITEM(1),
+ GEODATABASE(2),
+ GEOPACKAGE(3),
+ SHAPEFILE(4)
+ }
+
+ // keeps track of the previously selected feature layer source
+ private var previousSource: FeatureLayerSource? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+ // set the map to be displayed in as the BasemapStyle topographic
+ activityMainBinding.mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ setUpBottomUI()
+ }
+
+ /**
+ * Sets the map using the [layer] at the given [viewpoint]
+ */
+ private fun setFeatureLayer(layer: FeatureLayer, viewpoint: Viewpoint) {
+ activityMainBinding.mapView.apply {
+ // clears the existing layer on the map
+ map?.operationalLayers?.clear()
+ // adds the new layer to the map
+ map?.operationalLayers?.add(layer)
+ // updates the viewpoint to the given viewpoint
+ setViewpoint(viewpoint)
+ }
+ }
+
+ /**
+ * Load a feature layer with a URL
+ */
+ private fun loadFeatureServiceURL() {
+ // initialize the service feature table using a URL
+ val serviceFeatureTable =
+ ServiceFeatureTable(resources.getString(R.string.add_feature_layers_sample_service_url))
+ // create a feature layer with the feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
+ val viewpoint = Viewpoint(41.70, -88.20, 120000.0)
+ // set the feature layer on the map
+ setFeatureLayer(featureLayer, viewpoint)
+ }
+
+
+ /**
+ * Load a feature layer with a portal item
+ */
+ private suspend fun loadPortalItem() {
+ // set the portal
+ val portal = Portal("https://www.arcgis.com")
+ // create the portal item with the item ID for the Portland tree service data
+ val portalItem = PortalItem(portal, "1759fd3e8a324358a0c58d9a687a8578")
+ portalItem.load().onSuccess {
+ // create the feature layer with the item
+ val featureLayer = FeatureLayer.createWithItem(portalItem)
+ // set the viewpoint to Portland, Oregon
+ val viewpoint = Viewpoint(45.5266, -122.6219, 2500.0)
+ // set the feature layer on the map
+ setFeatureLayer(featureLayer, viewpoint)
+ }.onFailure {
+ showError("Error loading portal item: ${it.message}")
+ }
+ }
+
+ /**
+ * Load a feature layer with a local geodatabase file
+ */
+ private suspend fun loadGeodatabase() {
+ // locate the .geodatabase file in the device
+ val geodatabaseFile = File(provisionPath, getString(R.string.geodatabase_la_trails))
+ // instantiate the geodatabase with the file path
+ val geodatabase = Geodatabase(geodatabaseFile.path)
+ // load the geodatabase
+ geodatabase.load().onSuccess {
+ // get the feature table with the name
+ val geodatabaseFeatureTable =
+ geodatabase.getFeatureTable("Trailheads")
+ if (geodatabaseFeatureTable == null) {
+ showError("Feature table name not found in geodatabase")
+ return
+ }
+ // create a feature layer with the feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(geodatabaseFeatureTable)
+ // set the viewpoint to Malibu, California
+ val viewpoint = Viewpoint(34.0772, -118.7989, 600000.0)
+ // set the feature layer on the map
+ setFeatureLayer(featureLayer, viewpoint)
+ }.onFailure {
+ showError("Error loading geodatabase: ${it.message}")
+ }
+ }
+
+ /**
+ * Load a feature layer with a local geopackage file
+ */
+ private suspend fun loadGeopackage() {
+ // locate the .gpkg file in the device
+ val geopackageFile = File(provisionPath, "/AuroraCO.gpkg")
+ // instantiate the geopackage with the file path
+ val geoPackage = GeoPackage(geopackageFile.path)
+ // load the geopackage
+ geoPackage.load().onSuccess {
+ // get the first feature table in the geopackage
+ val geoPackageFeatureTable = geoPackage.geoPackageFeatureTables.first()
+ // create a feature layer with the feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(geoPackageFeatureTable)
+ // set the viewpoint to Denver, CO
+ val viewpoint = Viewpoint(39.7294, -104.8319, 500000.0)
+ // set the feature layer on the map
+ setFeatureLayer(featureLayer, viewpoint)
+ }.onFailure {
+ showError("Error loading geopackage: ${it.message}")
+ }
+ }
+
+ /**
+ * Load a feature layer with a local shapefile file
+ */
+ private suspend fun loadShapefile() {
+ // locate the shape file in device
+ val file = File(
+ provisionPath,
+ "/ScottishWildlifeTrust_ReserveBoundaries_20201102.shp"
+ )
+ // create a shapefile feature table from a named bundle resource
+ val shapeFileTable = ShapefileFeatureTable(file.path)
+ shapeFileTable.load().onSuccess {
+ // create a feature layer for the shapefile feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(shapeFileTable)
+ // set the viewpoint to Scotland
+ val viewpoint = Viewpoint(56.641344, -3.889066, 6000000.0)
+ // set the feature layer on the map
+ setFeatureLayer(featureLayer, viewpoint)
+ }.onFailure {
+ showError("Error loading shapefile: ${it.message}")
+ }
+ }
+
+ private fun showError(message: String) {
+ Toast.makeText(this@MainActivity, message, Toast.LENGTH_SHORT).show()
+ Log.e(localClassName, message)
+ }
+
+ /**
+ * Sets up the bottom UI selector to switch between
+ * different ways to load a feature layers
+ */
+ private fun setUpBottomUI() {
+ // create an adapter with the types of feature layer
+ // sources to be displayed in menu
+ val adapter = ArrayAdapter(
+ this,
+ android.R.layout.simple_list_item_1,
+ resources.getStringArray(R.array.feature_layer_sources)
+ )
+ activityMainBinding.bottomListItems.apply {
+ // populate the bottom list with the feature layer sources
+ setAdapter(adapter)
+ // click listener when feature layer source is selected
+ setOnItemClickListener { _, _, i, _ ->
+ // get the selected feature layer source
+ val selectedSource = FeatureLayerSource.entries.find { it.menuPosition == i }
+ // check if the same feature is selected
+ if (previousSource != null && (previousSource == selectedSource)) {
+ // same feature layer selected, return
+ return@setOnItemClickListener
+ }
+ lifecycleScope.launch {
+ // set the feature layer source using the selected source
+ when (selectedSource) {
+ FeatureLayerSource.SERVICE_FEATURE_TABLE -> loadFeatureServiceURL()
+ FeatureLayerSource.PORTAL_ITEM -> loadPortalItem()
+ FeatureLayerSource.GEODATABASE -> loadGeodatabase()
+ FeatureLayerSource.GEOPACKAGE -> loadGeopackage()
+ FeatureLayerSource.SHAPEFILE -> loadShapefile()
+ else -> {}
+ }
+ }
+ // update the previous feature layer source
+ previousSource = selectedSource
+ }
+ }
+ }
+}
diff --git a/add-feature-layers/src/main/res/layout/activity_main.xml b/samples/add-feature-layers/src/main/res/layout/add_feature_layers_activity_main.xml
similarity index 100%
rename from add-feature-layers/src/main/res/layout/activity_main.xml
rename to samples/add-feature-layers/src/main/res/layout/add_feature_layers_activity_main.xml
diff --git a/samples/add-feature-layers/src/main/res/values/strings.xml b/samples/add-feature-layers/src/main/res/values/strings.xml
new file mode 100644
index 000000000..73362a8c2
--- /dev/null
+++ b/samples/add-feature-layers/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+ Add feature layers
+ Select a feature layer source
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/NapervilleShelters/FeatureServer/0
+ /LA_Trails.geodatabase
+
+ - Service Feature Table
+ - Portal Item
+ - Geodatabase
+ - Geopackage
+ - Shapefile
+
+
diff --git a/add-features-with-contingent-values/README.md b/samples/add-features-with-contingent-values/README.md
similarity index 100%
rename from add-features-with-contingent-values/README.md
rename to samples/add-features-with-contingent-values/README.md
diff --git a/add-features-with-contingent-values/README.metadata.json b/samples/add-features-with-contingent-values/README.metadata.json
similarity index 100%
rename from add-features-with-contingent-values/README.metadata.json
rename to samples/add-features-with-contingent-values/README.metadata.json
diff --git a/add-features-with-contingent-values/add-features-with-contingent-values.png b/samples/add-features-with-contingent-values/add-features-with-contingent-values.png
similarity index 100%
rename from add-features-with-contingent-values/add-features-with-contingent-values.png
rename to samples/add-features-with-contingent-values/add-features-with-contingent-values.png
diff --git a/samples/add-features-with-contingent-values/build.gradle.kts b/samples/add-features-with-contingent-values/build.gradle.kts
new file mode 100644
index 000000000..f0063b919
--- /dev/null
+++ b/samples/add-features-with-contingent-values/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addfeatureswithcontingentvalues"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-features-with-contingent-values/src/main/AndroidManifest.xml b/samples/add-features-with-contingent-values/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d06dffa1e
--- /dev/null
+++ b/samples/add-features-with-contingent-values/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/DownloadActivity.kt b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/DownloadActivity.kt
new file mode 100644
index 000000000..3a3137a33
--- /dev/null
+++ b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/DownloadActivity.kt
@@ -0,0 +1,22 @@
+package com.esri.arcgismaps.sample.addfeatureswithcontingentvalues
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.add_features_with_contingent_values_app_name),
+ listOf(
+ // Geodatabase containing bird nests defined with contingent values
+ "https://www.arcgis.com/home/item.html?id=e12b54ea799f4606a2712157cf9f6e41",
+ // Vector tile package of the Fillmore area
+ "https://www.arcgis.com/home/item.html?id=b5106355f1634b8996e634c04b6a930a"
+ )
+ )
+ }
+}
diff --git a/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/MainActivity.kt b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/MainActivity.kt
new file mode 100644
index 000000000..e8829ada4
--- /dev/null
+++ b/samples/add-features-with-contingent-values/src/main/java/com/esri/arcgismaps/sample/addfeatureswithcontingentvalues/MainActivity.kt
@@ -0,0 +1,465 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addfeatureswithcontingentvalues
+
+import android.os.Bundle
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.AutoCompleteTextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.data.ArcGISFeature
+import com.arcgismaps.data.ArcGISFeatureTable
+import com.arcgismaps.data.CodedValue
+import com.arcgismaps.data.CodedValueDomain
+import com.arcgismaps.data.ContingentCodedValue
+import com.arcgismaps.data.ContingentRangeValue
+import com.arcgismaps.data.Feature
+import com.arcgismaps.data.Geodatabase
+import com.arcgismaps.data.QueryParameters
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.ArcGISVectorTiledLayer
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.addfeatureswithcontingentvalues.databinding.AddFeaturesWithContingentValuesActivityMainBinding
+import com.esri.arcgismaps.sample.addfeatureswithcontingentvalues.databinding.AddFeatureLayoutBinding
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: AddFeaturesWithContingentValuesActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.add_features_with_contingent_values_activity_main)
+ }
+
+ private val bottomSheetBinding by lazy {
+ AddFeatureLayoutBinding.inflate(layoutInflater)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.add_features_with_contingent_values_app_name)
+ }
+
+ // mobile database containing offline feature data. geodatabase is closed on app exit
+ private val geodatabase: Geodatabase by lazy {
+ Geodatabase("${cacheDir.path}/ContingentValuesBirdNests.geodatabase")
+ }
+
+ // graphic overlay instance to add the feature graphic to the map
+ private val graphicsOverlay = GraphicsOverlay()
+
+ // instance of the contingent feature to be added to the map
+ private var feature: ArcGISFeature? = null
+
+ // instance of the feature table retrieved from the geodatabase, updates when new feature is added
+ private var featureTable: ArcGISFeatureTable? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // use the offline vector tiled layer as a basemap
+ val fillmoreVectorTiledLayer = ArcGISVectorTiledLayer(
+ "$provisionPath/FillmoreTopographicMap.vtpk"
+ )
+
+ mapView.apply {
+ // set the basemap layer and the graphic overlay to the MapView
+ map = ArcGISMap(Basemap(fillmoreVectorTiledLayer))
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // add a listener to the MapView to detect when
+ // a user has performed a single tap to add a new feature
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect {
+ // open a bottom sheet view to add the feature
+ it.mapPoint?.let { mapPoint -> createBottomSheet(mapPoint) }
+ }
+ }
+
+ // create a temporary directory to use the geodatabase file
+ createGeodatabaseCacheDirectory()
+
+ lifecycleScope.launch {
+ // retrieve and load the offline mobile geodatabase file from the cache directory
+ geodatabase.load().getOrElse {
+ showError("Error loading GeoDatabase: ${it.message}")
+ }
+
+ // get the first geodatabase feature table
+ val featureTable = geodatabase.featureTables.firstOrNull()
+ ?: return@launch showError("No feature table found in geodatabase")
+ // load the geodatabase feature table
+ featureTable.load().getOrElse {
+ return@launch showError(it.message.toString())
+ }
+
+ // create and load the feature layer from the feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(featureTable)
+ // add the feature layer to the map
+ mapView.map?.operationalLayers?.add(featureLayer)
+
+ // set the map's viewpoint to the feature layer's full extent
+ val extent = featureLayer.fullExtent
+ ?: return@launch showError("Error retrieving extent of the feature layer")
+ mapView.setViewpoint(Viewpoint(extent))
+
+ // keep the instance of the featureTable
+ this@MainActivity.featureTable = featureTable
+
+ // add buffer graphics for the feature layer
+ queryFeatures()
+ }
+ }
+
+ /**
+ * Geodatabase creates and uses various temporary files while processing a database,
+ * which will need to be cleared before looking up the [geodatabase] again.
+ * A copy of the original geodatabase file is created in the cache folder.
+ */
+ private fun createGeodatabaseCacheDirectory() {
+ // clear cache directory
+ File(cacheDir.path).deleteRecursively()
+ // copy over the original Geodatabase file to be used in the temp cache directory
+ File("$provisionPath/ContingentValuesBirdNests.geodatabase").copyTo(
+ File("${cacheDir.path}/ContingentValuesBirdNests.geodatabase")
+ )
+ }
+
+ /**
+ * Create buffer graphics for the features and adds the graphics to
+ * the [graphicsOverlay]
+ */
+ private suspend fun queryFeatures() {
+ // clear the existing graphics
+ graphicsOverlay.graphics.clear()
+
+ // create buffer graphics for the features
+ val queryParameters = QueryParameters().apply {
+ // set the where clause to filter for buffer sizes greater than 0
+ whereClause = "BufferSize > 0"
+ }
+
+ // query the features using the queryParameters on the featureTable
+ val featureQueryResult = featureTable?.queryFeatures(queryParameters)?.getOrThrow()
+ // call get on the future to get the result
+ val featureResultList = featureQueryResult?.toList()
+
+ if (!featureResultList.isNullOrEmpty()) {
+ // create list of graphics for each query result
+ val graphics = featureResultList.map { createGraphic(it) }
+ // add the graphics to the graphics overlay
+ graphicsOverlay.graphics.addAll(graphics)
+ } else {
+ showError("No features found with BufferSize > 0")
+ }
+ }
+
+ /**
+ * Create a graphic for the given [feature] and returns a Graphic with the features attributes
+ */
+ private fun createGraphic(feature: Feature): Graphic {
+ // get the feature's buffer size
+ val bufferSize = feature.attributes["BufferSize"] as Int
+ // get a polygon using the feature's buffer size and geometry
+ val polygon = feature.geometry?.let { GeometryEngine.bufferOrNull(it, bufferSize.toDouble()) }
+ // create the outline for the buffers
+ val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 2f)
+ // create the buffer symbol
+ val bufferSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.ForwardDiagonal, Color.red, lineSymbol
+ )
+ // create an graphic using the geometry and fill symbol
+ return Graphic(polygon, bufferSymbol)
+ }
+
+ /**
+ * Creates a BottomSheetDialog view to handle contingent value interaction.
+ * Once the contingent values have been set and the apply button is clicked,
+ * the function will call validateContingency() to add the feature at the [mapPoint].
+ */
+ private fun createBottomSheet(mapPoint: Point) {
+ // creates a new BottomSheetDialog
+ val bottomSheet = BottomSheetDialog(this).apply {
+ behavior.state = BottomSheetBehavior.STATE_EXPANDED
+ }
+
+ // set up the first content value attribute
+ setUpStatusAttributes()
+
+ // clear and set bottom sheet content view to layout,
+ // to be able to set the content view on each bottom sheet draw
+ if (bottomSheetBinding.root.parent != null) {
+ (bottomSheetBinding.root.parent as ViewGroup).removeAllViews()
+ }
+
+ // reset feature to null since this is a new feature
+ feature = null
+
+ bottomSheetBinding.apply {
+
+ // reset bottom sheet values, this is needed to showcase contingent values behavior
+ statusInputLayout.editText?.setText("")
+ protectionInputLayout.editText?.setText("")
+ selectedBuffer.text = ""
+ protectionInputLayout.isEnabled = false
+ bufferSeekBar.isEnabled = false
+ bufferSeekBar.value = bufferSeekBar.valueFrom
+
+ // set apply button to validate and apply contingency feature on map
+ applyTv.setOnClickListener {
+ // check if the contingent features set is valid and set it to the map if valid
+ validateContingency(mapPoint)
+ bottomSheet.dismiss()
+ }
+
+ // dismiss on cancel clicked
+ cancelTv.setOnClickListener { bottomSheet.dismiss() }
+ }
+
+ // set the content view to the root of the binding layout
+ bottomSheet.setContentView(bottomSheetBinding.root)
+ // display the bottom sheet view
+ bottomSheet.show()
+ }
+
+ /**
+ * Retrieve the status fields, add the fields to a ContingentValueDomain, and set the values to the spinner
+ * When status attribute selected, createFeature() is called.
+ */
+ private fun setUpStatusAttributes() {
+ // get the first field by name
+ val statusField = featureTable?.fields?.find { field -> field.name == "Status" }
+ // get the field's domains as coded value domain
+ val codedValueDomain = statusField?.domain as CodedValueDomain
+ // get the coded value domain's coded values
+ val statusCodedValues = codedValueDomain.codedValues
+ // get the selected index if applicable
+ val statusNames = statusCodedValues.map { it.name }
+ // get the items to be added to the spinner adapter
+ val adapter = ArrayAdapter(bottomSheetBinding.root.context, R.layout.list_item, statusNames)
+ (bottomSheetBinding.statusInputLayout.editText as AutoCompleteTextView).apply {
+ setAdapter(adapter)
+ setOnItemClickListener { _, _, position, _ ->
+ // get the CodedValue of the item selected, and create a feature needed for feature attributes
+ createFeature(statusCodedValues[position])
+ }
+ }
+ }
+
+ /**
+ * Set up the [feature] using the status attribute's coded value
+ * by loading the [featureTable]'s Contingent Value Definition.
+ * This function calls setUpProtectionAttributes() once the [feature] has been set
+ */
+ private fun createFeature(codedValue: CodedValue) {
+ // get the contingent values definition from the feature table
+ val contingentValueDefinition = featureTable?.contingentValuesDefinition
+ if (contingentValueDefinition != null) {
+ lifecycleScope.launch {
+ // load the contingent values definition
+ contingentValueDefinition.load().getOrElse {
+ showError("Error loading the ContingentValuesDefinition")
+ }
+ // create a feature from the feature table and set the initial attribute
+ feature = featureTable?.createFeature() as ArcGISFeature
+ feature?.attributes?.set("Status", codedValue.code)
+ setUpProtectionAttributes()
+ }
+ } else {
+ showError("Error retrieving ContingentValuesDefinition from the FeatureTable")
+ }
+ }
+
+ /**
+ * Retrieve the protection attribute fields, add the fields to a ContingentCodedValue, and set the values to the spinner
+ * When status attribute selected, showBufferSeekbar() is called.
+ */
+ private fun setUpProtectionAttributes() {
+ // set the bottom sheet view to enable the Protection attribute, and disable input elsewhere
+ bottomSheetBinding.apply {
+ protectionInputLayout.isEnabled = true
+ bufferSeekBar.isEnabled = false
+ bufferSeekBar.value = bufferSeekBar.valueFrom
+ protectionInputLayout.editText?.setText("")
+ selectedBuffer.text = ""
+ }
+
+ // get the contingent value results with the feature for the protection field
+ val contingentValuesResult = feature?.let {
+ featureTable?.getContingentValuesOrNull(it, "Protection")
+ }
+
+ // get the list of contingent values by field group
+ val contingentValues = contingentValuesResult?.byFieldGroup?.get("ProtectionFieldGroup")
+
+ // convert the list of ContingentValues to a list of CodedValue
+ val protectionCodedValues =
+ contingentValues?.map { (it as ContingentCodedValue).codedValue }
+ ?: return showError("Error getting coded values by field group")
+
+ // get the attribute names for each coded value
+ val protectionNames = protectionCodedValues.map { it.name }
+
+ // set the items to be added to the spinner adapter
+ val adapter = ArrayAdapter(
+ bottomSheetBinding.root.context, R.layout.list_item, protectionNames
+ )
+
+ // set the choices of protection attribute values
+ (bottomSheetBinding.protectionInputLayout.editText as AutoCompleteTextView).apply {
+ setAdapter(adapter)
+ setOnItemClickListener { _, _, position, _ ->
+ // set the protection CodedValue of the item selected
+ feature?.attributes?.set("Protection", protectionCodedValues[position].code)
+ // enable buffer seekbar
+ showBufferSeekbar()
+ }
+ }
+ }
+
+ /**
+ * Retrieve the buffer attribute fields, add the fields
+ * to a ContingentRangeValue, and set the values to a SeekBar
+ */
+ private fun showBufferSeekbar() {
+ // set the bottom sheet view to enable the buffer attribute
+ bottomSheetBinding.apply {
+ bufferSeekBar.isEnabled = true
+ selectedBuffer.text = ""
+ }
+
+ // get the contingent value results using the feature and field
+ val contingentValueResult = feature?.let {
+ featureTable?.getContingentValuesOrNull(it, "BufferSize")
+ }
+
+ // get the contingent rang value of the buffer size field group
+ val bufferSizeRangeValue = contingentValueResult?.byFieldGroup?.get("BufferSizeFieldGroup")
+ ?.get(0) as ContingentRangeValue
+
+ // set the minimum and maximum possible buffer sizes
+ val minValue = bufferSizeRangeValue.minValue as Int
+ val maxValue = bufferSizeRangeValue.maxValue as Int
+
+ // check if there can be a max value, if not disable SeekBar
+ // & set value to attribute size to 0
+ if (maxValue > 0) {
+ // get SeekBar instance from the binding layout
+ bottomSheetBinding.bufferSeekBar.apply {
+ // set the min, max and current value of the SeekBar
+ valueFrom = minValue.toFloat()
+ valueTo = maxValue.toFloat()
+ value = valueFrom
+ // set the initial attribute and the text to the min of the ContingentRangeValue
+ feature?.attributes?.set("BufferSize", value.toInt())
+ bottomSheetBinding.selectedBuffer.text = value.toInt().toString()
+ // set the change listener to update the attribute value and the displayed value to the SeekBar position
+ addOnChangeListener { _, value, _ ->
+ feature?.attributes?.set("BufferSize", value.toInt())
+ bottomSheetBinding.selectedBuffer.text = value.toInt().toString()
+ }
+ }
+ } else {
+ // max value is 0, so disable seekbar and update the attribute value accordingly
+ bottomSheetBinding.apply {
+ bufferSeekBar.isEnabled = false
+ selectedBuffer.text = "0"
+ }
+ feature?.attributes?.set("BufferSize", 0)
+ }
+ }
+
+ /**
+ * Ensure that the selected values are a valid combination.
+ * If contingencies are valid, then display [feature] on the [mapPoint]
+ */
+ private fun validateContingency(mapPoint: Point) {
+ // check if all the features have been set
+ if (featureTable == null) {
+ showError("Input all values to add a feature to the map")
+ return
+ }
+
+ // validate the feature's contingencies
+ val contingencyViolations = feature?.let {
+ featureTable?.validateContingencyConstraints(it)
+ } ?: return showError("No feature attribute was selected")
+
+ // if there are no contingency violations
+ if (contingencyViolations.isEmpty()) {
+ // the feature is valid and ready to add to the feature table
+ // create a symbol to represent a bird's nest
+ val symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.black, 11F)
+ // add the graphic to the graphics overlay
+ graphicsOverlay.graphics.add(Graphic(mapPoint, symbol))
+
+ // set the geometry of the feature to the map point
+ feature?.geometry = mapPoint
+
+ // create the graphic of the feature
+ val graphic = feature?.let { createGraphic(it) }
+ // add the graphic to the graphics overlay
+ graphic?.let { graphicsOverlay.graphics.add(it) }
+
+ // add the feature to the feature table
+ lifecycleScope.launch {
+ feature?.let { featureTable?.addFeature(it) }
+ feature?.load()?.getOrElse {
+ return@launch showError(it.message.toString())
+ }
+ }
+ } else {
+ showError("Invalid contingent values: " + (contingencyViolations.size) + " violations found.")
+ }
+
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/add-features-with-contingent-values/src/main/res/layout/add_feature_layout.xml b/samples/add-features-with-contingent-values/src/main/res/layout/add_feature_layout.xml
similarity index 100%
rename from add-features-with-contingent-values/src/main/res/layout/add_feature_layout.xml
rename to samples/add-features-with-contingent-values/src/main/res/layout/add_feature_layout.xml
diff --git a/add-features-with-contingent-values/src/main/res/layout/activity_main.xml b/samples/add-features-with-contingent-values/src/main/res/layout/add_features_with_contingent_values_activity_main.xml
similarity index 100%
rename from add-features-with-contingent-values/src/main/res/layout/activity_main.xml
rename to samples/add-features-with-contingent-values/src/main/res/layout/add_features_with_contingent_values_activity_main.xml
diff --git a/add-features-with-contingent-values/src/main/res/layout/list_item.xml b/samples/add-features-with-contingent-values/src/main/res/layout/list_item.xml
similarity index 100%
rename from add-features-with-contingent-values/src/main/res/layout/list_item.xml
rename to samples/add-features-with-contingent-values/src/main/res/layout/list_item.xml
diff --git a/samples/add-features-with-contingent-values/src/main/res/values/strings.xml b/samples/add-features-with-contingent-values/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f897f0aa1
--- /dev/null
+++ b/samples/add-features-with-contingent-values/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+ Add features with contingent values
+ The options will vary depending on which values are selected
+ Add Feature
+ Apply
+ Cancel
+ Set the attributes:
+ Select status attribute
+ Select protection attribute
+ Exclusion area buffer size
+
diff --git a/samples/add-kml-layer-with-network-links/README.md b/samples/add-kml-layer-with-network-links/README.md
new file mode 100644
index 000000000..f38c54d8f
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/README.md
@@ -0,0 +1,32 @@
+# Add KML layer with network links
+
+Display a file with a KML network link, including displaying any network link control messages at launch.
+
+![Image of display KML network links](add-kml-layer-with-network-links.png)
+
+## Use case
+
+KML files can reference other KML files on the network and support automatically refreshing content. For example, survey workers will benefit from KML data shown on their devices automatically refreshing to show the most up-to-date state. Additionally, discovering KML files linked to the data they are currently viewing provides additional information to make better decisions in the field.
+
+## How to use the sample
+
+The sample will load the KML file automatically. The data shown should refresh automatically every few seconds. Pan and zoom to explore the map.
+
+## How it works
+
+1. Create a `KmlDataset` from a KML source which has network links.
+2. Construct a `KmlLayer` with the dataset and add the layer as an operational layer with `Scene.operationalLayers.add(kmlLayer)`.
+3. To listen for network messages, collect them from `KmlDataset.kmlNetworkLinkMessageReceived`.
+
+## Relevant API
+
+* KmlDataset
+* KmlLayer
+
+## About the data
+
+This map shows the current air traffic in parts of Europe with heading, altitude, and ground speed. Additionally, noise levels from ground monitoring stations are shown.
+
+## Tags
+
+keyhole, KML, KMZ, network link, network link control, OGC
diff --git a/samples/add-kml-layer-with-network-links/README.metadata.json b/samples/add-kml-layer-with-network-links/README.metadata.json
new file mode 100644
index 000000000..1f11c60c9
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/README.metadata.json
@@ -0,0 +1,31 @@
+{
+ "category": "Layers",
+ "description": "Display a file with a KML network link, including displaying any network link control messages at launch.",
+ "formal_name": "AddKmlLayerWithNetworkLinks",
+ "ignore": false,
+ "images": [
+ "add-kml-layer-with-network-links.png"
+ ],
+ "keywords": [
+ "KML",
+ "KMZ",
+ "OGC",
+ "keyhole",
+ "network link",
+ "network link control",
+ "KmlDataset",
+ "KmlLayer"
+ ],
+ "language": "kotlin",
+ "redirect_from": "",
+ "relevant_apis": [
+ "KmlDataset",
+ "KmlLayer"
+ ],
+ "snippets": [
+ "src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/MainActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/components/SceneViewModel.kt",
+ "src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/screens/MainScreen.kt"
+ ],
+ "title": "Add KML layer with network links"
+}
diff --git a/samples/add-kml-layer-with-network-links/add-kml-layer-with-network-links.png b/samples/add-kml-layer-with-network-links/add-kml-layer-with-network-links.png
new file mode 100644
index 000000000..69fd38ba7
Binary files /dev/null and b/samples/add-kml-layer-with-network-links/add-kml-layer-with-network-links.png differ
diff --git a/samples/add-kml-layer-with-network-links/build.gradle.kts b/samples/add-kml-layer-with-network-links/build.gradle.kts
new file mode 100644
index 000000000..dda00f1d0
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addkmllayerwithnetworklinks"
+ // For view based samples
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-kml-layer-with-network-links/src/main/AndroidManifest.xml b/samples/add-kml-layer-with-network-links/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ef0c89fdc
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/MainActivity.kt b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/MainActivity.kt
new file mode 100644
index 000000000..8c3fb0d11
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addkmllayerwithnetworklinks
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.addkmllayerwithnetworklinks.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.add_kml_layer_with_network_links_app_name)
+ )
+ }
+ }
+}
diff --git a/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/components/SceneViewModel.kt b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/components/SceneViewModel.kt
new file mode 100644
index 000000000..cdffee3db
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/components/SceneViewModel.kt
@@ -0,0 +1,74 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addkmllayerwithnetworklinks.components
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.kml.KmlDataset
+import com.arcgismaps.mapping.layers.KmlLayer
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+
+class SceneViewModel(application: Application) : AndroidViewModel(application) {
+
+ // create a KML dataset from a URL, then use it to create a KML layer
+ private val kmlDataset = KmlDataset("https://www.arcgis.com/sharing/rest/content/items/600748d4464442288f6db8a4ba27dc95/data")
+ private val kmlLayer = KmlLayer(kmlDataset)
+
+
+ // create a scene with the imagery basemap, centred over Germany and the Netherlands
+ val scene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
+ initialViewpoint = Viewpoint(52.0, 7.0, 8_000_000.0)
+ }
+
+ // create a ViewModel to handle dialog interactions
+ val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
+
+ init {
+ viewModelScope.launch(Dispatchers.Main) {
+
+ // show a popup when any network link messages are received
+ kmlDataset.kmlNetworkLinkMessageReceived.collect {
+ messageDialogVM.showMessageDialog(
+ title = "KML Network Link Message",
+ description = it.message
+ )
+ }
+ }
+
+ viewModelScope.launch(Dispatchers.IO) {
+
+ // wait for the KML layer to load, then add it to the scene view
+ kmlLayer.load().onSuccess {
+ scene.operationalLayers.add(kmlLayer)
+ }.onFailure { error ->
+ // report errors if the KML layer failed to load
+ messageDialogVM.showMessageDialog(
+ title = "Error",
+ description = error.message.toString()
+ )
+ }
+ }
+ }
+}
diff --git a/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/screens/MainScreen.kt b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/screens/MainScreen.kt
new file mode 100644
index 000000000..2d3397598
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/src/main/java/com/esri/arcgismaps/sample/addkmllayerwithnetworklinks/screens/MainScreen.kt
@@ -0,0 +1,60 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addkmllayerwithnetworklinks.screens
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+
+import com.arcgismaps.toolkit.geoviewcompose.SceneView
+import com.esri.arcgismaps.sample.addkmllayerwithnetworklinks.components.SceneViewModel
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+
+/**
+ * Main screen layout for the sample app
+ */
+@Composable
+fun MainScreen(sampleName: String) {
+
+ // create a ViewModel to handle SceneView interactions
+ val sceneViewModel: SceneViewModel = viewModel()
+
+ Scaffold(
+ topBar = { SampleTopAppBar(title = sampleName) },
+ content = {
+ SceneView(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it),
+ arcGISScene = sceneViewModel.scene,
+ )
+ sceneViewModel.messageDialogVM.apply {
+ if (dialogStatus) {
+ MessageDialog(
+ title = messageTitle,
+ description = messageDescription,
+ onDismissRequest = ::dismissDialog
+ )
+ }
+ }
+ }
+ )
+}
diff --git a/samples/add-kml-layer-with-network-links/src/main/res/values/strings.xml b/samples/add-kml-layer-with-network-links/src/main/res/values/strings.xml
new file mode 100644
index 000000000..29e92107c
--- /dev/null
+++ b/samples/add-kml-layer-with-network-links/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Add KML layer with network links
+
diff --git a/add-scene-layer-with-elevation/README.md b/samples/add-scene-layer-with-elevation/README.md
similarity index 100%
rename from add-scene-layer-with-elevation/README.md
rename to samples/add-scene-layer-with-elevation/README.md
diff --git a/add-scene-layer-with-elevation/README.metadata.json b/samples/add-scene-layer-with-elevation/README.metadata.json
similarity index 100%
rename from add-scene-layer-with-elevation/README.metadata.json
rename to samples/add-scene-layer-with-elevation/README.metadata.json
diff --git a/add-scene-layer-with-elevation/add-scene-layer-with-elevation.png b/samples/add-scene-layer-with-elevation/add-scene-layer-with-elevation.png
similarity index 100%
rename from add-scene-layer-with-elevation/add-scene-layer-with-elevation.png
rename to samples/add-scene-layer-with-elevation/add-scene-layer-with-elevation.png
diff --git a/samples/add-scene-layer-with-elevation/build.gradle.kts b/samples/add-scene-layer-with-elevation/build.gradle.kts
new file mode 100644
index 000000000..f9a26de4c
--- /dev/null
+++ b/samples/add-scene-layer-with-elevation/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addscenelayerwithelevation"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-scene-layer-with-elevation/src/main/AndroidManifest.xml b/samples/add-scene-layer-with-elevation/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/add-scene-layer-with-elevation/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/MainActivity.kt b/samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/MainActivity.kt
new file mode 100644
index 000000000..2a9543652
--- /dev/null
+++ b/samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/MainActivity.kt
@@ -0,0 +1,51 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addscenelayerwithelevation
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.addscenelayerwithelevation.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SceneLayerWithElevationApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SceneLayerWithElevationApp() {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ MainScreen(sampleName = getString(R.string.add_scene_layer_with_elevation_app_name))
+ }
+ }
+}
diff --git a/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/components/SceneViewModel.kt b/samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/components/SceneViewModel.kt
similarity index 100%
rename from add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/components/SceneViewModel.kt
rename to samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/components/SceneViewModel.kt
diff --git a/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/screens/MainScreen.kt b/samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/screens/MainScreen.kt
similarity index 100%
rename from add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/screens/MainScreen.kt
rename to samples/add-scene-layer-with-elevation/src/main/java/com/esri/arcgismaps/sample/addscenelayerwithelevation/screens/MainScreen.kt
diff --git a/samples/add-scene-layer-with-elevation/src/main/res/values/strings.xml b/samples/add-scene-layer-with-elevation/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5509fd873
--- /dev/null
+++ b/samples/add-scene-layer-with-elevation/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Add scene layer with elevation
+
+ https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer
+
diff --git a/add-web-tiled-layer/README.md b/samples/add-web-tiled-layer/README.md
similarity index 100%
rename from add-web-tiled-layer/README.md
rename to samples/add-web-tiled-layer/README.md
diff --git a/add-web-tiled-layer/README.metadata.json b/samples/add-web-tiled-layer/README.metadata.json
similarity index 100%
rename from add-web-tiled-layer/README.metadata.json
rename to samples/add-web-tiled-layer/README.metadata.json
diff --git a/add-web-tiled-layer/add-web-tiled-layer.png b/samples/add-web-tiled-layer/add-web-tiled-layer.png
similarity index 100%
rename from add-web-tiled-layer/add-web-tiled-layer.png
rename to samples/add-web-tiled-layer/add-web-tiled-layer.png
diff --git a/samples/add-web-tiled-layer/build.gradle.kts b/samples/add-web-tiled-layer/build.gradle.kts
new file mode 100644
index 000000000..7105b8909
--- /dev/null
+++ b/samples/add-web-tiled-layer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addwebtiledlayer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-web-tiled-layer/src/main/AndroidManifest.xml b/samples/add-web-tiled-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..7fa284305
--- /dev/null
+++ b/samples/add-web-tiled-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-web-tiled-layer/src/main/java/com/esri/arcgismaps/sample/addwebtiledlayer/MainActivity.kt b/samples/add-web-tiled-layer/src/main/java/com/esri/arcgismaps/sample/addwebtiledlayer/MainActivity.kt
new file mode 100644
index 000000000..d65bf0565
--- /dev/null
+++ b/samples/add-web-tiled-layer/src/main/java/com/esri/arcgismaps/sample/addwebtiledlayer/MainActivity.kt
@@ -0,0 +1,54 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addwebtiledlayer
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.layers.WebTiledLayer
+import com.esri.arcgismaps.sample.addwebtiledlayer.databinding.AddWebTiledLayerActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: AddWebTiledLayerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.add_web_tiled_layer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycle.addObserver(mapView)
+
+ // build the web tiled layer from ArcGIS Living Atlas of the World tile service url
+ val webTiledLayer = WebTiledLayer.create(
+ urlTemplate = getString(R.string.template_uri_living_atlas)
+ ).apply {
+ // set the attribution on the layer
+ attribution = getString(R.string.living_atlas_attribution)
+ }
+
+ // use web tiled layer as Basemap
+ val map = ArcGISMap(Basemap(webTiledLayer))
+ mapView.map = map
+ }
+}
diff --git a/add-web-tiled-layer/src/main/res/layout/activity_main.xml b/samples/add-web-tiled-layer/src/main/res/layout/add_web_tiled_layer_activity_main.xml
similarity index 100%
rename from add-web-tiled-layer/src/main/res/layout/activity_main.xml
rename to samples/add-web-tiled-layer/src/main/res/layout/add_web_tiled_layer_activity_main.xml
diff --git a/samples/add-web-tiled-layer/src/main/res/values/strings.xml b/samples/add-web-tiled-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..0d7e8fc02
--- /dev/null
+++ b/samples/add-web-tiled-layer/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Add web tiled layer
+ https://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{level}/{row}/{col}.jpg
+ Map tiles by ArcGIS Living Atlas of the World, under Esri Master License Agreement. Data by Esri, Garmin, GEBCO, NOAA NGDC, and other contributors.
+
diff --git a/add-wms-layer/README.md b/samples/add-wms-layer/README.md
similarity index 100%
rename from add-wms-layer/README.md
rename to samples/add-wms-layer/README.md
diff --git a/add-wms-layer/README.metadata.json b/samples/add-wms-layer/README.metadata.json
similarity index 100%
rename from add-wms-layer/README.metadata.json
rename to samples/add-wms-layer/README.metadata.json
diff --git a/add-wms-layer/add-wms-layer.png b/samples/add-wms-layer/add-wms-layer.png
similarity index 100%
rename from add-wms-layer/add-wms-layer.png
rename to samples/add-wms-layer/add-wms-layer.png
diff --git a/samples/add-wms-layer/build.gradle.kts b/samples/add-wms-layer/build.gradle.kts
new file mode 100644
index 000000000..f2296e90f
--- /dev/null
+++ b/samples/add-wms-layer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.addwmslayer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/add-wms-layer/src/main/AndroidManifest.xml b/samples/add-wms-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..263851926
--- /dev/null
+++ b/samples/add-wms-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/add-wms-layer/src/main/java/com/esri/arcgismaps/sample/addwmslayer/MainActivity.kt b/samples/add-wms-layer/src/main/java/com/esri/arcgismaps/sample/addwmslayer/MainActivity.kt
new file mode 100644
index 000000000..d05d11122
--- /dev/null
+++ b/samples/add-wms-layer/src/main/java/com/esri/arcgismaps/sample/addwmslayer/MainActivity.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.addwmslayer
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.WmsLayer
+import com.esri.arcgismaps.sample.addwmslayer.databinding.AddWmsLayerActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: AddWmsLayerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.add_wms_layer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a light gray basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISLightGray)
+
+ // apply mapView assignments
+ mapView.apply {
+ this.map = map
+ // set an initial viewpoint to a zoomed out view of North America
+ setViewpoint(Viewpoint(39.8, -98.6, 10e7))
+ }
+
+ lifecycleScope.launch {
+ // if the map load fails, show an error and return
+ map.load().onFailure {
+ return@launch showError("Error loading map")
+ }
+ // create a list representing names of layers to load from the WMS service
+ val wmsLayerNames = listOf("conus_base_reflectivity_mosaic")
+ // create a new WmsLayer with the WMS service url and the layers name list
+ val wmsLayer = WmsLayer(getString(R.string.wms_layer_url), wmsLayerNames)
+ // add the wmsLayer to the map as an operational layer
+ map.operationalLayers.add(wmsLayer)
+ // if loading the layer fails show an error
+ wmsLayer.load().onFailure {
+ showError("Error loading WmsLayer")
+ }
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/add-wms-layer/src/main/res/layout/activity_main.xml b/samples/add-wms-layer/src/main/res/layout/add_wms_layer_activity_main.xml
similarity index 100%
rename from add-wms-layer/src/main/res/layout/activity_main.xml
rename to samples/add-wms-layer/src/main/res/layout/add_wms_layer_activity_main.xml
diff --git a/samples/add-wms-layer/src/main/res/values/strings.xml b/samples/add-wms-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..a8a74a7ae
--- /dev/null
+++ b/samples/add-wms-layer/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Add WMS layer
+ https://nowcoast.noaa.gov/geoserver/observations/weather_radar/wms
+
diff --git a/analyze-hotspots/README.md b/samples/analyze-hotspots/README.md
similarity index 100%
rename from analyze-hotspots/README.md
rename to samples/analyze-hotspots/README.md
diff --git a/analyze-hotspots/README.metadata.json b/samples/analyze-hotspots/README.metadata.json
similarity index 100%
rename from analyze-hotspots/README.metadata.json
rename to samples/analyze-hotspots/README.metadata.json
diff --git a/analyze-hotspots/analyze-hotspots.png b/samples/analyze-hotspots/analyze-hotspots.png
similarity index 100%
rename from analyze-hotspots/analyze-hotspots.png
rename to samples/analyze-hotspots/analyze-hotspots.png
diff --git a/samples/analyze-hotspots/build.gradle.kts b/samples/analyze-hotspots/build.gradle.kts
new file mode 100644
index 000000000..21f433169
--- /dev/null
+++ b/samples/analyze-hotspots/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.analyzehotspots"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/analyze-hotspots/src/main/AndroidManifest.xml b/samples/analyze-hotspots/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/analyze-hotspots/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/MainActivity.kt b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/MainActivity.kt
new file mode 100644
index 000000000..93c9d8987
--- /dev/null
+++ b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.analyzehotspots
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.analyzehotspots.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ AnalyzeHotspotsApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun AnalyzeHotspotsApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.analyze_hotspots_app_name)
+ )
+ }
+ }
+}
diff --git a/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/components/MapViewModel.kt b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/components/MapViewModel.kt
similarity index 100%
rename from analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/components/MapViewModel.kt
rename to samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/components/MapViewModel.kt
diff --git a/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomAppContent.kt b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomAppContent.kt
similarity index 100%
rename from analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomAppContent.kt
rename to samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomAppContent.kt
diff --git a/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomSheetScreen.kt b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomSheetScreen.kt
similarity index 100%
rename from analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomSheetScreen.kt
rename to samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/BottomSheetScreen.kt
diff --git a/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/MainScreen.kt b/samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/MainScreen.kt
similarity index 100%
rename from analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/MainScreen.kt
rename to samples/analyze-hotspots/src/main/java/com/esri/arcgismaps/sample/analyzehotspots/screens/MainScreen.kt
diff --git a/samples/analyze-hotspots/src/main/res/values/strings.xml b/samples/analyze-hotspots/src/main/res/values/strings.xml
new file mode 100644
index 000000000..6be16fd3c
--- /dev/null
+++ b/samples/analyze-hotspots/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Analyze hotspots
+ http://sampleserver6.arcgisonline.com/arcgis/rest/services/911CallsHotspot/GPServer/911%20Calls%20Hotspot
+
diff --git a/analyze-network-with-subnetwork-trace/README.md b/samples/analyze-network-with-subnetwork-trace/README.md
similarity index 100%
rename from analyze-network-with-subnetwork-trace/README.md
rename to samples/analyze-network-with-subnetwork-trace/README.md
diff --git a/analyze-network-with-subnetwork-trace/README.metadata.json b/samples/analyze-network-with-subnetwork-trace/README.metadata.json
similarity index 100%
rename from analyze-network-with-subnetwork-trace/README.metadata.json
rename to samples/analyze-network-with-subnetwork-trace/README.metadata.json
diff --git a/analyze-network-with-subnetwork-trace/analyze-network-with-subnetwork-trace.png b/samples/analyze-network-with-subnetwork-trace/analyze-network-with-subnetwork-trace.png
similarity index 100%
rename from analyze-network-with-subnetwork-trace/analyze-network-with-subnetwork-trace.png
rename to samples/analyze-network-with-subnetwork-trace/analyze-network-with-subnetwork-trace.png
diff --git a/samples/analyze-network-with-subnetwork-trace/build.gradle.kts b/samples/analyze-network-with-subnetwork-trace/build.gradle.kts
new file mode 100644
index 000000000..d79085dcd
--- /dev/null
+++ b/samples/analyze-network-with-subnetwork-trace/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.analyzenetworkwithsubnetworktrace"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/analyze-network-with-subnetwork-trace/src/main/AndroidManifest.xml b/samples/analyze-network-with-subnetwork-trace/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..496350952
--- /dev/null
+++ b/samples/analyze-network-with-subnetwork-trace/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/analyze-network-with-subnetwork-trace/src/main/java/com/esri/arcgismaps/sample/analyzenetworkwithsubnetworktrace/MainActivity.kt b/samples/analyze-network-with-subnetwork-trace/src/main/java/com/esri/arcgismaps/sample/analyzenetworkwithsubnetworktrace/MainActivity.kt
new file mode 100644
index 000000000..3b33d27b9
--- /dev/null
+++ b/samples/analyze-network-with-subnetwork-trace/src/main/java/com/esri/arcgismaps/sample/analyzenetworkwithsubnetworktrace/MainActivity.kt
@@ -0,0 +1,522 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.analyzenetworkwithsubnetworktrace
+
+import android.os.Bundle
+import android.text.InputType
+import android.util.Log
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.AutoCompleteTextView
+import android.widget.CheckBox
+import android.widget.RelativeLayout
+import android.widget.TextView
+import android.widget.ToggleButton
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Guid
+import com.arcgismaps.LoadStatus
+import com.arcgismaps.data.CodedValue
+import com.arcgismaps.data.CodedValueDomain
+import com.arcgismaps.httpcore.authentication.ArcGISAuthenticationChallengeHandler
+import com.arcgismaps.httpcore.authentication.ArcGISAuthenticationChallengeResponse
+import com.arcgismaps.httpcore.authentication.TokenCredential
+import com.arcgismaps.utilitynetworks.UtilityAttributeComparisonOperator
+import com.arcgismaps.utilitynetworks.UtilityCategoryComparison
+import com.arcgismaps.utilitynetworks.UtilityElement
+import com.arcgismaps.utilitynetworks.UtilityElementTraceResult
+import com.arcgismaps.utilitynetworks.UtilityNetwork
+import com.arcgismaps.utilitynetworks.UtilityNetworkAttribute
+import com.arcgismaps.utilitynetworks.UtilityNetworkAttributeComparison
+import com.arcgismaps.utilitynetworks.UtilityNetworkAttributeDataType
+import com.arcgismaps.utilitynetworks.UtilityTier
+import com.arcgismaps.utilitynetworks.UtilityTraceAndCondition
+import com.arcgismaps.utilitynetworks.UtilityTraceConditionalExpression
+import com.arcgismaps.utilitynetworks.UtilityTraceConfiguration
+import com.arcgismaps.utilitynetworks.UtilityTraceOrCondition
+import com.arcgismaps.utilitynetworks.UtilityTraceParameters
+import com.arcgismaps.utilitynetworks.UtilityTraceType
+import com.arcgismaps.utilitynetworks.UtilityTraversability
+import com.esri.arcgismaps.sample.analyzenetworkwithsubnetworktrace.databinding.AnalyzeNetworkWithSubnetworkTraceActivityMainBinding
+import com.esri.arcgismaps.sample.analyzenetworkwithsubnetworktrace.databinding.LoadingOptionsDialogBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.textfield.TextInputEditText
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: AnalyzeNetworkWithSubnetworkTraceActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.analyze_network_with_subnetwork_trace_activity_main)
+ }
+
+ private val sourceDropdown: AutoCompleteTextView by lazy {
+ activityMainBinding.sourceDropdown
+ }
+
+ private val operatorDropdown: AutoCompleteTextView by lazy {
+ activityMainBinding.operatorDropdown
+ }
+
+ private val expressionTextView: TextView by lazy {
+ activityMainBinding.expressionTextView
+ }
+
+ private val valuesDropdown: AutoCompleteTextView by lazy {
+ activityMainBinding.valuesDropdown
+ }
+
+ private val valuesBackgroundView: RelativeLayout by lazy {
+ activityMainBinding.valuesBackgroundView
+ }
+
+ private val valueBooleanButton: ToggleButton by lazy {
+ activityMainBinding.valueBooleanButton
+ }
+
+ private val valuesEditText: TextInputEditText by lazy {
+ activityMainBinding.valuesEditText
+ }
+
+ private val barriersCheckbox: CheckBox by lazy {
+ activityMainBinding.barriersCheckBox
+ }
+
+ private val containersCheckbox: CheckBox by lazy {
+ activityMainBinding.containersCheckbox
+ }
+
+ private val traceButton: MaterialButton by lazy {
+ activityMainBinding.traceButton
+ }
+
+ private val utilityNetwork by lazy {
+ UtilityNetwork(getString(R.string.utility_network_url))
+ }
+
+ private var initialExpression: UtilityTraceConditionalExpression? = null
+ private var sourceTier: UtilityTier? = null
+ private var utilityTraceConfiguration: UtilityTraceConfiguration? = null
+ private var sourcesList: List? = null
+ private var operatorsList: Array? = null
+ private var startingLocation: UtilityElement? = null
+ private var codedValuesList: List? = null
+ private var sourcePosition: Int = 0
+ private var operatorPosition: Int = 0
+ private var valuePosition: Int = 0
+ private var dialog: AlertDialog? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ ArcGISEnvironment.applicationContext = this
+ ArcGISEnvironment.authenticationManager.arcGISAuthenticationChallengeHandler =
+ getAuthenticationChallengeHandler()
+
+ // create and display the loading dialog
+ showLoadingDialog(true)
+
+ // load the utility network
+ lifecycleScope.launch {
+ utilityNetwork.load().getOrElse {
+ dialog?.dismiss()
+ traceButton.isEnabled = false
+ return@launch showError("Error loading utility network: ${it.message}")
+ }
+
+ // create a list of utility network attributes whose system is not defined
+ sourcesList =
+ utilityNetwork.definition?.networkAttributes?.filter { !it.isSystemDefined }
+
+ sourceDropdown.apply {
+ // add the list of sources to the drop down view
+ setAdapter(sourcesList?.let { utilityNetworkAttributes ->
+ ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ utilityNetworkAttributes.map { it.name })
+ })
+
+ // add an on item selected listener which calls on comparison source changed
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ sourcePosition = position
+ sourcesList?.get(position)?.let { onComparisonSourceChanged(it) }
+ }
+ }
+
+ // create a list of utility attribute comparison operators
+ operatorsList =
+ UtilityAttributeComparisonOperator::class.sealedSubclasses.mapNotNull { it.objectInstance }
+ .toTypedArray()
+
+ operatorDropdown.apply {
+ // add the list of sources to the drop down view
+ setAdapter(operatorsList?.let { utilityAttributeComparisonOperator ->
+ ArrayAdapter(applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ utilityAttributeComparisonOperator.map { it::class.simpleName })
+ })
+
+ // add an on item selected listener which calls on comparison source changed
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ operatorPosition = position
+ }
+ }
+
+ // create a default starting location
+ val networkSource = utilityNetwork.definition?.getNetworkSource("Electric Distribution Device")
+
+ val assetGroup = networkSource?.getAssetGroup("Circuit Breaker")
+
+ val assetType = assetGroup?.getAssetType("Three Phase")
+
+ val globalId = Guid("1CAF7740-0BF4-4113-8DB2-654E18800028")
+
+ if (assetType == null) return@launch
+
+ val terminal = assetType.terminalConfiguration?.terminals?.first { it.name == "Load" }
+
+ // utility element to start the trace from
+ startingLocation = utilityNetwork.createElementOrNull(assetType, globalId, terminal)
+
+ // get a default trace configuration from a tier to update the UI
+ val domainNetwork = utilityNetwork.definition?.getDomainNetwork(
+ "ElectricDistribution"
+ )
+
+ // set source utility tier from the utility domain network
+ sourceTier = domainNetwork?.getTier("Medium Voltage Radial")?.apply {
+ utilityTraceConfiguration = getDefaultTraceConfiguration()
+ }
+
+ // set initial barrier condition
+ val defaultConditionalExpression = sourceTier.let {
+ utilityTraceConfiguration?.traversability?.barriers as UtilityTraceConditionalExpression
+ }
+ // set the text view
+ expressionTextView.text = expressionToString(defaultConditionalExpression)
+ // use the initial expression when resetting trace
+ initialExpression = defaultConditionalExpression
+
+ showLoadingDialog(false)
+ }
+ }
+
+ /**
+ * Returns a [ArcGISAuthenticationChallengeHandler] to access the utility network URL.
+ */
+ private fun getAuthenticationChallengeHandler(): ArcGISAuthenticationChallengeHandler {
+ return ArcGISAuthenticationChallengeHandler { challenge ->
+ val result: Result = runBlocking {
+ TokenCredential.create(challenge.requestUrl, "viewer01", "I68VGU^nMurF", 0)
+ }
+ if (result.getOrNull() != null) {
+ val credential = result.getOrNull()
+ return@ArcGISAuthenticationChallengeHandler ArcGISAuthenticationChallengeResponse
+ .ContinueWithCredential(credential!!)
+ } else {
+ val ex = result.exceptionOrNull()
+ return@ArcGISAuthenticationChallengeHandler ArcGISAuthenticationChallengeResponse
+ .ContinueAndFailWithError(ex!!)
+ }
+ }
+ }
+
+ /**
+ * When a comparison source [attribute] is chosen check if it's a coded value domain and, if it is,
+ * present a dropdown of coded value domains. If not, show the correct UI view for the utility
+ * network attribute data type.
+ */
+ private fun onComparisonSourceChanged(attribute: UtilityNetworkAttribute) {
+ // if the domain is a coded value domain
+ if (attribute.domain is CodedValueDomain) {
+ (attribute.domain as CodedValueDomain).let { codedValueDomain ->
+ // update the list of coded values
+ codedValuesList = codedValueDomain.codedValues
+ // show the values dropdown
+ setVisible(valuesBackgroundView.id)
+ // update the values dropdown adapter
+ valuesDropdown.setAdapter(ArrayAdapter(applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ // add the coded values from the coded value domain to the values dropdown
+ codedValueDomain.codedValues.map { it.name }))
+ // add an on item selected listener which calls on comparison source changed
+ valuesDropdown.onItemClickListener =
+ AdapterView.OnItemClickListener { _, _, position, _ ->
+ valuePosition = position
+ }
+ }
+ } // if the domain is not a coded value domain
+ else {
+ when (attribute.dataType) {
+ UtilityNetworkAttributeDataType.Boolean -> {
+ // show true/false toggle button
+ setVisible(valueBooleanButton.id)
+ }
+ UtilityNetworkAttributeDataType.Double, UtilityNetworkAttributeDataType.Float -> {
+ // show the edit text and only allow numbers (decimals allowed)
+ valuesEditText.inputType =
+ InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
+ setVisible(valuesEditText.id)
+ }
+ UtilityNetworkAttributeDataType.Integer -> {
+ // show the edit text only allowing for integer input
+ valuesEditText.inputType = InputType.TYPE_CLASS_NUMBER
+ setVisible(valuesEditText.id)
+ }
+ else -> {
+ showError("Unexpected utility network attribute data type.")
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a new barrier condition to the trace options when [addConditionButton] is tapped.
+ */
+ fun addBarrierCondition(addConditionButton: View) {
+ // if source tier doesn't contain a trace configuration, create one
+ val traceConfiguration = utilityTraceConfiguration ?: UtilityTraceConfiguration().apply {
+ // if the trace configuration doesn't contain traversability, create one
+ traversability ?: UtilityTraversability()
+ }
+
+ // get the currently selected attribute
+ sourcesList?.get(sourcePosition)?.let { sourceAttribute ->
+ // if the other value is a coded value domain
+ val otherValue = if (sourceAttribute.domain is CodedValueDomain) {
+ codedValuesList?.get(valuePosition)?.code?.let {
+ convertToDataType(it, sourceAttribute.dataType)
+ }
+ } else {
+ convertToDataType(valuesEditText.text.toString(), sourceAttribute.dataType)
+ }
+
+ if (otherValue.toString().contains("Error") || otherValue == null) {
+ return showError(otherValue.toString())
+ }
+
+ // get the currently selected attribute operator>
+ operatorsList?.get(operatorPosition)?.let { comparisonOperator ->
+ // NOTE: You may also create a UtilityNetworkAttributeComparison
+ // with another NetworkAttribute
+ var expression: UtilityTraceConditionalExpression =
+ UtilityNetworkAttributeComparison(
+ sourceAttribute,
+ comparisonOperator,
+ otherValue
+ )
+ (traceConfiguration.traversability?.barriers as? UtilityTraceConditionalExpression)?.let { otherExpression ->
+ // NOTE: You may also combine expressions with UtilityTraceAndCondition
+ expression = UtilityTraceOrCondition(otherExpression, expression)
+ }
+ traceConfiguration.traversability?.barriers = expression
+ expressionTextView.text = expressionToString(expression)
+ }
+ }
+ }
+
+ /**
+ * Show the UI of the given [id] and hide the others which share the same space.
+ */
+ private fun setVisible(id: Int) {
+ when (id) {
+ valuesBackgroundView.id -> {
+ valuesBackgroundView.visibility = View.VISIBLE
+ valueBooleanButton.visibility = View.GONE
+ valuesEditText.visibility = View.GONE
+ }
+ valuesEditText.id -> {
+ valuesEditText.visibility = View.VISIBLE
+ valueBooleanButton.visibility = View.GONE
+ valuesBackgroundView.visibility = View.GONE
+ }
+ valueBooleanButton.id -> {
+ valueBooleanButton.visibility = View.VISIBLE
+ valuesBackgroundView.visibility = View.GONE
+ valuesEditText.visibility = View.GONE
+ }
+ }
+ }
+
+ /**
+ * Run the network trace with the parameters and display the result in an alert dialog
+ * when the [traceButton] is clicked.
+ */
+ fun trace(traceButton: View) {
+ if (utilityNetwork.loadStatus.value != LoadStatus.Loaded) {
+ return showError("Utility network is not loaded")
+ }
+
+ // set the utility trace parameters
+ val parameters = UtilityTraceParameters(
+ UtilityTraceType.Subnetwork,
+ listOf(startingLocation).requireNoNulls()
+ ).apply {
+ // set the utility trace configuration options to include
+ traceConfiguration = utilityTraceConfiguration?.apply {
+ includeBarriers = barriersCheckbox.isChecked
+ includeContainers = containersCheckbox.isChecked
+ }
+ }
+
+ // launch trace in a coroutine scope
+ lifecycleScope.launch {
+ showLoadingDialog(true)
+ val utilityTraceResults = utilityNetwork.trace(parameters).getOrElse {
+ return@launch showError(it.message + getString(R.string.example_condition))
+ }
+ // get the UtilityElementTraceResult
+ val elementTraceResult = utilityTraceResults.first() as UtilityElementTraceResult
+
+ showLoadingDialog(false)
+ MaterialAlertDialogBuilder(this@MainActivity).apply {
+ // set the result dialog title
+ setTitle("Trace result")
+ // show the element result count
+ setMessage(elementTraceResult.elements.count().toString() + " elements found.")
+ }.show()
+ }
+ }
+
+ /**
+ * Convert the given [expression] into a string.
+ */
+ private fun expressionToString(expression: UtilityTraceConditionalExpression): String? {
+ when (expression) {
+ // when the expression is a category comparison expression
+ is UtilityCategoryComparison -> {
+ return expression.category.name + " " + expression.comparisonOperator
+ }
+ // when the expression is an utility trace AND condition
+ is UtilityTraceAndCondition -> {
+ return expressionToString(expression.leftExpression) + " AND\n" + expressionToString(
+ expression.rightExpression
+ )
+ }
+ // when the expression is an utility trace OR condition
+ is UtilityTraceOrCondition -> {
+ return expressionToString(expression.leftExpression) + " OR\n" + expressionToString(
+ expression.rightExpression
+ )
+ }
+ // when the expression is an attribute comparison expression
+ is UtilityNetworkAttributeComparison -> {
+ // the name and comparison operator of the expression
+ val networkAttributeNameAndOperator = expression.networkAttribute.name + " " +
+ expression.comparisonOperator::class.simpleName + " "
+
+ // check whether the network attribute has a coded value domain
+ val codedValueDomain = expression.networkAttribute.domain as? CodedValueDomain
+ return if (codedValueDomain != null) {
+ networkAttributeNameAndOperator +
+ getCodedValueFromExpression(codedValueDomain, expression)?.name
+ } else {
+ // if there's no coded value domain
+ networkAttributeNameAndOperator +
+ (expression.otherNetworkAttribute?.name ?: expression.value)
+ }
+ }
+ else -> {
+ return null
+ }
+ }
+ }
+
+ /**
+ * Convert the given value into the correct Kotlin data type by using the attribute's data type.
+ *
+ * @param otherValue which will be converted
+ * @param dataType to be converted to
+ */
+ private fun convertToDataType(
+ otherValue: Any,
+ dataType: UtilityNetworkAttributeDataType,
+ ): Any {
+ return try {
+ if (dataType::class.isInstance(UtilityNetworkAttributeDataType.Boolean)) {
+ otherValue.toString().toBoolean()
+ } else if (dataType::class.isInstance(UtilityNetworkAttributeDataType.Double)) {
+ otherValue.toString().toDouble()
+ } else if (dataType::class.isInstance(UtilityNetworkAttributeDataType.Float)) {
+ otherValue.toString().toFloat()
+ } else if (dataType::class.isInstance(UtilityNetworkAttributeDataType.Integer)) {
+ otherValue.toString().toInt()
+ } else {
+ }
+ } catch (e: Exception) {
+ return ("Error converting value to a datatype")
+ }
+ }
+
+ /**
+ * Returns a [CodedValue] found in the [expression] using the
+ * list of coded values in the [codedValueDomain].
+ */
+ private fun getCodedValueFromExpression(
+ codedValueDomain: CodedValueDomain,
+ expression: UtilityNetworkAttributeComparison,
+ ): CodedValue? {
+ // if there's a coded value domain name
+ return codedValueDomain.codedValues.first { codedValue ->
+ val code = codedValue.code
+ val value = expression.value
+ if (code != null && value != null) {
+ return@first (convertToDataType(code,
+ expression.networkAttribute.dataType) == convertToDataType(value,
+ expression.networkAttribute.dataType))
+ } else
+ return null
+ }
+ }
+
+ /**
+ * Reset the current barrier condition to the initial expression
+ * "Operational Device Status EQUAL Open" and resets the UI.
+ */
+ fun reset(view: View) {
+ initialExpression?.let {
+ utilityTraceConfiguration = sourceTier?.getDefaultTraceConfiguration()?.apply {
+ traversability?.barriers = it
+ }
+ expressionTextView.text = expressionToString(it)
+ }
+ }
+
+ private fun showLoadingDialog(isVisible: Boolean) {
+ if (isVisible) {
+ dialog = MaterialAlertDialogBuilder(this).apply {
+ setCancelable(false)
+ setView(LoadingOptionsDialogBinding.inflate(layoutInflater).root)
+ }.show()
+ } else {
+ dialog?.dismiss()
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(activityMainBinding.root, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/analyze-network-with-subnetwork-trace/src/main/res/drawable/border.xml b/samples/analyze-network-with-subnetwork-trace/src/main/res/drawable/border.xml
similarity index 100%
rename from analyze-network-with-subnetwork-trace/src/main/res/drawable/border.xml
rename to samples/analyze-network-with-subnetwork-trace/src/main/res/drawable/border.xml
diff --git a/analyze-network-with-subnetwork-trace/src/main/res/layout/activity_main.xml b/samples/analyze-network-with-subnetwork-trace/src/main/res/layout/analyze_network_with_subnetwork_trace_activity_main.xml
similarity index 100%
rename from analyze-network-with-subnetwork-trace/src/main/res/layout/activity_main.xml
rename to samples/analyze-network-with-subnetwork-trace/src/main/res/layout/analyze_network_with_subnetwork_trace_activity_main.xml
diff --git a/analyze-network-with-subnetwork-trace/src/main/res/layout/loading_options_dialog.xml b/samples/analyze-network-with-subnetwork-trace/src/main/res/layout/loading_options_dialog.xml
similarity index 100%
rename from analyze-network-with-subnetwork-trace/src/main/res/layout/loading_options_dialog.xml
rename to samples/analyze-network-with-subnetwork-trace/src/main/res/layout/loading_options_dialog.xml
diff --git a/samples/analyze-network-with-subnetwork-trace/src/main/res/values/strings.xml b/samples/analyze-network-with-subnetwork-trace/src/main/res/values/strings.xml
new file mode 100644
index 000000000..3e34b3241
--- /dev/null
+++ b/samples/analyze-network-with-subnetwork-trace/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+ Analyze network with subnetwork trace
+ Trace options:
+ Include barriers
+ Include containers
+ Define new condition:
+ Example barrier condition for this data:\n\'Transformer Load\' EQUAL \'15\'
+ False
+ True
+ Add condition
+ Barrier conditions:
+ Trace
+ Reset
+ https://sampleserver7.arcgisonline.com/server/rest/services/UtilityNetwork/NapervilleElectric/FeatureServer
+ Select source
+ Select operator
+ Select value
+ Value
+ Loading subnetwork trace
+
diff --git a/apply-dictionary-renderer-to-feature-layer/README.md b/samples/apply-dictionary-renderer-to-feature-layer/README.md
similarity index 100%
rename from apply-dictionary-renderer-to-feature-layer/README.md
rename to samples/apply-dictionary-renderer-to-feature-layer/README.md
diff --git a/apply-dictionary-renderer-to-feature-layer/README.metadata.json b/samples/apply-dictionary-renderer-to-feature-layer/README.metadata.json
similarity index 100%
rename from apply-dictionary-renderer-to-feature-layer/README.metadata.json
rename to samples/apply-dictionary-renderer-to-feature-layer/README.metadata.json
diff --git a/apply-dictionary-renderer-to-feature-layer/apply-dictionary-renderer-to-feature-layer.png b/samples/apply-dictionary-renderer-to-feature-layer/apply-dictionary-renderer-to-feature-layer.png
similarity index 100%
rename from apply-dictionary-renderer-to-feature-layer/apply-dictionary-renderer-to-feature-layer.png
rename to samples/apply-dictionary-renderer-to-feature-layer/apply-dictionary-renderer-to-feature-layer.png
diff --git a/samples/apply-dictionary-renderer-to-feature-layer/build.gradle.kts b/samples/apply-dictionary-renderer-to-feature-layer/build.gradle.kts
new file mode 100644
index 000000000..0a187cb3c
--- /dev/null
+++ b/samples/apply-dictionary-renderer-to-feature-layer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml b/samples/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..7eea56e3e
--- /dev/null
+++ b/samples/apply-dictionary-renderer-to-feature-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt b/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt
new file mode 100644
index 000000000..66c88b96d
--- /dev/null
+++ b/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/DownloadActivity.kt
@@ -0,0 +1,22 @@
+package com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.apply_dictionary_renderer_to_feature_layer_app_name),
+ listOf(
+ // A stylx file that incorporates the MIL-STD-2525D symbol dictionary
+ "https://www.arcgis.com/home/item.html?id=c78b149a1d52414682c86a5feeb13d30",
+ // A mobile geodatabase created from the ArcGIS for Defense Military Overlay template
+ "https://www.arcgis.com/home/item.html?id=e0d41b4b409a49a5a7ba11939d8535dc"
+ )
+ )
+ }
+}
diff --git a/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt b/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt
new file mode 100644
index 000000000..cc4963049
--- /dev/null
+++ b/samples/apply-dictionary-renderer-to-feature-layer/src/main/java/com/esri/arcgismaps/sample/applydictionaryrenderertofeaturelayer/MainActivity.kt
@@ -0,0 +1,115 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.data.Geodatabase
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.symbology.DictionaryRenderer
+import com.arcgismaps.mapping.symbology.DictionarySymbolStyle
+import com.esri.arcgismaps.sample.applydictionaryrenderertofeaturelayer.databinding.ApplyDictionaryRendererToFeatureLayerActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ApplyDictionaryRendererToFeatureLayerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.apply_dictionary_renderer_to_feature_layer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.apply_dictionary_renderer_to_feature_layer_app_name)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ mapView.map = map
+
+ // locate the .stylx file in the device
+ val styleFile = File(provisionPath, getString(R.string.mil2525d_stylx))
+ // instantiate the dictionarySymbolStyle using the file path
+ val dictionarySymbolStyle = DictionarySymbolStyle.createFromFile(styleFile.absolutePath)
+
+ // locate the .geodatabase file in the device
+ val geodatabaseFile = File(provisionPath, getString(R.string.militaryoverlay_geodatabase))
+ // instantiate the geodatabase with the file path
+ val geodatabase = Geodatabase(geodatabaseFile.path)
+
+ lifecycleScope.launch {
+ // load the dictionary symbol style
+ dictionarySymbolStyle.load().getOrElse {
+ return@launch showError("Error loading DictionarySymbolStyle: ${it.message}")
+ }
+
+ // load the geodatabase
+ geodatabase.load().getOrElse {
+ showError("Error loading Geodatabase: ${it.message}")
+ }
+
+ geodatabase.featureTables.forEach { geodatabaseFeatureTable ->
+ // load each geodatabaseFeatureTable and create featureLayer from it
+ geodatabaseFeatureTable.load().getOrElse {
+ return@launch showError("Error loading GeodatabaseFeatureTable: ${it.message}")
+ }
+ val featureLayer = FeatureLayer.createWithFeatureTable(geodatabaseFeatureTable)
+ featureLayer.load().getOrElse {
+ return@launch showError("Error loading FeatureLayer: ${it.message}")
+ }
+ // add featureLayer to the map's operational layer
+ mapView.map?.operationalLayers?.add(featureLayer)
+
+ // create dictionaryRenderer using the dictionarySymbolStyle and apply it to the featureLayer's renderer
+ val dictionaryRenderer = DictionaryRenderer(dictionarySymbolStyle)
+ featureLayer.renderer = dictionaryRenderer
+ // get the featureLayer's envelope to set the map viewpoint
+ val extent = featureLayer.fullExtent
+ ?: return@launch showError("Error retrieving extent of the feature layer")
+ mapView.setViewpoint(Viewpoint(extent))
+ }
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+
diff --git a/apply-dictionary-renderer-to-feature-layer/src/main/res/layout/activity_main.xml b/samples/apply-dictionary-renderer-to-feature-layer/src/main/res/layout/apply_dictionary_renderer_to_feature_layer_activity_main.xml
similarity index 100%
rename from apply-dictionary-renderer-to-feature-layer/src/main/res/layout/activity_main.xml
rename to samples/apply-dictionary-renderer-to-feature-layer/src/main/res/layout/apply_dictionary_renderer_to_feature_layer_activity_main.xml
diff --git a/samples/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml b/samples/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1a36b927c
--- /dev/null
+++ b/samples/apply-dictionary-renderer-to-feature-layer/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Apply dictionary renderer to feature layer
+ /militaryoverlay.geodatabase
+ /mil2525d.stylx
+
diff --git a/apply-function-to-raster-from-service/README.md b/samples/apply-function-to-raster-from-service/README.md
similarity index 100%
rename from apply-function-to-raster-from-service/README.md
rename to samples/apply-function-to-raster-from-service/README.md
diff --git a/apply-function-to-raster-from-service/README.metadata.json b/samples/apply-function-to-raster-from-service/README.metadata.json
similarity index 100%
rename from apply-function-to-raster-from-service/README.metadata.json
rename to samples/apply-function-to-raster-from-service/README.metadata.json
diff --git a/apply-function-to-raster-from-service/apply-function-to-raster-from-service.png b/samples/apply-function-to-raster-from-service/apply-function-to-raster-from-service.png
similarity index 100%
rename from apply-function-to-raster-from-service/apply-function-to-raster-from-service.png
rename to samples/apply-function-to-raster-from-service/apply-function-to-raster-from-service.png
diff --git a/samples/apply-function-to-raster-from-service/build.gradle.kts b/samples/apply-function-to-raster-from-service/build.gradle.kts
new file mode 100644
index 000000000..9810edd7b
--- /dev/null
+++ b/samples/apply-function-to-raster-from-service/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.applyfunctiontorasterfromservice"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/apply-function-to-raster-from-service/src/main/AndroidManifest.xml b/samples/apply-function-to-raster-from-service/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..8831f6146
--- /dev/null
+++ b/samples/apply-function-to-raster-from-service/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt b/samples/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt
new file mode 100644
index 000000000..94ff6c407
--- /dev/null
+++ b/samples/apply-function-to-raster-from-service/src/main/java/com/esri/arcgismaps/sample/applyfunctiontorasterfromservice/MainActivity.kt
@@ -0,0 +1,146 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.applyfunctiontorasterfromservice
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.LoadStatus
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.layers.RasterLayer
+import com.arcgismaps.raster.ImageServiceRaster
+import com.arcgismaps.raster.Raster
+import com.arcgismaps.raster.RasterFunction
+import com.esri.arcgismaps.sample.applyfunctiontorasterfromservice.databinding.ApplyFunctionToRasterFromServiceActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ private val activityMainBinding: ApplyFunctionToRasterFromServiceActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.apply_function_to_raster_from_service_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val imageServiceRaster: ImageServiceRaster by lazy {
+ ImageServiceRaster(getString(R.string.image_service_raster_url))
+ }
+
+ private val imageRasterLayer: RasterLayer by lazy {
+ RasterLayer(imageServiceRaster)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a dark gray basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISDarkGray)
+ mapView.map = map
+
+ // add the imageRasterLayer to the map
+ addImageRasterLayer()
+
+ activityMainBinding.apply {
+ rasterButton.setOnClickListener {
+ // update the raster with simplified hillshade
+ applyRasterFunction()
+ resetButton.isEnabled = true
+ rasterButton.isEnabled = false
+ }
+ resetButton.setOnClickListener {
+ // reset map to back to the RasterLayer
+ addImageRasterLayer()
+ resetButton.isEnabled = false
+ rasterButton.isEnabled = true
+ }
+ }
+ }
+
+ /**
+ * Adds the image raster layer to the map and set's the viewpoint
+ * to the image server raster's bounding geometry
+ */
+ private fun addImageRasterLayer() {
+ // clear and add the imageRasterLayer to the map
+ mapView.map?.operationalLayers?.apply {
+ clear()
+ add(imageRasterLayer)
+ }
+
+ // collect the load status of the RasterLayer
+ lifecycleScope.launch {
+ imageRasterLayer.loadStatus.collect { loadStatus ->
+ if (loadStatus == LoadStatus.Loaded) {
+ // get the center point of the image service raster
+ val extentEnvelope = imageServiceRaster.serviceInfo?.fullExtent
+ ?: return@collect showError("Error retrieving the ArcGISImageServiceInfo")
+ // set the viewpoint of the map to the envelope
+ mapView.setViewpointGeometry(extentEnvelope)
+ } else if (loadStatus is LoadStatus.FailedToLoad) {
+ showError("Error loading image raster layer: ${loadStatus.error.message}")
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a hillshade layer using a custom JSON raster function.
+ */
+ private fun applyRasterFunction() {
+ // create raster function from json string
+ val rasterFunction = RasterFunction.fromJsonOrNull(getString(R.string.hillshade_simplified))
+ ?: return showError("Error creating a raster function object from JSON")
+
+ // get parameter name value pairs used by hillside
+ val rasterFunctionArguments = rasterFunction.arguments
+ ?: return showError("Raster function arguments is null")
+
+ // get a list of raster names associated with the raster function
+ val rasterNames = rasterFunctionArguments.rasterNames
+ // check if raster function arguments contains raster variable names
+ if(rasterNames.isNotEmpty()){
+ // using the first raster variable name
+ rasterFunctionArguments.setRaster(rasterNames[0], imageServiceRaster)
+ // create raster as raster layer
+ val hillshadeRaster = Raster.createWithRasterFunction(rasterFunction)
+ val hillshadeLayer = RasterLayer(hillshadeRaster)
+ // clear and add the layer to the map
+ mapView.map?.operationalLayers?.add(hillshadeLayer)
+ } else{
+ showError("Raster function arguments does not contain raster variable names")
+ }
+
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/apply-function-to-raster-from-service/src/main/res/layout/activity_main.xml b/samples/apply-function-to-raster-from-service/src/main/res/layout/apply_function_to_raster_from_service_activity_main.xml
similarity index 100%
rename from apply-function-to-raster-from-service/src/main/res/layout/activity_main.xml
rename to samples/apply-function-to-raster-from-service/src/main/res/layout/apply_function_to_raster_from_service_activity_main.xml
diff --git a/samples/apply-function-to-raster-from-service/src/main/res/values/strings.xml b/samples/apply-function-to-raster-from-service/src/main/res/values/strings.xml
new file mode 100644
index 000000000..4f1b6cc16
--- /dev/null
+++ b/samples/apply-function-to-raster-from-service/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+ Apply function to raster from service
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/NLCDLandCover2001/ImageServer
+
+ {
+ \"raster_function_arguments\":
+ {
+ \"z_factor\":{\"double\":25.0,\"type\":\"Raster_function_variable\"},
+ \"slope_type\":{\"raster_slope_type\":\"none\",\"type\":\"Raster_function_variable\"},
+ \"azimuth\":{\"double\":315,\"type\":\"Raster_function_variable\"},
+ \"altitude\":{\"double\":45,\"type\":\"Raster_function_variable\"},
+ \"type\":\"Raster_function_arguments\",
+ \"raster\":{\"name\":\"raster\",\"is_raster\":true,\"type\":\"Raster_function_variable\"},
+ \"nbits\":{\"int\":8,\"type\":\"Raster_function_variable\"}
+ },
+ \"raster_function\":{\"type\":\"Hillshade_function\"},
+ \"type\":\"Raster_function_template\"
+ }
+
+ Apply Layer
+ Reset
+
diff --git a/authenticate-with-oauth/README.md b/samples/authenticate-with-oauth/README.md
similarity index 100%
rename from authenticate-with-oauth/README.md
rename to samples/authenticate-with-oauth/README.md
diff --git a/authenticate-with-oauth/README.metadata.json b/samples/authenticate-with-oauth/README.metadata.json
similarity index 100%
rename from authenticate-with-oauth/README.metadata.json
rename to samples/authenticate-with-oauth/README.metadata.json
diff --git a/authenticate-with-oauth/authenticate-with-oauth.png b/samples/authenticate-with-oauth/authenticate-with-oauth.png
similarity index 100%
rename from authenticate-with-oauth/authenticate-with-oauth.png
rename to samples/authenticate-with-oauth/authenticate-with-oauth.png
diff --git a/samples/authenticate-with-oauth/build.gradle.kts b/samples/authenticate-with-oauth/build.gradle.kts
new file mode 100644
index 000000000..c5bfb74ac
--- /dev/null
+++ b/samples/authenticate-with-oauth/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.authenticatewithoauth"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
+}
diff --git a/samples/authenticate-with-oauth/src/main/AndroidManifest.xml b/samples/authenticate-with-oauth/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..aa8cb935b
--- /dev/null
+++ b/samples/authenticate-with-oauth/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt b/samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt
new file mode 100644
index 000000000..55b0ca654
--- /dev/null
+++ b/samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/MainActivity.kt
@@ -0,0 +1,65 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.authenticatewithoauth
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.arcgismaps.toolkit.authentication.DialogAuthenticator
+import com.esri.arcgismaps.sample.authenticatewithoauth.components.MapViewModel
+import com.esri.arcgismaps.sample.authenticatewithoauth.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ SampleAppTheme {
+ AuthenticateWithOAuthApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun AuthenticateWithOAuthApp() {
+
+ // create a ViewModel to handle interactions
+ val mapViewModel: MapViewModel = viewModel()
+
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.authenticate_with_oauth_app_name)
+ )
+ // Displays appropriate Authentication UI when an authentication challenge is issued.
+ // Because the authenticatorState has an oAuthUserConfiguration set, authentication
+ // challenges will happen via OAuth.
+ // Call the DialogAuthenticator composable function at the top level of your view
+ // hierarchy, for example at the same level as MainScreen(). This ensures that
+ // authentication handling is set up before any components of the ArcGIS Maps SDK that
+ // may require authentication are used.
+ DialogAuthenticator(authenticatorState = mapViewModel.authenticatorState)
+ }
+ }
+}
diff --git a/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/components/MapViewModel.kt b/samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/components/MapViewModel.kt
similarity index 100%
rename from authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/components/MapViewModel.kt
rename to samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/components/MapViewModel.kt
diff --git a/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/screens/MainScreen.kt b/samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/screens/MainScreen.kt
similarity index 100%
rename from authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/screens/MainScreen.kt
rename to samples/authenticate-with-oauth/src/main/java/com/esri/arcgismaps/sample/authenticatewithoauth/screens/MainScreen.kt
diff --git a/samples/authenticate-with-oauth/src/main/res/values/strings.xml b/samples/authenticate-with-oauth/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1cf9a257a
--- /dev/null
+++ b/samples/authenticate-with-oauth/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Authenticate with OAuth
+
diff --git a/browse-building-floors/README.md b/samples/browse-building-floors/README.md
similarity index 100%
rename from browse-building-floors/README.md
rename to samples/browse-building-floors/README.md
diff --git a/browse-building-floors/README.metadata.json b/samples/browse-building-floors/README.metadata.json
similarity index 100%
rename from browse-building-floors/README.metadata.json
rename to samples/browse-building-floors/README.metadata.json
diff --git a/browse-building-floors/browse-building-floors.png b/samples/browse-building-floors/browse-building-floors.png
similarity index 100%
rename from browse-building-floors/browse-building-floors.png
rename to samples/browse-building-floors/browse-building-floors.png
diff --git a/samples/browse-building-floors/build.gradle.kts b/samples/browse-building-floors/build.gradle.kts
new file mode 100644
index 000000000..8dac81e29
--- /dev/null
+++ b/samples/browse-building-floors/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.browsebuildingfloors"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/browse-building-floors/src/main/AndroidManifest.xml b/samples/browse-building-floors/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a1cdede4b
--- /dev/null
+++ b/samples/browse-building-floors/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt b/samples/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt
new file mode 100644
index 000000000..0f7b23d95
--- /dev/null
+++ b/samples/browse-building-floors/src/main/java/com/esri/arcgismaps/sample/browsebuildingfloors/MainActivity.kt
@@ -0,0 +1,186 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.browsebuildingfloors
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.PortalItem
+import com.arcgismaps.mapping.floor.FloorLevel
+import com.arcgismaps.mapping.floor.FloorManager
+import com.arcgismaps.portal.Portal
+import com.esri.arcgismaps.sample.browsebuildingfloors.databinding.BrowseBuildingFloorsActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: BrowseBuildingFloorsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.browse_building_floors_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val currentFloorTV by lazy {
+ activityMainBinding.selectedFloorTV
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // load the portal and create a map from the portal item
+ val portalItem = PortalItem(
+ Portal("https://www.arcgis.com/"),
+ "f133a698536f44c8884ad81f80b6cfc7"
+ )
+
+ // set the map to be displayed in the layout's MapView
+ val map = ArcGISMap(portalItem)
+ mapView.map = map
+
+ lifecycleScope.launch {
+ //load the portal item on the map
+ map.load().getOrElse {
+ showError("Error loading map" + it.message.toString())
+ return@launch
+ }
+
+ // load the web map's floor manager
+ val floorManager =
+ map.floorManager ?: return@launch showError("Map is not floor-aware")
+ floorManager.load().getOrElse {
+ showError("Error loading floor manager" + it.message.toString())
+ return@launch
+ }
+
+ // set up dropdown and initial floor level to ground floor
+ initializeFloorDropdown(floorManager)
+ }
+ }
+
+ /**
+ * Set and update the floor dropdown. Shows the currently selected floor
+ * and hides the other floors using [floorManager].
+ */
+ private fun initializeFloorDropdown(floorManager: FloorManager) {
+ // enable the dropdown view
+ activityMainBinding.dropdownMenu.isEnabled = true
+
+ // Select the ground floor using `verticalOrder`.
+ // The floor at index 0 might not have a vertical order of 0 if,
+ // for example, the building starts with basements.
+ // To select the ground floor, we can search for a level with a
+ // `verticalOrder` of 0. You can also use level ID, number or name
+ // to locate a floor.
+ val firstFloorIndex = floorManager.levels.indexOf(
+ floorManager.levels.first { it.verticalOrder == 0 }
+ )
+
+ currentFloorTV.apply {
+ // set the displayed floor to the first floor
+ setSelection(firstFloorIndex)
+
+ // set the name of the first floor
+ setText(floorManager.levels[firstFloorIndex].longName)
+
+ // set the dropdown adapter for the floor selection
+ setAdapter(
+ FloorsAdapter(
+ this@MainActivity,
+ android.R.layout.simple_list_item_1,
+ floorManager.levels
+ )
+ )
+
+ // handle on dropdown item selected
+ onItemClickListener =
+ AdapterView.OnItemClickListener { _, _, position, _ ->
+ // set all the floors to invisible to reset the floorManager
+ floorManager.levels.forEach { floorLevel ->
+ floorLevel.isVisible = false
+ }
+
+ // set the currently selected floor to be visible
+ floorManager.levels[position].isVisible = true
+
+ // set the floor name
+ currentFloorTV.setText(floorManager.levels[position].longName)
+ }
+ }
+ }
+
+ /**
+ * Adapter to display a list [floorLevels]
+ */
+ private class FloorsAdapter(
+ context: Context,
+ @LayoutRes private val layoutResourceId: Int,
+ private val floorLevels: List
+ ) : ArrayAdapter(context, layoutResourceId, floorLevels) {
+
+ private val mLayoutInflater: LayoutInflater =
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+
+ override fun getCount(): Int {
+ return floorLevels.size
+ }
+
+ override fun getItem(position: Int): FloorLevel {
+ return floorLevels[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ // bind the view to the layout inflater
+ val view = convertView ?: mLayoutInflater.inflate(layoutResourceId, parent, false)
+ val dropdownItemTV = view.findViewById(android.R.id.text1)
+
+ // bind the long name of the floor to it's respective text view
+ dropdownItemTV.text = floorLevels[position].longName
+ return view
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/browse-building-floors/src/main/res/layout/activity_main.xml b/samples/browse-building-floors/src/main/res/layout/browse_building_floors_activity_main.xml
similarity index 100%
rename from browse-building-floors/src/main/res/layout/activity_main.xml
rename to samples/browse-building-floors/src/main/res/layout/browse_building_floors_activity_main.xml
diff --git a/samples/browse-building-floors/src/main/res/values/strings.xml b/samples/browse-building-floors/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5be303569
--- /dev/null
+++ b/samples/browse-building-floors/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Browse building floors
+ Select the floor to display
+
diff --git a/change-camera-controller/README.md b/samples/change-camera-controller/README.md
similarity index 100%
rename from change-camera-controller/README.md
rename to samples/change-camera-controller/README.md
diff --git a/change-camera-controller/README.metadata.json b/samples/change-camera-controller/README.metadata.json
similarity index 100%
rename from change-camera-controller/README.metadata.json
rename to samples/change-camera-controller/README.metadata.json
diff --git a/samples/change-camera-controller/build.gradle.kts b/samples/change-camera-controller/build.gradle.kts
new file mode 100644
index 000000000..758bffc0d
--- /dev/null
+++ b/samples/change-camera-controller/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.changecameracontroller"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/change-camera-controller/change-camera-controller.png b/samples/change-camera-controller/change-camera-controller.png
similarity index 100%
rename from change-camera-controller/change-camera-controller.png
rename to samples/change-camera-controller/change-camera-controller.png
diff --git a/change-camera-controller/src/debug/res/drawable-anydpi/ic_camera.xml b/samples/change-camera-controller/src/debug/res/drawable-anydpi/ic_camera.xml
similarity index 100%
rename from change-camera-controller/src/debug/res/drawable-anydpi/ic_camera.xml
rename to samples/change-camera-controller/src/debug/res/drawable-anydpi/ic_camera.xml
diff --git a/change-camera-controller/src/debug/res/drawable-hdpi/ic_camera.png b/samples/change-camera-controller/src/debug/res/drawable-hdpi/ic_camera.png
similarity index 100%
rename from change-camera-controller/src/debug/res/drawable-hdpi/ic_camera.png
rename to samples/change-camera-controller/src/debug/res/drawable-hdpi/ic_camera.png
diff --git a/change-camera-controller/src/debug/res/drawable-mdpi/ic_camera.png b/samples/change-camera-controller/src/debug/res/drawable-mdpi/ic_camera.png
similarity index 100%
rename from change-camera-controller/src/debug/res/drawable-mdpi/ic_camera.png
rename to samples/change-camera-controller/src/debug/res/drawable-mdpi/ic_camera.png
diff --git a/change-camera-controller/src/debug/res/drawable-xhdpi/ic_camera.png b/samples/change-camera-controller/src/debug/res/drawable-xhdpi/ic_camera.png
similarity index 100%
rename from change-camera-controller/src/debug/res/drawable-xhdpi/ic_camera.png
rename to samples/change-camera-controller/src/debug/res/drawable-xhdpi/ic_camera.png
diff --git a/change-camera-controller/src/debug/res/drawable-xxhdpi/ic_camera.png b/samples/change-camera-controller/src/debug/res/drawable-xxhdpi/ic_camera.png
similarity index 100%
rename from change-camera-controller/src/debug/res/drawable-xxhdpi/ic_camera.png
rename to samples/change-camera-controller/src/debug/res/drawable-xxhdpi/ic_camera.png
diff --git a/samples/change-camera-controller/src/main/AndroidManifest.xml b/samples/change-camera-controller/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..502d1697f
--- /dev/null
+++ b/samples/change-camera-controller/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/change-camera-controller/src/main/assets/Bristol.dae b/samples/change-camera-controller/src/main/assets/Bristol.dae
similarity index 100%
rename from change-camera-controller/src/main/assets/Bristol.dae
rename to samples/change-camera-controller/src/main/assets/Bristol.dae
diff --git a/change-camera-controller/src/main/assets/Bristol.png b/samples/change-camera-controller/src/main/assets/Bristol.png
similarity index 100%
rename from change-camera-controller/src/main/assets/Bristol.png
rename to samples/change-camera-controller/src/main/assets/Bristol.png
diff --git a/samples/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt b/samples/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt
new file mode 100644
index 000000000..d0adbc723
--- /dev/null
+++ b/samples/change-camera-controller/src/main/java/com/esri/arcgismaps/sample/changecameracontroller/MainActivity.kt
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.esri.arcgismaps.sample.changecameracontroller
+
+import android.os.Bundle
+import android.util.Log
+import android.widget.ArrayAdapter
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.ArcGISTiledElevationSource
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.symbology.ModelSceneSymbol
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.SurfacePlacement
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.OrbitLocationCameraController
+import com.arcgismaps.mapping.view.OrbitGeoElementCameraController
+import com.arcgismaps.mapping.view.GlobeCameraController
+import com.arcgismaps.mapping.view.Camera
+import com.esri.arcgismaps.sample.changecameracontroller.databinding.ChangeCameraControllerActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.ensureActive
+import java.io.File
+import java.io.FileOutputStream
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ChangeCameraControllerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.change_camera_controller_activity_main)
+ }
+
+ private val sceneView by lazy {
+ activityMainBinding.sceneView
+ }
+
+ // options dropdown view for the camera controller types
+ private val cameraControllerOptionsView by lazy {
+ // create an array adapter data source using the list of camera controller modes
+ val arrayAdapter = ArrayAdapter(
+ this,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ CameraControllerMode.getValuesByDisplayName()
+ )
+ activityMainBinding.bottomListItems.apply {
+ setAdapter(arrayAdapter)
+ }
+ }
+
+ // list of available asset files
+ private val assetFiles by lazy {
+ resources.getStringArray(R.array.asset_files).toList()
+ }
+
+ // the graphic representing the airplane 3d model
+ private val airplane3DGraphic by lazy {
+ // location for the target graphic
+ val point = Point(-109.937516, 38.456714, 5000.0, SpatialReference.wgs84())
+ // create the graphic with the target location
+ Graphic(point)
+ }
+
+ // camera controller which orbits the plane graphic
+ private val orbitPlaneCameraController by lazy {
+ // instantiate a new camera controller with a distance from airplane graphic
+ OrbitGeoElementCameraController(airplane3DGraphic, 100.0).apply {
+ // set a relative pitch to the target
+ setCameraPitchOffset(3.0)
+ // set a relative heading to the target
+ setCameraHeadingOffset(150.0)
+ }
+ }
+
+ // camera controller which orbits a target location
+ private val orbitLocationCameraController by lazy {
+ // target location for the camera controller
+ val point = Point(-109.929589, 38.437304, 1700.0, SpatialReference.wgs84())
+ // instantiate a new camera controller with a distance from the target
+ OrbitLocationCameraController(point, 5000.0).apply {
+ // set a relative pitch to the target
+ setCameraPitchOffset(3.0)
+ // set a relative heading to the target
+ setCameraHeadingOffset(150.0)
+ }
+ }
+
+ // camera controller for free roam navigation
+ private val globeCameraController = GlobeCameraController()
+
+ // camera looking at the Upheaval Dome crater in Utah
+ private val defaultCamera = Camera(
+ latitude = 38.459291,
+ longitude = -109.937576,
+ altitude = 5500.0,
+ heading = 150.0,
+ pitch = 20.0,
+ roll = 0.0
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(sceneView)
+
+ // create and add a scene with an imagery basemap style
+ val terrainScene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
+ // add an elevation data source to the base surface
+ baseSurface.elevationSources.add(
+ ArcGISTiledElevationSource(getString(R.string.elevation_service_url))
+ )
+ }
+
+ // graphics overlay for the scene to draw the 3d graphics on
+ val graphicsOverlay = GraphicsOverlay().apply {
+ // set the altitude values in the scene to be absolute
+ sceneProperties.surfacePlacement = SurfacePlacement.Absolute
+ // add the airplane 3d graphic to the graphics overlay
+ graphics.add(airplane3DGraphic)
+ }
+
+ sceneView.apply {
+ // set the scene to the SceneView
+ scene = terrainScene
+ // add the graphics overlay to the SceneView
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ lifecycleScope.launch {
+ // if the map load failed show an error and return
+ terrainScene.load().onFailure {
+ showError("Failed to load the scene: ${it.message}")
+ return@launch
+ }
+ // set the sceneView viewpoint to the default camera
+ sceneView.setViewpointCamera(defaultCamera)
+ // copy the airplane model assets to the cache directory if needed
+ copyAssetsToCache(assetFiles, cacheDir, false)
+ // load the airplane model file and update the the airplane3DGraphic
+ loadModel(getString(R.string.bristol_model_file), airplane3DGraphic)
+ }
+
+ // set the click listener for the options dropdown view
+ cameraControllerOptionsView.setOnItemClickListener { parent, _, position, _ ->
+ // get the selected camera mode item
+ val selectedItem = parent.getItemAtPosition(position) as String
+ // get the CameraControllerMode from the selected item
+ val mode = CameraControllerMode.getValue(selectedItem)
+ // update the camera controller
+ setCameraControllerMode(mode)
+ }
+ }
+
+ /**
+ * Loads a [ModelSceneSymbol] from the [filename] in [getCacheDir] and updates the [graphic].
+ */
+ private suspend fun loadModel(filename: String, graphic: Graphic) {
+ val modelFile = File(cacheDir, filename)
+ if (modelFile.exists()) {
+ // create a new ModelSceneSymbol with the file
+ val modelSceneSymbol = ModelSceneSymbol(modelFile.path).apply {
+ heading = 45f
+ }
+ // if the symbol load failed show and error and return
+ modelSceneSymbol.load().onFailure {
+ return showError("Error loading airplane model: ${it.message}")
+ }
+ // update the graphic's symbol
+ graphic.symbol = modelSceneSymbol
+ } else {
+ showError("Error loading airplane model: file does not exist.")
+ }
+ }
+
+ /**
+ * Updates the SceneView's camera controller based on the [mode] specified.
+ */
+ private fun setCameraControllerMode(mode: CameraControllerMode) {
+ sceneView.cameraController = when (mode) {
+ CameraControllerMode.OrbitPlane -> orbitPlaneCameraController
+ CameraControllerMode.OrbitLocation -> orbitLocationCameraController
+ CameraControllerMode.Globe -> globeCameraController
+ }
+ }
+
+ /**
+ * Copies the list of [assets] files from the assets folder to a given [cache] directory. This
+ * suspending function runs on the [Dispatchers.IO] context. If [overwrite] is true, any assets
+ * already in the [cache] directory are overwritten, otherwise copy is skipped.
+ */
+ private suspend fun copyAssetsToCache(
+ assets: List,
+ cache: File,
+ overwrite: Boolean
+ ) = withContext(Dispatchers.IO) {
+ // get the AssetManager
+ val assetManager = applicationContext.assets ?: return@withContext
+ assets.forEach { assetName ->
+ // check for cancellation before reading/writing the asset files
+ ensureActive()
+ try {
+ // create an output file in the cache directory
+ val outFile = File(cache, assetName)
+ // if the output file doesn't exist or overwrite is enabled
+ if (!outFile.exists() || overwrite) {
+ // create an input stream to the asset
+ assetManager.open(assetName).use { inputStream ->
+ // create an file output stream to the output file
+ FileOutputStream(outFile).use { outputStream ->
+ // copy the input file stream to the output file stream
+ inputStream.copyTo(outputStream)
+ }
+ Log.i(localClassName, "$assetName copied to cache.")
+ }
+ } else {
+ Log.i(localClassName, "$assetName already in cache, skipping copy.")
+ }
+ } catch (exception: Exception) {
+ showError("Error caching asset :${exception.message}")
+ }
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(sceneView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+// enum to keep track of the selected camera controller mode set for the SceneView
+enum class CameraControllerMode(val displayName: String) {
+ OrbitPlane("Orbit camera around a plane model"),
+ OrbitLocation("Orbit camera around a crater"),
+ Globe("Free pan around the globe");
+
+ companion object {
+ /**
+ * Returns a List containing the [displayName] property of this enum type.
+ * */
+ fun getValuesByDisplayName(): List {
+ return entries.map { cameraControllerMode ->
+ cameraControllerMode.displayName
+ }
+ }
+
+ /**
+ * Returns the enum constant of this type with the specified [displayName] property.
+ */
+ fun getValue(displayName: String): CameraControllerMode {
+ return entries.first {
+ it.displayName == displayName
+ }
+ }
+ }
+}
diff --git a/change-camera-controller/src/main/res/layout/activity_main.xml b/samples/change-camera-controller/src/main/res/layout/change_camera_controller_activity_main.xml
similarity index 100%
rename from change-camera-controller/src/main/res/layout/activity_main.xml
rename to samples/change-camera-controller/src/main/res/layout/change_camera_controller_activity_main.xml
diff --git a/samples/change-camera-controller/src/main/res/values/strings.xml b/samples/change-camera-controller/src/main/res/values/strings.xml
new file mode 100644
index 000000000..967ede0f9
--- /dev/null
+++ b/samples/change-camera-controller/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+ Change camera controller
+ https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
+
+ Bristol.dae
+ Bristol.png
+ Select camera controller
+
+
+ - @string/bristol_model_file
+ - @string/bristol_skin_file
+
+
diff --git a/change-viewpoint/README.md b/samples/change-viewpoint/README.md
similarity index 100%
rename from change-viewpoint/README.md
rename to samples/change-viewpoint/README.md
diff --git a/change-viewpoint/README.metadata.json b/samples/change-viewpoint/README.metadata.json
similarity index 100%
rename from change-viewpoint/README.metadata.json
rename to samples/change-viewpoint/README.metadata.json
diff --git a/samples/change-viewpoint/build.gradle.kts b/samples/change-viewpoint/build.gradle.kts
new file mode 100644
index 000000000..870225956
--- /dev/null
+++ b/samples/change-viewpoint/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.changeviewpoint"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/change-viewpoint/change-viewpoint.png b/samples/change-viewpoint/change-viewpoint.png
similarity index 100%
rename from change-viewpoint/change-viewpoint.png
rename to samples/change-viewpoint/change-viewpoint.png
diff --git a/samples/change-viewpoint/src/main/AndroidManifest.xml b/samples/change-viewpoint/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..dbb8295b6
--- /dev/null
+++ b/samples/change-viewpoint/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt b/samples/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt
new file mode 100644
index 000000000..4b8e55ccb
--- /dev/null
+++ b/samples/change-viewpoint/src/main/java/com/esri/arcgismaps/sample/changeviewpoint/MainActivity.kt
@@ -0,0 +1,99 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.changeviewpoint
+
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.esri.arcgismaps.sample.changeviewpoint.databinding.ChangeViewpointActivityMainBinding
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ private val viewpointScale = 5000.0
+
+ // set up data binding for the activity
+ private val activityMainBinding: ChangeViewpointActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.change_viewpoint_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // add the MapView to the lifecycle
+ lifecycle.addObserver(mapView)
+ // create and add a map with a imagery basemap style
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISImagery)
+ // set the start point of the ViewPoint
+ val startPoint = Point(-14093.0, 6711377.0, SpatialReference.webMercator())
+ lifecycleScope.launch {
+ // set viewpoint of map view to starting point and scale
+ mapView.setViewpointCenter(startPoint, viewpointScale)
+ }
+ }
+
+ fun onGeometryClicked(view: View) {
+ // create a collection of points around Westminster
+ val westminsterPolylineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
+ addPoint(Point(-13823.0, 6710390.0))
+ addPoint(Point(-13823.0, 6710150.0))
+ addPoint(Point(-14680.0, 6710390.0))
+ addPoint(Point(-14680.0, 6710150.0))
+ }
+ val geometry = westminsterPolylineBuilder.toGeometry()
+ // set the map view's viewpoint to Westminster
+ lifecycleScope.launch {
+ mapView.setViewpointGeometry(geometry)
+ }
+ }
+
+ fun onCenterClicked(view: View) {
+ // create the Waterloo location point
+ val waterlooPoint = Point(-12153.0, 6710527.0, SpatialReference.webMercator())
+ // set the map view's viewpoint centered on Waterloo and scaled
+ lifecycleScope.launch {
+ mapView.setViewpointCenter(waterlooPoint, viewpointScale)
+ }
+ }
+
+ fun onAnimateClicked(view: View) {
+ // create the London location point
+ val londonPoint = Point(-14093.0, 6711377.0, SpatialReference.webMercator())
+ // create the viewpoint with the London point and scale
+ val viewpoint = Viewpoint(londonPoint, viewpointScale)
+ // set the map view's viewpoint to London with a seven second animation duration
+ lifecycleScope.launch {
+ mapView.setViewpointAnimated(viewpoint, 7f)
+ }
+ }
+}
diff --git a/change-viewpoint/src/main/res/layout/activity_main.xml b/samples/change-viewpoint/src/main/res/layout/change_viewpoint_activity_main.xml
similarity index 100%
rename from change-viewpoint/src/main/res/layout/activity_main.xml
rename to samples/change-viewpoint/src/main/res/layout/change_viewpoint_activity_main.xml
diff --git a/samples/change-viewpoint/src/main/res/values/strings.xml b/samples/change-viewpoint/src/main/res/values/strings.xml
new file mode 100644
index 000000000..fe67be2f7
--- /dev/null
+++ b/samples/change-viewpoint/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Change viewpoint
+ Animate
+ Center
+ Geometry
+
diff --git a/clip-geometry/README.md b/samples/clip-geometry/README.md
similarity index 100%
rename from clip-geometry/README.md
rename to samples/clip-geometry/README.md
diff --git a/clip-geometry/README.metadata.json b/samples/clip-geometry/README.metadata.json
similarity index 100%
rename from clip-geometry/README.metadata.json
rename to samples/clip-geometry/README.metadata.json
diff --git a/samples/clip-geometry/build.gradle.kts b/samples/clip-geometry/build.gradle.kts
new file mode 100644
index 000000000..3dcdb97c7
--- /dev/null
+++ b/samples/clip-geometry/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.clipgeometry"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/clip-geometry/clip-geometry.png b/samples/clip-geometry/clip-geometry.png
similarity index 100%
rename from clip-geometry/clip-geometry.png
rename to samples/clip-geometry/clip-geometry.png
diff --git a/samples/clip-geometry/src/main/AndroidManifest.xml b/samples/clip-geometry/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..2e197d43e
--- /dev/null
+++ b/samples/clip-geometry/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt b/samples/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt
new file mode 100644
index 000000000..c41d8de35
--- /dev/null
+++ b/samples/clip-geometry/src/main/java/com/esri/arcgismaps/sample/clipgeometry/MainActivity.kt
@@ -0,0 +1,164 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.clipgeometry
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.clipgeometry.databinding.ClipGeometryActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ // graphics overlay along the Colorado state border
+ private val coloradoOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ // graphics overlay to contain the clipping envelopes
+ private val envelopesOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // set up data binding for the activity
+ val activityMainBinding: ClipGeometryActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.clip_geometry_activity_main)
+ // get the MapView instance from the data binding
+ val mapView = activityMainBinding.mapView
+ lifecycle.addObserver(mapView)
+
+ // create buttons to perform the clip and reset operations on the geometry
+ val clipButton = activityMainBinding.clipButton
+ val resetButton = activityMainBinding.resetButton
+
+ // create and add a map with a topographic basemap style
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ // set the viewpoint of the MapView
+ mapView.setViewpoint(Viewpoint(40.0, -106.0, 10000000.0))
+
+ // create a graphics overlay to contain the geometry to clip
+ mapView.graphicsOverlays.add(coloradoOverlay)
+ val (coloradoGraphic, fillSymbol) = createGraphics(coloradoOverlay)
+
+ // create a graphics overlay to contain the clipping envelopes
+ mapView.graphicsOverlays.add(envelopesOverlay)
+ createEnvelope(envelopesOverlay)
+
+ // create a graphics overlay to contain the clipped areas
+ val clipAreasOverlay = GraphicsOverlay()
+ mapView.graphicsOverlays.add(clipAreasOverlay)
+
+ clipButton.setOnClickListener {
+ // disable button
+ clipButton.isEnabled = false
+ resetButton.isEnabled = true
+ // for each envelope, clip the Colorado geometry and show the result,
+ // replacing the original Colorado graphic
+ coloradoGraphic.isVisible = false
+ for (graphic in envelopesOverlay.graphics) {
+ val geometry =
+ coloradoGraphic.geometry?.let { coloradoGeometry ->
+ GeometryEngine.clipOrNull(coloradoGeometry, graphic.geometry as Envelope)
+ }
+ val clippedGraphic = Graphic(geometry, fillSymbol)
+ clipAreasOverlay.graphics.add(clippedGraphic)
+ }
+ }
+
+ resetButton.setOnClickListener {
+ // clear clipped graphic
+ clipAreasOverlay.graphics.clear()
+
+ // set the visibility of the colorado graphic to true
+ coloradoGraphic.isVisible = true
+
+ clipButton.isEnabled = true
+ resetButton.isEnabled = false
+ }
+ }
+
+ /**
+ * Create colorado graphic.
+ */
+ private fun createGraphics(
+ coloradoOverlay: GraphicsOverlay,
+ ) : Pair{
+ // create a blue graphic of Colorado
+ val colorado = Envelope(
+ Point(-11362327.128340, 5012861.290274),
+ Point(-12138232.018408, 4441198.773776)
+ )
+ val fillSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.Solid,
+ Color(R.color.transparentDarkBlue),
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
+ )
+ val coloradoGraphic = Graphic(colorado, fillSymbol)
+ coloradoOverlay.graphics.add(coloradoGraphic)
+ return Pair(coloradoGraphic, fillSymbol)
+ }
+
+ /**
+ * Create three envelopes, outside, inside and intersecting colorado graphic.
+ */
+ private fun createEnvelope(
+ envelopesOverlay: GraphicsOverlay,
+ ) {
+ // create a dotted red outline symbol
+ val redOutline = SimpleLineSymbol(SimpleLineSymbolStyle.Dot, Color.red, 3f)
+
+ // create a envelope outside Colorado
+ val outsideEnvelope = Envelope(
+ Point(-11858344.321294, 5147942.225174),
+ Point(-12201990.219681, 5297071.577304)
+ )
+ val outside = Graphic(outsideEnvelope, redOutline)
+ envelopesOverlay.graphics.add(outside)
+
+ // create a envelope intersecting Colorado
+ val intersectingEnvelope = Envelope(
+ Point(-11962086.479298, 4566553.881363),
+ Point(-12260345.183558, 4332053.378376)
+ )
+ val intersecting = Graphic(intersectingEnvelope, redOutline)
+ envelopesOverlay.graphics.add(intersecting)
+
+ // create a envelope inside Colorado
+ val containedEnvelope = Envelope(
+ Point(-11655182.595204, 4741618.772994),
+ Point(-11431488.567009, 4593570.068343)
+ )
+ val contained = Graphic(containedEnvelope, redOutline)
+ envelopesOverlay.graphics.add(contained)
+ }
+}
diff --git a/clip-geometry/src/main/res/layout/activity_main.xml b/samples/clip-geometry/src/main/res/layout/clip_geometry_activity_main.xml
similarity index 100%
rename from clip-geometry/src/main/res/layout/activity_main.xml
rename to samples/clip-geometry/src/main/res/layout/clip_geometry_activity_main.xml
diff --git a/clip-geometry/src/main/res/values/colors.xml b/samples/clip-geometry/src/main/res/values/colors.xml
similarity index 100%
rename from clip-geometry/src/main/res/values/colors.xml
rename to samples/clip-geometry/src/main/res/values/colors.xml
diff --git a/samples/clip-geometry/src/main/res/values/strings.xml b/samples/clip-geometry/src/main/res/values/strings.xml
new file mode 100644
index 000000000..0e8586fbd
--- /dev/null
+++ b/samples/clip-geometry/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Clip geometry
+ Clip geometry
+ Reset
+
diff --git a/configure-basemap-style-parameters/README.md b/samples/configure-basemap-style-parameters/README.md
similarity index 100%
rename from configure-basemap-style-parameters/README.md
rename to samples/configure-basemap-style-parameters/README.md
diff --git a/configure-basemap-style-parameters/README.metadata.json b/samples/configure-basemap-style-parameters/README.metadata.json
similarity index 100%
rename from configure-basemap-style-parameters/README.metadata.json
rename to samples/configure-basemap-style-parameters/README.metadata.json
diff --git a/samples/configure-basemap-style-parameters/build.gradle.kts b/samples/configure-basemap-style-parameters/build.gradle.kts
new file mode 100644
index 000000000..42e433e14
--- /dev/null
+++ b/samples/configure-basemap-style-parameters/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.configurebasemapstyleparameters"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/configure-basemap-style-parameters/configure-basemap-style-parameters.png b/samples/configure-basemap-style-parameters/configure-basemap-style-parameters.png
similarity index 100%
rename from configure-basemap-style-parameters/configure-basemap-style-parameters.png
rename to samples/configure-basemap-style-parameters/configure-basemap-style-parameters.png
diff --git a/samples/configure-basemap-style-parameters/src/main/AndroidManifest.xml b/samples/configure-basemap-style-parameters/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/configure-basemap-style-parameters/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt b/samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt
new file mode 100644
index 000000000..a1967b411
--- /dev/null
+++ b/samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/MainActivity.kt
@@ -0,0 +1,54 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.configurebasemapstyleparameters
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.configurebasemapstyleparameters.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.configure_basemap_style_parameters_app_name)
+ )
+ }
+ }
+}
diff --git a/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/components/MapViewModel.kt b/samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/components/MapViewModel.kt
similarity index 100%
rename from configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/components/MapViewModel.kt
rename to samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/components/MapViewModel.kt
diff --git a/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/screens/MainScreen.kt b/samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/screens/MainScreen.kt
similarity index 100%
rename from configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/screens/MainScreen.kt
rename to samples/configure-basemap-style-parameters/src/main/java/com/esri/arcgismaps/sample/configurebasemapstyleparameters/screens/MainScreen.kt
diff --git a/samples/configure-basemap-style-parameters/src/main/res/values/strings.xml b/samples/configure-basemap-style-parameters/src/main/res/values/strings.xml
new file mode 100644
index 000000000..7fe6e30d4
--- /dev/null
+++ b/samples/configure-basemap-style-parameters/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Configure basemap style parameters
+
diff --git a/configure-clusters/README.md b/samples/configure-clusters/README.md
similarity index 100%
rename from configure-clusters/README.md
rename to samples/configure-clusters/README.md
diff --git a/configure-clusters/README.metadata.json b/samples/configure-clusters/README.metadata.json
similarity index 100%
rename from configure-clusters/README.metadata.json
rename to samples/configure-clusters/README.metadata.json
diff --git a/samples/configure-clusters/build.gradle.kts b/samples/configure-clusters/build.gradle.kts
new file mode 100644
index 000000000..f911ecea4
--- /dev/null
+++ b/samples/configure-clusters/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.configureclusters"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/configure-clusters/configure-clusters.png b/samples/configure-clusters/configure-clusters.png
similarity index 100%
rename from configure-clusters/configure-clusters.png
rename to samples/configure-clusters/configure-clusters.png
diff --git a/samples/configure-clusters/src/main/AndroidManifest.xml b/samples/configure-clusters/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/configure-clusters/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt
new file mode 100644
index 000000000..dd94289a7
--- /dev/null
+++ b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.configureclusters
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.configureclusters.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ ConfigureClustersApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ConfigureClustersApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.configure_clusters_app_name)
+ )
+ }
+ }
+}
diff --git a/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/components/MapViewModel.kt b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/components/MapViewModel.kt
similarity index 100%
rename from configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/components/MapViewModel.kt
rename to samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/components/MapViewModel.kt
diff --git a/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt
new file mode 100644
index 000000000..e5b516c22
--- /dev/null
+++ b/samples/configure-clusters/src/main/java/com/esri/arcgismaps/sample/configureclusters/screens/MainScreen.kt
@@ -0,0 +1,395 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.configureclusters.screens
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material.icons.rounded.Close
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuAnchorType
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SheetState
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.configureclusters.components.MapViewModel
+import com.esri.arcgismaps.sample.sampleslib.components.BottomSheet
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleTypography
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlin.math.roundToInt
+
+/**
+ * Main screen layout for the sample app
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MainScreen(sampleName: String) {
+ // create a ViewModel to handle MapView interactions
+ val mapViewModel: MapViewModel = viewModel()
+
+ val composableScope = rememberCoroutineScope()
+
+ Scaffold(
+ topBar = { SampleTopAppBar(title = sampleName) },
+ content = {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it),
+ contentAlignment = Alignment.Center
+ ) {
+ var mapScale by remember { mutableIntStateOf(0) }
+ MapView(
+ modifier = Modifier
+ .fillMaxSize(),
+ mapViewProxy = mapViewModel.mapViewProxy,
+ // identify on single tap
+ onSingleTapConfirmed = { singleTapConfirmedEvent ->
+ mapViewModel.identify(singleTapConfirmedEvent)
+ },
+ arcGISMap = mapViewModel.arcGISMap,
+ // update the map scale in the UI on map scale change
+ onMapScaleChanged = { currentMapScale ->
+ if (!currentMapScale.isNaN()) {
+ mapScale = currentMapScale.roundToInt()
+ }
+ },
+ )
+
+ val controlsBottomSheetState =
+ rememberModalBottomSheetState(skipPartiallyExpanded = true)
+ // show the "Show controls" button only when the bottom sheet is not visible
+ if (!controlsBottomSheetState.isVisible) {
+ FloatingActionButton(
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .padding(bottom = 36.dp, end = 24.dp),
+ onClick = {
+ composableScope.launch {
+ controlsBottomSheetState.show()
+ }
+ },
+ ) {
+ Icon(Icons.Filled.Settings, contentDescription = "Show controls")
+ }
+ }
+ if (controlsBottomSheetState.isVisible) {
+ ClusterControlsBottomSheet(
+ composableScope = composableScope,
+ controlsBottomSheetState = controlsBottomSheetState,
+ showClusterLabels = mapViewModel.showClusterLabels,
+ updateClusterLabelState = mapViewModel::updateShowClusterLabelState,
+ clusterRadiusOptions = mapViewModel.clusterRadiusOptions,
+ clusterRadius = mapViewModel.clusterRadius,
+ updateClusterRadiusState = mapViewModel::updateClusterRadiusState,
+ clusterMaxScaleOptions = mapViewModel.clusterMaxScaleOptions,
+ clusterMaxScale = mapViewModel.clusterMaxScale,
+ updateClusterMaxScaleState = mapViewModel::updateClusterMaxScaleState,
+ mapScale = mapScale
+ )
+ }
+ }
+
+ // display a bottom sheet to show popup details
+ BottomSheet(
+ isVisible = mapViewModel.showPopUpContent,
+ bottomSheetContent = {
+ ClusterInfoContent(
+ popUpTitle = mapViewModel.popUpTitle,
+ popUpInfo = mapViewModel.popUpInfo,
+ onDismiss = { mapViewModel.updateShowPopUpContentState(false) }
+ )
+ })
+
+ }
+ )
+}
+
+/**
+ * Composable function to display the cluster controls bottom sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ClusterControlsBottomSheet(
+ composableScope: CoroutineScope,
+ controlsBottomSheetState: SheetState,
+ showClusterLabels: Boolean,
+ updateClusterLabelState: (Boolean) -> Unit,
+ clusterRadiusOptions: List,
+ clusterRadius: Int,
+ updateClusterRadiusState: (Int) -> Unit,
+ clusterMaxScaleOptions: List,
+ clusterMaxScale: Int,
+ updateClusterMaxScaleState: (Int) -> Unit,
+ mapScale: Int,
+) {
+ ModalBottomSheet(
+ modifier = Modifier.wrapContentHeight(),
+ sheetState = controlsBottomSheetState,
+ onDismissRequest = {
+ composableScope.launch {
+ controlsBottomSheetState.hide()
+ }
+ }) {
+ Column(
+ Modifier
+ .padding(12.dp)
+ .navigationBarsPadding()) {
+ Text(
+ "Cluster labels visibility:",
+ style = MaterialTheme.typography.titleMedium
+ )
+ Spacer(Modifier.size(8.dp))
+ Row(
+ Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text("Show labels")
+ Switch(
+ checked = showClusterLabels,
+ onCheckedChange = { showClusterLabels ->
+ updateClusterLabelState(
+ showClusterLabels
+ )
+ }
+ )
+ }
+ Spacer(Modifier.size(8.dp))
+ Row(
+ Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text("Current map scale:")
+ Text("1:$mapScale")
+ }
+ HorizontalDivider(Modifier.padding(vertical = 12.dp, horizontal = 8.dp))
+ Text(
+ "Clustering properties:",
+ style = MaterialTheme.typography.titleMedium
+ )
+ Spacer(Modifier.size(8.dp))
+ ClusterRadiusControls(
+ clusterRadiusOptions,
+ clusterRadius,
+ updateClusterRadiusState
+ )
+ Spacer(Modifier.size(8.dp))
+ ClusterMaxScaleControls(
+ clusterMaxScaleOptions,
+ clusterMaxScale,
+ updateClusterMaxScaleState
+ )
+ }
+ }
+}
+
+/**
+ * Composable function to display the cluster radius controls within the cluster controls bottom
+ * sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ClusterRadiusControls(
+ clusterRadiusOptions: List,
+ clusterRadius: Int,
+ updateClusterRadius: (Int) -> Unit
+) {
+ Row(
+ Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ "Cluster radius",
+ modifier = Modifier.padding(8.dp)
+ )
+ var expanded by rememberSaveable { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ modifier = Modifier.width(150.dp),
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ TextField(
+ value = clusterRadius.toString(),
+ onValueChange = {},
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
+ modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable)
+ )
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false }
+ ) {
+ clusterRadiusOptions.forEachIndexed { index, clusterRadius ->
+ DropdownMenuItem(
+ text = { Text(clusterRadius.toString()) },
+ onClick = {
+ updateClusterRadius(index)
+ expanded = false
+ })
+ // show a divider between dropdown menu options
+ if (index < clusterRadiusOptions.lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Composable function to display the cluster max scale controls within the cluster controls bottom
+ * sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ClusterMaxScaleControls(
+ clusterMaxScaleOptions: List,
+ clusterMaxScale: Int,
+ updateClusterMaxScale: (Int) -> Unit
+) {
+ Row(
+ Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ "Cluster max scale",
+ modifier = Modifier.padding(8.dp)
+ )
+ var expanded by rememberSaveable { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ modifier = Modifier.width(150.dp),
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ TextField(
+ value = clusterMaxScale.toString(),
+ onValueChange = {},
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
+ modifier = Modifier.menuAnchor(type = MenuAnchorType.PrimaryNotEditable)
+ )
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false }
+ ) {
+ clusterMaxScaleOptions.forEachIndexed { index, clusterRadius ->
+ DropdownMenuItem(
+ text = { Text(clusterRadius.toString()) },
+ onClick = {
+ updateClusterMaxScale(index)
+ expanded = false
+ })
+ // show a divider between dropdown menu options
+ if (index < clusterMaxScaleOptions.lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Composable function to display the cluster info content from the pop up within a bottom sheet.
+ */
+@Composable
+private fun ClusterInfoContent(
+ popUpTitle: String,
+ popUpInfo: Map,
+ onDismiss: () -> Unit
+) {
+ Column(Modifier.background(MaterialTheme.colorScheme.background)) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 30.dp, vertical = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ text = popUpTitle.ifEmpty { "Cluster Info:" },
+ style = SampleTypography.headlineSmall
+ )
+ IconButton(
+ onClick = onDismiss
+ ) {
+ Icon(
+ imageVector = Icons.Rounded.Close,
+ contentDescription = "Close button"
+ )
+ }
+ }
+ popUpInfo.forEach {
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = 30.dp,
+ vertical = 8.dp
+ ),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(text = "${it.key}:", style = MaterialTheme.typography.labelMedium)
+ Text(text = "${it.value}")
+ }
+ }
+ Spacer(modifier = Modifier.size(24.dp))
+ }
+}
diff --git a/samples/configure-clusters/src/main/res/values/strings.xml b/samples/configure-clusters/src/main/res/values/strings.xml
new file mode 100644
index 000000000..19ca592ba
--- /dev/null
+++ b/samples/configure-clusters/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Configure clusters
+
diff --git a/samples/create-and-save-map/README.md b/samples/create-and-save-map/README.md
new file mode 100644
index 000000000..3978ff69f
--- /dev/null
+++ b/samples/create-and-save-map/README.md
@@ -0,0 +1,35 @@
+# Create and save map
+
+Create and save a map as a web map item to an ArcGIS portal.
+
+![Image of create and save map](create-and-save-map.png)
+
+## Use case
+
+Maps can be created programmatically in code and then serialized and saved as an ArcGIS portal item. In this case, the portal item is a web map which can be shared with others and opened in various applications and APIs throughout the platform, such as ArcGIS Pro, ArcGIS Online, the JavaScript API, Collector, and Explorer.
+
+## How to use the sample
+
+When you run the sample, you will be challenged for an ArcGIS Online login. Enter a username and password for an ArcGIS Online named user account (such as your ArcGIS for Developers account). Then, tap the Edit Map button to choose the basemap and layers for your new map. To save the map, add a title, tags and description (optional), and a folder on your portal (you will need to create one in your portal's My Content section if you don't already have one). Click the Save to Account button to save the map to the chosen folder.
+
+## How it works
+
+1. Configure an `AuthenticatorState` to handle authentication challenges.
+2. Add the `AuthenticatorState` to a `DialogAuthenticator` in the app's `MainActivity` to invoke the authentication challenge.
+3. Create a new `Portal` and load it.
+4. Access the `PortalUserContent` with `portal.portalInfo?.user?.fetchContent()?.onSuccess`, to get the user's list of portal folders with `portalUserContent.folders`.
+5. Create an `ArcGISMap` with a `BasemapStyle` and a few operational layers.
+6. Call `ArcGISMap.saveMap()` to save a new `ArcGISMap` with the specified title, tags, and folder to the portal.
+
+## Relevant API
+
+* ArcGISMap
+* Portal
+
+## Additional information
+
+This sample uses the GeoViewCompose Toolkit module to be able to implement a Composable MapView.
+
+## Tags
+
+ArcGIS Online, ArcGIS Pro, geoviewcompose, portal, publish, share, web map
diff --git a/samples/create-and-save-map/README.metadata.json b/samples/create-and-save-map/README.metadata.json
new file mode 100644
index 000000000..7e906b602
--- /dev/null
+++ b/samples/create-and-save-map/README.metadata.json
@@ -0,0 +1,32 @@
+{
+ "category": "Maps",
+ "description": "Create and save a map as a web map item to an ArcGIS portal.",
+ "formal_name": "CreateAndSaveMap",
+ "ignore": false,
+ "images": [
+ "create-and-save-map.png"
+ ],
+ "keywords": [
+ "ArcGIS Online",
+ "ArcGIS Pro",
+ "geoviewcompose",
+ "portal",
+ "publish",
+ "share",
+ "web map",
+ "ArcGISMap",
+ "Portal"
+ ],
+ "language": "kotlin",
+ "redirect_from": "",
+ "relevant_apis": [
+ "ArcGISMap",
+ "Portal"
+ ],
+ "snippets": [
+ "src/main/java/com/esri/arcgismaps/sample/createandsavemap/MainActivity.kt",
+ "src/main/java/com/esri/arcgismaps/sample/createandsavemap/components/MapViewModel.kt",
+ "src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt"
+ ],
+ "title": "Create and save map"
+}
diff --git a/samples/create-and-save-map/build.gradle.kts b/samples/create-and-save-map/build.gradle.kts
new file mode 100644
index 000000000..6374698ba
--- /dev/null
+++ b/samples/create-and-save-map/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.createandsavemap"
+ // For view based samples
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
+}
diff --git a/samples/create-and-save-map/create-and-save-map.png b/samples/create-and-save-map/create-and-save-map.png
new file mode 100644
index 000000000..6b4a2df50
Binary files /dev/null and b/samples/create-and-save-map/create-and-save-map.png differ
diff --git a/samples/create-and-save-map/src/main/AndroidManifest.xml b/samples/create-and-save-map/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..3feeefb87
--- /dev/null
+++ b/samples/create-and-save-map/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/MainActivity.kt b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/MainActivity.kt
new file mode 100644
index 000000000..705038e9f
--- /dev/null
+++ b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/MainActivity.kt
@@ -0,0 +1,56 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createandsavemap
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.arcgismaps.toolkit.authentication.DialogAuthenticator
+import com.esri.arcgismaps.sample.createandsavemap.components.MapViewModel
+import com.esri.arcgismaps.sample.createandsavemap.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ val mapViewModel: MapViewModel = viewModel()
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.create_and_save_map_app_name)
+ )
+ }
+
+ // authenticator at bottom can draw over the top of the sample
+ DialogAuthenticator(authenticatorState = mapViewModel.authenticatorState)
+ }
+}
diff --git a/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/components/MapViewModel.kt b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/components/MapViewModel.kt
new file mode 100644
index 000000000..d3c347af5
--- /dev/null
+++ b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/components/MapViewModel.kt
@@ -0,0 +1,178 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createandsavemap.components
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+import android.app.Application
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.viewModelScope
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.AndroidViewModel
+
+import com.arcgismaps.httpcore.authentication.OAuthUserConfiguration
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.ArcGISMapImageLayer
+import com.arcgismaps.mapping.layers.Layer
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.portal.PortalFolder
+import com.arcgismaps.toolkit.authentication.AuthenticatorState
+import com.esri.arcgismaps.sample.createandsavemap.R
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+
+class MapViewModel(application: Application) : AndroidViewModel(application) {
+
+ // view model to handle popup dialogs
+ val messageDialogVM = MessageDialogViewModel()
+
+ // set up authenticator state to handle authentication challenges
+ val authenticatorState = AuthenticatorState().apply {
+ oAuthUserConfiguration = OAuthUserConfiguration(
+ portalUrl = application.getString(R.string.portal_url),
+ clientId = application.getString(R.string.client_id),
+ redirectUrl = application.getString(R.string.redirect_url)
+ )
+ }
+
+ // require use of user credential to load portal
+ private val portal = Portal("https://www.arcgis.com", Portal.Connection.Authenticated)
+
+ // update displayed map once user is authenticated
+ var arcGISMap by mutableStateOf(ArcGISMap())
+
+ // folders on portal associated with the authenticated user
+ private val _portalFolders = MutableStateFlow>(listOf())
+ val portalFolders = _portalFolders.asStateFlow()
+
+
+ // define a couple of feature layers to be loaded
+ private val worldElevation =
+ ArcGISMapImageLayer("https://sampleserver6.arcgisonline.com/arcgis/rest/services/WorldTimeZones/MapServer")
+ private val usCensus =
+ ArcGISMapImageLayer("https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer")
+ val availableLayers: List = listOf(worldElevation, usCensus)
+
+ // associate basemap styles with friendly names
+ val stylesMap: Map = mapOf(
+ Pair("Streets", BasemapStyle.ArcGISStreets),
+ Pair("Imagery", BasemapStyle.ArcGISImageryStandard),
+ Pair("Topographic", BasemapStyle.ArcGISTopographic),
+ Pair("Oceans", BasemapStyle.ArcGISOceans)
+ )
+
+ // properties hoisted from UI bottom sheet
+
+ var selectedBasemapStyle by mutableStateOf("Streets")
+ private set
+
+ fun updateBasemapStyle(style: String) {
+ selectedBasemapStyle = style
+
+ // update map to display selected basemap style
+ arcGISMap.setBasemap(Basemap(stylesMap.getValue(selectedBasemapStyle)))
+ }
+
+ fun updateActiveLayers(layer: Layer) {
+ arcGISMap.operationalLayers.apply {
+ if (contains(layer)) {
+ remove(layer)
+ } else {
+ add(layer)
+ }
+ }
+ }
+
+ var mapName by mutableStateOf("My Map")
+ private set
+
+ fun updateName(name: String) {
+ mapName = name
+ }
+
+ var mapTags by mutableStateOf("map, census, layers")
+ private set
+
+ fun updateTags(tags: String) {
+ mapTags = tags
+ }
+
+ var portalFolder by mutableStateOf(null)
+
+ fun updateFolder(folder: PortalFolder?) {
+ portalFolder = folder
+ }
+
+ var mapDescription by mutableStateOf("")
+ private set
+
+ fun updateDescription(description: String) {
+ mapDescription = description
+ }
+
+ init {
+ viewModelScope.launch {
+ portal.load().onSuccess {
+ // when the user has successfully authenticated and the portal is loaded...
+
+ // populate the portal folders flow
+ portal.portalInfo?.apply {
+ this.user?.fetchContent()?.onSuccess {
+ _portalFolders.value = it.folders
+ }
+ }
+
+ // load the streets basemap and set an initial viewpoint
+ arcGISMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
+ initialViewpoint = Viewpoint(38.85, -90.2, 1e7)
+ }
+
+ // load operational layers
+ worldElevation.load()
+ usCensus.load()
+ }.onFailure {
+ // login was cancelled or failed to authenticate
+ messageDialogVM.showMessageDialog(
+ application.getString(R.string.createAndSaveMap_failedToLoadPortal),
+ it.message.toString()
+ )
+ }
+ }
+ }
+
+ /**
+ * Saves the map to a user's account.
+ */
+ suspend fun save(): Result {
+ return arcGISMap.saveAs(
+ portal,
+ description = mapDescription,
+ folder = portalFolder,
+ tags = mapTags.split(",").map { str -> str.trim() },
+ forceSaveToSupportedVersion = false,
+ thumbnail = null,
+ title = mapName
+ )
+ }
+
+
+}
\ No newline at end of file
diff --git a/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt
new file mode 100644
index 000000000..35bd75098
--- /dev/null
+++ b/samples/create-and-save-map/src/main/java/com/esri/arcgismaps/sample/createandsavemap/screens/MainScreen.kt
@@ -0,0 +1,431 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createandsavemap.screens
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material3.BottomSheetScaffold
+import androidx.compose.material3.Button
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuAnchorType
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberBottomSheetScaffoldState
+import androidx.compose.material3.rememberStandardBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.layers.Layer
+import com.arcgismaps.portal.PortalFolder
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.createandsavemap.components.MapViewModel
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Main screen layout for the sample app
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MainScreen(sampleName: String) {
+ // create a ViewModel to handle MapView interactions
+ val mapViewModel: MapViewModel = viewModel()
+
+ val composableScope = rememberCoroutineScope()
+ val snackbarHostState = remember { SnackbarHostState() }
+ val controlsBottomSheetState = rememberBottomSheetScaffoldState(
+ bottomSheetState = rememberStandardBottomSheetState(skipHiddenState = false).apply {
+ composableScope.launch { hide() }
+ }
+ )
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
+ topBar = { SampleTopAppBar(title = sampleName) },
+ content = {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it),
+ contentAlignment = Alignment.Center
+ ) {
+ // map view not shown until the portal is loaded and a basemap has been set
+ MapView(
+ arcGISMap = mapViewModel.arcGISMap,
+ Modifier.fillMaxSize(),
+ )
+
+ // show the "Edit map" button only when the bottom sheet is not visible
+ if (!controlsBottomSheetState.bottomSheetState.isVisible) {
+ FloatingActionButton(
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .padding(bottom = 36.dp, end = 24.dp),
+ onClick = {
+ composableScope.launch {
+ controlsBottomSheetState.bottomSheetState.show()
+ }
+ })
+ {
+ Icon(Icons.Filled.Edit, contentDescription = "Edit map button")
+ Spacer(Modifier.padding(8.dp))
+ }
+ }
+
+ BottomSheetScaffold(
+ modifier = Modifier.wrapContentHeight(),
+ scaffoldState = controlsBottomSheetState,
+ sheetPeekHeight = LocalConfiguration.current.screenHeightDp.dp.times(0.33f),
+ sheetContent = {
+ Column(
+ Modifier
+ .padding(12.dp)
+ .navigationBarsPadding()
+ .verticalScroll(rememberScrollState())
+ ) {
+ CreateMapBottomSheet(
+ mapViewModel = mapViewModel
+ )
+
+ Button(
+ modifier = Modifier
+ .align(Alignment.CenterHorizontally)
+ .padding(12.dp),
+ onClick = {
+ composableScope.launch {
+ // dismiss bottom sheet immediately
+ controlsBottomSheetState.bottomSheetState.hide()
+
+ // report success in a snack bar, failure in a popup
+ mapViewModel.save().onSuccess {
+ snackbarHostState.showSnackbar(
+ "Map saved to portal.",
+ withDismissAction = true
+ )
+ }.onFailure { err ->
+ mapViewModel.messageDialogVM.showMessageDialog(
+ "Error",
+ err.message.toString()
+ )
+ }
+ }
+ }) {
+ Text("Save to account")
+ }
+ }
+ Spacer(Modifier.size(8.dp))
+ }
+ ) {}
+ }
+
+ // message dialog can draw over all other content in the main screen
+ mapViewModel.messageDialogVM.apply {
+ if (dialogStatus) {
+ MessageDialog(
+ title = messageTitle,
+ description = messageDescription,
+ onDismissRequest = ::dismissDialog
+ )
+ }
+ }
+ }
+ )
+}
+
+/**
+ * Composable function to populate bottom sheet with sample options.
+ */
+@Composable
+fun CreateMapBottomSheet(
+ mapViewModel: MapViewModel
+) {
+
+ Column(
+ modifier = Modifier.padding(vertical = 8.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = "Choose map settings:",
+ style = MaterialTheme.typography.titleMedium
+ )
+ BasemapDropdown(
+ mapViewModel.selectedBasemapStyle,
+ mapViewModel.stylesMap,
+ mapViewModel::updateBasemapStyle
+ )
+
+ LayersDropdown(
+ mapViewModel.availableLayers,
+ mapViewModel.arcGISMap,
+ mapViewModel::updateActiveLayers
+ )
+
+ HorizontalDivider(Modifier.padding(vertical = 12.dp, horizontal = 8.dp))
+
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = mapViewModel.mapName,
+ onValueChange = { newName -> mapViewModel.updateName(newName) },
+ label = { Text(text = "Enter map name:") },
+ singleLine = true,
+ )
+
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = mapViewModel.mapTags,
+ onValueChange = { newTags -> mapViewModel.updateTags(newTags) },
+ label = { Text(text = "Tags:") },
+ singleLine = true,
+ )
+
+ FolderDropdown(
+ mapViewModel.portalFolder,
+ mapViewModel.portalFolders,
+ mapViewModel::updateFolder
+ )
+
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = mapViewModel.mapDescription,
+ onValueChange = { newDescription -> mapViewModel.updateDescription(newDescription) },
+ label = { Text(text = "Description:") },
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done)
+ )
+ }
+}
+
+/**
+ * Composable function to display portal folder dropdown in the bottom sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun FolderDropdown(
+ currentFolder: PortalFolder?,
+ portalFolders: StateFlow>,
+ updateFolder: (PortalFolder?) -> Unit
+) {
+
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ ExposedDropdownMenuBox(
+ modifier = Modifier.fillMaxWidth(),
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ var label by remember {
+ mutableStateOf(
+ currentFolder?.title ?: "(No folder)"
+ )
+ }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .menuAnchor(type = MenuAnchorType.PrimaryNotEditable),
+ value = label,
+ onValueChange = { newDescription -> label = newDescription },
+ label = { Text(text = "Folder:") },
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }
+ )
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ Modifier.padding(vertical = 0.dp)
+ ) {
+ val folders by portalFolders.collectAsState(initial = listOf())
+ if (folders.isEmpty()) {
+ Text("No folders to display", Modifier.padding(8.dp))
+ } else {
+ DropdownMenuItem(
+ text = { Text("(No folder)") },
+ onClick = {
+ updateFolder(null)
+ label = "(No folder)"
+ expanded = false
+ }
+ )
+ HorizontalDivider()
+ folders.forEachIndexed { index, folder ->
+ DropdownMenuItem(
+ text = { Text(folder.title) },
+ onClick = {
+ updateFolder(folder)
+ label = folder.title
+ expanded = false
+ })
+ // show a divider between dropdown menu options
+ if (index < folders.lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+ }
+
+}
+
+/**
+ * Composable function to display basemap dropdown in the bottom sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun BasemapDropdown(
+ basemapStyle: String,
+ stylesNameMap: Map,
+ updateBasemapStyle: (String) -> Unit
+) {
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ ExposedDropdownMenuBox(
+ modifier = Modifier.fillMaxWidth(),
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .menuAnchor(type = MenuAnchorType.PrimaryNotEditable),
+ value = basemapStyle,
+ onValueChange = {},
+ label = { Text(text = "Basemap Style:") },
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }
+ )
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ Modifier.padding(vertical = 0.dp)
+ ) {
+
+ stylesNameMap.keys.toList().forEachIndexed { index, basemapName ->
+ DropdownMenuItem(
+ text = { Text(basemapName) },
+ onClick = {
+ updateBasemapStyle(basemapName)
+ expanded = false
+ })
+ // show a divider between dropdown menu options
+ if (index < stylesNameMap.keys.toList().lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Composable function to display active layers dropdown in the bottom sheet.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun LayersDropdown(
+ availableLayers: List,
+ arcGISMap: ArcGISMap,
+ updateActiveLayers: (Layer) -> Unit
+
+) {
+ var expanded by rememberSaveable { mutableStateOf(false) }
+
+ ExposedDropdownMenuBox(
+ modifier = Modifier.fillMaxWidth(),
+ expanded = expanded,
+ onExpandedChange = { expanded = !expanded }
+ ) {
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .menuAnchor(type = MenuAnchorType.PrimaryNotEditable),
+ value = "Select...",
+ onValueChange = {},
+ label = { Text(text = "Operational Layers:") },
+ readOnly = true,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }
+ )
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false }
+ ) {
+ availableLayers.forEachIndexed { index, layer ->
+ var checked by
+ mutableStateOf(
+ arcGISMap.operationalLayers.contains(layer)
+ )
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(8.dp)
+ ) {
+ Text(layer.name, Modifier.weight(1f))
+ Checkbox(
+ checked = checked,
+ onCheckedChange = {
+ updateActiveLayers(layer)
+ checked = !checked
+ }
+ )
+ }
+ // show a divider between dropdown menu options
+ if (index < availableLayers.lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ }
+ }
+}
diff --git a/samples/create-and-save-map/src/main/res/values/strings.xml b/samples/create-and-save-map/src/main/res/values/strings.xml
new file mode 100644
index 000000000..10deb976f
--- /dev/null
+++ b/samples/create-and-save-map/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Create and save map
+ https://www.arcgis.com
+ InMihrA8yZXBALCv
+ create-save-map://auth
+ Failed to load portal
+
\ No newline at end of file
diff --git a/create-convex-hull-around-points/README.md b/samples/create-convex-hull-around-points/README.md
similarity index 100%
rename from create-convex-hull-around-points/README.md
rename to samples/create-convex-hull-around-points/README.md
diff --git a/create-convex-hull-around-points/README.metadata.json b/samples/create-convex-hull-around-points/README.metadata.json
similarity index 100%
rename from create-convex-hull-around-points/README.metadata.json
rename to samples/create-convex-hull-around-points/README.metadata.json
diff --git a/samples/create-convex-hull-around-points/build.gradle.kts b/samples/create-convex-hull-around-points/build.gradle.kts
new file mode 100644
index 000000000..093df6d25
--- /dev/null
+++ b/samples/create-convex-hull-around-points/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.createconvexhullaroundpoints"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/create-convex-hull-around-points/create-convex-hull-around-points.png b/samples/create-convex-hull-around-points/create-convex-hull-around-points.png
similarity index 100%
rename from create-convex-hull-around-points/create-convex-hull-around-points.png
rename to samples/create-convex-hull-around-points/create-convex-hull-around-points.png
diff --git a/samples/create-convex-hull-around-points/src/main/AndroidManifest.xml b/samples/create-convex-hull-around-points/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..920521906
--- /dev/null
+++ b/samples/create-convex-hull-around-points/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt b/samples/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt
new file mode 100644
index 000000000..f215b3238
--- /dev/null
+++ b/samples/create-convex-hull-around-points/src/main/java/com/esri/arcgismaps/sample/createconvexhullaroundpoints/MainActivity.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createconvexhullaroundpoints
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Multipoint
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.createconvexhullaroundpoints.databinding.CreateConvexHullAroundPointsActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: CreateConvexHullAroundPointsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.create_convex_hull_around_points_activity_main)
+ }
+
+ // setup binding for the MapView
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // action button that creates the canvas hull
+ private val createButton by lazy {
+ activityMainBinding.createButton
+ }
+
+ // action button to reset the map
+ private val resetButton by lazy {
+ activityMainBinding.resetButton
+ }
+
+ // a red marker symbol for points
+ private val pointSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10f)
+
+ // a blue line symbol
+ private val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 3f)
+
+ // a fill symbol with an empty fill for polygons
+ private val fillSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Null, Color.red, lineSymbol)
+
+ // set up the point graphic with point symbol
+ private val pointGraphic = Graphic(symbol = pointSymbol)
+
+ // init the convex hull graphic
+ private val convexHullGraphic = Graphic()
+
+ // create a graphics overlay to draw all graphics
+ private val graphicsOverlay = GraphicsOverlay()
+
+ // list to store the selected map points
+ private val inputPoints = mutableListOf()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // add point and convex hull graphics to the graphics overlay
+ graphicsOverlay.graphics.addAll(listOf(pointGraphic, convexHullGraphic))
+
+ // create and add a map with topographic basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
+ // set a default initial point and scale
+ initialViewpoint = Viewpoint(Point(34.77, -10.24), 20e7)
+ }
+
+ // configure map view assignments
+ mapView.apply {
+ this.map = map
+ // add the graphics overlay to the mapview
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ lifecycleScope.launch {
+ // if the map load fails show the error and return
+ map.load().onFailure {
+ return@launch showError("Error loading map")
+ }
+ // capture and collect when the user taps on the screen
+ mapView.onSingleTapConfirmed.collect { event ->
+ event.mapPoint?.let { point ->
+ addMapPoint(point)
+ }
+ }
+ }
+
+ // add a click listener to create a convex hull
+ createButton.setOnClickListener {
+ // check if the pointGraphic's geometry is not null
+ pointGraphic.geometry?.let { geometry ->
+ createConvexHull(geometry)
+ }
+ }
+
+ // add a click listener to reset the map
+ resetButton.setOnClickListener {
+ resetMap()
+ }
+ }
+
+ /**
+ * Adds the [point] to the map drawn as a Multipoint geometry
+ */
+ private fun addMapPoint(point: Point) {
+ // add the new point to the points list
+ inputPoints.add(point)
+ // recreate the graphics geometry representing the input points
+ pointGraphic.geometry = Multipoint(inputPoints)
+ // enable all the action buttons, since we have at least one point drawn
+ createButton.isEnabled = true
+ resetButton.isEnabled = true
+ }
+
+ /**
+ * Creates and draws a convex hull graphic on the map using [pointGeometry] points
+ */
+ private fun createConvexHull(pointGeometry: Geometry) {
+ // normalize the geometry for panning beyond the meridian
+ // and proceed if the resulting geometry is not null
+ val normalizedPointGeometry = GeometryEngine.normalizeCentralMeridian(pointGeometry)
+ ?: return showError("Error normalizing point geometry")
+
+ // create a convex hull from the points and proceed if it's not null
+ val convexHullGeometry = GeometryEngine.convexHullOrNull(normalizedPointGeometry)
+
+ // the convex hull's geometry may be a point or polyline if the number of
+ // points is less than 3, set its symbol accordingly
+ convexHullGraphic.symbol = when (convexHullGeometry) {
+ is Point -> {
+ // set symbol to use the pointSymbol
+ pointSymbol
+ }
+ is Polyline -> {
+ // set symbol to use the lineSymbol
+ lineSymbol
+ }
+ is Polygon -> {
+ // set symbol to use the fillSymbol
+ fillSymbol
+ }
+ else -> {
+ showError("Unknown geometry for convex hull")
+ null
+ }
+ }
+ // update the convex hull graphics geometry
+ convexHullGraphic.geometry = convexHullGeometry
+ // disable the create button until new input points are created
+ createButton.isEnabled = false
+ }
+
+
+ /**
+ * Resets the map by clearing any drawn points, graphics and disables all buttons
+ */
+ private fun resetMap() {
+ // remove all the selected points
+ inputPoints.clear()
+ // remove the geometry for the point graphic and convex hull graphics
+ pointGraphic.geometry = null
+ convexHullGraphic.geometry = null
+ // disable the buttons
+ resetButton.isEnabled = false
+ createButton.isEnabled = false
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+/**
+ * Simple extension property that represents a blue color
+ */
+private val Color.Companion.blue
+ get() = fromRgba(0, 0, 255)
diff --git a/create-convex-hull-around-points/src/main/res/layout/activity_main.xml b/samples/create-convex-hull-around-points/src/main/res/layout/create_convex_hull_around_points_activity_main.xml
similarity index 100%
rename from create-convex-hull-around-points/src/main/res/layout/activity_main.xml
rename to samples/create-convex-hull-around-points/src/main/res/layout/create_convex_hull_around_points_activity_main.xml
diff --git a/samples/create-convex-hull-around-points/src/main/res/values/strings.xml b/samples/create-convex-hull-around-points/src/main/res/values/strings.xml
new file mode 100644
index 000000000..de9544805
--- /dev/null
+++ b/samples/create-convex-hull-around-points/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Create convex hull around points
+ Create Convex Hull
+ Reset
+
diff --git a/create-mobile-geodatabase/README.md b/samples/create-mobile-geodatabase/README.md
similarity index 100%
rename from create-mobile-geodatabase/README.md
rename to samples/create-mobile-geodatabase/README.md
diff --git a/create-mobile-geodatabase/README.metadata.json b/samples/create-mobile-geodatabase/README.metadata.json
similarity index 100%
rename from create-mobile-geodatabase/README.metadata.json
rename to samples/create-mobile-geodatabase/README.metadata.json
diff --git a/samples/create-mobile-geodatabase/build.gradle.kts b/samples/create-mobile-geodatabase/build.gradle.kts
new file mode 100644
index 000000000..ef60142e1
--- /dev/null
+++ b/samples/create-mobile-geodatabase/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.createmobilegeodatabase"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/create-mobile-geodatabase/create-mobile-geodatabase.png b/samples/create-mobile-geodatabase/create-mobile-geodatabase.png
similarity index 100%
rename from create-mobile-geodatabase/create-mobile-geodatabase.png
rename to samples/create-mobile-geodatabase/create-mobile-geodatabase.png
diff --git a/samples/create-mobile-geodatabase/src/main/AndroidManifest.xml b/samples/create-mobile-geodatabase/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ac2f8e38e
--- /dev/null
+++ b/samples/create-mobile-geodatabase/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt b/samples/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt
new file mode 100644
index 000000000..c7e7d76c1
--- /dev/null
+++ b/samples/create-mobile-geodatabase/src/main/java/com/esri/arcgismaps/sample/createmobilegeodatabase/MainActivity.kt
@@ -0,0 +1,297 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createmobilegeodatabase
+
+import android.app.Dialog
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.FileProvider
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.LoadStatus
+import com.arcgismaps.data.FieldDescription
+import com.arcgismaps.data.FieldType
+import com.arcgismaps.data.Geodatabase
+import com.arcgismaps.data.GeodatabaseFeatureTable
+import com.arcgismaps.data.QueryParameters
+import com.arcgismaps.data.TableDescription
+import com.arcgismaps.geometry.GeometryType
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.CreateMobileGeodatabaseActivityMainBinding
+import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.TableLayoutBinding
+import com.esri.arcgismaps.sample.createmobilegeodatabase.databinding.TableRowBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+import java.time.Instant
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: CreateMobileGeodatabaseActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.create_mobile_geodatabase_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val createButton: MaterialButton by lazy {
+ activityMainBinding.createButton
+ }
+
+ private val viewTableButton: MaterialButton by lazy {
+ activityMainBinding.viewTableButton
+ }
+
+ private val featureCountTextView: TextView by lazy {
+ activityMainBinding.featureCount
+ }
+
+ // feature table created using mobile geodatabase and added to the MapView
+ private var featureTable: GeodatabaseFeatureTable? = null
+
+ // mobile geodatabase used to create and store
+ // the feature attributes (LocationHistory.geodatabase)
+ private var geodatabase: Geodatabase? = null
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect { tapConfirmedEvent ->
+ tapConfirmedEvent.mapPoint?.let {
+ // create a feature on where the user clicked
+ addFeature(it)
+ }
+ }
+ }
+
+ // displays a dialog to show the attributes of each feature in a feature table
+ viewTableButton.setOnClickListener {
+ lifecycleScope.launch {
+ displayTable()
+ }
+ }
+
+ // opens a share-sheet with the "LocationHistory.geodatabase" file
+ createButton.setOnClickListener {
+ // close the mobile geodatabase before sharing
+ geodatabase?.close()
+
+ // get the URI of the geodatabase file using FileProvider
+ val geodatabaseURI = FileProvider.getUriForFile(
+ this,
+ getString(R.string.provider_authority),
+ File(geodatabase?.path.toString())
+ )
+
+ // set up the sharing intent with the geodatabase URI
+ val geodatabaseIntent = Intent(Intent.ACTION_SEND).apply {
+ type = "*/*"
+ putExtra(Intent.EXTRA_STREAM, geodatabaseURI)
+ }
+
+ // open the Android share sheet
+ startActivity(geodatabaseIntent)
+ }
+ }
+
+
+ /**
+ * Create and load a new geodatabase file with TableDescription fields
+ */
+ private fun createGeodatabase() {
+ // define the path and name of the geodatabase file
+ // note: the path defined must be non-empty, available,
+ // allow read/write access, and end in ".geodatabase"
+ val file = File(getExternalFilesDir(null)?.path, "/LocationHistory.geodatabase")
+ if (file.exists()) {
+ file.delete()
+ }
+ // close the existing geodatabase
+ geodatabase?.close()
+ lifecycleScope.launch {
+ // create a geodatabase file at the file path
+ Geodatabase.create(file.path).onSuccess { geodatabase ->
+ // keep the instance of the new geodatabase for sharing
+ this@MainActivity.geodatabase = geodatabase
+ createGeodatabaseFeatureTable()
+ }.onFailure {
+ showError(it.message.toString())
+ }
+
+ }
+ }
+
+ /**
+ * Create a new [featureTable] using a custom table description
+ * and add the feature layer to the MapView.
+ */
+ private suspend fun createGeodatabaseFeatureTable() {
+ // construct a table description which stores features as points on map
+ val tableDescription =
+ TableDescription(
+ "LocationHistory",
+ SpatialReference.wgs84(),
+ GeometryType.Point
+ )
+ // set up the fields to the table,
+ // Field.Type.OID is the primary key of the SQLite table
+ // Field.Type.DATE is a date column used to store a Calendar date
+ // FieldDescriptions can be a SHORT, INTEGER, GUID, FLOAT, DOUBLE, DATE, TEXT, OID, GLOBALID, BLOB, GEOMETRY, RASTER, or XML.
+ tableDescription.fieldDescriptions.addAll(
+ listOf(
+ FieldDescription("oid", FieldType.Oid),
+ FieldDescription("collection_timestamp", FieldType.Date)
+ )
+ )
+
+ // set any properties not needed to false
+ tableDescription.apply {
+ hasAttachments = false
+ hasM = false
+ hasZ = false
+ }
+
+ // add a new table to the geodatabase by creating one from the tableDescription
+ geodatabase?.createTable(tableDescription)?.onSuccess { featureTable ->
+ // get the result of the loaded "LocationHistory" table
+ this.featureTable = featureTable
+ // create a feature layer for the map using the GeodatabaseFeatureTable
+ val featureLayer = FeatureLayer.createWithFeatureTable(featureTable)
+ mapView.map?.operationalLayers?.add(featureLayer)
+ // display the current count of features in the FeatureTable
+ featureCountTextView.text =
+ "Number of features added: ${featureTable.numberOfFeatures}"
+ }?.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ /**
+ * Create a feature with attributes on map click and add it to the [featureTable]
+ * Also, updates the TotalFeatureCount on the screen
+ */
+ private fun addFeature(mapPoint: Point) {
+ // set up the feature attributes
+ val featureAttributes = mutableMapOf()
+ featureAttributes["collection_timestamp"] = Instant.now()
+
+ // create a new feature at the mapPoint
+ val feature = featureTable?.createFeature(featureAttributes, mapPoint)
+ ?: return showError("Error creating feature using attributes")
+
+ lifecycleScope.launch {
+ // add the feature to the feature table
+ featureTable?.addFeature(feature)?.onSuccess {
+ // feature added successfully, update count
+ featureCountTextView.text =
+ "Number of features added: ${featureTable?.numberOfFeatures}"
+ // enable table button since at least 1 feature loaded on the GeodatabaseFeatureTable
+ viewTableButton.isEnabled = true
+ }?.onFailure {
+ showError(it.message.toString())
+ }
+ }
+ }
+
+ /**
+ * Displays a dialog with the table of features
+ * added to the GeodatabaseFeatureTable [featureTable]
+ */
+ private suspend fun displayTable() {
+ // query all the features loaded to the table
+ featureTable?.queryFeatures(QueryParameters())?.onSuccess { queryResults ->
+ // inflate the table layout
+ val tableLayoutBinding = TableLayoutBinding.inflate(layoutInflater)
+ // set up a dialog to be displayed
+ Dialog(this).apply {
+ setContentView(tableLayoutBinding.root)
+ setCancelable(true)
+ // grab the instance of the TableLayout
+ val table = tableLayoutBinding.tableLayout
+ // iterate through each feature to add to the TableLayout
+ queryResults.forEach { feature ->
+ // prepare the table row
+ val tableRowBinding = TableRowBinding.inflate(layoutInflater).apply {
+ oid.text = feature.attributes["oid"].toString()
+ collectionTimestamp.text =
+ (feature.attributes["collection_timestamp"] as Instant).toString()
+ }
+ // add the row to the TableLayout
+ table.addView(tableRowBinding.root)
+ }
+ }.show()
+ }?.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ /**
+ * Called on app launch or when Android share sheet is closed
+ */
+ private fun setMapView() {
+ // create and add a map with a navigation night basemap style
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ mapView.setViewpoint(Viewpoint(41.5, -100.0, 100_000_000.0))
+
+ lifecycleScope.launch {
+ mapView.map?.loadStatus?.collect { loadStatus ->
+ if (loadStatus == LoadStatus.Loaded) {
+ // clear any feature layers displayed on the map
+ mapView.map?.operationalLayers?.clear()
+ // disable the button since no features are displayed
+ viewTableButton.isEnabled = false
+ // create a new geodatabase file to add features into the feature table
+ createGeodatabase()
+ } else if (loadStatus is LoadStatus.FailedToLoad) {
+ showError("Error loading MapView: ${loadStatus.error.message}")
+ }
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ // set up map view and create new geodatabase file
+ // on every app launch or on share sheet close
+ setMapView()
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/create-mobile-geodatabase/src/main/res/drawable/cell_shape.xml b/samples/create-mobile-geodatabase/src/main/res/drawable/cell_shape.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/drawable/cell_shape.xml
rename to samples/create-mobile-geodatabase/src/main/res/drawable/cell_shape.xml
diff --git a/create-mobile-geodatabase/src/main/res/drawable/round_create_24.xml b/samples/create-mobile-geodatabase/src/main/res/drawable/round_create_24.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/drawable/round_create_24.xml
rename to samples/create-mobile-geodatabase/src/main/res/drawable/round_create_24.xml
diff --git a/create-mobile-geodatabase/src/main/res/layout/activity_main.xml b/samples/create-mobile-geodatabase/src/main/res/layout/create_mobile_geodatabase_activity_main.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/layout/activity_main.xml
rename to samples/create-mobile-geodatabase/src/main/res/layout/create_mobile_geodatabase_activity_main.xml
diff --git a/create-mobile-geodatabase/src/main/res/layout/table_layout.xml b/samples/create-mobile-geodatabase/src/main/res/layout/table_layout.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/layout/table_layout.xml
rename to samples/create-mobile-geodatabase/src/main/res/layout/table_layout.xml
diff --git a/create-mobile-geodatabase/src/main/res/layout/table_row.xml b/samples/create-mobile-geodatabase/src/main/res/layout/table_row.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/layout/table_row.xml
rename to samples/create-mobile-geodatabase/src/main/res/layout/table_row.xml
diff --git a/samples/create-mobile-geodatabase/src/main/res/values/strings.xml b/samples/create-mobile-geodatabase/src/main/res/values/strings.xml
new file mode 100644
index 000000000..97c40ce0a
--- /dev/null
+++ b/samples/create-mobile-geodatabase/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ Create mobile geodatabase
+ Number of features added:
+ Create and share mobile geodatabase
+ View table
+ Attribute table loaded from the mobile geodatabase file. File can be loaded on ArcGIS Pro or ArcGIS Runtime
+ OID
+ Collection Timestamp
+ com.esri.arcgismaps.sample.createmobilegeodatabase.provider
+
diff --git a/create-mobile-geodatabase/src/main/res/xml/provider.xml b/samples/create-mobile-geodatabase/src/main/res/xml/provider.xml
similarity index 100%
rename from create-mobile-geodatabase/src/main/res/xml/provider.xml
rename to samples/create-mobile-geodatabase/src/main/res/xml/provider.xml
diff --git a/create-planar-and-geodetic-buffers/README.md b/samples/create-planar-and-geodetic-buffers/README.md
similarity index 100%
rename from create-planar-and-geodetic-buffers/README.md
rename to samples/create-planar-and-geodetic-buffers/README.md
diff --git a/create-planar-and-geodetic-buffers/README.metadata.json b/samples/create-planar-and-geodetic-buffers/README.metadata.json
similarity index 100%
rename from create-planar-and-geodetic-buffers/README.metadata.json
rename to samples/create-planar-and-geodetic-buffers/README.metadata.json
diff --git a/samples/create-planar-and-geodetic-buffers/build.gradle.kts b/samples/create-planar-and-geodetic-buffers/build.gradle.kts
new file mode 100644
index 000000000..57564380d
--- /dev/null
+++ b/samples/create-planar-and-geodetic-buffers/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.createplanarandgeodeticbuffers"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/create-planar-and-geodetic-buffers/create-planar-and-geodetic-buffers.png b/samples/create-planar-and-geodetic-buffers/create-planar-and-geodetic-buffers.png
similarity index 100%
rename from create-planar-and-geodetic-buffers/create-planar-and-geodetic-buffers.png
rename to samples/create-planar-and-geodetic-buffers/create-planar-and-geodetic-buffers.png
diff --git a/samples/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml b/samples/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d6d047f8b
--- /dev/null
+++ b/samples/create-planar-and-geodetic-buffers/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt b/samples/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt
new file mode 100644
index 000000000..0c8dcc7ec
--- /dev/null
+++ b/samples/create-planar-and-geodetic-buffers/src/main/java/com/esri/arcgismaps/sample/createplanarandgeodeticbuffers/MainActivity.kt
@@ -0,0 +1,187 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.createplanarandgeodeticbuffers
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.GeodeticCurveType
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.LinearUnit
+import com.arcgismaps.geometry.LinearUnitId
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleRenderer
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.createplanarandgeodeticbuffers.databinding.CreatePlanarAndGeodeticBuffersActivityMainBinding
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.slider.Slider
+import kotlinx.coroutines.launch
+import java.util.Locale
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: CreatePlanarAndGeodeticBuffersActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.create_planar_and_geodetic_buffers_activity_main)
+
+ // get the views from the layout
+ val mapView = activityMainBinding.mapView
+ val optionsButton = activityMainBinding.optionsButton
+ val clearButton = activityMainBinding.clearButton
+ // add mapview to the lifecycle
+ lifecycle.addObserver(mapView)
+
+ // create a map with a topographic basemap
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+
+ // create a fill symbol for geodesic buffer polygons
+ val geodesicOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 2F)
+ val geodesicBufferFillSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.Solid, Color.green,
+ geodesicOutlineSymbol
+ )
+
+ // create a graphics overlay to display geodesic polygons and set its renderer
+ val geodesicGraphicsOverlay = GraphicsOverlay().apply {
+ renderer = SimpleRenderer(geodesicBufferFillSymbol)
+ opacity = 0.5f
+ }
+
+ // create a fill symbol for planar buffer polygons
+ val planarOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 2F)
+ val planarBufferFillSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.Solid, Color.red,
+ planarOutlineSymbol
+ )
+
+ // create a graphics overlay to display planar polygons and set its renderer
+ val planarGraphicsOverlay = GraphicsOverlay().apply {
+ renderer = SimpleRenderer(planarBufferFillSymbol)
+ opacity = 0.5f
+ }
+
+ // create a marker symbol for tap locations
+ val tapSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Cross, Color.white, 14F)
+
+ // create a graphics overlay to display tap locations for buffers and set its renderer
+ val tapLocationsOverlay = GraphicsOverlay().apply {
+ renderer = SimpleRenderer(tapSymbol)
+ }
+
+ // add overlays to the mapView
+ mapView.graphicsOverlays.addAll(
+ listOf(
+ geodesicGraphicsOverlay,
+ planarGraphicsOverlay,
+ tapLocationsOverlay
+ )
+ )
+
+ // set the default buffer distance in miles
+ var bufferInMiles = 500f
+
+ // create a buffer around the clicked location
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect { event ->
+ // get map point tapped, return if null
+ val mapPoint = event.mapPoint ?: return@collect
+
+ // convert the input distance to meters, 1609.34 meters in one mile
+ val bufferInMeters = bufferInMiles * 1609.34
+
+ // create a planar buffer graphic around the input location at the specified distance
+ val bufferGeometryPlanar = GeometryEngine.bufferOrNull(mapPoint, bufferInMeters)
+ val planarBufferGraphic = Graphic(bufferGeometryPlanar)
+
+ // create a geodesic buffer graphic using the same location and distance
+ val bufferGeometryGeodesic =
+ GeometryEngine.bufferGeodeticOrNull(
+ mapPoint, bufferInMeters,
+ LinearUnit(LinearUnitId.Meters), Double.NaN, GeodeticCurveType.Geodesic
+ )
+ val geodesicBufferGraphic = Graphic(bufferGeometryGeodesic)
+
+ // create a graphic for the user tap location
+ val locationGraphic = Graphic(mapPoint)
+
+ // add the buffer polygons and tap location graphics to the appropriate graphic overlays
+ planarGraphicsOverlay.graphics.add(planarBufferGraphic)
+ geodesicGraphicsOverlay.graphics.add(geodesicBufferGraphic)
+ tapLocationsOverlay.graphics.add(locationGraphic)
+
+ // set button interaction
+ clearButton.isEnabled = true
+
+ }
+ }
+ // open the option dialog
+ optionsButton.setOnClickListener {
+ val optionsDialog: View = layoutInflater.inflate(R.layout.buffer_options_dialog, null)
+ // set up the dialog builder and the title
+ val dialogBuilder = MaterialAlertDialogBuilder(this)
+ .setView(optionsDialog)
+ .setPositiveButton("Set buffer", null)
+
+ dialogBuilder.setTitle("Set buffer radius")
+ // set up the dialog views
+ val bufferValue = optionsDialog.findViewById(R.id.bufferValue)
+ val bufferSlider = optionsDialog.findViewById(R.id.bufferInput)
+ bufferSlider.value = bufferInMiles
+
+ // set initial buffer text
+ bufferValue.text = String.format(Locale.getDefault(),"%d miles", bufferSlider.value.toInt())
+ bufferSlider.addOnChangeListener { _, value, _ ->
+ // update buffer text value on slider change
+ bufferValue.text = String.format(Locale.getDefault(),"%d miles", value.toInt())
+ bufferInMiles = value
+ }
+
+ // display the dialog
+ dialogBuilder.show()
+ }
+
+ // clear the graphics from the graphics overlays
+ clearButton.setOnClickListener {
+ planarGraphicsOverlay.graphics.clear()
+ geodesicGraphicsOverlay.graphics.clear()
+ tapLocationsOverlay.graphics.clear()
+ // set button interaction
+ clearButton.isEnabled = false
+ }
+ }
+}
diff --git a/create-planar-and-geodetic-buffers/src/main/res/drawable/geodesic_swatch.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/drawable/geodesic_swatch.xml
similarity index 100%
rename from create-planar-and-geodetic-buffers/src/main/res/drawable/geodesic_swatch.xml
rename to samples/create-planar-and-geodetic-buffers/src/main/res/drawable/geodesic_swatch.xml
diff --git a/create-planar-and-geodetic-buffers/src/main/res/drawable/planar_swatch.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/drawable/planar_swatch.xml
similarity index 100%
rename from create-planar-and-geodetic-buffers/src/main/res/drawable/planar_swatch.xml
rename to samples/create-planar-and-geodetic-buffers/src/main/res/drawable/planar_swatch.xml
diff --git a/create-planar-and-geodetic-buffers/src/main/res/layout/buffer_options_dialog.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/layout/buffer_options_dialog.xml
similarity index 100%
rename from create-planar-and-geodetic-buffers/src/main/res/layout/buffer_options_dialog.xml
rename to samples/create-planar-and-geodetic-buffers/src/main/res/layout/buffer_options_dialog.xml
diff --git a/create-planar-and-geodetic-buffers/src/main/res/layout/activity_main.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/layout/create_planar_and_geodetic_buffers_activity_main.xml
similarity index 100%
rename from create-planar-and-geodetic-buffers/src/main/res/layout/activity_main.xml
rename to samples/create-planar-and-geodetic-buffers/src/main/res/layout/create_planar_and_geodetic_buffers_activity_main.xml
diff --git a/create-planar-and-geodetic-buffers/src/main/res/values/colors.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/values/colors.xml
similarity index 100%
rename from create-planar-and-geodetic-buffers/src/main/res/values/colors.xml
rename to samples/create-planar-and-geodetic-buffers/src/main/res/values/colors.xml
diff --git a/samples/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml b/samples/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml
new file mode 100644
index 000000000..24e4ae5ae
--- /dev/null
+++ b/samples/create-planar-and-geodetic-buffers/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Create planar and geodetic buffers
+ Specify the buffer in miles then tap on map
+ Geodesic Buffer
+ Planar Buffer
+ 2000
+ Clear all
+ miles
+
diff --git a/cut-geometry/README.md b/samples/cut-geometry/README.md
similarity index 100%
rename from cut-geometry/README.md
rename to samples/cut-geometry/README.md
diff --git a/cut-geometry/README.metadata.json b/samples/cut-geometry/README.metadata.json
similarity index 100%
rename from cut-geometry/README.metadata.json
rename to samples/cut-geometry/README.metadata.json
diff --git a/samples/cut-geometry/build.gradle.kts b/samples/cut-geometry/build.gradle.kts
new file mode 100644
index 000000000..b422decad
--- /dev/null
+++ b/samples/cut-geometry/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.cutgeometry"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/cut-geometry/cut-geometry.png b/samples/cut-geometry/cut-geometry.png
similarity index 100%
rename from cut-geometry/cut-geometry.png
rename to samples/cut-geometry/cut-geometry.png
diff --git a/samples/cut-geometry/src/main/AndroidManifest.xml b/samples/cut-geometry/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d8a40f11c
--- /dev/null
+++ b/samples/cut-geometry/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt b/samples/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt
new file mode 100644
index 000000000..e14dbaa53
--- /dev/null
+++ b/samples/cut-geometry/src/main/java/com/esri/arcgismaps/sample/cutgeometry/MainActivity.kt
@@ -0,0 +1,198 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.cutgeometry
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.MapView
+import com.esri.arcgismaps.sample.cutgeometry.databinding.CutGeometryActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ private val lakeSuperiorPolygon by lazy {
+ PolygonBuilder(SpatialReference.webMercator()) {
+ addPoint(Point(-10254374.668616, 5908345.076380))
+ addPoint(Point(-10178382.525314, 5971402.386779))
+ addPoint(Point(-10118558.923141, 6034459.697178))
+ addPoint(Point(-9993252.729399, 6093474.872295))
+ addPoint(Point(-9882498.222673, 6209888.368416))
+ addPoint(Point(-9821057.766387, 6274562.532928))
+ addPoint(Point(-9690092.583250, 6241417.023616))
+ addPoint(Point(-9605207.742329, 6206654.660191))
+ addPoint(Point(-9564786.389509, 6108834.986367))
+ addPoint(Point(-9449989.747500, 6095091.726408))
+ addPoint(Point(-9462116.153346, 6044160.821855))
+ addPoint(Point(-9417652.665244, 5985145.646738))
+ addPoint(Point(-9438671.768711, 5946341.148031))
+ addPoint(Point(-9398250.415891, 5922088.336339))
+ addPoint(Point(-9419269.519357, 5855797.317714))
+ addPoint(Point(-9467775.142741, 5858222.598884))
+ addPoint(Point(-9462924.580403, 5902686.086985))
+ addPoint(Point(-9598740.325877, 5884092.264688))
+ addPoint(Point(-9643203.813979, 5845287.765981))
+ addPoint(Point(-9739406.633691, 5879241.702350))
+ addPoint(Point(-9783061.694736, 5922896.763395))
+ addPoint(Point(-9844502.151022, 5936640.023354))
+ addPoint(Point(-9773360.570059, 6019099.583107))
+ addPoint(Point(-9883306.649729, 5968977.105610))
+ addPoint(Point(-9957681.938918, 5912387.211662))
+ addPoint(Point(-10055501.612742, 5871965.858842))
+ addPoint(Point(-10116942.069028, 5884092.264688))
+ addPoint(Point(-10111283.079633, 5933406.315128))
+ addPoint(Point(-10214761.742852, 5888134.399970))
+ addPoint(Point(-10254374.668616, 5901877.659929))
+ }.toGeometry()
+ }
+
+ private val borderPolyline by lazy {
+ PolylineBuilder(SpatialReference.webMercator()) {
+ addPoint(Point(-9981328.687124, 6111053.281447))
+ addPoint(Point(-9946518.044066, 6102350.620682))
+ addPoint(Point(-9872545.427566, 6152390.920079))
+ addPoint(Point(-9838822.617103, 6157830.083057))
+ addPoint(Point(-9446115.050097, 5927209.572793))
+ addPoint(Point(-9430885.393759, 5876081.440801))
+ addPoint(Point(-9415655.737420, 5860851.784463))
+ }.toGeometry()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: CutGeometryActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.cut_geometry_activity_main)
+ val mapView = activityMainBinding.mapView
+ lifecycle.addObserver(mapView)
+ val cutButton = activityMainBinding.cutButton
+ val resetButton = activityMainBinding.resetButton
+
+ // set the map to be displayed in this view
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+
+ // create a graphic overlay
+ val graphicsOverlay = GraphicsOverlay()
+ mapView.graphicsOverlays.add(graphicsOverlay)
+
+ val (polygonGraphic, polylineGraphic) = createGraphics(graphicsOverlay, mapView)
+
+ cutButton.setOnClickListener {
+ // cut the graphic along the polyline to create 2 graphic parts
+ polygonGraphic.geometry?.let { graphicGeometry ->
+ val parts = GeometryEngine.tryCut(
+ graphicGeometry,
+ polylineGraphic.geometry as Polyline
+ )
+
+ // create graphics for the US and Canada sides
+ val canadaSide = Graphic(
+ parts[0], SimpleFillSymbol(
+ SimpleFillSymbolStyle.BackwardDiagonal,
+ Color.green, SimpleLineSymbol(SimpleLineSymbolStyle.Null, Color.blue, 0F)
+ )
+ )
+ val usSide = Graphic(
+ parts[1], SimpleFillSymbol(
+ SimpleFillSymbolStyle.ForwardDiagonal,
+ Color.yellow, SimpleLineSymbol(SimpleLineSymbolStyle.Null, Color.blue, 0F)
+ )
+ )
+ // add the graphics to the graphics overlay
+ graphicsOverlay.graphics.addAll(listOf(canadaSide, usSide))
+
+ // swap button state
+ cutButton.isEnabled = false
+ resetButton.isEnabled = true
+ }
+ }
+
+ resetButton.setOnClickListener {
+ // clear existing graphics
+ graphicsOverlay.graphics.clear()
+
+ // recreate original graphics
+ createGraphics(graphicsOverlay, mapView)
+
+ // swap button state
+ cutButton.isEnabled = true
+ resetButton.isEnabled = false
+ }
+
+ }
+
+ /**
+ * Create polygon and polyline graphics.
+ *
+ * @return polygon and polyline graphics
+ */
+ private fun createGraphics(
+ graphicsOverlay: GraphicsOverlay,
+ mapView: MapView
+ ): Pair {
+ // create a blue polygon graphic to cut
+ val polygonGraphic = Graphic(
+ lakeSuperiorPolygon,
+ SimpleFillSymbol(
+ SimpleFillSymbolStyle.Solid, Color(R.color.transparentBlue),
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 2F)
+ )
+ )
+ graphicsOverlay.graphics.add(polygonGraphic)
+
+ // create a red polyline graphic to cut the polygon
+ val polylineGraphic = Graphic(
+ borderPolyline, SimpleLineSymbol(
+ SimpleLineSymbolStyle.Dot,
+ Color.red, 3F
+ )
+ )
+ graphicsOverlay.graphics.add(polylineGraphic)
+ // zoom to show the polygon graphic
+ polygonGraphic.geometry?.let { graphicGeometry ->
+ mapView.setViewpoint(Viewpoint(graphicGeometry))
+ }
+ return Pair(polygonGraphic, polylineGraphic)
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+}
diff --git a/cut-geometry/src/main/res/layout/activity_main.xml b/samples/cut-geometry/src/main/res/layout/cut_geometry_activity_main.xml
similarity index 100%
rename from cut-geometry/src/main/res/layout/activity_main.xml
rename to samples/cut-geometry/src/main/res/layout/cut_geometry_activity_main.xml
diff --git a/cut-geometry/src/main/res/values/colors.xml b/samples/cut-geometry/src/main/res/values/colors.xml
similarity index 100%
rename from cut-geometry/src/main/res/values/colors.xml
rename to samples/cut-geometry/src/main/res/values/colors.xml
diff --git a/samples/cut-geometry/src/main/res/values/strings.xml b/samples/cut-geometry/src/main/res/values/strings.xml
new file mode 100644
index 000000000..6e2a36af5
--- /dev/null
+++ b/samples/cut-geometry/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Cut geometry
+ Cut geometry
+ Reset
+
diff --git a/display-clusters/README.md b/samples/display-clusters/README.md
similarity index 100%
rename from display-clusters/README.md
rename to samples/display-clusters/README.md
diff --git a/display-clusters/README.metadata.json b/samples/display-clusters/README.metadata.json
similarity index 100%
rename from display-clusters/README.metadata.json
rename to samples/display-clusters/README.metadata.json
diff --git a/samples/display-clusters/build.gradle.kts b/samples/display-clusters/build.gradle.kts
new file mode 100644
index 000000000..55935b0b6
--- /dev/null
+++ b/samples/display-clusters/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displayclusters"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-clusters/display-clusters-popup.png b/samples/display-clusters/display-clusters-popup.png
similarity index 100%
rename from display-clusters/display-clusters-popup.png
rename to samples/display-clusters/display-clusters-popup.png
diff --git a/display-clusters/display-clusters.png b/samples/display-clusters/display-clusters.png
similarity index 100%
rename from display-clusters/display-clusters.png
rename to samples/display-clusters/display-clusters.png
diff --git a/samples/display-clusters/src/main/AndroidManifest.xml b/samples/display-clusters/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/display-clusters/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt b/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt
new file mode 100644
index 000000000..6c1d17c0c
--- /dev/null
+++ b/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displayclusters
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.displayclusters.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ FeatureReductionApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun FeatureReductionApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.display_clusters_app_name)
+ )
+ }
+ }
+}
diff --git a/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/ClusterInfoContent.kt b/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/ClusterInfoContent.kt
similarity index 100%
rename from display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/ClusterInfoContent.kt
rename to samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/ClusterInfoContent.kt
diff --git a/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/MapViewModel.kt b/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/MapViewModel.kt
similarity index 100%
rename from display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/MapViewModel.kt
rename to samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/components/MapViewModel.kt
diff --git a/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/screens/MainScreen.kt b/samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/screens/MainScreen.kt
similarity index 100%
rename from display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/screens/MainScreen.kt
rename to samples/display-clusters/src/main/java/com/esri/arcgismaps/sample/displayclusters/screens/MainScreen.kt
diff --git a/samples/display-clusters/src/main/res/values/strings.xml b/samples/display-clusters/src/main/res/values/strings.xml
new file mode 100644
index 000000000..089111a64
--- /dev/null
+++ b/samples/display-clusters/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Display clusters
+ https://www.arcgis.com/
+
diff --git a/display-composable-mapview/README.md b/samples/display-composable-mapview/README.md
similarity index 100%
rename from display-composable-mapview/README.md
rename to samples/display-composable-mapview/README.md
diff --git a/display-composable-mapview/README.metadata.json b/samples/display-composable-mapview/README.metadata.json
similarity index 100%
rename from display-composable-mapview/README.metadata.json
rename to samples/display-composable-mapview/README.metadata.json
diff --git a/samples/display-composable-mapview/build.gradle.kts b/samples/display-composable-mapview/build.gradle.kts
new file mode 100644
index 000000000..451ce79a7
--- /dev/null
+++ b/samples/display-composable-mapview/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displaycomposablemapview"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-composable-mapview/display-composable-mapview.png b/samples/display-composable-mapview/display-composable-mapview.png
similarity index 100%
rename from display-composable-mapview/display-composable-mapview.png
rename to samples/display-composable-mapview/display-composable-mapview.png
diff --git a/samples/display-composable-mapview/src/main/AndroidManifest.xml b/samples/display-composable-mapview/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/display-composable-mapview/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt b/samples/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt
new file mode 100644
index 000000000..396460afa
--- /dev/null
+++ b/samples/display-composable-mapview/src/main/java/com/esri/arcgismaps/sample/displaycomposablemapview/MainActivity.kt
@@ -0,0 +1,57 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaycomposablemapview
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.ui.Modifier
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ Scaffold(topBar = { SampleTopAppBar(getString(R.string.display_composable_map_view_app_name)) }) {
+ // create a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ MapView(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it),
+ arcGISMap = map
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/samples/display-composable-mapview/src/main/res/values/strings.xml b/samples/display-composable-mapview/src/main/res/values/strings.xml
new file mode 100644
index 000000000..65154f22b
--- /dev/null
+++ b/samples/display-composable-mapview/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Display composable map view
+
diff --git a/display-device-location-with-nmea-data-sources/README.md b/samples/display-device-location-with-nmea-data-sources/README.md
similarity index 100%
rename from display-device-location-with-nmea-data-sources/README.md
rename to samples/display-device-location-with-nmea-data-sources/README.md
diff --git a/display-device-location-with-nmea-data-sources/README.metadata.json b/samples/display-device-location-with-nmea-data-sources/README.metadata.json
similarity index 100%
rename from display-device-location-with-nmea-data-sources/README.metadata.json
rename to samples/display-device-location-with-nmea-data-sources/README.metadata.json
diff --git a/samples/display-device-location-with-nmea-data-sources/build.gradle.kts b/samples/display-device-location-with-nmea-data-sources/build.gradle.kts
new file mode 100644
index 000000000..6344e90a1
--- /dev/null
+++ b/samples/display-device-location-with-nmea-data-sources/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-device-location-with-nmea-data-sources/display-device-location-with-nmea-data-sources.png b/samples/display-device-location-with-nmea-data-sources/display-device-location-with-nmea-data-sources.png
similarity index 100%
rename from display-device-location-with-nmea-data-sources/display-device-location-with-nmea-data-sources.png
rename to samples/display-device-location-with-nmea-data-sources/display-device-location-with-nmea-data-sources.png
diff --git a/samples/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml b/samples/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..f7f3d11df
--- /dev/null
+++ b/samples/display-device-location-with-nmea-data-sources/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt b/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt
new file mode 100644
index 000000000..1711d3575
--- /dev/null
+++ b/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/DownloadActivity.kt
@@ -0,0 +1,22 @@
+package com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.display_device_location_with_nmea_data_sources_app_name),
+ listOf(
+ // ArcGIS Portal item containing the Redlands.nmea
+ // which features a vehicle driving around southern Redlands, CA.
+ "https://www.arcgis.com/home/item.html?id=d5bad9f4fee9483791e405880fb466da"
+ )
+
+ )
+ }
+}
diff --git a/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt b/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt
new file mode 100644
index 000000000..9d842176b
--- /dev/null
+++ b/samples/display-device-location-with-nmea-data-sources/src/main/java/com/esri/arcgismaps/sample/displaydevicelocationwithnmeadatasources/MainActivity.kt
@@ -0,0 +1,277 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.location.LocationDataSourceStatus
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.location.NmeaGnssSystem
+import com.arcgismaps.location.NmeaLocationDataSource
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.esri.arcgismaps.sample.displaydevicelocationwithnmeadatasources.databinding.DisplayDeviceLocationWithNmeaDataSourcesActivityMainBinding
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+import java.nio.charset.StandardCharsets
+import java.util.*
+import kotlin.concurrent.timerTask
+
+class MainActivity : AppCompatActivity() {
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.display_device_location_with_nmea_data_sources_app_name)
+ }
+
+ // create a new NMEA location data source
+ private val nmeaLocationDataSource: NmeaLocationDataSource =
+ NmeaLocationDataSource(SpatialReference.wgs84())
+
+ // create a timer to simulate a stream of NMEA data
+ private var timer = Timer()
+
+ // list of nmea location sentences
+ private var nmeaSentences: List? = emptyList()
+
+ // index of nmea location sentence
+ private var locationIndex = 0
+
+ // set up data binding for the activity
+ private val activityMainBinding: DisplayDeviceLocationWithNmeaDataSourcesActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.display_device_location_with_nmea_data_sources_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val accuracyTV: TextView by lazy {
+ activityMainBinding.accuracyTV
+ }
+
+ private val satelliteCountTV: TextView by lazy {
+ activityMainBinding.satelliteCountTV
+ }
+
+ private val satelliteIDsTV: TextView by lazy {
+ activityMainBinding.satelliteIDsTV
+ }
+
+ private val systemTypeTV: TextView by lazy {
+ activityMainBinding.systemTypeTV
+ }
+
+ private val playPauseFAB: FloatingActionButton by lazy {
+ activityMainBinding.playPauseFAB
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ mapView.map = map
+
+ // set a viewpoint on the map view centered on Redlands, California
+ mapView.setViewpoint(
+ Viewpoint(
+ Point(-117.191, 34.0306, SpatialReference.wgs84()), 100000.0
+ )
+ )
+
+ mapView.locationDisplay.apply {
+ // set the map view's location display to use the nmea location data source
+ dataSource = nmeaLocationDataSource
+ // set the map view to recenter on location changed events
+ setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ }
+
+ // disable map view interaction, the location display will automatically center on the mock device location
+ mapView.interactionOptions.apply {
+ isPanEnabled = false
+ isZoomEnabled = false
+ isRotateEnabled = false
+ }
+
+ // read nmea location sentences from file
+ nmeaSentences = getNMEASentenceList()
+ // collects the accuracy for each location change
+ collectLocationChanges()
+ // collects satellite changes and display satellite information
+ collectSatelliteChanges()
+ }
+
+ /**
+ * Reads NMEA location sentences from the .nmea file and
+ * returns it as a [MutableList]
+ */
+ private fun getNMEASentenceList(): List? {
+ val simulatedNmeaDataFile = File("$provisionPath/Redlands.nmea")
+ if (!simulatedNmeaDataFile.exists()) {
+ showError("NMEA file does not exist")
+ return null
+ }
+ // create list of nmea location sentences
+ var nmeaSentences: List = emptyList()
+ // create a buffered reader using the .nmea file
+ val bufferedReader = File(simulatedNmeaDataFile.path).bufferedReader()
+ // read the nmea file contents using a buffered reader and store the mock data sentences in a list
+ bufferedReader.useLines { bufferReaderLines ->
+ // add carriage return for nmea location data source parser
+ nmeaSentences = bufferReaderLines.map { it + "\n" }.toList()
+ }
+ return nmeaSentences
+ }
+
+ /**
+ * Control the start/stop status of the NMEA location data source
+ */
+ fun playPauseClick(view: View) = lifecycleScope.launch {
+ if (nmeaLocationDataSource.status.value != LocationDataSourceStatus.Started) {
+ // initialize the location data source and prepare to begin receiving location updates when data is pushed
+ // as updates are received, they will be displayed on the map
+ nmeaLocationDataSource.start().onFailure {
+ showError("NmeaLocationDataSource failed to start: ${it.message}")
+ return@launch
+ }
+ // starts the NMEA mock data sentences
+ nmeaSentences?.let { startNMEAMockData(it) }
+ setButtonStatus(true)
+ } else {
+ // stop receiving and displaying location data
+ nmeaLocationDataSource.stop()
+ // cancel up the timer task
+ timer.cancel()
+ setButtonStatus(false)
+ clearUI()
+ }
+ }
+
+ /**
+ * Initializes the location data source, reads the mock data NMEA sentences, and displays location updates from that file
+ * on the location display. Data is pushed to the data source using a timeline to simulate live updates, as they would
+ * appear if using real-time data from a GPS dongle
+ */
+
+ /**
+ * Push the mock data NMEA sentences into the data source every 250 ms
+ */
+ private fun startNMEAMockData(nmeaSentences: List) {
+ timer = Timer()
+ timer.schedule(timerTask {
+ // only push data when started
+ if (nmeaLocationDataSource.status.value == LocationDataSourceStatus.Started)
+ nmeaLocationDataSource.pushData(
+ nmeaSentences[locationIndex++].toByteArray(StandardCharsets.UTF_8)
+ )
+ // reset the location index after the last data point is reached
+ if (locationIndex == nmeaSentences.size) locationIndex = 0
+ }, 250, 250)
+ }
+
+ /**
+ * Sets the FAB button to "Start"/"Stop" based on [isShowingLocation]
+ */
+ private fun setButtonStatus(isShowingLocation: Boolean) = if (isShowingLocation) {
+ playPauseFAB.setImageDrawable(
+ AppCompatResources.getDrawable(
+ this, R.drawable.ic_round_pause_24
+ )
+ )
+ } else {
+ playPauseFAB.setImageDrawable(
+ AppCompatResources.getDrawable(
+ this, R.drawable.ic_round_play_arrow_24
+ )
+ )
+ }
+
+ /**
+ * Collects location changes of the NMEA location data source,
+ * and displays the location accuracy
+ */
+ private fun collectLocationChanges() = lifecycleScope.launch {
+ nmeaLocationDataSource.locationChanged.collect { nmeaLocation ->
+ // convert from meters to foot
+ val horizontalAccuracy = nmeaLocation.horizontalAccuracy * 3.28084
+ val verticalAccuracy = nmeaLocation.verticalAccuracy * 3.28084
+ accuracyTV.text =
+ getString(R.string.accuracy) + "Horizontal-%.1fft, Vertical-%.1fft".format(
+ horizontalAccuracy, verticalAccuracy
+ )
+ }
+ }
+
+ /**
+ * Obtains NMEA satellite information from the NMEA location data source,
+ * and displays satellite information on the app
+ */
+ private fun collectSatelliteChanges() = lifecycleScope.launch {
+ nmeaLocationDataSource.satellitesChanged.collect { nmeaSatelliteInfoList ->
+ // set the text of the satellite count label
+ satelliteCountTV.text = getString(R.string.satellite_count) + nmeaSatelliteInfoList.size
+ // get the system of the first satellite
+ val satelliteSystems = when (nmeaSatelliteInfoList.first().system) {
+ NmeaGnssSystem.Bds -> "BDS"
+ NmeaGnssSystem.Galileo -> "Galileo"
+ NmeaGnssSystem.Glonass -> "Glonass"
+ NmeaGnssSystem.Gps -> "GPS"
+ NmeaGnssSystem.NavIc -> "NavIc"
+ NmeaGnssSystem.Qzss -> "Qzss"
+ NmeaGnssSystem.Unknown -> "Unknown"
+ }
+ // get the satellite IDs from the info list
+ val uniqueSatelliteIDs = nmeaSatelliteInfoList.map { it.id }
+ // display the satellite system and id information
+ systemTypeTV.text = getString(R.string.system) + satelliteSystems
+ satelliteIDsTV.text = getString(R.string.satellite_ids) + uniqueSatelliteIDs
+ }
+ }
+
+ /**
+ * Clears out the info messages when LocationDataSource is paused.
+ */
+ private fun clearUI() {
+ accuracyTV.text = getString(R.string.accuracy)
+ satelliteCountTV.text = getString(R.string.satellite_count)
+ satelliteIDsTV.text = getString(R.string.satellite_ids)
+ systemTypeTV.text = getString(R.string.system)
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_pause_24.xml b/samples/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_pause_24.xml
similarity index 100%
rename from display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_pause_24.xml
rename to samples/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_pause_24.xml
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_play_arrow_24.xml b/samples/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_play_arrow_24.xml
similarity index 100%
rename from display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_play_arrow_24.xml
rename to samples/display-device-location-with-nmea-data-sources/src/main/res/drawable/ic_round_play_arrow_24.xml
diff --git a/display-device-location-with-nmea-data-sources/src/main/res/layout/activity_main.xml b/samples/display-device-location-with-nmea-data-sources/src/main/res/layout/display_device_location_with_nmea_data_sources_activity_main.xml
similarity index 100%
rename from display-device-location-with-nmea-data-sources/src/main/res/layout/activity_main.xml
rename to samples/display-device-location-with-nmea-data-sources/src/main/res/layout/display_device_location_with_nmea_data_sources_activity_main.xml
diff --git a/samples/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml b/samples/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml
new file mode 100644
index 000000000..e949f43c2
--- /dev/null
+++ b/samples/display-device-location-with-nmea-data-sources/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ Display device location with NMEA data sources
+ Start/Stop button
+ Accuracy:
+ Satellite Count:
+ System:
+ Satellite IDs:
+
diff --git a/samples/display-dimensions/README.md b/samples/display-dimensions/README.md
new file mode 100644
index 000000000..f05200b39
--- /dev/null
+++ b/samples/display-dimensions/README.md
@@ -0,0 +1,38 @@
+# Display dimensions
+
+Display dimension features from a mobile map package.
+
+![Image showing the Display Dimensions sample](display-dimensions.png)
+
+## Use case
+
+Dimensions show specific lengths or distances on a map. A dimension may indicate the length of a side of a building or land parcel, or the distance between two features, such as a fire hydrant and the corner of a building.
+
+## How to use the sample
+
+When the sample loads, it will automatically display the map containing dimension features from the mobile map package. The name of the dimension layer containing the dimension features is displayed in the controls box. Control the visibility of the dimension layer with the "Dimension Settings" button, and apply a definition expression to show dimensions of greater than or equal to 450m in length using the "Definition Expression" switch.
+
+## How it works
+
+1. Create a `MobileMapPackage` specifying the path to the .mmpk file.
+2. Load the mobile map package with `mobileMapPackage.load()`.
+3. After it successfully loads, get the first map from the mmpk and set it to the map view: `mapView.map = mobileMapPackage.maps[0]`.
+4. Loop through the map's layers to create a `DimensionLayer`.
+5. Control the dimension layer's visibility with `dimensionLayer.isVisible` and set a definition expression with `dimensionLayer.definitionExpression`.
+
+## Relevant API
+
+* DimensionLayer
+* MobileMapPackage
+
+## About the data
+
+This sample shows a subset of the Edinburgh, Scotland network of pylons, substations, and powerlines within an [Edinburgh Pylon Dimensions mobile map package](https://arcgis.com/home/item.html?id=f5ff6f5556a945bca87ca513b8729a1e), digitized from satellite imagery. Note the data is intended as illustrative of the network only.
+
+## Additional information
+
+Dimension layers can be taken offline from a feature service hosted on ArcGIS Enterprise 10.9 or later, using the [GeodatabaseSyncTask](https://developers.arcgis.com/kotlin/api-reference/arcgis-maps-kotlin/com.arcgismaps.tasks.geodatabase/-geodatabase-sync-task/index.html). Dimension layers are also supported in mobile map packages or mobile geodatabases created in ArcGIS Pro 2.9 or later.
+
+## Tags
+
+dimension, layer, mmpk, mobile map package, utility
diff --git a/display-dimensions/README.metadata.json b/samples/display-dimensions/README.metadata.json
similarity index 100%
rename from display-dimensions/README.metadata.json
rename to samples/display-dimensions/README.metadata.json
diff --git a/samples/display-dimensions/build.gradle.kts b/samples/display-dimensions/build.gradle.kts
new file mode 100644
index 000000000..fb95a09c0
--- /dev/null
+++ b/samples/display-dimensions/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displaydimensions"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-dimensions/display-dimensions.png b/samples/display-dimensions/display-dimensions.png
similarity index 100%
rename from display-dimensions/display-dimensions.png
rename to samples/display-dimensions/display-dimensions.png
diff --git a/samples/display-dimensions/src/main/AndroidManifest.xml b/samples/display-dimensions/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..c17246a0f
--- /dev/null
+++ b/samples/display-dimensions/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt b/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt
new file mode 100644
index 000000000..070b8d248
--- /dev/null
+++ b/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/DownloadActivity.kt
@@ -0,0 +1,21 @@
+package com.esri.arcgismaps.sample.displaydimensions
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.display_dimensions_app_name),
+ listOf(
+ // ArcGIS Portal item containing the mmpk file which is a section of the
+ // high-voltage electricity transmission network around Edinburgh, Scotland.
+ "https://www.arcgis.com/home/item.html?id=f5ff6f5556a945bca87ca513b8729a1e"
+ )
+ )
+ }
+}
diff --git a/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt b/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt
new file mode 100644
index 000000000..73ac1d602
--- /dev/null
+++ b/samples/display-dimensions/src/main/java/com/esri/arcgismaps/sample/displaydimensions/MainActivity.kt
@@ -0,0 +1,135 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaydimensions
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.MobileMapPackage
+import com.arcgismaps.mapping.layers.DimensionLayer
+import com.esri.arcgismaps.sample.displaydimensions.databinding.DisplayDimensionsActivityMainBinding
+import com.esri.arcgismaps.sample.displaydimensions.databinding.DimensionsDialogLayoutBinding
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.display_dimensions_app_name)
+ }
+
+ private val activityMainBinding: DisplayDimensionsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.display_dimensions_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val optionsButton by lazy {
+ activityMainBinding.optionsButton
+ }
+
+ // keep an instance of the MapView's dimension layer
+ private var dimensionLayer: DimensionLayer? = null
+
+ // track if the layer is enabled
+ private var isDimensionLayerEnabled: Boolean = true
+
+ // track if the custom definition is enabled
+ private var isDefinitionEnabled: Boolean = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // check if the .mmpk file exits
+ val mmpkFile = File(provisionPath, getString(R.string.file_name))
+ if (!mmpkFile.exists()) return showError("Mobile map package file does not exist.")
+
+ // create and load a mobile map package
+ val mobileMapPackage = MobileMapPackage(mmpkFile.path)
+
+ lifecycleScope.launch {
+ // load the mobile map package
+ mobileMapPackage.load().getOrElse {
+ return@launch showError("Failed to load the mobile map package: ${it.message}")
+ }
+ // if the loaded mobile map package does not contain a map
+ if (mobileMapPackage.maps.isEmpty()) {
+ return@launch showError("Mobile map package does not contain a map")
+ }
+
+ // add the map from the mobile map package to the map view,
+ // and set a min scale to maintain dimension readability
+ mapView.map = mobileMapPackage.maps[0]
+ mapView.map?.minScale = 35000.0
+
+ // set the dimension layer within the map
+ dimensionLayer = mapView.map?.operationalLayers?.firstOrNull { layer ->
+ layer is DimensionLayer
+ } as DimensionLayer
+
+ }
+
+ optionsButton.setOnClickListener {
+ // inflate the dialog layout and get references to each of its components
+ val dialogBinding = DimensionsDialogLayoutBinding.inflate(layoutInflater)
+ dialogBinding.dimensionLayerSwitch.apply {
+ isChecked = isDimensionLayerEnabled
+ setOnCheckedChangeListener { _, isEnabled ->
+ // set the visibility of the dimension layer
+ dimensionLayer?.isVisible = isEnabled
+ isDimensionLayerEnabled = isEnabled
+ }
+ }
+ dialogBinding.definitionSwitch.apply {
+ isChecked = isDefinitionEnabled
+ setOnCheckedChangeListener { _, isEnabled ->
+ // set a definition expression to show dimension lengths of
+ // greater than or equal to 450m when the checkbox is selected,
+ // or to reset the definition expression to show all
+ // dimension lengths when unselected
+ val defExpression = if (isEnabled) "DIMLENGTH >= 450" else ""
+ dimensionLayer?.definitionExpression = defExpression
+ isDefinitionEnabled = isEnabled
+ }
+ }
+
+ // set up the dialog
+ MaterialAlertDialogBuilder(this).apply {
+ setView(dialogBinding.root)
+ setTitle("${getString(R.string.settings)}: ${dimensionLayer?.name}")
+ }.show()
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/display-dimensions/src/main/res/layout/dimensions_dialog_layout.xml b/samples/display-dimensions/src/main/res/layout/dimensions_dialog_layout.xml
similarity index 100%
rename from display-dimensions/src/main/res/layout/dimensions_dialog_layout.xml
rename to samples/display-dimensions/src/main/res/layout/dimensions_dialog_layout.xml
diff --git a/display-dimensions/src/main/res/layout/activity_main.xml b/samples/display-dimensions/src/main/res/layout/display_dimensions_activity_main.xml
similarity index 100%
rename from display-dimensions/src/main/res/layout/activity_main.xml
rename to samples/display-dimensions/src/main/res/layout/display_dimensions_activity_main.xml
diff --git a/samples/display-dimensions/src/main/res/values/strings.xml b/samples/display-dimensions/src/main/res/values/strings.xml
new file mode 100644
index 000000000..61f9b37e7
--- /dev/null
+++ b/samples/display-dimensions/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Display dimensions
+ Dimension settings
+ Dimension Layer
+ Definition expression: Dimensions >= 450m
+ Edinburgh_Pylon_Dimensions.mmpk
+
diff --git a/display-map-from-mobile-map-package/README.md b/samples/display-map-from-mobile-map-package/README.md
similarity index 100%
rename from display-map-from-mobile-map-package/README.md
rename to samples/display-map-from-mobile-map-package/README.md
diff --git a/display-map-from-mobile-map-package/README.metadata.json b/samples/display-map-from-mobile-map-package/README.metadata.json
similarity index 100%
rename from display-map-from-mobile-map-package/README.metadata.json
rename to samples/display-map-from-mobile-map-package/README.metadata.json
diff --git a/samples/display-map-from-mobile-map-package/build.gradle.kts b/samples/display-map-from-mobile-map-package/build.gradle.kts
new file mode 100644
index 000000000..c14b759b0
--- /dev/null
+++ b/samples/display-map-from-mobile-map-package/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displaymapfrommobilemappackage"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-map-from-mobile-map-package/display-map-from-mobile-map-package.png b/samples/display-map-from-mobile-map-package/display-map-from-mobile-map-package.png
similarity index 100%
rename from display-map-from-mobile-map-package/display-map-from-mobile-map-package.png
rename to samples/display-map-from-mobile-map-package/display-map-from-mobile-map-package.png
diff --git a/samples/display-map-from-mobile-map-package/src/main/AndroidManifest.xml b/samples/display-map-from-mobile-map-package/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..cf75f2449
--- /dev/null
+++ b/samples/display-map-from-mobile-map-package/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt b/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt
new file mode 100644
index 000000000..4d84e6914
--- /dev/null
+++ b/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/DownloadActivity.kt
@@ -0,0 +1,37 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaymapfrommobilemappackage
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.display_map_from_mobile_map_package_app_name),
+ listOf(
+ // ArcGIS Portal item containing the .mmpk mobile map package
+ "https://www.arcgis.com/home/item.html?id=e1f3a7254cb845b09450f54937c16061"
+ )
+
+ )
+ }
+}
diff --git a/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt b/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt
new file mode 100644
index 000000000..44bcd2ef5
--- /dev/null
+++ b/samples/display-map-from-mobile-map-package/src/main/java/com/esri/arcgismaps/sample/displaymapfrommobilemappackage/MainActivity.kt
@@ -0,0 +1,73 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaymapfrommobilemappackage
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.MobileMapPackage
+import com.esri.arcgismaps.sample.displaymapfrommobilemappackage.databinding.DisplayMapFromMobileMapPackageActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.display_map_from_mobile_map_package_app_name)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: DisplayMapFromMobileMapPackageActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.display_map_from_mobile_map_package_activity_main)
+ // create and add the MapView to the lifecycle
+ val mapView = activityMainBinding.mapView
+ lifecycle.addObserver(mapView)
+
+ // get the file path of the (.mmpk) file
+ val filePath = provisionPath + getString(R.string.yellowstone_mmpk)
+
+ // create the mobile map package
+ val mapPackage = MobileMapPackage(filePath)
+
+ lifecycleScope.launch {
+ // load the mobile map package
+ mapPackage.load().getOrElse {
+ showError(it.message.toString(), mapView)
+ }
+ // add the map from the mobile map package to the MapView
+ mapView.map = mapPackage.maps.first()
+ }
+ }
+
+ private fun showError(message: String, view: View) {
+ Log.e(localClassName, message)
+ Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/display-map-from-mobile-map-package/src/main/res/layout/activity_main.xml b/samples/display-map-from-mobile-map-package/src/main/res/layout/display_map_from_mobile_map_package_activity_main.xml
similarity index 100%
rename from display-map-from-mobile-map-package/src/main/res/layout/activity_main.xml
rename to samples/display-map-from-mobile-map-package/src/main/res/layout/display_map_from_mobile_map_package_activity_main.xml
diff --git a/samples/display-map-from-mobile-map-package/src/main/res/values/strings.xml b/samples/display-map-from-mobile-map-package/src/main/res/values/strings.xml
new file mode 100644
index 000000000..99fbf5a01
--- /dev/null
+++ b/samples/display-map-from-mobile-map-package/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Display map from mobile map package
+ /Yellowstone.mmpk
+
diff --git a/display-map/README.md b/samples/display-map/README.md
similarity index 100%
rename from display-map/README.md
rename to samples/display-map/README.md
diff --git a/display-map/README.metadata.json b/samples/display-map/README.metadata.json
similarity index 100%
rename from display-map/README.metadata.json
rename to samples/display-map/README.metadata.json
diff --git a/samples/display-map/build.gradle.kts b/samples/display-map/build.gradle.kts
new file mode 100644
index 000000000..47192382b
--- /dev/null
+++ b/samples/display-map/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displaymap"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-map/display-map.png b/samples/display-map/display-map.png
similarity index 100%
rename from display-map/display-map.png
rename to samples/display-map/display-map.png
diff --git a/samples/display-map/src/main/AndroidManifest.xml b/samples/display-map/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..5b1eb6071
--- /dev/null
+++ b/samples/display-map/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt b/samples/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt
new file mode 100644
index 000000000..89055585b
--- /dev/null
+++ b/samples/display-map/src/main/java/com/esri/arcgismaps/sample/displaymap/MainActivity.kt
@@ -0,0 +1,46 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displaymap
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.esri.arcgismaps.sample.displaymap.databinding.DisplayMapActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: DisplayMapActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.display_map_activity_main)
+ lifecycle.addObserver(activityMainBinding.mapView)
+
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ activityMainBinding.mapView.map = map
+ }
+}
diff --git a/display-map/src/main/res/layout/activity_main.xml b/samples/display-map/src/main/res/layout/display_map_activity_main.xml
similarity index 100%
rename from display-map/src/main/res/layout/activity_main.xml
rename to samples/display-map/src/main/res/layout/display_map_activity_main.xml
diff --git a/samples/display-map/src/main/res/values/strings.xml b/samples/display-map/src/main/res/values/strings.xml
new file mode 100644
index 000000000..22352885c
--- /dev/null
+++ b/samples/display-map/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Display map
+
diff --git a/display-scene-from-mobile-scene-package/README.md b/samples/display-scene-from-mobile-scene-package/README.md
similarity index 100%
rename from display-scene-from-mobile-scene-package/README.md
rename to samples/display-scene-from-mobile-scene-package/README.md
diff --git a/display-scene-from-mobile-scene-package/README.metadata.json b/samples/display-scene-from-mobile-scene-package/README.metadata.json
similarity index 100%
rename from display-scene-from-mobile-scene-package/README.metadata.json
rename to samples/display-scene-from-mobile-scene-package/README.metadata.json
diff --git a/samples/display-scene-from-mobile-scene-package/build.gradle.kts b/samples/display-scene-from-mobile-scene-package/build.gradle.kts
new file mode 100644
index 000000000..282737413
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displayscenefrommobilescenepackage"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-scene-from-mobile-scene-package/display-scene-from-mobile-scene-package.png b/samples/display-scene-from-mobile-scene-package/display-scene-from-mobile-scene-package.png
similarity index 100%
rename from display-scene-from-mobile-scene-package/display-scene-from-mobile-scene-package.png
rename to samples/display-scene-from-mobile-scene-package/display-scene-from-mobile-scene-package.png
diff --git a/samples/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml b/samples/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b47362fa2
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt
new file mode 100644
index 000000000..754843bea
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/DownloadActivity.kt
@@ -0,0 +1,36 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.display_scene_from_mobile_scene_package_app_name),
+ listOf(
+ // ArcGIS Portal item containing the .mspk mobile scene package
+ "https://www.arcgis.com/home/item.html?id=7dd2f97bb007466ea939160d0de96a9d"
+ )
+ )
+ }
+}
diff --git a/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt
new file mode 100644
index 000000000..0502503b5
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/MainActivity.kt
@@ -0,0 +1,51 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.screens.MainScreen
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ DisplaySceneFromMobileScenePackageApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun DisplaySceneFromMobileScenePackageApp() {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ MainScreen(sampleName = getString(R.string.display_scene_from_mobile_scene_package_app_name))
+ }
+ }
+}
diff --git a/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt
new file mode 100644
index 000000000..7e7b8fce7
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/components/SceneViewModel.kt
@@ -0,0 +1,72 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.components
+
+import android.app.Application
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.AndroidViewModel
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.MobileScenePackage
+import com.esri.arcgismaps.sample.displayscenefrommobilescenepackage.R
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import java.io.File
+
+class SceneViewModel(
+ private val application: Application,
+ private val sampleCoroutineScope: CoroutineScope
+) : AndroidViewModel(application) {
+
+ // create a base scene to be used to load the mobile scene package
+ var scene by mutableStateOf(ArcGISScene(BasemapStyle.ArcGISStreets))
+
+ // create a ViewModel to handle dialog interactions
+ val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
+
+ private val provisionPath: String by lazy {
+ application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
+ R.string.display_scene_from_mobile_scene_package_app_name
+ )
+ }
+
+ init {
+ createMobileScenePackage()
+ }
+
+ private fun createMobileScenePackage() {
+ // get the file path of the (.mspk) file
+ val filePath = provisionPath + application.getString(R.string.philadelphia_mspk)
+
+ // create the mobile scene package
+ val mobileScenePackage = MobileScenePackage(filePath)
+
+ sampleCoroutineScope.launch {
+ // load the mobile scene package
+ mobileScenePackage.load().onSuccess {
+ // update the mutable state holder with the first scene from the MobileScenePackage
+ scene = mobileScenePackage.scenes.first()
+ }.onFailure { error ->
+ // show the message dialog and pass the error message to be displayed in the dialog
+ messageDialogVM.showMessageDialog(error.message.toString(), error.cause.toString())
+ }
+ }
+ }
+}
diff --git a/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/screens/MainScreen.kt b/samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/screens/MainScreen.kt
similarity index 100%
rename from display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/screens/MainScreen.kt
rename to samples/display-scene-from-mobile-scene-package/src/main/java/com/esri/arcgismaps/sample/displayscenefrommobilescenepackage/screens/MainScreen.kt
diff --git a/samples/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml b/samples/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml
new file mode 100644
index 000000000..e14228453
--- /dev/null
+++ b/samples/display-scene-from-mobile-scene-package/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Display scene from mobile scene package
+ /philadelphia.mspk
+
diff --git a/display-scene/README.md b/samples/display-scene/README.md
similarity index 100%
rename from display-scene/README.md
rename to samples/display-scene/README.md
diff --git a/display-scene/README.metadata.json b/samples/display-scene/README.metadata.json
similarity index 100%
rename from display-scene/README.metadata.json
rename to samples/display-scene/README.metadata.json
diff --git a/samples/display-scene/build.gradle.kts b/samples/display-scene/build.gradle.kts
new file mode 100644
index 000000000..753f3eb0d
--- /dev/null
+++ b/samples/display-scene/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.displayscene"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/display-scene/display-scene.png b/samples/display-scene/display-scene.png
similarity index 100%
rename from display-scene/display-scene.png
rename to samples/display-scene/display-scene.png
diff --git a/samples/display-scene/src/main/AndroidManifest.xml b/samples/display-scene/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..dce91fa1b
--- /dev/null
+++ b/samples/display-scene/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt b/samples/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt
new file mode 100644
index 000000000..3f267330f
--- /dev/null
+++ b/samples/display-scene/src/main/java/com/esri/arcgismaps/sample/displayscene/MainActivity.kt
@@ -0,0 +1,76 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.displayscene
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.ArcGISTiledElevationSource
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.view.Camera
+import com.esri.arcgismaps.sample.displayscene.databinding.DisplaySceneActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: DisplaySceneActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.display_scene_activity_main)
+ }
+
+ private val sceneView by lazy {
+ activityMainBinding.sceneView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(sceneView)
+
+ // create an elevation source, and add this to the base surface of the scene
+ val elevationSource = ArcGISTiledElevationSource(
+ resources.getString(R.string.elevation_image_service)
+ )
+
+ // create a scene with a imagery basemap style
+ val imageryScene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
+ // add the elevation source to the base surface
+ baseSurface.elevationSources.add(elevationSource)
+ }
+
+ // add a camera and initial camera position
+ val camera = Camera(
+ latitude = 28.4,
+ longitude = 83.9,
+ altitude = 10010.0,
+ heading = 10.0,
+ pitch = 80.0,
+ roll = 0.0
+ )
+
+ // apply the scene to the sceneView and set its viewpoint
+ sceneView.apply {
+ scene = imageryScene
+ setViewpointCamera(camera)
+ }
+ }
+}
diff --git a/display-scene/src/main/res/layout/activity_main.xml b/samples/display-scene/src/main/res/layout/display_scene_activity_main.xml
similarity index 100%
rename from display-scene/src/main/res/layout/activity_main.xml
rename to samples/display-scene/src/main/res/layout/display_scene_activity_main.xml
diff --git a/samples/display-scene/src/main/res/values/strings.xml b/samples/display-scene/src/main/res/values/strings.xml
new file mode 100644
index 000000000..2e3621abb
--- /dev/null
+++ b/samples/display-scene/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Display scene
+ https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
+
diff --git a/download-vector-tiles-to-local-cache/README.md b/samples/download-vector-tiles-to-local-cache/README.md
similarity index 100%
rename from download-vector-tiles-to-local-cache/README.md
rename to samples/download-vector-tiles-to-local-cache/README.md
diff --git a/download-vector-tiles-to-local-cache/README.metadata.json b/samples/download-vector-tiles-to-local-cache/README.metadata.json
similarity index 100%
rename from download-vector-tiles-to-local-cache/README.metadata.json
rename to samples/download-vector-tiles-to-local-cache/README.metadata.json
diff --git a/samples/download-vector-tiles-to-local-cache/build.gradle.kts b/samples/download-vector-tiles-to-local-cache/build.gradle.kts
new file mode 100644
index 000000000..bc08ac45a
--- /dev/null
+++ b/samples/download-vector-tiles-to-local-cache/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.downloadvectortilestolocalcache"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/download-vector-tiles-to-local-cache/download-vector-tiles-to-local-cache.png b/samples/download-vector-tiles-to-local-cache/download-vector-tiles-to-local-cache.png
similarity index 100%
rename from download-vector-tiles-to-local-cache/download-vector-tiles-to-local-cache.png
rename to samples/download-vector-tiles-to-local-cache/download-vector-tiles-to-local-cache.png
diff --git a/samples/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml b/samples/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..03e257664
--- /dev/null
+++ b/samples/download-vector-tiles-to-local-cache/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt b/samples/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt
new file mode 100644
index 000000000..7e793d6a0
--- /dev/null
+++ b/samples/download-vector-tiles-to-local-cache/src/main/java/com/esri/arcgismaps/sample/downloadvectortilestolocalcache/MainActivity.kt
@@ -0,0 +1,329 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.downloadvectortilestolocalcache
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.ViewpointType
+import com.arcgismaps.mapping.layers.ArcGISVectorTiledLayer
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesJob
+import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesParameters
+import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesResult
+import com.arcgismaps.tasks.exportvectortiles.ExportVectorTilesTask
+import com.esri.arcgismaps.sample.downloadvectortilestolocalcache.databinding.DownloadVectorTilesToLocalCacheActivityMainBinding
+import com.esri.arcgismaps.sample.downloadvectortilestolocalcache.databinding.ProgressDialogLayoutBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ private val downloadArea: Graphic = Graphic()
+ private var hasCurrentJobCompleted: Boolean = true
+
+ // set up data binding for the activity
+ private val activityMainBinding: DownloadVectorTilesToLocalCacheActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.download_vector_tiles_to_local_cache_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val previewMapView by lazy {
+ activityMainBinding.previewMapView
+ }
+
+ private val exportVectorTilesButton: MaterialButton by lazy {
+ activityMainBinding.exportVectorTilesButton
+ }
+
+ private val closePreviewButton: MaterialButton by lazy {
+ activityMainBinding.closePreviewButton
+ }
+
+ // inflate the progress dialog
+ private val dialogLayoutBinding by lazy {
+ ProgressDialogLayoutBinding.inflate(layoutInflater)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // add mapView to the lifecycle
+ lifecycle.addObserver(mapView)
+ lifecycle.addObserver(previewMapView)
+
+ // create a graphic to show a red outline square around the vector tiles to be downloaded
+ downloadArea.symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2f)
+
+ // create a graphics overlay and add the downloadArea graphic
+ val graphicsOverlay = GraphicsOverlay(listOf(downloadArea))
+
+ mapView.apply {
+ // set the map to BasemapType navigation night
+ map = ArcGISMap(BasemapStyle.ArcGISStreetsNight)
+ // disable rotation
+ interactionOptions.isRotateEnabled = false
+ // set the viewpoint of the sample to ESRI Redlands, CA campus
+ setViewpoint(Viewpoint(34.056295, -117.195800, 100000.0))
+ // add the graphics overlay to the MapView
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ lifecycleScope.launch {
+ mapView.map?.load()?.onSuccess {
+ // enable the export tiles button
+ exportVectorTilesButton.isEnabled = true
+ // update the red square whenever the viewpoint changes
+ mapView.viewpointChanged.collect {
+ updateDownloadAreaGeometry()
+ }
+ }?.onFailure {
+ showMessage("Error loading map")
+ }
+ }
+ }
+
+ /**
+ * Sets up the ExportVectorTilesTask on export button click.
+ * Then calls handleExportVectorTilesJob()
+ */
+ fun exportButtonClick(view: View) {
+ // check that the layer from the basemap is a vector tiled layer
+ val vectorTiledLayer =
+ mapView.map?.basemap?.value?.baseLayers?.get(0) as ArcGISVectorTiledLayer
+
+ lifecycleScope.launch {
+ // update the download area's geometry using the current viewpoint
+ updateDownloadAreaGeometry()
+ // create a new export vector tiles task
+ val exportVectorTilesTask = ExportVectorTilesTask(vectorTiledLayer.uri.toString())
+ val geometry = downloadArea.geometry
+ if (geometry == null) {
+ showMessage("Error retrieving download area geometry")
+ return@launch
+ }
+ // set the parameters of the export vector tiles task
+ // using the geometry of the area to export and it's max scale
+ val exportVectorTilesParametersResult = exportVectorTilesTask
+ .createDefaultExportVectorTilesParameters(
+ geometry,
+ // set the max scale parameter to 10% of the map's scale so the
+ // number of tiles exported are within the vector tiled layer's max tile export limit
+ mapView.mapScale.value * 0.1
+ )
+
+ // get the loaded vector tile parameters
+ val exportVectorTilesParameters = exportVectorTilesParametersResult.getOrElse {
+ showMessage(it.message.toString())
+ return@launch
+ }
+
+ if (hasCurrentJobCompleted) {
+ // create a job to export vector tiles
+ initiateExportTilesJob(
+ exportVectorTilesParameters,
+ exportVectorTilesTask
+ )
+ } else {
+ showMessage("Previous job is cancelling asynchronously")
+ }
+ }
+
+ }
+
+ /**
+ * Start the export vector tiles job using [exportVectorTilesTask] and the
+ * [exportVectorTilesParameters]. The vector tile package is exported as "file.vtpk"
+ */
+ private fun initiateExportTilesJob(
+ exportVectorTilesParameters: ExportVectorTilesParameters,
+ exportVectorTilesTask: ExportVectorTilesTask
+ ) {
+ // create a .vtpk and directory in the app's cache for saving exported tiles
+ val vtpkFile = File(externalCacheDir, "/StyleItemResources/myVectorTiles.vtpk")
+ val resDir = File(externalCacheDir, "/StyleItemResources")
+ resDir.deleteRecursively()
+ resDir.mkdir()
+
+ // create a job with the export vector tile parameters
+ // and exports the vector tile package as "file.vtpk"
+ val exportVectorTilesJob = exportVectorTilesTask.createExportVectorTilesJob(
+ exportVectorTilesParameters,
+ vtpkFile.absolutePath, resDir.absolutePath
+ ).apply {
+ // start the export vector tile cache job
+ start()
+ }
+
+ // display the progress dialog
+ val dialog = createProgressDialog(exportVectorTilesJob).show()
+
+ // since job is now started, set to false
+ hasCurrentJobCompleted = false
+
+ // set the value of the job's progress
+ with(lifecycleScope) {
+ // collect the progress of the job
+ launch {
+ exportVectorTilesJob.progress.collect {
+ val progress = exportVectorTilesJob.progress.value
+ dialogLayoutBinding.progressBar.progress = progress
+ dialogLayoutBinding.progressTextView.text = "$progress% completed"
+ }
+ }
+ // display map if job succeeds
+ launch {
+ exportVectorTilesJob.result().onSuccess {
+ // display the map preview using the result from the completed job
+ showMapPreview(it)
+ // set job is completed
+ hasCurrentJobCompleted = true
+ // display the path of the saved vector tiles
+ showMessage(it.vectorTileCache?.path.toString())
+ // dismiss loading dialog
+ dialog.dismiss()
+ }.onFailure {
+ showMessage(it.message.toString())
+ dialog.dismiss()
+ hasCurrentJobCompleted = true
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Updates the [downloadArea]'s geometry when called with viewpoint change
+ * or when export tiles button is clicked.
+ */
+ private fun updateDownloadAreaGeometry() {
+ // upper left corner of the downloaded tile cache area
+ val minScreenPoint = ScreenCoordinate(150.0, 175.0)
+ // lower right corner of the downloaded tile cache area
+ val maxScreenPoint = ScreenCoordinate(
+ mapView.width - 150.0,
+ mapView.height - 250.0
+ )
+ // convert screen points to map points
+ val minPoint = mapView.screenToLocation(minScreenPoint)
+ val maxPoint = mapView.screenToLocation(maxScreenPoint)
+ if (minPoint != null && maxPoint != null) {
+ // use the points to define and return an envelope
+ downloadArea.geometry = Envelope(minPoint, maxPoint)
+ } else {
+ showMessage("Error getting screen coordinate")
+ }
+ }
+
+ /**
+ * Create a progress dialog to track the progress of the [exportVectorTilesJob]
+ */
+ private fun createProgressDialog(exportVectorTilesJob: ExportVectorTilesJob): MaterialAlertDialogBuilder {
+ return MaterialAlertDialogBuilder(this@MainActivity).apply {
+ setTitle("Exporting vector tiles")
+ setNegativeButton("Cancel job") { _, _ ->
+ lifecycleScope.launch {
+ // cancels the export job asynchronously
+ exportVectorTilesJob.cancel().getOrElse {
+ showMessage(it.message.toString())
+ }
+ // cancel is completed, so set to true
+ hasCurrentJobCompleted = true
+ }
+ }
+ // removes parent of the progressDialog layout, if previously assigned
+ dialogLayoutBinding.root.parent?.let { parent ->
+ (parent as ViewGroup).removeAllViews()
+ }
+ setCancelable(false)
+ setView(dialogLayoutBinding.root)
+ }
+ }
+
+ /**
+ * Display the preview of the exported map using the
+ * [vectorTilesResult] from the completed job
+ */
+ private fun showMapPreview(vectorTilesResult: ExportVectorTilesResult) {
+ val vectorTileCache = vectorTilesResult.vectorTileCache
+ if (vectorTileCache == null) {
+ showMessage("Cannot find tile cache")
+ return
+ }
+ // get the layer exported for the preview MapView
+ val vectorTiledLayer = ArcGISVectorTiledLayer(
+ vectorTileCache,
+ vectorTilesResult.itemResourceCache
+ )
+
+ // control UI visibility
+ previewMapVisibility(true)
+
+ // set up the preview MapView
+ previewMapView.apply {
+ map = ArcGISMap(Basemap(vectorTiledLayer))
+ mapView.getCurrentViewpoint(ViewpointType.CenterAndScale)?.let { setViewpoint(it) }
+ }
+ closePreviewButton.setOnClickListener {
+ previewMapVisibility(false)
+ }
+
+ }
+
+ /**
+ * Controls the visibility of the preview map and the export buttons.
+ */
+ private fun previewMapVisibility(isVisible: Boolean) = if (isVisible) {
+ exportVectorTilesButton.visibility = View.INVISIBLE
+ closePreviewButton.visibility = View.VISIBLE
+ previewMapView.visibility = View.VISIBLE
+ } else {
+ exportVectorTilesButton.visibility = View.VISIBLE
+ closePreviewButton.visibility = View.INVISIBLE
+ previewMapView.visibility = View.GONE
+ }
+
+ private fun showMessage(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/download-vector-tiles-to-local-cache/src/main/res/layout/activity_main.xml b/samples/download-vector-tiles-to-local-cache/src/main/res/layout/download_vector_tiles_to_local_cache_activity_main.xml
similarity index 100%
rename from download-vector-tiles-to-local-cache/src/main/res/layout/activity_main.xml
rename to samples/download-vector-tiles-to-local-cache/src/main/res/layout/download_vector_tiles_to_local_cache_activity_main.xml
diff --git a/download-vector-tiles-to-local-cache/src/main/res/layout/progress_dialog_layout.xml b/samples/download-vector-tiles-to-local-cache/src/main/res/layout/progress_dialog_layout.xml
similarity index 100%
rename from download-vector-tiles-to-local-cache/src/main/res/layout/progress_dialog_layout.xml
rename to samples/download-vector-tiles-to-local-cache/src/main/res/layout/progress_dialog_layout.xml
diff --git a/samples/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml b/samples/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml
new file mode 100644
index 000000000..08870ec6c
--- /dev/null
+++ b/samples/download-vector-tiles-to-local-cache/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Download vector tiles to local cache
+ Download Vector Tiles
+ Close vector tile preview
+
diff --git a/edit-and-sync-features-with-feature-service/README.md b/samples/edit-and-sync-features-with-feature-service/README.md
similarity index 100%
rename from edit-and-sync-features-with-feature-service/README.md
rename to samples/edit-and-sync-features-with-feature-service/README.md
diff --git a/edit-and-sync-features-with-feature-service/README.metadata.json b/samples/edit-and-sync-features-with-feature-service/README.metadata.json
similarity index 100%
rename from edit-and-sync-features-with-feature-service/README.metadata.json
rename to samples/edit-and-sync-features-with-feature-service/README.metadata.json
diff --git a/samples/edit-and-sync-features-with-feature-service/build.gradle.kts b/samples/edit-and-sync-features-with-feature-service/build.gradle.kts
new file mode 100644
index 000000000..dc5dbf63a
--- /dev/null
+++ b/samples/edit-and-sync-features-with-feature-service/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/edit-and-sync-features-with-feature-service/edit-and-sync-features-with-feature-service.png b/samples/edit-and-sync-features-with-feature-service/edit-and-sync-features-with-feature-service.png
similarity index 100%
rename from edit-and-sync-features-with-feature-service/edit-and-sync-features-with-feature-service.png
rename to samples/edit-and-sync-features-with-feature-service/edit-and-sync-features-with-feature-service.png
diff --git a/samples/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml b/samples/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..95d583ab0
--- /dev/null
+++ b/samples/edit-and-sync-features-with-feature-service/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt b/samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt
new file mode 100644
index 000000000..ffb80a53c
--- /dev/null
+++ b/samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.editandsyncfeatureswithfeatureservice.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.edit_and_sync_features_with_feature_service_app_name)
+ )
+ }
+ }
+}
diff --git a/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/components/MapViewModel.kt b/samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/components/MapViewModel.kt
similarity index 100%
rename from edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/components/MapViewModel.kt
rename to samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/components/MapViewModel.kt
diff --git a/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/screens/MainScreen.kt b/samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/screens/MainScreen.kt
similarity index 100%
rename from edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/screens/MainScreen.kt
rename to samples/edit-and-sync-features-with-feature-service/src/main/java/com/esri/arcgismaps/sample/editandsyncfeatureswithfeatureservice/screens/MainScreen.kt
diff --git a/samples/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml b/samples/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml
new file mode 100644
index 000000000..acb1261c2
--- /dev/null
+++ b/samples/edit-and-sync-features-with-feature-service/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Edit and Sync Features with Feature Service
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/WildfireSync/FeatureServer
+ Generate Geodatabase
+ Sync Geodatabase
+ wildfire_gdb.geodatabase
+
diff --git a/edit-feature-attachments/README.md b/samples/edit-feature-attachments/README.md
similarity index 100%
rename from edit-feature-attachments/README.md
rename to samples/edit-feature-attachments/README.md
diff --git a/edit-feature-attachments/README.metadata.json b/samples/edit-feature-attachments/README.metadata.json
similarity index 100%
rename from edit-feature-attachments/README.metadata.json
rename to samples/edit-feature-attachments/README.metadata.json
diff --git a/samples/edit-feature-attachments/build.gradle.kts b/samples/edit-feature-attachments/build.gradle.kts
new file mode 100644
index 000000000..ed7fd74e4
--- /dev/null
+++ b/samples/edit-feature-attachments/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.editfeatureattachments"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/edit-feature-attachments/edit-feature-attachments.png b/samples/edit-feature-attachments/edit-feature-attachments.png
similarity index 100%
rename from edit-feature-attachments/edit-feature-attachments.png
rename to samples/edit-feature-attachments/edit-feature-attachments.png
diff --git a/samples/edit-feature-attachments/src/main/AndroidManifest.xml b/samples/edit-feature-attachments/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..6be80bdd9
--- /dev/null
+++ b/samples/edit-feature-attachments/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/AttachmentsBottomSheet.kt b/samples/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/AttachmentsBottomSheet.kt
similarity index 100%
rename from edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/AttachmentsBottomSheet.kt
rename to samples/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/AttachmentsBottomSheet.kt
diff --git a/samples/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt b/samples/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt
new file mode 100644
index 000000000..e5d0cb1a9
--- /dev/null
+++ b/samples/edit-feature-attachments/src/main/java/com/esri/arcgismaps/sample/editfeatureattachments/MainActivity.kt
@@ -0,0 +1,341 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.editfeatureattachments
+
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.net.Uri
+import android.os.Bundle
+import android.provider.MediaStore
+import android.util.Log
+import android.view.ViewGroup
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.FileProvider
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.data.ArcGISFeature
+import com.arcgismaps.data.Attachment
+import com.arcgismaps.data.FeatureRequestMode
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.GeoElement
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.view.IdentifyLayerResult
+import com.esri.arcgismaps.sample.editfeatureattachments.databinding.EditFeatureAttachmentsActivityMainBinding
+import com.esri.arcgismaps.sample.editfeatureattachments.databinding.AttachmentEditSheetBinding
+import com.esri.arcgismaps.sample.editfeatureattachments.databinding.AttachmentLoadingDialogBinding
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+import java.io.FileOutputStream
+
+class MainActivity : AppCompatActivity() {
+
+ private val activityMainBinding: EditFeatureAttachmentsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.edit_feature_attachments_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val attachmentsSheetBinding by lazy {
+ AttachmentEditSheetBinding.inflate(layoutInflater)
+ }
+
+ private val loadingDialogBinding by lazy {
+ AttachmentLoadingDialogBinding.inflate(layoutInflater)
+ }
+
+ // load the Damage to Residential Buildings feature server
+ private val serviceFeatureTable by lazy {
+ ServiceFeatureTable(getString(R.string.edit_feature_attachments_sample_service_url)).apply {
+ // set the feature request mode to request from the server as they are needed
+ featureRequestMode = FeatureRequestMode.OnInteractionCache
+ }
+ }
+
+ // registers the activity for an image data result from the default image picker
+ private val activityResultLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ result.data?.data?.let { imageUri ->
+ // add the image data as a feature attachment
+ addFeatureAttachment(imageUri)
+ }
+ }
+
+ // tracks the selected ArcGISFeature and it's attachments
+ private var selectedArcGISFeature: ArcGISFeature? = null
+
+ // tracks the instance of the bottom sheet
+ private var bottomSheet: BottomSheetDialog? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create the feature layer using the service feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
+
+ // create and add a map with a streets basemap style
+ val streetsMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
+ operationalLayers.add(featureLayer)
+ }
+ // set the map and the viewpoint to the MapView
+ mapView.apply {
+ map = streetsMap
+ setViewpoint(Viewpoint(40.0, -95.0, 1e8))
+ }
+
+ // identify feature selected on map tap
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect { tapConfirmedEvent ->
+ // clear any previous selection
+ featureLayer.clearSelection()
+ // identify tapped feature
+ val layerResult = mapView.identifyLayer(
+ layer = featureLayer,
+ screenCoordinate = tapConfirmedEvent.screenCoordinate,
+ tolerance = 5.0,
+ returnPopupsOnly = false,
+ maximumResults = 1
+ ).getOrElse { exception ->
+ showError("Failed to select feature: ${exception.message}")
+ } as IdentifyLayerResult
+
+ // get a list of identified elements
+ val resultGeoElements: List = layerResult.geoElements
+ // check if a feature was identified
+ if (resultGeoElements.isNotEmpty() && resultGeoElements.first() is ArcGISFeature) {
+ // retrieve and set the currently selected feature
+ val selectedFeature = resultGeoElements.first() as ArcGISFeature
+ // highlight the currently selected feature
+ featureLayer.selectFeature(selectedFeature)
+
+ // show the bottom sheet layout
+ createBottomSheet(selectedFeature)
+
+ // keep track of the selected feature
+ selectedArcGISFeature = selectedFeature
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates and displays a bottom sheet to display and modify
+ * the attachments of [selectedFeature]. Calls [AttachmentsBottomSheet] to
+ * inflate bottom sheet and listen for interactions.
+ */
+ private suspend fun createBottomSheet(selectedFeature: ArcGISFeature) {
+ // get the number of attachments
+ val attachmentList = selectedFeature.fetchAttachments().getOrElse {
+ return showError(it.message.toString())
+ }
+ // get the attribute "typdamage" of the selected feature
+ val damageTypeAttribute = selectedFeature.attributes["typdamage"].toString()
+
+ // creates a new BottomSheetDialog
+ bottomSheet = AttachmentsBottomSheet(
+ context = this@MainActivity,
+ bottomSheetBinding = attachmentsSheetBinding,
+ attachments = attachmentList,
+ damageType = damageTypeAttribute
+ )
+ // set the content view to the root of the binding layout
+ bottomSheet?.setContentView(attachmentsSheetBinding.root)
+ // display the bottom sheet view
+ bottomSheet?.show()
+ }
+
+ /**
+ * Retrieves the [attachment] data in the form of a byte array, converts it
+ * to a [BitmapDrawable], caches the bitmap as a png image, and open's the
+ * attachment image in the default image viewer.
+ */
+ suspend fun fetchAttachment(attachment: Attachment) {
+ // display loading dialog
+ val dialog = createLoadingDialog("Fetching attachment data").show()
+
+ // create folder /ArcGIS/Attachments in external storage
+ val fileDir = File(getExternalFilesDir(null)?.path + "/Attachments")
+ fileDir.mkdirs()
+ // create the file with the attachment name
+ val file = File(fileDir, attachment.name)
+
+ // file provider URI
+ val contentUri = FileProvider.getUriForFile(
+ this, getString(R.string.provider_authority), file
+ )
+ // open the file in gallery
+ val imageIntent = Intent().apply {
+ flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ action = Intent.ACTION_VIEW
+ setDataAndType(contentUri, "image/png")
+ }
+
+ // fetch the attachment data
+ attachment.fetchData().onSuccess {
+ // create a drawable from InputStream, then create the Bitmap
+ val bitmapDrawable = BitmapDrawable(
+ resources,
+ BitmapFactory.decodeByteArray(it, 0, it.size)
+ )
+ // create a file output stream using the attachment file
+ FileOutputStream(file).use { imageOutputStream ->
+ // compress the bitmap to PNG format
+ bitmapDrawable.bitmap.compress(Bitmap.CompressFormat.PNG, 90, imageOutputStream)
+ // start activity using created intent
+ startActivity(imageIntent)
+ // dismiss dialog
+ dialog.dismiss()
+ }
+ }.onFailure {
+ // dismiss dialog
+ dialog.dismiss()
+ showError(it.message.toString())
+ }
+ }
+
+ /**
+ * Adds a new attachment to the [selectedArcGISFeature] using the [selectedImageUri]
+ * and updates the changes with the feature service table
+ */
+ private fun addFeatureAttachment(selectedImageUri: Uri) {
+ // display a loading dialog
+ val dialog = createLoadingDialog("Adding feature attachment").show()
+
+ // create an input stream at the selected URI
+ contentResolver.openInputStream(selectedImageUri)?.use { imageInputStream ->
+ // get the byte array of the image input stream
+ val imageBytes: ByteArray = imageInputStream.readBytes()
+ // create the attachment name with the current time
+ val attachmentName = "attachment_${System.currentTimeMillis()}.png"
+
+ lifecycleScope.launch {
+ selectedArcGISFeature?.let { arcGISFeature ->
+ // add the attachment to the selected feature
+ arcGISFeature.addAttachment(
+ name = attachmentName,
+ contentType = "image/png",
+ data = imageBytes
+ ).onFailure {
+ return@launch showError(it.message.toString())
+ }
+ // update the feature changes in the loaded service feature table
+ serviceFeatureTable.updateFeature(arcGISFeature).getOrElse {
+ return@launch showError(it.message.toString())
+ }
+ }
+ applyServerEdits(dialog)
+ }
+ }
+ }
+
+ /**
+ * Delete the [attachment] from the [selectedArcGISFeature] and update the changes
+ * with the feature service table
+ */
+ fun deleteAttachment(attachment: Attachment) {
+ lifecycleScope.launch {
+ val dialog = createLoadingDialog("Deleting feature attachment").show()
+ selectedArcGISFeature?.let { arcGISFeature ->
+ // delete the attachment from the selected feature
+ arcGISFeature.deleteAttachment(attachment).getOrElse {
+ return@launch showError(it.message.toString())
+ }
+ // update the feature changes in the loaded service feature table
+ serviceFeatureTable.updateFeature(arcGISFeature).getOrElse {
+ return@launch showError(it.message.toString())
+ }
+ }
+ // apply changes back to the server
+ applyServerEdits(dialog)
+ }
+ }
+
+ /**
+ * Applies changes from a Service Feature Table to the server.
+ * The [dialog] will be dismissed when changes are applied.
+ */
+ private suspend fun applyServerEdits(dialog: AlertDialog) {
+ // close the bottom sheet, as it will be created
+ // after service changes are made
+ bottomSheet?.dismiss()
+
+ // apply edits to the server
+ val updatedServerResult = serviceFeatureTable.applyEdits()
+ updatedServerResult.onSuccess { edits ->
+ dialog.dismiss()
+ // check that the feature table was successfully updated
+ if (edits.isEmpty()) {
+ return showError(getString(R.string.failure_edit_results))
+ }
+ // if the edits were made successfully, create the bottom sheet to display new changes.
+ selectedArcGISFeature?.let { createBottomSheet(it) }
+ }.onFailure {
+ showError(it.message.toString())
+ dialog.dismiss()
+ }
+ }
+
+ /**
+ * Opens the default Android image selector
+ */
+ fun selectAttachment() {
+ val mediaIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
+ activityResultLauncher.launch(mediaIntent)
+ }
+
+ /**
+ * Creates a loading dialog with the [message]
+ */
+ private fun createLoadingDialog(message: String): MaterialAlertDialogBuilder {
+ // build and return a new alert dialog
+ return MaterialAlertDialogBuilder(this).apply {
+ // set message
+ setMessage(message)
+ // allow it to be cancellable
+ setCancelable(false)
+ // removes parent of the progressDialog layout, if previously assigned
+ loadingDialogBinding.root.parent?.let { parent ->
+ (parent as ViewGroup).removeAllViews()
+ }
+ // set the loading dialog layout to this alert dialog
+ setView(loadingDialogBinding.root)
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/edit-feature-attachments/src/main/res/drawable/right_arrow.xml b/samples/edit-feature-attachments/src/main/res/drawable/right_arrow.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/drawable/right_arrow.xml
rename to samples/edit-feature-attachments/src/main/res/drawable/right_arrow.xml
diff --git a/edit-feature-attachments/src/main/res/layout/attachment_edit_sheet.xml b/samples/edit-feature-attachments/src/main/res/layout/attachment_edit_sheet.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/layout/attachment_edit_sheet.xml
rename to samples/edit-feature-attachments/src/main/res/layout/attachment_edit_sheet.xml
diff --git a/edit-feature-attachments/src/main/res/layout/attachment_entry.xml b/samples/edit-feature-attachments/src/main/res/layout/attachment_entry.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/layout/attachment_entry.xml
rename to samples/edit-feature-attachments/src/main/res/layout/attachment_entry.xml
diff --git a/edit-feature-attachments/src/main/res/layout/attachment_loading_dialog.xml b/samples/edit-feature-attachments/src/main/res/layout/attachment_loading_dialog.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/layout/attachment_loading_dialog.xml
rename to samples/edit-feature-attachments/src/main/res/layout/attachment_loading_dialog.xml
diff --git a/edit-feature-attachments/src/main/res/layout/activity_main.xml b/samples/edit-feature-attachments/src/main/res/layout/edit_feature_attachments_activity_main.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/layout/activity_main.xml
rename to samples/edit-feature-attachments/src/main/res/layout/edit_feature_attachments_activity_main.xml
diff --git a/samples/edit-feature-attachments/src/main/res/values/strings.xml b/samples/edit-feature-attachments/src/main/res/values/strings.xml
new file mode 100644
index 000000000..0cadf0efb
--- /dev/null
+++ b/samples/edit-feature-attachments/src/main/res/values/strings.xml
@@ -0,0 +1,17 @@
+
+ Edit feature attachments
+
+
+ Are you sure you want to delete the attachment?
+ Yes
+ No
+ Server did not return edit results
+
+
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0
+ attachment_click
+ Edit attachments
+ Done
+ Add attachment
+ com.esri.arcgismaps.sample.editfeatureattachments.provider
+
diff --git a/edit-feature-attachments/src/main/res/xml/provider_paths.xml b/samples/edit-feature-attachments/src/main/res/xml/provider_paths.xml
similarity index 100%
rename from edit-feature-attachments/src/main/res/xml/provider_paths.xml
rename to samples/edit-feature-attachments/src/main/res/xml/provider_paths.xml
diff --git a/samples/edit-features-using-feature-forms/README.md b/samples/edit-features-using-feature-forms/README.md
new file mode 100644
index 000000000..10fd2b1d2
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/README.md
@@ -0,0 +1,53 @@
+# Edit features using feature forms
+
+Display and edit feature attributes using feature forms.
+
+![Image of Edit features using feature forms](edit-features-using-feature-forms.png)
+
+## Use case
+
+Feature forms help enhance the accuracy, efficiency, and user experience of attribute editing in your application. Forms can be authored as part of the web map using [Field Maps Designer](https://www.arcgis.com/apps/fieldmaps/) or using Map Viewer. This allows a simplified user experience to edit feature attribute data on the web map.
+
+## How to use the sample
+
+Tap a feature on the map to open a sheet displaying the feature form. Select form elements in the list and perform edits to update the field values. Tap the submit icon to commit the changes on the web map.
+
+## How it works
+
+1. Add a `Map` to the `MapView` using `Portal` URL and item ID.
+2. When the map is tapped, perform an identify operation to check if the tapped location is an `ArcGISFeature`.
+3. Create a `FeatureForm` object using the identified `ArcGISFeature`.
+* **Note:** If the feature's `FeatureLayer`, `ArcGISFeatureTable`, or the `SubtypeSublayer` has an authored `FeatureFormDefinition`, then this definition will be used to create the `FeatureForm`. If such a definition is not found, a default definition is generated.
+4. Use the `FeatureForm` toolkit component to display the feature form configuration by providing the created `featureForm` object.
+5. Optionally, you can add a `validationErrorVisibility` option to the `FeatureForm` toolkit component that determines the visibility of validation errors.
+6. Once edits are added to the form fields, check if the validation errors list are empty using `featureForm.validationErrors` to verify that there are no errors.
+7. To commit edits on the service geodatabase:
+ 1. Call `featureForm.finishEditing()` to save edits to the database.
+ 2. Retrieve the backing service feature table's geodatabase using `serviceFeatureTable?.serviceGeodatabase`.
+ 3. Verify the service geodatabase can commit changes back to the service using `serviceGeodatabase.serviceInfo?.canUseServiceGeodatabaseApplyEdits`
+ 4. If apply edits are allowed, call `serviceGeodatabase.applyEdits()` to apply local edits to the online service.
+ 5. If edits are not allowed on the `ServiceGeodatabase`, then apply edits to the `ServiceFeatureTable` using `ServiceFeatureTable.applyEdits()`
+
+## Relevant API
+
+* ArcGISFeature
+* FeatureForm
+* FeatureLayer
+* FieldFormElement
+* GroupFormElement
+* ServiceFeatureTable
+* ServiceGeodatabase
+
+## About the data
+
+This sample uses a feature forms enabled [Feature Form Places web map](https://www.arcgis.com/home/item.html?id=516e4d6aeb4c495c87c41e11274c767f), which contains fictional places in San Diego of various hotels, restaurants, and shopping centers, with relevant reviews and ratings.
+
+## Additional information
+
+Follow the [tutorial](https://doc.arcgis.com/en/arcgis-online/create-maps/create-form-mv.htm) to create your own form using the Map Viewer. This sample uses the FeatureForm and GeoViewCompose Toolkit modules to be able to implement a Composable MapView which displays a Composable FeatureForm UI.
+
+This sample uses the feature forms [toolkit](https://github.com/Esri/arcgis-maps-sdk-kotlin-toolkit/tree/main/toolkit/featureforms) component. For information about setting up the toolkit, as well as code for the underlying component, visit the [toolkit repository](https://github.com/Esri/arcgis-maps-sdk-kotlin-toolkit).
+
+## Tags
+
+compose, edits, feature, featureforms, form, geoviewcompose, jetpack, toolkit
diff --git a/edit-features-using-feature-forms/README.metadata.json b/samples/edit-features-using-feature-forms/README.metadata.json
similarity index 100%
rename from edit-features-using-feature-forms/README.metadata.json
rename to samples/edit-features-using-feature-forms/README.metadata.json
diff --git a/samples/edit-features-using-feature-forms/build.gradle.kts b/samples/edit-features-using-feature-forms/build.gradle.kts
new file mode 100644
index 000000000..1509cc72c
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.editfeaturesusingfeatureforms"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.arcgis.maps.kotlin.toolkit.featureforms)
+}
diff --git a/edit-features-using-feature-forms/edit-features-using-feature-forms.png b/samples/edit-features-using-feature-forms/edit-features-using-feature-forms.png
similarity index 100%
rename from edit-features-using-feature-forms/edit-features-using-feature-forms.png
rename to samples/edit-features-using-feature-forms/edit-features-using-feature-forms.png
diff --git a/samples/edit-features-using-feature-forms/src/main/AndroidManifest.xml b/samples/edit-features-using-feature-forms/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..74dbf1536
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/MainActivity.kt b/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/MainActivity.kt
similarity index 100%
rename from edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/MainActivity.kt
rename to samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/MainActivity.kt
diff --git a/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt b/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt
new file mode 100644
index 000000000..82a9ca422
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/components/MapViewModel.kt
@@ -0,0 +1,240 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components
+
+import android.app.Application
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.arcgismaps.data.ArcGISFeature
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.exceptions.FeatureFormValidationException
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.PortalItem
+import com.arcgismaps.mapping.featureforms.FeatureForm
+import com.arcgismaps.mapping.featureforms.FeatureFormDefinition
+import com.arcgismaps.mapping.featureforms.FieldFormElement
+import com.arcgismaps.mapping.featureforms.FormElement
+import com.arcgismaps.mapping.featureforms.GroupFormElement
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.view.SingleTapConfirmedEvent
+import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
+import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.R
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * A view model for the MainScreen UI
+ */
+class MapViewModel(application: Application) : AndroidViewModel(application) {
+
+ val mapViewProxy = MapViewProxy()
+
+ // feature forms enabled web-map showcasing places of interest with form fields
+ private var portalItem = PortalItem(application.getString(R.string.feature_form_web_map))
+
+ val map = ArcGISMap(portalItem)
+
+ // keep track of the selected feature form
+ private val _featureForm = MutableStateFlow(null)
+ val featureForm: StateFlow = _featureForm.asStateFlow()
+
+ // keep track of the list of validation errors
+ private val _errors = MutableStateFlow>(listOf())
+ val errors: StateFlow> = _errors.asStateFlow()
+
+ // create a ViewModel to handle dialog interactions
+ val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
+
+ init {
+ viewModelScope.launch {
+ // load a map that has a FeatureFormDefinition on any of its layers
+ map.load()
+ }
+ }
+
+ /**
+ * Apply attribute edits to the Geodatabase backing the ServiceFeatureTable
+ * and refresh the local feature. Persisting changes to attributes is
+ * not part of the FeatureForm API.
+ *
+ * @param onEditsCompleted Invoked when edits are applied successfully
+ */
+ fun applyEdits(onEditsCompleted: () -> Unit) {
+ val featureForm = _featureForm.value
+ ?: return messageDialogVM.showMessageDialog("Feature form state is not configured")
+
+ // update the state flow with the list of validation errors found
+ _errors.value = validateFormInputEdits(featureForm)
+ // if there are no errors then update the feature
+ if (_errors.value.isEmpty()) {
+ val serviceFeatureTable = featureForm.feature.featureTable as? ServiceFeatureTable
+ ?: return messageDialogVM.showMessageDialog("Cannot save feature edit without a ServiceFeatureTable")
+
+ viewModelScope.launch {
+ // commits changes of the edited feature to the database
+ featureForm.finishEditing().onSuccess {
+ serviceFeatureTable.serviceGeodatabase?.let { database ->
+ if (database.serviceInfo?.canUseServiceGeodatabaseApplyEdits == true) {
+ // applies all local edits in the tables to the service
+ database.applyEdits().onFailure {
+ return@onFailure messageDialogVM.showMessageDialog(
+ title = it.message.toString(),
+ description = it.cause.toString()
+ )
+ }
+ } else {
+ // uploads any changes to the local table to the feature service
+ serviceFeatureTable.applyEdits().onFailure {
+ return@onFailure messageDialogVM.showMessageDialog(
+ title = it.message.toString(),
+ description = it.cause.toString()
+ )
+ }
+ }
+ }
+ // resets the attributes and geometry to the values in the data source
+ featureForm.feature.refresh()
+ // unselect the feature after the edits have been saved
+ (featureForm.feature.featureTable?.layer as FeatureLayer).clearSelection()
+ // dismiss dialog when edits are completed
+ onEditsCompleted()
+ }.onFailure {
+ return@onFailure messageDialogVM.showMessageDialog(
+ title = it.message.toString(),
+ description = it.cause.toString()
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs validation checks on the given [featureForm] with local edits.
+ * Return a list of [ErrorInfo] if errors are found, if not, empty list is returned.
+ */
+ private fun validateFormInputEdits(featureForm: FeatureForm): List {
+ val errors = mutableListOf()
+ // If an element is editable or derives its value from an arcade expression,
+ // its errors must be corrected before submitting the form
+ featureForm.validationErrors.value.forEach { entry ->
+ entry.value.forEach { error ->
+ featureForm.elements.getFormElement(entry.key)?.let { formElement ->
+ // Ignore validation on non-editable and non-visible elements
+ if (formElement.isEditable.value || formElement.hasValueExpression) {
+ errors.add(
+ ErrorInfo(
+ fieldName = formElement.label,
+ error = error as FeatureFormValidationException
+ )
+ )
+ }
+ }
+ }
+ }
+ return errors
+ }
+
+ /**
+ * Cancels the commit by resetting the validation errors.
+ */
+ fun cancelCommit() {
+ // reset the validation errors
+ _errors.value = listOf()
+ }
+
+ /**
+ * Discard edits and unselects feature from the layer
+ */
+ fun rollbackEdits() {
+ // discard local edits to the feature form
+ _featureForm.value?.discardEdits()
+ // unselect the feature
+ (_featureForm.value?.feature?.featureTable?.layer as FeatureLayer).clearSelection()
+ // reset the validation errors
+ _errors.value = listOf()
+ }
+
+ /**
+ * Perform an identify the tapped [ArcGISFeature] and retrieve the
+ * layer's [FeatureFormDefinition] to create the respective [FeatureForm]
+ */
+ fun onSingleTapConfirmed(singleTapEvent: SingleTapConfirmedEvent) {
+ viewModelScope.launch {
+ mapViewProxy.identifyLayers(
+ screenCoordinate = singleTapEvent.screenCoordinate,
+ tolerance = 22.dp,
+ returnPopupsOnly = false
+ ).onSuccess { results ->
+ try {
+ results.forEach { result ->
+ result.geoElements.firstOrNull {
+ it is ArcGISFeature && (it.featureTable?.layer as? FeatureLayer)?.featureFormDefinition != null
+ }?.let {
+ val feature = it as ArcGISFeature
+ val layer = feature.featureTable!!.layer as FeatureLayer
+ val featureForm = FeatureForm(feature)
+ // select the feature
+ layer.selectFeature(feature)
+ // set the UI to an editing state with the FeatureForm
+ _featureForm.value = featureForm
+ }
+ }
+ } catch (e: Exception) {
+ messageDialogVM.showMessageDialog(
+ title = "Failed to create feature form for the feature",
+ description = e.message.toString()
+ )
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Returns the [FieldFormElement] with the given [fieldName] in the [FeatureForm]. If none exists
+ * null is returned.
+ */
+fun List.getFormElement(fieldName: String): FieldFormElement? {
+ val fieldElements = filterIsInstance()
+ val element = if (fieldElements.isNotEmpty()) {
+ fieldElements.firstNotNullOfOrNull {
+ if (it.fieldName == fieldName) it else null
+ }
+ } else {
+ null
+ }
+
+ return element ?: run {
+ val groupElements = filterIsInstance()
+ if (groupElements.isNotEmpty()) {
+ groupElements.firstNotNullOfOrNull {
+ it.elements.getFormElement(fieldName)
+ }
+ } else {
+ null
+ }
+ }
+}
+
+/**
+ * Class that provides a validation error [error] for the field with name [fieldName].
+ */
+data class ErrorInfo(val fieldName: String, val error: FeatureFormValidationException)
diff --git a/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt b/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt
new file mode 100644
index 000000000..df62a51ab
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/src/main/java/com/esri/arcgismaps/sample/editfeaturesusingfeatureforms/screens/MainScreen.kt
@@ -0,0 +1,352 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.screens
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.arcgismaps.toolkit.featureforms.FeatureForm
+import com.arcgismaps.toolkit.featureforms.ValidationErrorVisibility
+import com.arcgismaps.toolkit.featureforms.theme.FeatureFormDefaults
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.R
+import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components.ErrorInfo
+import com.esri.arcgismaps.sample.editfeaturesusingfeatureforms.components.MapViewModel
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MainScreen(mapViewModel: MapViewModel) {
+
+ val scope = rememberCoroutineScope()
+ var showBottomSheet by remember { mutableStateOf(false) }
+ val sheetState = rememberModalBottomSheetState()
+
+ // the feature form the currently selected feature
+ val featureForm by mapViewModel.featureForm.collectAsState()
+ // the validation errors found when the edits are applied
+ val formValidationErrors by mapViewModel.errors.collectAsState()
+
+ // boolean trackers for save and discard edits dialogs
+ var showSaveEditsDialog by remember { mutableStateOf(false) }
+ var showDiscardEditsDialog by remember { mutableStateOf(false) }
+
+ Scaffold(
+ modifier = Modifier.fillMaxSize(),
+ topBar = { SampleTopAppBar(title = stringResource(R.string.edit_features_using_feature_forms_app_name)) }
+ ) { padding ->
+ // display the composable map using the mapViewModel
+ MapView(
+ arcGISMap = mapViewModel.map,
+ mapViewProxy = mapViewModel.mapViewProxy,
+ modifier = Modifier
+ .padding(padding)
+ .fillMaxSize(),
+ onSingleTapConfirmed = { mapViewModel.onSingleTapConfirmed(it) }
+ )
+
+ // update bottom sheet visibility when a feature is selected
+ LaunchedEffect(featureForm) {
+ showBottomSheet = featureForm != null
+ }
+
+ if (showBottomSheet && featureForm != null) {
+ // display feature form bottom sheet
+ ModalBottomSheet(
+ onDismissRequest = {
+ showBottomSheet = false
+ showDiscardEditsDialog = true
+ },
+ sheetState = sheetState
+ ) {
+ // top bar to manage save or discard edits
+ TopFormBar(
+ onClose = { showDiscardEditsDialog = true },
+ onSave = {
+ showSaveEditsDialog = true
+ mapViewModel.applyEdits {
+ scope.launch {
+ sheetState.hide()
+ showBottomSheet = false
+ showSaveEditsDialog = false
+ }
+ }
+ })
+ // display the selected feature form using the Toolkit component
+ FeatureForm(
+ featureForm = featureForm!!,
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(top = 20.dp)
+ .navigationBarsPadding(),
+ validationErrorVisibility = ValidationErrorVisibility.Automatic,
+ colorScheme = FeatureFormDefaults.colorScheme(
+ groupElementColors = FeatureFormDefaults.groupElementColors(
+ outlineColor = MaterialTheme.colorScheme.secondary,
+ labelColor = MaterialTheme.colorScheme.onSurface,
+ supportingTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ containerColor = MaterialTheme.colorScheme.surfaceContainer
+ )
+ ),
+ typography = FeatureFormDefaults.typography(
+ readOnlyFieldTypography = FeatureFormDefaults.readOnlyFieldTypography(
+ labelStyle = MaterialTheme.typography.headlineSmall,
+ textStyle = MaterialTheme.typography.bodyMedium,
+ supportingTextStyle = MaterialTheme.typography.labelLarge
+ )
+ )
+ )
+ }
+ }
+ }
+
+ if (showSaveEditsDialog && formValidationErrors.isNotEmpty() && showBottomSheet) {
+ // validation errors found, cancel the commit and show validation errors
+ ValidationErrorsDialog(errors = formValidationErrors) {
+ showSaveEditsDialog = false
+ mapViewModel.cancelCommit()
+ }
+ } else if (showSaveEditsDialog && showBottomSheet) {
+ // no validation errors found, show dialog when committing edits
+ SaveFormDialog()
+ }
+
+ if (showDiscardEditsDialog) {
+ DiscardEditsDialog(
+ onConfirm = {
+ mapViewModel.rollbackEdits()
+ scope.launch {
+ sheetState.hide()
+ showDiscardEditsDialog = false
+ showBottomSheet = false
+ }
+ },
+ onCancel = {
+ showDiscardEditsDialog = false
+ showBottomSheet = true
+ }
+ )
+ }
+
+ // Display a MessageDialog with any error information
+ mapViewModel.messageDialogVM.apply {
+ if (dialogStatus) {
+ MessageDialog(
+ title = messageTitle,
+ description = messageDescription,
+ onDismissRequest = ::dismissDialog
+ )
+ }
+ }
+}
+
+@Composable
+fun DiscardEditsDialog(onConfirm: () -> Unit, onCancel: () -> Unit) {
+ AlertDialog(
+ onDismissRequest = onCancel,
+ confirmButton = {
+ Button(onClick = onConfirm) {
+ Text(text = stringResource(R.string.discard))
+ }
+ },
+ dismissButton = {
+ OutlinedButton(onClick = onCancel) {
+ Text(text = stringResource(R.string.cancel))
+ }
+ },
+ title = {
+ Text(text = stringResource(R.string.discard_edits))
+ },
+ text = {
+ Text(text = stringResource(R.string.all_changes_will_be_lost))
+ }
+ )
+}
+
+@Composable
+fun TopFormBar(
+ onClose: () -> Unit = {},
+ onSave: () -> Unit = {},
+) {
+ Column {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 12.dp),
+ horizontalArrangement = Arrangement.Absolute.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ IconButton(onClick = onClose) {
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = "Close Feature Editor"
+ )
+ }
+ Text(
+ text = "Edit feature",
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center
+ )
+ IconButton(onClick = onSave) {
+ Icon(
+ imageVector = Icons.Default.Check,
+ contentDescription = "Save Feature",
+ tint = MaterialTheme.colorScheme.primary
+ )
+ }
+ }
+ HorizontalDivider()
+ }
+}
+
+@Composable
+private fun SaveFormDialog() {
+ // show a progress dialog when no errors are present
+ Dialog(onDismissRequest = { /* cannot be dismissed */ }) {
+ Card(modifier = Modifier.wrapContentSize()) {
+ Column(
+ modifier = Modifier.padding(25.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ ) {
+ CircularProgressIndicator()
+ Spacer(modifier = Modifier.height(10.dp))
+ Text(text = "Saving..")
+ }
+ }
+ }
+}
+
+@Composable
+private fun ValidationErrorsDialog(errors: List, onDismissRequest: () -> Unit) {
+ // show all the validation errors in a dialog
+ AlertDialog(
+ onDismissRequest = onDismissRequest,
+ modifier = Modifier.heightIn(max = 600.dp),
+ confirmButton = {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Button(onClick = onDismissRequest) {
+ Text(text = stringResource(R.string.view))
+ }
+ }
+ },
+ title = {
+ Column {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "Form Validation Errors",
+ style = MaterialTheme.typography.titleLarge,
+ textAlign = TextAlign.Center
+ )
+ }
+ },
+ text = {
+ Card(modifier = Modifier.fillMaxWidth()) {
+ Column(modifier = Modifier.padding(15.dp)) {
+ Text(
+ text = stringResource(R.string.attributes_failed, errors.count()),
+ color = MaterialTheme.colorScheme.error
+ )
+ Spacer(modifier = Modifier.height(10.dp))
+ LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) {
+ items(errors.count()) { index ->
+ Text(
+ text = "${errors[index].fieldName}: ${errors[index].error::class.simpleName.toString()}",
+ color = MaterialTheme.colorScheme.error
+ )
+ }
+ }
+ }
+ }
+ }
+ )
+}
+
+@Preview
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun SavePreview() {
+ SampleAppTheme { SaveFormDialog() }
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun ValidationErrorsPreview() {
+ SampleAppTheme { ValidationErrorsDialog(listOf()) { } }
+}
+
+@Preview(showBackground = true)
+@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+fun DiscardEditsDialogPreview() {
+ SampleAppTheme { DiscardEditsDialog(onConfirm = {}) {} }
+}
+
+@Preview(showBackground = true)
+@Composable
+fun TopFormBarPreview() {
+ SampleAppTheme { TopFormBar() }
+}
diff --git a/samples/edit-features-using-feature-forms/src/main/res/values/strings.xml b/samples/edit-features-using-feature-forms/src/main/res/values/strings.xml
new file mode 100644
index 000000000..7cf980259
--- /dev/null
+++ b/samples/edit-features-using-feature-forms/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ Edit features using feature forms
+ https://www.arcgis.com/home/item.html?id=516e4d6aeb4c495c87c41e11274c767f
+ Cancel
+ %1$s attributes failed
+ View
+ Discard
+ Discard Edits?
+ All changes made within the form will be lost.
+
diff --git a/find-address-with-reverse-geocode/README.md b/samples/find-address-with-reverse-geocode/README.md
similarity index 100%
rename from find-address-with-reverse-geocode/README.md
rename to samples/find-address-with-reverse-geocode/README.md
diff --git a/find-address-with-reverse-geocode/README.metadata.json b/samples/find-address-with-reverse-geocode/README.metadata.json
similarity index 100%
rename from find-address-with-reverse-geocode/README.metadata.json
rename to samples/find-address-with-reverse-geocode/README.metadata.json
diff --git a/samples/find-address-with-reverse-geocode/build.gradle.kts b/samples/find-address-with-reverse-geocode/build.gradle.kts
new file mode 100644
index 000000000..4177a7418
--- /dev/null
+++ b/samples/find-address-with-reverse-geocode/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findaddresswithreversegeocode"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-address-with-reverse-geocode/find-address-with-reverse-geocode.png b/samples/find-address-with-reverse-geocode/find-address-with-reverse-geocode.png
similarity index 100%
rename from find-address-with-reverse-geocode/find-address-with-reverse-geocode.png
rename to samples/find-address-with-reverse-geocode/find-address-with-reverse-geocode.png
diff --git a/samples/find-address-with-reverse-geocode/src/main/AndroidManifest.xml b/samples/find-address-with-reverse-geocode/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..5fca52ee5
--- /dev/null
+++ b/samples/find-address-with-reverse-geocode/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt b/samples/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt
new file mode 100644
index 000000000..d99c9354c
--- /dev/null
+++ b/samples/find-address-with-reverse-geocode/src/main/java/com/esri/arcgismaps/sample/findaddresswithreversegeocode/MainActivity.kt
@@ -0,0 +1,174 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findaddresswithreversegeocode
+
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.tasks.geocode.LocatorTask
+import com.esri.arcgismaps.sample.findaddresswithreversegeocode.databinding.FindAddressWithReverseGeocodeActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // service url to be provided to the LocatorTask (geocoder)
+ private val geocodeServer =
+ "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer"
+
+ // set up data binding for the activity
+ private val activityMainBinding: FindAddressWithReverseGeocodeActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.find_address_with_reverse_geocode_activity_main)
+ }
+
+ // create a MapView using binding
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // display the street of the tapped location
+ private val titleTV by lazy {
+ activityMainBinding.titleTV
+ }
+
+ // display the metro area of the tapped location
+ private val descriptionTV by lazy {
+ activityMainBinding.descriptionTV
+ }
+
+ // set the pin graphic for tapped location
+ private val pinSymbol by lazy {
+ createPinSymbol()
+ }
+
+ // create a graphics overlay
+ private val graphicsOverlay = GraphicsOverlay()
+
+ // locator task to provide geocoding services
+ private val locatorTask = LocatorTask(geocodeServer)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ mapView.apply {
+ // add a map with a imagery basemap style
+ map = ArcGISMap(BasemapStyle.ArcGISImagery)
+ // add a graphics overlay to the map for showing where the user tapped
+ graphicsOverlays.add(graphicsOverlay)
+ // set initial viewpoint
+ setViewpoint(Viewpoint(34.058, -117.195, 5e4))
+ }
+
+ lifecycleScope.launch {
+ // load geocode locator task
+ locatorTask.load().onSuccess {
+ // locator task loaded, look for geo view tapped
+ mapView.onSingleTapConfirmed.collect { event ->
+ event.mapPoint?.let { mapPoint -> geoViewTapped(mapPoint) }
+ }
+ }.onFailure {
+ showError(it.message.toString())
+ }
+ }
+ }
+
+ /**
+ * Displays a pin of the tapped location using [mapPoint]
+ * and finds address with reverse geocode
+ */
+ private suspend fun geoViewTapped(mapPoint: Point) {
+ // create graphic for tapped point
+ val pinGraphic = Graphic(mapPoint, pinSymbol)
+ graphicsOverlay.graphics.apply {
+ // clear existing graphics
+ clear()
+ // add the pin graphic
+ add(pinGraphic)
+ }
+ // normalize the geometry - needed if the user crosses the international date line.
+ val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
+ // reverse geocode to get address
+ locatorTask.reverseGeocode(normalizedPoint).onSuccess { addresses ->
+ // get the first result
+ val address = addresses.firstOrNull()
+ if (address == null) {
+ showError("Could not find address at tapped point")
+ return@onSuccess
+ }
+ // use the street and region for the title
+ val title = address.attributes["Address"].toString()
+ // use the metro area for the description details
+ val description = "${address.attributes["City"]} " +
+ "${address.attributes["Region"]} " +
+ "${address.attributes["CountryCode"]}"
+ // set the strings to the text views
+ titleTV.text = title
+ descriptionTV.text = description
+ }.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ /**
+ * Create a picture marker symbol to represent a pin at the tapped location
+ */
+ private fun createPinSymbol(): PictureMarkerSymbol {
+ // get pin drawable
+ val pinDrawable = ContextCompat.getDrawable(
+ this,
+ R.drawable.baseline_location_pin_red_48
+ )
+ //add a graphic for the tapped point
+ val pinSymbol = PictureMarkerSymbol.createWithImage(
+ pinDrawable as BitmapDrawable
+ )
+ pinSymbol.apply {
+ // resize the dimensions of the symbol
+ width = 50f
+ height = 50f
+ // the image is a pin so offset the image so that the pinpoint
+ // is on the point rather than the image's true center
+ leaderOffsetX = 30f
+ offsetY = 25f
+ }
+ return pinSymbol
+ }
+
+ private fun showError(errorMessage: String) {
+ Log.e(localClassName, errorMessage)
+ Snackbar.make(mapView, errorMessage, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/find-address-with-reverse-geocode/src/main/res/drawable/baseline_location_pin_red_48.png b/samples/find-address-with-reverse-geocode/src/main/res/drawable/baseline_location_pin_red_48.png
similarity index 100%
rename from find-address-with-reverse-geocode/src/main/res/drawable/baseline_location_pin_red_48.png
rename to samples/find-address-with-reverse-geocode/src/main/res/drawable/baseline_location_pin_red_48.png
diff --git a/find-address-with-reverse-geocode/src/main/res/layout/activity_main.xml b/samples/find-address-with-reverse-geocode/src/main/res/layout/find_address_with_reverse_geocode_activity_main.xml
similarity index 100%
rename from find-address-with-reverse-geocode/src/main/res/layout/activity_main.xml
rename to samples/find-address-with-reverse-geocode/src/main/res/layout/find_address_with_reverse_geocode_activity_main.xml
diff --git a/samples/find-address-with-reverse-geocode/src/main/res/values/strings.xml b/samples/find-address-with-reverse-geocode/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f1a11bd27
--- /dev/null
+++ b/samples/find-address-with-reverse-geocode/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Find address with reverse geocode
+ Tap on map to find address
+
diff --git a/find-closest-facility-from-point/README.md b/samples/find-closest-facility-from-point/README.md
similarity index 100%
rename from find-closest-facility-from-point/README.md
rename to samples/find-closest-facility-from-point/README.md
diff --git a/find-closest-facility-from-point/README.metadata.json b/samples/find-closest-facility-from-point/README.metadata.json
similarity index 100%
rename from find-closest-facility-from-point/README.metadata.json
rename to samples/find-closest-facility-from-point/README.metadata.json
diff --git a/samples/find-closest-facility-from-point/build.gradle.kts b/samples/find-closest-facility-from-point/build.gradle.kts
new file mode 100644
index 000000000..14442030a
--- /dev/null
+++ b/samples/find-closest-facility-from-point/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findclosestfacilityfrompoint"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-closest-facility-from-point/find-closest-facility-from-point.png b/samples/find-closest-facility-from-point/find-closest-facility-from-point.png
similarity index 100%
rename from find-closest-facility-from-point/find-closest-facility-from-point.png
rename to samples/find-closest-facility-from-point/find-closest-facility-from-point.png
diff --git a/samples/find-closest-facility-from-point/src/main/AndroidManifest.xml b/samples/find-closest-facility-from-point/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/find-closest-facility-from-point/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt b/samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt
new file mode 100644
index 000000000..f0c6d1893
--- /dev/null
+++ b/samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findclosestfacilityfrompoint
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.findclosestfacilityfrompoint.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.find_closest_facility_from_point_app_name)
+ )
+ }
+ }
+}
diff --git a/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/components/MapViewModel.kt b/samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/components/MapViewModel.kt
similarity index 100%
rename from find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/components/MapViewModel.kt
rename to samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/components/MapViewModel.kt
diff --git a/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/screens/MainScreen.kt b/samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/screens/MainScreen.kt
similarity index 100%
rename from find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/screens/MainScreen.kt
rename to samples/find-closest-facility-from-point/src/main/java/com/esri/arcgismaps/sample/findclosestfacilityfrompoint/screens/MainScreen.kt
diff --git a/samples/find-closest-facility-from-point/src/main/res/values/strings.xml b/samples/find-closest-facility-from-point/src/main/res/values/strings.xml
new file mode 100644
index 000000000..fdcef5ddd
--- /dev/null
+++ b/samples/find-closest-facility-from-point/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Find closest facility from point
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ClosestFacility
+ https://static.arcgis.com/images/Symbols/SafetyHealth/Hospital.png
+
diff --git a/find-nearest-vertex/README.md b/samples/find-nearest-vertex/README.md
similarity index 100%
rename from find-nearest-vertex/README.md
rename to samples/find-nearest-vertex/README.md
diff --git a/find-nearest-vertex/README.metadata.json b/samples/find-nearest-vertex/README.metadata.json
similarity index 100%
rename from find-nearest-vertex/README.metadata.json
rename to samples/find-nearest-vertex/README.metadata.json
diff --git a/samples/find-nearest-vertex/build.gradle.kts b/samples/find-nearest-vertex/build.gradle.kts
new file mode 100644
index 000000000..b210be0f8
--- /dev/null
+++ b/samples/find-nearest-vertex/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findnearestvertex"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-nearest-vertex/find-nearest-vertex.png b/samples/find-nearest-vertex/find-nearest-vertex.png
similarity index 100%
rename from find-nearest-vertex/find-nearest-vertex.png
rename to samples/find-nearest-vertex/find-nearest-vertex.png
diff --git a/samples/find-nearest-vertex/src/main/AndroidManifest.xml b/samples/find-nearest-vertex/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..8fd3e1626
--- /dev/null
+++ b/samples/find-nearest-vertex/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt b/samples/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt
new file mode 100644
index 000000000..494309f31
--- /dev/null
+++ b/samples/find-nearest-vertex/src/main/java/com/esri/arcgismaps/sample/findnearestvertex/MainActivity.kt
@@ -0,0 +1,192 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findnearestvertex
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.mapping.PortalItem
+import com.esri.arcgismaps.sample.findnearestvertex.databinding.FindNearestVertexActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: FindNearestVertexActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.find_nearest_vertex_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val distanceLayout: ConstraintLayout by lazy {
+ activityMainBinding.distanceLayout
+ }
+
+ private val vertexDistanceTextView: TextView by lazy {
+ activityMainBinding.vertexDistanceTextView
+ }
+
+ private val coordinateDistanceTextView: TextView by lazy {
+ activityMainBinding.coordinateDistanceTextView
+ }
+
+ // California zone 5 (ftUS) state plane coordinate system.
+ private val statePlaneCaliforniaZone5SpatialReference = SpatialReference(2229)
+
+ // create graphics with symbols for tapped location, nearest coordinate, and nearest vertex
+ private val tappedLocationGraphic =
+ Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.X, Color.magenta, 15f))
+
+ // create graphic symbol of the nearest coordinate
+ private val nearestCoordinateGraphic =
+ Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Diamond, Color.red, 10f))
+
+ // create graphic symbol of the nearest vertex
+ private val nearestVertexGraphic =
+ Graphic(symbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.blue, 15f))
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a polygon geometry
+ val polygon = PolygonBuilder(statePlaneCaliforniaZone5SpatialReference) {
+ addPoint(Point(6627416.41469281, 1804532.53233782))
+ addPoint(Point(6669147.89779046, 2479145.16609522))
+ addPoint(Point(7265673.02678292, 2484254.50442408))
+ addPoint(Point(7676192.55880379, 2001458.66365744))
+ }.toGeometry()
+ // set up the outline and fill color of the polygon
+ val polygonOutlineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
+ val polygonFillSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.ForwardDiagonal,
+ Color.green,
+ polygonOutlineSymbol
+ )
+ // create a polygon graphic
+ val polygonGraphic = Graphic(polygon, polygonFillSymbol)
+ // create a graphics overlay to show the polygon, tapped location, and nearest vertex/coordinate
+ val graphicsOverlay = GraphicsOverlay(
+ listOf(
+ polygonGraphic,
+ tappedLocationGraphic,
+ nearestCoordinateGraphic,
+ nearestVertexGraphic
+ )
+ )
+
+ // create a map using the portal item
+ val map = ArcGISMap(statePlaneCaliforniaZone5SpatialReference)
+ val portal = Portal("https://arcgisruntime.maps.arcgis.com")
+ val portalItem = PortalItem(portal, "99fd67933e754a1181cc755146be21ca")
+ val usStatesGeneralizedLayer = FeatureLayer.createWithItemAndLayerId(portalItem, 0)
+ // and add the feature layer to the map's operational layers
+ map.operationalLayers.add(usStatesGeneralizedLayer)
+ // add the map to the map view
+ mapView.map = map
+ // add the graphics overlay to the map view
+ mapView.graphicsOverlays.add(graphicsOverlay)
+ lifecycleScope.launch {
+ // check if map has loaded
+ map.load().onSuccess {
+ // zoom to the polygon's extent
+ mapView.setViewpointGeometry(polygon.extent, 100.0)
+ // get point on map tapped
+ mapView.onSingleTapConfirmed.collect { event ->
+ // find nearest vertex on map tapped
+ event.mapPoint?.let { findNearestVertex(it, polygon) }
+ }
+ }.onFailure {
+ showError("Error loading map")
+ }
+ }
+ }
+
+ /**
+ * Finds the nearest vertex from [mapPoint] from the [polygon]
+ */
+ private fun findNearestVertex(mapPoint: Point, polygon: Polygon) {
+ // show where the user clicked
+ tappedLocationGraphic.geometry = mapPoint
+ // use the geometry engine to get the nearest vertex
+ val nearestVertexResult =
+ GeometryEngine.nearestVertex(polygon, mapPoint)
+ // set the nearest vertex graphic's geometry to the nearest vertex
+ nearestVertexGraphic.geometry = nearestVertexResult?.coordinate
+ // use the geometry engine to get the nearest coordinate
+ val nearestCoordinateResult =
+ GeometryEngine.nearestCoordinate(polygon, mapPoint)
+ // set the nearest coordinate graphic's geometry to the nearest coordinate
+ nearestCoordinateGraphic.geometry = nearestCoordinateResult?.coordinate
+ // show the distances to the nearest vertex and nearest coordinate
+ distanceLayout.visibility = View.VISIBLE
+ // convert distance to miles
+ val vertexDistance = ((nearestVertexResult?.distance)?.div(5280.0))?.toInt()
+ val coordinateDistance = ((nearestCoordinateResult?.distance)?.div(5280.0))?.toInt()
+ // set the distance to the text views
+ vertexDistanceTextView.text = getString(R.string.nearest_vertex, vertexDistance)
+ coordinateDistanceTextView.text =
+ getString(R.string.nearest_coordinate, coordinateDistance)
+
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+ private val Color.Companion.magenta: Color
+ get() {
+ return fromRgba(255, 0, 255, 255)
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/find-nearest-vertex/src/main/res/layout/activity_main.xml b/samples/find-nearest-vertex/src/main/res/layout/find_nearest_vertex_activity_main.xml
similarity index 100%
rename from find-nearest-vertex/src/main/res/layout/activity_main.xml
rename to samples/find-nearest-vertex/src/main/res/layout/find_nearest_vertex_activity_main.xml
diff --git a/samples/find-nearest-vertex/src/main/res/values/strings.xml b/samples/find-nearest-vertex/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d09a2c223
--- /dev/null
+++ b/samples/find-nearest-vertex/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Find nearest vertex
+ Vertex distance: %1$d mi
+ "Coordinate distance: %1$d mi"
+ Tap on the map to find vertex
+
diff --git a/find-route-around-barriers/README.md b/samples/find-route-around-barriers/README.md
similarity index 100%
rename from find-route-around-barriers/README.md
rename to samples/find-route-around-barriers/README.md
diff --git a/find-route-around-barriers/README.metadata.json b/samples/find-route-around-barriers/README.metadata.json
similarity index 100%
rename from find-route-around-barriers/README.metadata.json
rename to samples/find-route-around-barriers/README.metadata.json
diff --git a/samples/find-route-around-barriers/build.gradle.kts b/samples/find-route-around-barriers/build.gradle.kts
new file mode 100644
index 000000000..5bc6da65a
--- /dev/null
+++ b/samples/find-route-around-barriers/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findroutearoundbarriers"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-route-around-barriers/find-route-around-barriers.png b/samples/find-route-around-barriers/find-route-around-barriers.png
similarity index 100%
rename from find-route-around-barriers/find-route-around-barriers.png
rename to samples/find-route-around-barriers/find-route-around-barriers.png
diff --git a/samples/find-route-around-barriers/src/main/AndroidManifest.xml b/samples/find-route-around-barriers/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..74e15f37f
--- /dev/null
+++ b/samples/find-route-around-barriers/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt b/samples/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt
new file mode 100644
index 000000000..85187dbb0
--- /dev/null
+++ b/samples/find-route-around-barriers/src/main/java/com/esri/arcgismaps/sample/findroutearoundbarriers/MainActivity.kt
@@ -0,0 +1,462 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findroutearoundbarriers
+
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.ImageView
+import android.widget.ListView
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.CompositeSymbol
+import com.arcgismaps.mapping.symbology.HorizontalAlignment
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.symbology.VerticalAlignment
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.tasks.networkanalysis.DirectionManeuver
+import com.arcgismaps.tasks.networkanalysis.PolygonBarrier
+import com.arcgismaps.tasks.networkanalysis.RouteParameters
+import com.arcgismaps.tasks.networkanalysis.RouteTask
+import com.arcgismaps.tasks.networkanalysis.Stop
+import com.esri.arcgismaps.sample.findroutearoundbarriers.databinding.FindRouteAroundBarriersActivityMainBinding
+import com.esri.arcgismaps.sample.findroutearoundbarriers.databinding.OptionsDialogBinding
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: FindRouteAroundBarriersActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.find_route_around_barriers_activity_main)
+ }
+
+ // show the options dialog
+ private val optionsDialogBinding by lazy {
+ OptionsDialogBinding.inflate(layoutInflater)
+ }
+
+ // set up the dialog UI views
+ private val findBestSequenceSwitch by lazy {
+ optionsDialogBinding.findBestSequenceSwitch
+ }
+ private val firstStopSwitch by lazy {
+ optionsDialogBinding.firstStopSwitch
+ }
+ private val lastStopSwitch by lazy {
+ optionsDialogBinding.lastStopSwitch
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val mainContainer: ConstraintLayout by lazy {
+ activityMainBinding.mainContainer
+ }
+
+ private val addStopsButton: MaterialButton by lazy {
+ activityMainBinding.addStopsButton
+ }
+
+ private val addBarriersButton: MaterialButton by lazy {
+ activityMainBinding.addBarriersButton
+ }
+
+ private val resetButton by lazy {
+ activityMainBinding.resetButton
+ }
+
+ private val optionsButton by lazy {
+ activityMainBinding.optionsButton
+ }
+
+ private val directionsButton by lazy {
+ activityMainBinding.directionsButton
+ }
+
+ private val bottomSheet by lazy {
+ activityMainBinding.directionSheet.directionSheetLayout
+ }
+
+ private val header: ConstraintLayout by lazy {
+ activityMainBinding.directionSheet.header
+ }
+
+ private val imageView: ImageView by lazy {
+ activityMainBinding.directionSheet.imageView
+ }
+
+ private val cancelTV: TextView by lazy {
+ activityMainBinding.directionSheet.cancelTv
+ }
+
+ private val directionsLV: ListView by lazy {
+ activityMainBinding.directionSheet.directionsLV
+ }
+
+ private val stopList by lazy { mutableListOf() }
+
+ private val barriersList by lazy { mutableListOf() }
+
+ private val directionsList by lazy { mutableListOf() }
+
+ private val stopsOverlay by lazy { GraphicsOverlay() }
+
+ private val barriersOverlay by lazy { GraphicsOverlay() }
+
+ private val routeOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ private val barrierSymbol by lazy {
+ SimpleFillSymbol(SimpleFillSymbolStyle.DiagonalCross, Color.red, null)
+ }
+
+ // create route task from San Diego service
+ private val routeTask by lazy {
+ RouteTask(getString(R.string.routing_service_url))
+ }
+
+ private var routeParameters: RouteParameters? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // some parts of the API require an Android Context to properly interact with Android system
+ // features, such as LocationProvider and application resources
+ ArcGISEnvironment.applicationContext = applicationContext
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ mapView.apply {
+ map = ArcGISMap(BasemapStyle.ArcGISStreets)
+ setViewpoint(Viewpoint(32.7270, -117.1750, 40000.0))
+ graphicsOverlays.addAll(listOf(stopsOverlay, barriersOverlay, routeOverlay))
+ }
+
+ // set an on touch listener on the map view
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect { event ->
+ // add stop or barriers graphics to overlay
+ event.mapPoint?.let { mapPoint -> addStopOrBarrier(mapPoint) }
+ resetButton.isEnabled = true
+ }
+ }
+
+ // coroutine scope to use the default parameters for the route calculation
+ lifecycleScope.launch {
+ routeTask.load().onSuccess {
+ routeParameters = routeTask.createDefaultParameters().getOrThrow().apply {
+ returnStops = true
+ returnDirections = true
+ }
+ }.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ // make a clear button to reset the stops and routes
+ resetButton.setOnClickListener {
+ // clear stops from route parameters and stops list
+ routeParameters?.clearStops()
+ stopList.clear()
+ // clear barriers from route parameters and barriers list
+ routeParameters?.clearPolygonBarriers()
+ barriersList.clear()
+ // clear the directions list
+ directionsList.clear()
+ // clear all graphics overlays
+ mapView.graphicsOverlays.forEach { it.graphics.clear() }
+ resetButton.isEnabled = false
+ }
+
+ // display the options dialog having the route finding parameters
+ optionsButton.setOnClickListener {
+ displayOptionsDialog()
+ }
+
+ // display the bottom sheet with directions when the button is clicked
+ directionsButton.setOnClickListener {
+ if (directionsList.isEmpty()) return@setOnClickListener showError("Add stops on map to find route")
+ setupBottomSheet(directionsList)
+ }
+
+ // hide the bottom sheet and make the map view span the whole screen
+ bottomSheet.visibility = View.INVISIBLE
+ (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
+ }
+
+ /**
+ * Create options dialog with the route finding parameters to reorder stops to find the optimized route
+ */
+ private fun displayOptionsDialog() {
+ // removes parent of the progressDialog layout, if previously assigned
+ optionsDialogBinding.root.parent?.let { parent ->
+ (parent as ViewGroup).removeAllViews()
+ }
+
+ // set up the dialog builder
+ MaterialAlertDialogBuilder(this).apply {
+ setView(optionsDialogBinding.root)
+ show()
+ }
+
+ // set the best sequence toggle state
+ findBestSequenceSwitch.isChecked = routeParameters?.findBestSequence ?: false
+
+ // solve route on each state change
+ findBestSequenceSwitch.setOnCheckedChangeListener { _, _ ->
+ // update route params if the switch is toggled
+ routeParameters?.findBestSequence = findBestSequenceSwitch.isChecked
+ createAndDisplayRoute()
+
+ // if best sequence switch is enabled, then enable the options
+ if (findBestSequenceSwitch.isChecked) {
+ firstStopSwitch.isEnabled = true
+ lastStopSwitch.isEnabled = true
+
+ } else {
+ firstStopSwitch.apply {
+ isChecked = false
+ isEnabled = false
+ }
+ lastStopSwitch.apply {
+ isChecked = false
+ isEnabled = false
+ }
+ }
+ }
+ firstStopSwitch.setOnCheckedChangeListener { _, _ ->
+ routeParameters?.preserveFirstStop = firstStopSwitch.isChecked
+ createAndDisplayRoute()
+ }
+ lastStopSwitch.setOnCheckedChangeListener { _, _ ->
+ routeParameters?.preserveLastStop = lastStopSwitch.isChecked
+ createAndDisplayRoute()
+ }
+ }
+
+ /**
+ * Add a stop or a barrier at the selected [mapPoint] to the correct graphics
+ * overlay depending on which button is currently checked.
+ */
+ private fun addStopOrBarrier(mapPoint: Point) {
+ if (addStopsButton.isChecked) {
+ // normalize the geometry - needed if the user crosses the international date line.
+ val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
+ // use the mapPoint to create a stop
+ val stop = Stop(Point(normalizedPoint.x, normalizedPoint.y, mapPoint.spatialReference))
+ // add the new stop to the list of stops
+ stopList.add(stop)
+ // create a marker symbol and graphics, and add the graphics to the graphics overlay
+ stopsOverlay.graphics.add(Graphic(mapPoint, createStopSymbol(stopList.size)))
+ } else if (addBarriersButton.isChecked) {
+ // create a buffered polygon around the clicked point
+ val barrierBufferPolygon = GeometryEngine.bufferOrNull(mapPoint, 200.0)
+ ?: return showError("Error creating buffer polygon")
+ // create a polygon barrier for the routing task, and add it to the list of barriers
+ barriersList.add(PolygonBarrier(barrierBufferPolygon))
+ barriersOverlay.graphics.add(Graphic(barrierBufferPolygon, barrierSymbol))
+ }
+ // solve the route once the graphics are created
+ createAndDisplayRoute()
+ }
+
+ /**
+ * Create route parameters and a route task from them. Display the route result geometry as a
+ * graphic and call showDirectionsInBottomSheet which shows directions in a list view.
+ */
+ private fun createAndDisplayRoute() = lifecycleScope.launch {
+
+ // clear the previous route from the graphics overlay, if it exists
+ routeOverlay.graphics.clear()
+ // clear the directions list from the directions list view, if they exist
+ directionsList.clear()
+
+ val routeParameters = routeParameters ?: return@launch
+
+ if (stopList.size <= 1) return@launch
+
+ routeParameters.apply {
+ // add the existing stops and barriers to the route parameters
+ setStops(stopList)
+ setPolygonBarriers(barriersList)
+ }
+
+ // solve the route task
+ val routeResults = routeParameters.let { routeTask.solveRoute(it) }
+
+ routeResults.onSuccess { routeResult ->
+ // get the first solved route
+ val firstRoute = routeResult.routes[0]
+
+ // create Graphic for route
+ val graphic = Graphic(
+ firstRoute.routeGeometry,
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 3f)
+ )
+ routeOverlay.graphics.add(graphic)
+ // get the direction text for each maneuver and add them to the list to display
+ directionsList.addAll(firstRoute.directionManeuvers)
+ }.onFailure {
+ showError("No route solution. ${it.message}")
+ }
+
+ }
+
+ /** Creates a bottom sheet to display a list of direction maneuvers.
+ * [directions] a list of DirectionManeuver which represents the route
+ */
+ private fun setupBottomSheet(directions: List) {
+ val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet).apply {
+ // expand the bottom sheet, and ensure it is displayed on the screen when collapsed
+ state = BottomSheetBehavior.STATE_EXPANDED
+ peekHeight = header.height
+ // animate the arrow when the bottom sheet slides
+ addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ imageView.rotation = slideOffset * 180f
+ }
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ imageView.rotation = when (newState) {
+ BottomSheetBehavior.STATE_EXPANDED -> 180f
+ else -> imageView.rotation
+ }
+ }
+ })
+ }
+
+ bottomSheet.apply {
+ visibility = View.VISIBLE
+ // expand or collapse the bottom sheet when the header is clicked
+ header.setOnClickListener {
+ bottomSheetBehavior.state = when (bottomSheetBehavior.state) {
+ BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED
+ else -> BottomSheetBehavior.STATE_COLLAPSED
+ }
+
+ }
+ // rotate the arrow so it starts off in the correct rotation
+ imageView.rotation = 180f
+
+ directionsLV.apply {
+ // set the adapter for the list view
+ adapter = ArrayAdapter(
+ this@MainActivity,
+ android.R.layout.simple_list_item_1,
+ directions.map { it.directionText }
+ )
+
+ // when the user taps a maneuver, set the viewpoint to that portion of the route
+ onItemClickListener =
+ AdapterView.OnItemClickListener { _, _, position, _ ->
+ // remove any graphics that are not the original (blue) route graphic
+ if (routeOverlay.graphics.size > 1) {
+ routeOverlay.graphics.removeAt(routeOverlay.graphics.size - 1)
+ }
+ // set the viewpoint to the selected maneuver
+ val geometry = directionsList[position].geometry
+ geometry?.let { mapView.setViewpoint(Viewpoint(it.extent, 20.0)) }
+ // create a graphic with a symbol for the maneuver and add it to the graphics overlay
+ val selectedRouteSymbol = SimpleLineSymbol(
+ SimpleLineSymbolStyle.Solid,
+ Color.green, 3f
+ )
+ routeOverlay.graphics.add(Graphic(geometry, selectedRouteSymbol))
+ // collapse the bottom sheet
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+ }
+ // hide the bottom sheet when cancel button is clicked
+ cancelTV.setOnClickListener {
+ bottomSheet.visibility = View.INVISIBLE
+ }
+ }
+
+ }
+
+ /**
+ * Create a composite symbol consisting of a pin graphic overlaid with a particular [stopNumber].
+ * Returns a [CompositeSymbol] consisting of the pin graphic overlaid with the stop number
+ */
+ private fun createStopSymbol(stopNumber: Int): CompositeSymbol {
+ // create black stop number TextSymbol
+ val stopNumberSymbol = TextSymbol(
+ stopNumber.toString(),
+ Color.black,
+ 12f,
+ HorizontalAlignment.Center,
+ VerticalAlignment.Bottom
+ ).apply {
+ offsetY = 4f
+ }
+
+ // create a new picture marker from a pin drawable
+ val pinSymbol = PictureMarkerSymbol.createWithImage(
+ ContextCompat.getDrawable(
+ this,
+ R.drawable.pin_symbol
+ ) as BitmapDrawable
+ ).apply {
+ // set the scale of the symbol
+ width = 24f
+ height = 24f
+ // set in pin "drop" to be offset to the point on map
+ offsetY = 10f
+ }
+
+ // create a composite symbol and add the picture marker symbol and text symbol
+ val compositeSymbol = CompositeSymbol()
+ compositeSymbol.symbols.addAll(listOf(pinSymbol, stopNumberSymbol))
+
+ return compositeSymbol
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_expand_less_black_24dp.xml b/samples/find-route-around-barriers/src/main/res/drawable/ic_expand_less_black_24dp.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/ic_expand_less_black_24dp.xml
rename to samples/find-route-around-barriers/src/main/res/drawable/ic_expand_less_black_24dp.xml
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_navigate.png b/samples/find-route-around-barriers/src/main/res/drawable/ic_navigate.png
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/ic_navigate.png
rename to samples/find-route-around-barriers/src/main/res/drawable/ic_navigate.png
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_outline_delete_24.xml b/samples/find-route-around-barriers/src/main/res/drawable/ic_outline_delete_24.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/ic_outline_delete_24.xml
rename to samples/find-route-around-barriers/src/main/res/drawable/ic_outline_delete_24.xml
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_round_directions_24.xml b/samples/find-route-around-barriers/src/main/res/drawable/ic_round_directions_24.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/ic_round_directions_24.xml
rename to samples/find-route-around-barriers/src/main/res/drawable/ic_round_directions_24.xml
diff --git a/find-route-around-barriers/src/main/res/drawable/ic_round_settings_24.xml b/samples/find-route-around-barriers/src/main/res/drawable/ic_round_settings_24.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/ic_round_settings_24.xml
rename to samples/find-route-around-barriers/src/main/res/drawable/ic_round_settings_24.xml
diff --git a/find-route-around-barriers/src/main/res/drawable/pin_symbol.png b/samples/find-route-around-barriers/src/main/res/drawable/pin_symbol.png
similarity index 100%
rename from find-route-around-barriers/src/main/res/drawable/pin_symbol.png
rename to samples/find-route-around-barriers/src/main/res/drawable/pin_symbol.png
diff --git a/find-route-around-barriers/src/main/res/layout/direction_sheet.xml b/samples/find-route-around-barriers/src/main/res/layout/direction_sheet.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/layout/direction_sheet.xml
rename to samples/find-route-around-barriers/src/main/res/layout/direction_sheet.xml
diff --git a/find-route-around-barriers/src/main/res/layout/activity_main.xml b/samples/find-route-around-barriers/src/main/res/layout/find_route_around_barriers_activity_main.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/layout/activity_main.xml
rename to samples/find-route-around-barriers/src/main/res/layout/find_route_around_barriers_activity_main.xml
diff --git a/find-route-around-barriers/src/main/res/layout/options_dialog.xml b/samples/find-route-around-barriers/src/main/res/layout/options_dialog.xml
similarity index 100%
rename from find-route-around-barriers/src/main/res/layout/options_dialog.xml
rename to samples/find-route-around-barriers/src/main/res/layout/options_dialog.xml
diff --git a/samples/find-route-around-barriers/src/main/res/values/strings.xml b/samples/find-route-around-barriers/src/main/res/values/strings.xml
new file mode 100644
index 000000000..6e4c125ed
--- /dev/null
+++ b/samples/find-route-around-barriers/src/main/res/values/strings.xml
@@ -0,0 +1,17 @@
+
+ Find route around barriers
+ Reset
+ Stops
+ Barriers
+ Directions
+ Collapse
+ Cancel
+ Find the best sequence
+ Preserve last stop
+ Preserve first stop
+ https://sampleserver7.arcgisonline.com/server/rest/services/NetworkAnalysis/SanDiego/NAServer/Route
+ Reset button
+ Options button
+ Directions button
+ Collapse directions
+
diff --git a/find-route-in-transport-network/README.md b/samples/find-route-in-transport-network/README.md
similarity index 100%
rename from find-route-in-transport-network/README.md
rename to samples/find-route-in-transport-network/README.md
diff --git a/find-route-in-transport-network/README.metadata.json b/samples/find-route-in-transport-network/README.metadata.json
similarity index 100%
rename from find-route-in-transport-network/README.metadata.json
rename to samples/find-route-in-transport-network/README.metadata.json
diff --git a/samples/find-route-in-transport-network/build.gradle.kts b/samples/find-route-in-transport-network/build.gradle.kts
new file mode 100644
index 000000000..0b825fb29
--- /dev/null
+++ b/samples/find-route-in-transport-network/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findrouteintransportnetwork"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-route-in-transport-network/find-route-in-transport-network.png b/samples/find-route-in-transport-network/find-route-in-transport-network.png
similarity index 100%
rename from find-route-in-transport-network/find-route-in-transport-network.png
rename to samples/find-route-in-transport-network/find-route-in-transport-network.png
diff --git a/samples/find-route-in-transport-network/src/main/AndroidManifest.xml b/samples/find-route-in-transport-network/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..01e5ae5ab
--- /dev/null
+++ b/samples/find-route-in-transport-network/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt b/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt
new file mode 100644
index 000000000..50c5d5262
--- /dev/null
+++ b/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/DownloadActivity.kt
@@ -0,0 +1,21 @@
+package com.esri.arcgismaps.sample.findrouteintransportnetwork
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.find_route_in_transport_network_app_name),
+ listOf(
+ //A zip file containing an offline routing network and .tpkx basemap
+ "https://arcgisruntime.maps.arcgis.com/home/item.html?id=df193653ed39449195af0c9725701dca"
+ )
+
+ )
+ }
+}
diff --git a/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt b/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt
new file mode 100644
index 000000000..19a6ae5fc
--- /dev/null
+++ b/samples/find-route-in-transport-network/src/main/java/com/esri/arcgismaps/sample/findrouteintransportnetwork/MainActivity.kt
@@ -0,0 +1,316 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findrouteintransportnetwork
+
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.layers.ArcGISTiledLayer
+import com.arcgismaps.mapping.layers.TileCache
+import com.arcgismaps.mapping.symbology.CompositeSymbol
+import com.arcgismaps.mapping.symbology.HorizontalAlignment
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.symbology.VerticalAlignment
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.tasks.networkanalysis.RouteParameters
+import com.arcgismaps.tasks.networkanalysis.RouteTask
+import com.arcgismaps.tasks.networkanalysis.Stop
+import com.esri.arcgismaps.sample.findrouteintransportnetwork.databinding.FindRouteInTransportNetworkActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.math.roundToInt
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: FindRouteInTransportNetworkActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.find_route_in_transport_network_activity_main)
+ }
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.find_route_in_transport_network_app_name)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val toggleButtons by lazy {
+ activityMainBinding.toggleButtons
+ }
+
+ private val clearButton by lazy {
+ activityMainBinding.clearButton
+ }
+
+ private val distanceTimeTextView by lazy {
+ activityMainBinding.distanceTimeTextView
+ }
+
+ private val stopsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+ private val routeOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ private val envelope = Envelope(
+ Point(-1.3045e7, 3.87e6, 0.0, SpatialReference.webMercator()),
+ Point(-1.3025e7, 3.84e6, 0.0, SpatialReference.webMercator())
+ )
+
+ // create a route task to calculate routes
+ private var routeTask: RouteTask? = null
+
+ private var routeParameters: RouteParameters? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // some parts of the API require an Android Context to properly interact with Android system
+ // features, such as LocationProvider and application resources
+ ArcGISEnvironment.applicationContext = applicationContext
+ lifecycle.addObserver(mapView)
+
+ // create a tile cache from the .tpkx file
+ val tileCache = TileCache(provisionPath + getString(R.string.tpkx_path))
+ val tiledLayer = ArcGISTiledLayer(tileCache)
+ // make a basemap with the tiled layer and add it to the mapview as an ArcGISMap
+ mapView.map = ArcGISMap(Basemap(tiledLayer))
+
+ // add the graphics overlays to the map view
+ mapView.graphicsOverlays.addAll(listOf(routeOverlay, stopsOverlay))
+
+ // create a route task using the geodatabase file
+ val geodatabaseFile = File(provisionPath + getString(R.string.geodatabase_path))
+ routeTask = RouteTask(geodatabaseFile.path, "Streets_ND")
+
+ // load the route task
+ lifecycleScope.launch {
+ routeTask?.load()?.onFailure {
+ showError(it.message.toString())
+ }?.onSuccess {
+ // use the default parameters for the route calculation
+ routeParameters = routeTask?.createDefaultParameters()?.getOrThrow()
+ }
+ }
+
+ toggleButtons.addOnButtonCheckedListener { _, checkedId, isChecked ->
+ if (isChecked) {
+ when (checkedId) {
+ R.id.fastestButton -> {
+ // calculate fastest route
+ routeParameters?.travelMode =
+ routeTask?.getRouteTaskInfo()?.travelModes?.get(0)
+
+ // update route based on selection
+ updateRoute()
+ }
+ R.id.shortestButton -> {
+ // calculate shortest route
+ routeParameters?.travelMode =
+ routeTask?.getRouteTaskInfo()?.travelModes?.get(1)
+
+ // update route based on selection
+ updateRoute()
+ }
+ }
+ }
+ }
+
+ // make a clear button to reset the stops and routes
+ clearButton.setOnClickListener {
+ stopsOverlay.graphics.clear()
+ routeOverlay.graphics.clear()
+ clearButton.isEnabled = false
+ distanceTimeTextView.text = getString(R.string.tap_on_map_to_create_a_transport_network)
+ }
+
+ // set up the touch listeners on the map view
+ setUpMapView()
+ }
+
+ /**
+ * Sets up the viewpoint and onSingleTapConfirmed for the mapView.
+ * For single taps, graphics will be selected.
+ * */
+ private fun setUpMapView() {
+ with(lifecycleScope) {
+ // set the viewpoint of the MapView
+ launch {
+ mapView.setViewpointGeometry(envelope)
+ }
+
+ // add graphic at the tapped coordinate
+ launch {
+ mapView.onSingleTapConfirmed.collect { tapEvent ->
+ val screenCoordinate = tapEvent.screenCoordinate
+ addOrSelectGraphic(screenCoordinate)
+ clearButton.isEnabled = true
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the calculated route using the
+ * stops on the map by calling routeTask.solveRoute().
+ * Creates a graphic to display the route.
+ * */
+ private fun updateRoute() = lifecycleScope.launch {
+ // get a list of stops from the graphics currently on the graphics overlay.
+ val stops = stopsOverlay.graphics.map {
+ Stop(it.geometry as Point)
+ }
+
+ // do not calculate a route if there is only one stop
+ if (stops.size <= 1) return@launch
+
+ routeParameters?.setStops(stops)
+
+ // solve the route
+ val results = routeParameters?.let { routeTask?.solveRoute(it) }
+ if (results != null) {
+ results.onFailure {
+ showError("No route solution. ${it.message}")
+ routeOverlay.graphics.clear()
+ }.onSuccess { routeResult ->
+ // get the first solved route result
+ val route = routeResult.routes[0]
+
+ // create graphic for route
+ val graphic = Graphic(
+ route.routeGeometry, SimpleLineSymbol(
+ SimpleLineSymbolStyle.Solid,
+ Color.black, 3F
+ )
+ )
+ routeOverlay.graphics.clear()
+ routeOverlay.graphics.add(graphic)
+
+ // set distance-time text
+ val travelTime = route.travelTime.roundToInt()
+ val travelDistance = "%.2f".format(
+ route.totalLength * 0.000621371192 // convert meters to miles and round 2 decimals
+ )
+ distanceTimeTextView.text = String.format("$travelTime min ($travelDistance mi)")
+ }
+ }
+ }
+
+ /**
+ * Selects a graphic if there is one at the
+ * provided [screenCoordinate] or, if there is
+ * none, creates a new graphic.
+ * */
+ private suspend fun addOrSelectGraphic(screenCoordinate: ScreenCoordinate) {
+ // identify the selected graphic
+ val result =
+ mapView.identifyGraphicsOverlay(stopsOverlay, screenCoordinate, 10.0, false)
+
+ result.onFailure {
+ showError(it.message.toString())
+ }.onSuccess { identifyGraphicsOverlayResult ->
+ val graphics = identifyGraphicsOverlayResult.graphics
+
+ // unselect everything
+ if (stopsOverlay.selectedGraphics.isNotEmpty()) {
+ stopsOverlay.unselectGraphics(stopsOverlay.selectedGraphics)
+ }
+
+ // if the user tapped on something, select it
+ if (graphics.isNotEmpty()) {
+ val firstGraphic = graphics[0]
+ firstGraphic.isSelected = true
+ } else { // there is no graphic at this location
+ val locationPoint = mapView.screenToLocation(screenCoordinate)
+ // check if tapped location is within the envelope
+ if (GeometryEngine.within(locationPoint as Geometry, envelope))
+ // make a new graphic at the tapped location
+ createStopSymbol(stopsOverlay.graphics.size + 1, locationPoint)
+ else
+ showError("Tapped location is outside the transport network")
+ }
+ }
+ }
+
+ /**
+ * Creates a composite symbol to represent a numbered stop.
+ * The [stopNumber] is the ordinal number of this stop and the
+ * symbol will be placed at the [locationPoint].
+ */
+ private fun createStopSymbol(stopNumber: Int, locationPoint: Point?) {
+ // create a orange pin PictureMarkerSymbol
+ val pinSymbol = PictureMarkerSymbol.createWithImage(
+ ContextCompat.getDrawable(
+ this,
+ R.drawable.pin_symbol
+ ) as BitmapDrawable
+ ).apply {
+ // set the scale of the symbol
+ width = 24f
+ height = 24f
+ // set in pin "drop" to be offset to the point on map
+ offsetY = 10f
+ }
+
+ // create black stop number TextSymbol
+ val stopNumberSymbol = TextSymbol(
+ stopNumber.toString(),
+ Color.black,
+ 12f,
+ HorizontalAlignment.Center,
+ VerticalAlignment.Bottom
+ ).apply {
+ offsetY = 4f
+ }
+
+ // create a composite symbol and add the picture marker symbol and text symbol
+ val compositeSymbol = CompositeSymbol()
+ compositeSymbol.symbols.addAll(listOf(pinSymbol, stopNumberSymbol))
+
+ // create a graphic to add to the overlay and update the route
+ val graphic = Graphic(locationPoint, compositeSymbol)
+ stopsOverlay.graphics.add(graphic)
+
+ updateRoute()
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/find-route-in-transport-network/src/main/res/drawable/ic_outline_delete_24.xml b/samples/find-route-in-transport-network/src/main/res/drawable/ic_outline_delete_24.xml
similarity index 100%
rename from find-route-in-transport-network/src/main/res/drawable/ic_outline_delete_24.xml
rename to samples/find-route-in-transport-network/src/main/res/drawable/ic_outline_delete_24.xml
diff --git a/find-route-in-transport-network/src/main/res/drawable/pin_symbol.png b/samples/find-route-in-transport-network/src/main/res/drawable/pin_symbol.png
similarity index 100%
rename from find-route-in-transport-network/src/main/res/drawable/pin_symbol.png
rename to samples/find-route-in-transport-network/src/main/res/drawable/pin_symbol.png
diff --git a/find-route-in-transport-network/src/main/res/layout/activity_main.xml b/samples/find-route-in-transport-network/src/main/res/layout/find_route_in_transport_network_activity_main.xml
similarity index 100%
rename from find-route-in-transport-network/src/main/res/layout/activity_main.xml
rename to samples/find-route-in-transport-network/src/main/res/layout/find_route_in_transport_network_activity_main.xml
diff --git a/samples/find-route-in-transport-network/src/main/res/values/strings.xml b/samples/find-route-in-transport-network/src/main/res/values/strings.xml
new file mode 100644
index 000000000..460bdd88f
--- /dev/null
+++ b/samples/find-route-in-transport-network/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Find route in transport network
+ /streetmap_SD.tpkx
+ /sandiego.geodatabase
+ Tap on map to create a transport network
+ Shortest
+ Fastest
+ Clear
+
diff --git a/find-route/README.md b/samples/find-route/README.md
similarity index 100%
rename from find-route/README.md
rename to samples/find-route/README.md
diff --git a/find-route/README.metadata.json b/samples/find-route/README.metadata.json
similarity index 100%
rename from find-route/README.metadata.json
rename to samples/find-route/README.metadata.json
diff --git a/samples/find-route/build.gradle.kts b/samples/find-route/build.gradle.kts
new file mode 100644
index 000000000..e2ace3c9c
--- /dev/null
+++ b/samples/find-route/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.findroute"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/find-route/find-route.png b/samples/find-route/find-route.png
similarity index 100%
rename from find-route/find-route.png
rename to samples/find-route/find-route.png
diff --git a/samples/find-route/src/main/AndroidManifest.xml b/samples/find-route/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..802f080b7
--- /dev/null
+++ b/samples/find-route/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt b/samples/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt
new file mode 100644
index 000000000..458a1d63f
--- /dev/null
+++ b/samples/find-route/src/main/java/com/esri/arcgismaps/sample/findroute/MainActivity.kt
@@ -0,0 +1,296 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.findroute
+
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ListView
+import android.widget.ProgressBar
+import androidx.appcompat.app.AppCompatActivity
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.MapView
+import com.arcgismaps.tasks.networkanalysis.DirectionManeuver
+import com.arcgismaps.tasks.networkanalysis.RouteResult
+import com.arcgismaps.tasks.networkanalysis.RouteTask
+import com.arcgismaps.tasks.networkanalysis.Stop
+import com.esri.arcgismaps.sample.findroute.databinding.FindRouteActivityMainBinding
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: FindRouteActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.find_route_activity_main)
+ }
+
+ private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ private val mapView: MapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val mainContainer: ConstraintLayout by lazy {
+ activityMainBinding.mainContainer
+ }
+
+ private val mainProgressBar: ProgressBar by lazy {
+ activityMainBinding.mainProgressBar
+ }
+
+ private val directionFab: FloatingActionButton by lazy {
+ activityMainBinding.directionFab
+ }
+
+ private val bottomSheet: LinearLayout by lazy {
+ activityMainBinding.bottomSheet.bottomSheetLayout
+ }
+
+ private val header: ConstraintLayout by lazy {
+ activityMainBinding.bottomSheet.header
+ }
+
+ private val imageView: ImageView by lazy {
+ activityMainBinding.bottomSheet.imageView
+ }
+
+ private val directionsListView: ListView by lazy {
+ activityMainBinding.bottomSheet.directionsListView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(activityMainBinding.mapView)
+
+ mapView.apply {
+ // set the map to a new map with the navigation base map
+ map = ArcGISMap(BasemapStyle.ArcGISNavigation)
+ // set initial viewpoint to San Diego
+ setViewpoint(Viewpoint(32.7157, -117.1611, 200000.0))
+ mapView.graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // create the symbols for the route
+ setupSymbols()
+
+ // hide the bottom sheet and make the map view span the whole screen
+ bottomSheet.visibility = View.INVISIBLE
+ (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
+
+ // solve the route and display the bottom sheet when the FAB is clicked
+ directionFab.setOnClickListener { lifecycleScope.launch { solveRoute() } }
+ }
+
+ /**
+ * Solves the route using a Route Task, populates the navigation drawer with the directions,
+ * and displays a graphic of the route on the map.
+ */
+ private suspend fun solveRoute() {
+ // set the applicationContext as it is required with RouteTask
+ ArcGISEnvironment.applicationContext = applicationContext
+ // create a route task instance
+ val routeTask =
+ RouteTask(
+ "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"
+ )
+
+ // show the progress bar
+ mainProgressBar.visibility = View.VISIBLE
+ routeTask.createDefaultParameters().onSuccess { routeParams ->
+ // create stops
+ val stops = listOf(
+ Stop(Point(-117.1508, 32.7411, SpatialReference.wgs84())),
+ Stop(Point(-117.1555, 32.7033, SpatialReference.wgs84()))
+ )
+
+ routeParams.apply {
+ setStops(stops)
+ // set return directions as true to return turn-by-turn directions in the route's directionManeuvers
+ returnDirections = true
+ }
+
+ // solve the route
+ val routeResult = routeTask.solveRoute(routeParams).getOrElse {
+ showError(it.message.toString())
+ } as RouteResult
+ val route = routeResult.routes[0]
+ // create a simple line symbol for the route
+ val routeSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 5f)
+
+ // create a graphic for the route and add it to the graphics overlay
+ graphicsOverlay.graphics.add(Graphic(route.routeGeometry, routeSymbol))
+ // get the list of direction maneuvers and display it
+ // NOTE: to get turn-by-turn directions the route parameters
+ // must have the isReturnDirections parameter set to true.
+ val directions = route.directionManeuvers
+ setupBottomSheet(directions)
+
+ // when the route is solved, hide the FAB and the progress bar
+ directionFab.visibility = View.GONE
+ mainProgressBar.visibility = View.GONE
+ }.onFailure {
+ showError(it.message.toString())
+ mainProgressBar.visibility = View.GONE
+ }
+ }
+
+ /** Creates a bottom sheet to display a list of direction maneuvers.
+ * [directions] a list of DirectionManeuver which represents the route
+ */
+ private fun setupBottomSheet(directions: List) {
+ // create a bottom sheet behavior from the bottom sheet view in the main layout
+ val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet).apply {
+ // expand the bottom sheet, and ensure it is displayed on the screen when collapsed
+ state = BottomSheetBehavior.STATE_EXPANDED
+ peekHeight = header.height
+ // animate the arrow when the bottom sheet slides
+ addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ imageView.rotation = slideOffset * 180f
+ }
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ imageView.rotation = when (newState) {
+ BottomSheetBehavior.STATE_EXPANDED -> 180f
+ else -> imageView.rotation
+ }
+ }
+ })
+ }
+
+ bottomSheet.apply {
+ visibility = View.VISIBLE
+ // expand or collapse the bottom sheet when the header is clicked
+ header.setOnClickListener {
+ bottomSheetBehavior.state = when (bottomSheetBehavior.state) {
+ BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED
+ else -> BottomSheetBehavior.STATE_COLLAPSED
+ }
+ }
+ // rotate the arrow so it starts off in the correct rotation
+ imageView.rotation = 180f
+ }
+
+ directionsListView.apply {
+ // Set the adapter for the list view
+ adapter = ArrayAdapter(
+ this@MainActivity,
+ android.R.layout.simple_list_item_1,
+ directions.map { it.directionText }
+ )
+ // when the user taps a maneuver, set the viewpoint to that portion of the route
+ onItemClickListener =
+ AdapterView.OnItemClickListener { _, _, position, _ ->
+ directions[position].geometry?.let { geometry ->
+ // set the viewpoint to the selected maneuver
+ mapView.setViewpoint(
+ Viewpoint(geometry.extent, 20.0)
+ )
+ // create a graphic with a symbol for the maneuver and add it to the graphics overlay
+ val selectedRouteSymbol = SimpleLineSymbol(
+ SimpleLineSymbolStyle.Solid,
+ Color.green, 5f
+ )
+ graphicsOverlay.graphics.add(Graphic(geometry, selectedRouteSymbol))
+ // collapse the bottom sheet
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+ }
+ }
+
+ // shrink the map view so it is not hidden under the bottom sheet header
+ (mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin =
+ header.height
+ }
+
+ /**
+ * Set up the source, destination and route symbols.
+ */
+ private fun setupSymbols() {
+ val startDrawable =
+ ContextCompat.getDrawable(this, R.drawable.ic_source) as BitmapDrawable
+ val pinSourceSymbol = PictureMarkerSymbol.createWithImage(startDrawable).apply {
+ // make the graphic smaller
+ width = 30f
+ height = 30f
+ offsetY = 20f
+ }
+ // create a point for the new graphic
+ val sourcePoint = Point(
+ -117.1508, 32.7411, SpatialReference.wgs84()
+ )
+ // create a graphic and it to the graphics overlay
+ graphicsOverlay.graphics.add(Graphic(sourcePoint, pinSourceSymbol))
+
+ val endDrawable =
+ ContextCompat.getDrawable(this, R.drawable.ic_destination) as BitmapDrawable
+
+ endDrawable.let {
+ val pinDestinationSymbol =
+ PictureMarkerSymbol.createWithImage(endDrawable).apply {
+ // make the graphic smaller
+ width = 30f
+ height = 30f
+ offsetY = 20f
+ }
+ // create a point for the new graphic
+ val destinationPoint = Point(-117.1555, 32.7033, SpatialReference.wgs84())
+ // create a graphic and add it to the graphics overlay
+ graphicsOverlay.graphics.add(Graphic(destinationPoint, pinDestinationSymbol))
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+}
diff --git a/find-route/src/main/res/drawable/ic_destination.png b/samples/find-route/src/main/res/drawable/ic_destination.png
similarity index 100%
rename from find-route/src/main/res/drawable/ic_destination.png
rename to samples/find-route/src/main/res/drawable/ic_destination.png
diff --git a/find-route/src/main/res/drawable/ic_expand_less_black_24dp.xml b/samples/find-route/src/main/res/drawable/ic_expand_less_black_24dp.xml
similarity index 100%
rename from find-route/src/main/res/drawable/ic_expand_less_black_24dp.xml
rename to samples/find-route/src/main/res/drawable/ic_expand_less_black_24dp.xml
diff --git a/find-route/src/main/res/drawable/ic_navigate.png b/samples/find-route/src/main/res/drawable/ic_navigate.png
similarity index 100%
rename from find-route/src/main/res/drawable/ic_navigate.png
rename to samples/find-route/src/main/res/drawable/ic_navigate.png
diff --git a/find-route/src/main/res/drawable/ic_source.png b/samples/find-route/src/main/res/drawable/ic_source.png
similarity index 100%
rename from find-route/src/main/res/drawable/ic_source.png
rename to samples/find-route/src/main/res/drawable/ic_source.png
diff --git a/find-route/src/main/res/layout/bottom_sheet.xml b/samples/find-route/src/main/res/layout/bottom_sheet.xml
similarity index 100%
rename from find-route/src/main/res/layout/bottom_sheet.xml
rename to samples/find-route/src/main/res/layout/bottom_sheet.xml
diff --git a/find-route/src/main/res/layout/activity_main.xml b/samples/find-route/src/main/res/layout/find_route_activity_main.xml
similarity index 100%
rename from find-route/src/main/res/layout/activity_main.xml
rename to samples/find-route/src/main/res/layout/find_route_activity_main.xml
diff --git a/samples/find-route/src/main/res/values/strings.xml b/samples/find-route/src/main/res/values/strings.xml
new file mode 100644
index 000000000..b5325e72d
--- /dev/null
+++ b/samples/find-route/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Find route
+ Directions
+ Collapse directions
+ Display Directions button
+
diff --git a/generate-geodatabase-replica-from-feature-service/README.md b/samples/generate-geodatabase-replica-from-feature-service/README.md
similarity index 100%
rename from generate-geodatabase-replica-from-feature-service/README.md
rename to samples/generate-geodatabase-replica-from-feature-service/README.md
diff --git a/generate-geodatabase-replica-from-feature-service/README.metadata.json b/samples/generate-geodatabase-replica-from-feature-service/README.metadata.json
similarity index 100%
rename from generate-geodatabase-replica-from-feature-service/README.metadata.json
rename to samples/generate-geodatabase-replica-from-feature-service/README.metadata.json
diff --git a/samples/generate-geodatabase-replica-from-feature-service/build.gradle.kts b/samples/generate-geodatabase-replica-from-feature-service/build.gradle.kts
new file mode 100644
index 000000000..e4871efcd
--- /dev/null
+++ b/samples/generate-geodatabase-replica-from-feature-service/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/generate-geodatabase-replica-from-feature-service/generate-geodatabase-replica-from-feature-service.png b/samples/generate-geodatabase-replica-from-feature-service/generate-geodatabase-replica-from-feature-service.png
similarity index 100%
rename from generate-geodatabase-replica-from-feature-service/generate-geodatabase-replica-from-feature-service.png
rename to samples/generate-geodatabase-replica-from-feature-service/generate-geodatabase-replica-from-feature-service.png
diff --git a/samples/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml b/samples/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a72cf0d12
--- /dev/null
+++ b/samples/generate-geodatabase-replica-from-feature-service/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt b/samples/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt
new file mode 100644
index 000000000..c0d4a7b40
--- /dev/null
+++ b/samples/generate-geodatabase-replica-from-feature-service/src/main/java/com/esri/arcgismaps/sample/generategeodatabasereplicafromfeatureservice/MainActivity.kt
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice
+
+import android.os.Bundle
+import android.util.Log
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.data.Geodatabase
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.tasks.geodatabase.GenerateGeodatabaseJob
+import com.arcgismaps.tasks.geodatabase.GeodatabaseSyncTask
+import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.databinding.GenerateGeodatabaseReplicaFromFeatureServiceActivityMainBinding
+import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.databinding.GenerateGeodatabaseDialogLayoutBinding
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: GenerateGeodatabaseReplicaFromFeatureServiceActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.generate_geodatabase_replica_from_feature_service_activity_main)
+ }
+
+ // setup data binding for the mapview
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // starts the geodatabase replica process
+ private val generateButton by lazy {
+ activityMainBinding.generateButton
+ }
+
+ private val resetButton by lazy {
+ activityMainBinding.resetButton
+ }
+
+ // shows the geodatabase loading progress
+ private val progressDialog by lazy {
+ GenerateGeodatabaseDialogLayoutBinding.inflate(layoutInflater)
+ }
+
+ // local file path to the geodatabase
+ private val geodatabaseFilePath by lazy {
+ getExternalFilesDir(null)?.path + getString(R.string.portland_trees_geodatabase_file)
+ }
+
+ // keep track of the geodatabase replica generated by the feature service
+ private var geodatabase: Geodatabase? = null
+
+ private val downloadArea: Graphic = Graphic()
+
+ // create a Trees FeatureLayer using the first layer of the ServiceFeatureTable
+ private val featureLayer: FeatureLayer by lazy {
+ FeatureLayer.createWithFeatureTable(
+ featureTable = ServiceFeatureTable(
+ uri = application.getString(R.string.feature_server_url) + "/0"
+ )
+ )
+ }
+
+ // creates a graphic overlay
+ private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a Topographic basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
+ operationalLayers.add(featureLayer)
+ }
+ // set the max map extents to that of the feature service
+ // representing portland area
+ map.maxExtent = Envelope(
+ -13687689.2185849,
+ 5687273.88331375,
+ -13622795.3756647,
+ 5727520.22085841,
+ spatialReference = SpatialReference.webMercator()
+ )
+ // configure mapview assignments
+ mapView.apply {
+ this.map = map
+ // add the graphics overlay to display the boundary
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // create a geodatabase sync task with the feature service url
+ // This feature service shows a web map of portland street trees,
+ // their attributes, as well as related inspection information
+ val geodatabaseSyncTask = GeodatabaseSyncTask(getString(R.string.feature_server_url))
+
+ // set the button's onClickListener
+ generateButton.setOnClickListener {
+ // start the geodatabase generation process
+ generateGeodatabase(geodatabaseSyncTask, map, downloadArea.geometry?.extent)
+ }
+
+ resetButton.setOnClickListener {
+ // clear any layers already on the map
+ map.operationalLayers.clear()
+ // clear all symbols drawn
+ graphicsOverlay.graphics.clear()
+ // add the download boundary
+ graphicsOverlay.graphics.add(downloadArea)
+ // add back the feature layer
+ map.operationalLayers.add(featureLayer)
+ // close the current geodatabase, if a replica was already generated
+ geodatabase?.close()
+ // show generate button
+ generateButton.isEnabled = true
+ resetButton.isEnabled = false
+ }
+
+ lifecycleScope.launch {
+ // show the error and return if map load failed
+ map.load().onFailure {
+ showError("Unable to load map")
+ return@launch
+ }
+
+ geodatabaseSyncTask.load().onFailure {
+ // if the metadata load fails, show the error and return
+ showError("Failed to fetch geodatabase metadata")
+ return@launch
+ }
+
+ // show download area once map is loaded
+ updateDownloadArea()
+
+ // enable the generate button since the task is now loaded
+ generateButton.isEnabled = true
+
+ // create a symbol to show a box around the extent we want to download
+ downloadArea.symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2F)
+ // add the graphic to the graphics overlay when it is created
+ graphicsOverlay.graphics.add(downloadArea)
+ // update the download area on viewpoint change
+ mapView.viewpointChanged.collect {
+ updateDownloadArea()
+ }
+ }
+ }
+
+ /**
+ * Displays a red border on the map to signify the [downloadArea]
+ */
+ private fun updateDownloadArea() {
+ // define screen area to create replica
+ val minScreenPoint = ScreenCoordinate(200.0, 200.0)
+ val maxScreenPoint = ScreenCoordinate(
+ mapView.measuredWidth - 200.0,
+ mapView.measuredHeight - 200.0
+ )
+ // convert screen points to map points
+ val minPoint = mapView.screenToLocation(minScreenPoint)
+ val maxPoint = mapView.screenToLocation(maxScreenPoint)
+ // use the points to define and return an envelope
+ if (minPoint != null && maxPoint != null) {
+ val envelope = Envelope(minPoint, maxPoint)
+ downloadArea.geometry = envelope
+ }
+ }
+
+ /**
+ * Starts a [geodatabaseSyncTask] with the given [map] and [extents],
+ * runs a GenerateGeodatabaseJob and saves the geodatabase file into local storage
+ */
+ private fun generateGeodatabase(
+ geodatabaseSyncTask: GeodatabaseSyncTask,
+ map: ArcGISMap,
+ extents: Envelope?
+ ) {
+ if (extents == null) {
+ return showError("Download area extent is null")
+ }
+
+ lifecycleScope.launch {
+ // create generate geodatabase parameters for the selected extents
+ val defaultParameters =
+ geodatabaseSyncTask.createDefaultGenerateGeodatabaseParameters(extents).getOrElse {
+ // show the error and return if the task fails
+ showError("Error creating geodatabase parameters")
+ return@launch
+ }.apply {
+ // set the parameters to only create a replica of the Trees (0) layer
+ layerOptions.removeIf { layerOptions ->
+ layerOptions.layerId != 0L
+ }
+ }
+
+ // set return attachments option to false
+ // indicates if any attachments are added to the geodatabase from the feature service
+ defaultParameters.returnAttachments = false
+ // create a generate geodatabase job
+ geodatabaseSyncTask.createGenerateGeodatabaseJob(defaultParameters, geodatabaseFilePath)
+ .run {
+ // create a dialog to show the jobs progress
+ val materialDialogBuilder = createProgressDialog(this)
+
+ // show the dialog and obtain a reference to it
+ val jobProgressDialog = materialDialogBuilder.show()
+
+ // launch a progress collector to display progress
+ launch {
+ progress.collect { value ->
+ // update the progress bar and progress text
+ progressDialog.progressBar.progress = value
+ progressDialog.progressTextView.text = value.toString()
+ }
+ }
+ // start the generateGeodatabase job
+ start()
+ // if the job completed successfully, get the geodatabase from the result
+ val geodatabase = result().getOrElse {
+ // show an error and return if job failed
+ showError("Error fetching geodatabase: ${it.message}")
+ // dismiss the dialog
+ jobProgressDialog.dismiss()
+ return@launch
+ }
+
+ // load and display the geodatabase
+ loadGeodatabase(geodatabase, map)
+ // dismiss the dialog view
+ jobProgressDialog.dismiss()
+ // unregister since we are not syncing
+ geodatabaseSyncTask.unregisterGeodatabase(geodatabase)
+ // show reset button as the task is now complete
+ generateButton.isEnabled = false
+ resetButton.isEnabled = true
+ }
+ }
+ }
+
+ /**
+ * Loads the [replicaGeodatabase] and renders the feature layers on to the [map]
+ */
+ private suspend fun loadGeodatabase(replicaGeodatabase: Geodatabase, map: ArcGISMap) {
+ // clear any layers already on the map
+ map.operationalLayers.clear()
+ // clear all symbols drawn
+ graphicsOverlay.graphics.clear()
+
+ // load the geodatabase
+ replicaGeodatabase.load().onFailure {
+ // if the load failed, show the error and return
+ showError("Error loading geodatabase")
+ return
+ }
+
+ // add all of the geodatabase feature tables to the map as feature layers
+ map.operationalLayers += replicaGeodatabase.featureTables.map { featureTable ->
+ FeatureLayer.createWithFeatureTable(featureTable)
+ }
+
+ // keep track of the geodatabase to close it before generating a new replica
+ geodatabase = replicaGeodatabase
+ }
+
+ /**
+ * Creates a new alert dialog using the progressDialog and provides
+ * GenerateGeodatabaseJob cancellation on dialog cancellation
+ *
+ * @param generateGeodatabaseJob the job to cancel
+ *
+ * @return returns an alert dialog
+ */
+ private fun createProgressDialog(generateGeodatabaseJob: GenerateGeodatabaseJob): MaterialAlertDialogBuilder {
+ // build and return a new alert dialog
+ return MaterialAlertDialogBuilder(this).apply {
+ // setting it title
+ setTitle(getString(R.string.dialog_title))
+ // allow it to be cancellable
+ setCancelable(false)
+ // sets negative button configuration
+ setNegativeButton("Cancel") { _, _ ->
+ // cancels the generateGeodatabaseJob
+ lifecycleScope.launch {
+ generateGeodatabaseJob.cancel()
+ }
+ }
+ // removes parent of the progressDialog layout, if previously assigned
+ progressDialog.root.parent?.let { parent ->
+ (parent as ViewGroup).removeAllViews()
+ }
+ // set the progressDialog Layout to this alert dialog
+ setView(progressDialog.root)
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml b/samples/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml
similarity index 97%
rename from generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml
rename to samples/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml
index c19c7162b..bb1aeb60e 100644
--- a/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml
+++ b/samples/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_dialog_layout.xml
@@ -17,7 +17,6 @@
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:indeterminate="false"
- android:theme="@style/AppTheme"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/progressTextView"
diff --git a/generate-geodatabase-replica-from-feature-service/src/main/res/layout/activity_main.xml b/samples/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_replica_from_feature_service_activity_main.xml
similarity index 100%
rename from generate-geodatabase-replica-from-feature-service/src/main/res/layout/activity_main.xml
rename to samples/generate-geodatabase-replica-from-feature-service/src/main/res/layout/generate_geodatabase_replica_from_feature_service_activity_main.xml
diff --git a/samples/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml b/samples/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml
new file mode 100644
index 000000000..74dacf670
--- /dev/null
+++ b/samples/generate-geodatabase-replica-from-feature-service/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ Generate geodatabase replica from feature service
+ https://services2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/rest/services/Mobile_Data_Collection_WFL1/FeatureServer
+ Generate
+ Fetching result
+ /portland_trees_gdb.geodatabase
+ Reset map
+
diff --git a/generate-offline-map-using-android-jetpack-workmanager/README.md b/samples/generate-offline-map-using-android-jetpack-workmanager/README.md
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/README.md
rename to samples/generate-offline-map-using-android-jetpack-workmanager/README.md
diff --git a/generate-offline-map-using-android-jetpack-workmanager/README.metadata.json b/samples/generate-offline-map-using-android-jetpack-workmanager/README.metadata.json
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/README.metadata.json
rename to samples/generate-offline-map-using-android-jetpack-workmanager/README.metadata.json
diff --git a/samples/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts b/samples/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts
new file mode 100644
index 000000000..ae2896fe3
--- /dev/null
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/build.gradle.kts
@@ -0,0 +1,25 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.androidx.work.runtime.ktx)
+ implementation(libs.androidx.lifecycle.livedata.ktx)
+}
diff --git a/generate-offline-map-using-android-jetpack-workmanager/generate-offline-map-using-android-jetpack-workmanager.png b/samples/generate-offline-map-using-android-jetpack-workmanager/generate-offline-map-using-android-jetpack-workmanager.png
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/generate-offline-map-using-android-jetpack-workmanager.png
rename to samples/generate-offline-map-using-android-jetpack-workmanager/generate-offline-map-using-android-jetpack-workmanager.png
diff --git a/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..74e62b9bd
--- /dev/null
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt
new file mode 100644
index 000000000..e5015f72c
--- /dev/null
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/MainActivity.kt
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager
+
+import android.Manifest.permission.POST_NOTIFICATIONS
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.asFlow
+import androidx.lifecycle.lifecycleScope
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.OutOfQuotaPolicy
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+import androidx.work.workDataOf
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.MobileMapPackage
+import com.arcgismaps.mapping.PortalItem
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.tasks.offlinemaptask.GenerateOfflineMapJob
+import com.arcgismaps.tasks.offlinemaptask.GenerateOfflineMapParameters
+import com.arcgismaps.tasks.offlinemaptask.OfflineMapTask
+import com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager.databinding.GenerateOfflineMapUsingAndroidJetpackWorkmanagerActivityMainBinding
+import com.esri.arcgismaps.sample.generateofflinemapusingandroidjetpackworkmanager.databinding.OfflineJobProgressDialogLayoutBinding
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.random.Random
+
+// data parameter keys for the WorkManager
+// key for the NotificationId parameter
+const val notificationIdParameter = "NotificationId"
+
+// key for the json job file path
+const val jobParameter = "JsonJobPath"
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: GenerateOfflineMapUsingAndroidJetpackWorkmanagerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.generate_offline_map_using_android_jetpack_workmanager_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val takeMapOfflineButton by lazy {
+ activityMainBinding.takeMapOfflineButton
+ }
+
+ private val resetMapButton by lazy {
+ activityMainBinding.resetButton
+ }
+
+ // instance of the WorkManager
+ private val workManager by lazy {
+ WorkManager.getInstance(this)
+ }
+
+ // file path to store the offline map package
+ private val offlineMapPath by lazy {
+ getExternalFilesDir(null)?.path + getString(R.string.offlineMapFile)
+ }
+
+ // shows the offline map job loading progress
+ private val progressLayout by lazy {
+ OfflineJobProgressDialogLayoutBinding.inflate(layoutInflater)
+ }
+
+ // alert dialog view for the progress layout
+ private val progressDialog by lazy {
+ createProgressDialog().create()
+ }
+
+ // used to uniquely identify the work request so that only one worker is active at a time
+ // also allows us to query and observe work progress
+ private val uniqueWorkName = "ArcgisMaps.Sample.OfflineMapJob.Worker"
+
+ // create a graphic overlay
+ private val graphicsOverlay = GraphicsOverlay()
+
+ // represents bounds of the downloadable area of the map
+ private val downloadArea = Graphic(
+ symbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.red, 2F)
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // request notifications permission
+ requestNotificationPermission()
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // set up the portal item to take offline
+ setUpMapView()
+
+ // clear the preview map and display the Portal Item
+ resetMapButton.setOnClickListener {
+ // enable offline button
+ takeMapOfflineButton.isEnabled = true
+ resetMapButton.isEnabled = false
+ // clear graphic overlays
+ graphicsOverlay.graphics.clear()
+ mapView.graphicsOverlays.clear()
+
+ // set up the portal item to take offline
+ setUpMapView()
+ }
+ }
+
+ /**
+ * Sets up a portal item and displays map area to take offline
+ */
+ private fun setUpMapView() {
+ // create a portal item with the itemId of the web map
+ val portal = Portal(getString(R.string.portal_url))
+ val portalItem = PortalItem(portal, getString(R.string.item_id))
+
+ // add the graphic to the graphics overlay when it is created
+ graphicsOverlay.graphics.add(downloadArea)
+ // create and add a map with with portal item
+ val map = ArcGISMap(portalItem)
+ // apply mapview assignments
+ mapView.apply {
+ this.map = map
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ lifecycleScope.launch {
+ map.load().onFailure {
+ // show an error and return if the map load failed
+ showMessage("Error loading map: ${it.message}")
+ return@launch
+ }
+
+ // enable the take map offline button only after the map is loaded
+ takeMapOfflineButton.isEnabled = true
+
+ // get the Control Valve layer from the map's operational layers
+ val operationalLayer =
+ map.operationalLayers.firstOrNull { layer ->
+ layer.name == "Control Valve"
+ } ?: return@launch showMessage("Error finding Control Valve layer")
+
+ // limit the map scale to the layer's scale
+ map.maxScale = operationalLayer.maxScale ?: 0.0
+ map.minScale = operationalLayer.minScale ?: 0.0
+
+ mapView.viewpointChanged.collect {
+ // upper left corner of the area to take offline
+ val minScreenPoint = ScreenCoordinate(200.0, 200.0)
+ // lower right corner of the downloaded area
+ val maxScreenPoint = ScreenCoordinate(
+ mapView.width - 200.0,
+ mapView.height - 200.0
+ )
+ // convert screen points to map points
+ val minPoint = mapView.screenToLocation(minScreenPoint) ?: return@collect
+ val maxPoint = mapView.screenToLocation(maxScreenPoint) ?: return@collect
+ // use the points to define and set an envelope for the downloadArea graphic
+ val envelope = Envelope(minPoint, maxPoint)
+ downloadArea.geometry = envelope
+ }
+ }
+
+ // set onclick listener for the takeMapOfflineButton
+ takeMapOfflineButton.setOnClickListener {
+ // if the downloadArea's geometry is not null
+ downloadArea.geometry?.let { geometry ->
+ // create an OfflineMapJob
+ val offlineMapJob = createOfflineMapJob(map, geometry)
+ // start the OfflineMapJob
+ startOfflineMapJob(offlineMapJob)
+ // show the progress dialog
+ progressDialog.show()
+ // disable the button
+ takeMapOfflineButton.isEnabled = false
+ }
+ }
+
+ // start observing the worker's progress and status
+ observeWorkStatus()
+ }
+
+ /**
+ * Creates and returns a new GenerateOfflineMapJob for the [map] and its [areaOfInterest]
+ */
+ private fun createOfflineMapJob(
+ map: ArcGISMap,
+ areaOfInterest: Geometry
+ ): GenerateOfflineMapJob {
+ // check and delete if the offline map package file already exists
+ File(offlineMapPath).deleteRecursively()
+ // specify the min scale and max scale as parameters
+ val maxScale = map.maxScale ?: 0.0
+ var minScale = map.minScale ?: 0.0
+ // minScale must always be larger than maxScale
+ if (minScale <= maxScale) {
+ minScale = maxScale + 1
+ }
+ // set the offline map parameters
+ val generateOfflineMapParameters = GenerateOfflineMapParameters(
+ areaOfInterest,
+ minScale,
+ maxScale
+ ).apply {
+ // set job to cancel on any errors
+ continueOnErrors = false
+ }
+ // create an offline map task with the map
+ val offlineMapTask = OfflineMapTask(map)
+ // create an offline map job with the download directory path and parameters and
+ // return the job
+ return offlineMapTask.createGenerateOfflineMapJob(
+ generateOfflineMapParameters,
+ offlineMapPath
+ )
+ }
+
+ /**
+ * Starts the [offlineMapJob] using OfflineJobWorker with WorkManager. The [offlineMapJob] is
+ * serialized into a json file and the uri is passed to the OfflineJobWorker, since WorkManager
+ * enforces a MAX_DATA_BYTES for the WorkRequest's data
+ */
+ private fun startOfflineMapJob(offlineMapJob: GenerateOfflineMapJob) {
+ // create a temporary file path to save the offlineMapJob json file
+ val offlineJobJsonPath = getExternalFilesDir(null)?.path +
+ getString(R.string.offlineJobJsonFile)
+
+ // create the json file
+ val offlineJobJsonFile = File(offlineJobJsonPath)
+ // serialize the offlineMapJob into the file
+ offlineJobJsonFile.writeText(offlineMapJob.toJson())
+
+ // create a non-zero notification id for the OfflineJobWorker
+ // this id will be used to post or update any progress/status notifications
+ val notificationId = Random.Default.nextInt(1, 100)
+
+ // create a one-time work request with an instance of OfflineJobWorker
+ val workRequest = OneTimeWorkRequestBuilder()
+ // run it as an expedited work
+ .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+ // add the input data
+ .setInputData(
+ // add the notificationId and the json file path as a key/value pair
+ workDataOf(
+ notificationIdParameter to notificationId,
+ jobParameter to offlineJobJsonFile.absolutePath
+ )
+ ).build()
+
+ // enqueue the work request to run as a unique work with the uniqueWorkName, so that
+ // only one instance of OfflineJobWorker is running at any time
+ // if any new work request with the uniqueWorkName is enqueued, it replaces any existing
+ // ones that are active
+ workManager.enqueueUniqueWork(uniqueWorkName, ExistingWorkPolicy.REPLACE, workRequest)
+ }
+
+ /**
+ * Starts observing any running or completed OfflineJobWorker work requests by capturing the
+ * LiveData as a flow. The flow starts receiving updates when the activity is in started
+ * or resumed state. This allows the application to capture immediate progress when
+ * in foreground and latest progress when the app resumes or restarts.
+ */
+ private fun observeWorkStatus() {
+ // get the livedata observer of the unique work as a flow
+ val liveDataFlow = workManager.getWorkInfosForUniqueWorkLiveData(uniqueWorkName).asFlow()
+
+ lifecycleScope.launch {
+ // collect the live data flow to get the latest work info list
+ liveDataFlow.collect { workInfoList ->
+ if (workInfoList.isNotEmpty()) {
+ // fetch the first work info as we only ever run one work request at any time
+ val workInfo = workInfoList[0]
+ // check the current state of the work request
+ when (workInfo.state) {
+ // if work completed successfully
+ WorkInfo.State.SUCCEEDED -> {
+ // load and display the offline map
+ displayOfflineMap()
+ // dismiss the progress dialog
+ if (progressDialog.isShowing) {
+ progressDialog.dismiss()
+ }
+ }
+ // if the work failed or was cancelled
+ WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> {
+ // show an error message based on if it was cancelled or failed
+ if (workInfo.state == WorkInfo.State.FAILED) {
+ showMessage("Error generating offline map")
+ } else {
+ showMessage("Cancelled offline map generation")
+ }
+ // dismiss the progress dialog
+ if (progressDialog.isShowing) {
+ progressDialog.dismiss()
+ }
+ // enable the takeMapOfflineButton
+ takeMapOfflineButton.isEnabled = true
+ // this removes the completed WorkInfo from the WorkManager's database
+ // otherwise, the observer will emit the WorkInfo on every launch
+ // until WorkManager auto-prunes
+ workManager.pruneWork()
+ }
+ // if the work is currently in progress
+ WorkInfo.State.RUNNING -> {
+ // get the current progress value
+ val value = workInfo.progress.getInt("Progress", 0)
+ // update the progress bar and progress text
+ progressLayout.progressBar.progress = value
+ progressLayout.progressTextView.text = "$value%"
+ // shows the progress dialog if the app is relaunched and the
+ // dialog is not visible
+ if (!progressDialog.isShowing) {
+ progressDialog.show()
+ }
+ }
+ else -> { /* don't have to handle other states */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads the offline map package into the mapView
+ */
+ private fun displayOfflineMap() {
+ lifecycleScope.launch {
+ // check if the offline map package file exists
+ if (File(offlineMapPath).exists()) {
+ // load it as a MobileMapPackage
+ val mapPackage = MobileMapPackage(offlineMapPath)
+ mapPackage.load().onFailure {
+ // if the load fails, show an error and return
+ showMessage("Error loading map package: ${it.message}")
+ return@launch
+ }
+ // add the map from the mobile map package to the MapView
+ mapView.map = mapPackage.maps.first()
+ // clear all the drawn graphics
+ graphicsOverlay.graphics.clear()
+ // disable the button to take the map offline once the offline map is showing
+ takeMapOfflineButton.isEnabled = false
+ resetMapButton.isEnabled = true
+ // this removes the completed WorkInfo from the WorkManager's database
+ // otherwise, the observer will emit the WorkInfo on every launch
+ // until WorkManager auto-prunes
+ workManager.pruneWork()
+ // display the offline map loaded message
+ showMessage("Loaded offline map. Map saved at: $offlineMapPath")
+ } else {
+ showMessage("Offline map does not exists at path: $offlineMapPath")
+ }
+ }
+ }
+
+ /**
+ * Creates a progress dialog to show the OfflineMapJob worker progress. It cancels all the
+ * running workers when the dialog is cancelled
+ */
+ private fun createProgressDialog(): MaterialAlertDialogBuilder {
+ // build and return a new alert dialog
+ return MaterialAlertDialogBuilder(this).apply {
+ // set it title
+ setTitle(getString(R.string.dialog_title))
+ // allow it to be cancellable
+ setCancelable(false)
+ // set negative button configuration
+ setNegativeButton("Cancel") { _, _ ->
+ // cancel all the running work
+ workManager.cancelAllWork()
+ }
+ // removes parent of the progressDialog layout, if previously assigned
+ progressLayout.root.parent?.let { parent ->
+ (parent as ViewGroup).removeAllViews()
+ }
+ // set the progressDialog Layout to this alert dialog
+ setView(progressLayout.root)
+ }
+ }
+
+ /**
+ * Request Post Notifications permission for API level 33+
+ * https://developer.android.com/develop/ui/views/notifications/notification-permission
+ */
+ private fun requestNotificationPermission() {
+ // request notification permission only for android versions >= 33
+ if (Build.VERSION.SDK_INT >= 33) {
+ // check if push notifications permission is granted
+ val permissionCheckPostNotifications =
+ ContextCompat.checkSelfPermission(this@MainActivity, POST_NOTIFICATIONS) ==
+ PackageManager.PERMISSION_GRANTED
+
+ // if permission is not already granted, request permission from the user
+ if (!permissionCheckPostNotifications) {
+ ActivityCompat.requestPermissions(
+ this@MainActivity,
+ arrayOf(POST_NOTIFICATIONS),
+ 2
+ )
+ }
+ }
+ }
+
+ /**
+ * Handle the permissions request response.
+ */
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) {
+ Snackbar.make(
+ mapView,
+ "Notification permissions required to show progress!",
+ Snackbar.LENGTH_LONG
+ ).show()
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ // dismiss the dialog when the activity is destroyed
+ progressDialog.dismiss()
+ }
+
+ private fun showMessage(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/NotificationActionReceiver.kt b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/NotificationActionReceiver.kt
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/NotificationActionReceiver.kt
rename to samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/NotificationActionReceiver.kt
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/OfflineJobWorker.kt b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/OfflineJobWorker.kt
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/OfflineJobWorker.kt
rename to samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/OfflineJobWorker.kt
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt
similarity index 98%
rename from generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt
rename to samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt
index 3ce98be28..d327c8499 100644
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/java/com/esri/arcgismaps/sample/generateofflinemapusingandroidjetpackworkmanager/WorkerNotification.kt
@@ -151,7 +151,7 @@ class WorkerNotification(
.setContentTitle(applicationContext.getString(R.string.notification_title))
// sets the content that is displayed on expanding the notification
.setContentText(contentText)
- .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setSmallIcon(com.esri.arcgismaps.sample.sampleslib.R.mipmap.arcgis_sdk_round)
// sets it to only show the notification alert once, in case of progress
.setOnlyAlertOnce(true)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/activity_main.xml b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/generate_offline_map_using_android_jetpack_workmanager_activity_main.xml
similarity index 100%
rename from generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/activity_main.xml
rename to samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/generate_offline_map_using_android_jetpack_workmanager_activity_main.xml
diff --git a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml
similarity index 97%
rename from generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml
rename to samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml
index 8b048c613..da5db77ac 100644
--- a/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/layout/offline_job_progress_dialog_layout.xml
@@ -16,7 +16,6 @@
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:indeterminate="false"
- android:theme="@style/AppTheme"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/progressTextView"
diff --git a/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml
new file mode 100644
index 000000000..71b739e3e
--- /dev/null
+++ b/samples/generate-offline-map-using-android-jetpack-workmanager/src/main/res/values/strings.xml
@@ -0,0 +1,14 @@
+
+ Generate offline map using android jetpack workmanager
+ https://www.arcgis.com
+ acc027394bc84c2fb04d1ed317aac674
+ Generating offline map..
+ /offlineMap
+ /offlineJobJson
+ Offline Map Job Notifications
+ Shows notifications for offline map job progress
+ ArcGIS Maps Sample: Offline Map Download
+ NotificationAction
+ Take Map Offline
+ Reset Map
+
diff --git a/generate-offline-map/README.md b/samples/generate-offline-map/README.md
similarity index 100%
rename from generate-offline-map/README.md
rename to samples/generate-offline-map/README.md
diff --git a/generate-offline-map/README.metadata.json b/samples/generate-offline-map/README.metadata.json
similarity index 100%
rename from generate-offline-map/README.metadata.json
rename to samples/generate-offline-map/README.metadata.json
diff --git a/samples/generate-offline-map/build.gradle.kts b/samples/generate-offline-map/build.gradle.kts
new file mode 100644
index 000000000..a90743342
--- /dev/null
+++ b/samples/generate-offline-map/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.generateofflinemap"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/generate-offline-map/generate-offline-map.png b/samples/generate-offline-map/generate-offline-map.png
similarity index 100%
rename from generate-offline-map/generate-offline-map.png
rename to samples/generate-offline-map/generate-offline-map.png
diff --git a/samples/generate-offline-map/src/main/AndroidManifest.xml b/samples/generate-offline-map/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/generate-offline-map/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt b/samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt
new file mode 100644
index 000000000..bcd429733
--- /dev/null
+++ b/samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.generateofflinemap
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.generateofflinemap.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.generate_offline_map_app_name)
+ )
+ }
+ }
+}
diff --git a/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/components/MapViewModel.kt b/samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/components/MapViewModel.kt
similarity index 100%
rename from generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/components/MapViewModel.kt
rename to samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/components/MapViewModel.kt
diff --git a/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/screens/MainScreen.kt b/samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/screens/MainScreen.kt
similarity index 100%
rename from generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/screens/MainScreen.kt
rename to samples/generate-offline-map/src/main/java/com/esri/arcgismaps/sample/generateofflinemap/screens/MainScreen.kt
diff --git a/samples/generate-offline-map/src/main/res/layout/generate_offline_map_activity_main.xml b/samples/generate-offline-map/src/main/res/layout/generate_offline_map_activity_main.xml
new file mode 100644
index 000000000..bba38b4c5
--- /dev/null
+++ b/samples/generate-offline-map/src/main/res/layout/generate_offline_map_activity_main.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/generate-offline-map/src/main/res/layout/generate_offline_map_dialog_layout.xml b/samples/generate-offline-map/src/main/res/layout/generate_offline_map_dialog_layout.xml
new file mode 100644
index 000000000..da5db77ac
--- /dev/null
+++ b/samples/generate-offline-map/src/main/res/layout/generate_offline_map_dialog_layout.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/generate-offline-map/src/main/res/values/strings.xml b/samples/generate-offline-map/src/main/res/values/strings.xml
new file mode 100644
index 000000000..997987823
--- /dev/null
+++ b/samples/generate-offline-map/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Generate offline map
+ https://www.arcgis.com
+ acc027394bc84c2fb04d1ed317aac674
+ Take Map Offline
+ Reset Map
+
diff --git a/geocode-offline/README.md b/samples/geocode-offline/README.md
similarity index 100%
rename from geocode-offline/README.md
rename to samples/geocode-offline/README.md
diff --git a/geocode-offline/README.metadata.json b/samples/geocode-offline/README.metadata.json
similarity index 100%
rename from geocode-offline/README.metadata.json
rename to samples/geocode-offline/README.metadata.json
diff --git a/samples/geocode-offline/build.gradle.kts b/samples/geocode-offline/build.gradle.kts
new file mode 100644
index 000000000..6eba8a097
--- /dev/null
+++ b/samples/geocode-offline/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.geocodeoffline"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/geocode-offline/geocode-offline.png b/samples/geocode-offline/geocode-offline.png
similarity index 100%
rename from geocode-offline/geocode-offline.png
rename to samples/geocode-offline/geocode-offline.png
diff --git a/samples/geocode-offline/src/main/AndroidManifest.xml b/samples/geocode-offline/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d22ce1f1d
--- /dev/null
+++ b/samples/geocode-offline/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt b/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt
new file mode 100644
index 000000000..b1bb1314b
--- /dev/null
+++ b/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/DownloadActivity.kt
@@ -0,0 +1,22 @@
+package com.esri.arcgismaps.sample.geocodeoffline
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.geocode_offline_app_name),
+ listOf(
+ // A .tpkx Tile Package file covering the San Diego, CA, USA area
+ "https://www.arcgis.com/home/item.html?id=22c3083d4fa74e3e9b25adfc9f8c0496",
+ // San Diego Locator Offline Dataset
+ "https://www.arcgis.com/home/item.html?id=3424d442ebe54f3cbf34462382d3aebe"
+ )
+ )
+ }
+}
diff --git a/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt b/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt
new file mode 100644
index 000000000..93335bb8e
--- /dev/null
+++ b/samples/geocode-offline/src/main/java/com/esri/arcgismaps/sample/geocodeoffline/MainActivity.kt
@@ -0,0 +1,269 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.geocodeoffline
+
+import android.database.MatrixCursor
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.provider.BaseColumns
+import android.util.Log
+import android.view.Menu
+import android.widget.AutoCompleteTextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.SearchView
+import androidx.core.content.ContextCompat
+import androidx.cursoradapter.widget.SimpleCursorAdapter
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.Basemap
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.ArcGISTiledLayer
+import com.arcgismaps.mapping.layers.TileCache
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.tasks.geocode.GeocodeParameters
+import com.arcgismaps.tasks.geocode.GeocodeResult
+import com.arcgismaps.tasks.geocode.LocatorTask
+import com.esri.arcgismaps.sample.geocodeoffline.databinding.GeocodeOfflineActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.io.File
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: GeocodeOfflineActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.geocode_offline_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // display the metro area of the tapped location
+ private val descriptionTV by lazy {
+ activityMainBinding.descriptionTV
+ }
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.geocode_offline_app_name)
+ }
+
+ // create a picture marker symbol
+ private val pinSymbol: PictureMarkerSymbol by lazy {
+ createPinSymbol()
+ }
+
+ // geocode parameters used to perform a search
+ private val geocodeParameters: GeocodeParameters by lazy {
+ GeocodeParameters().apply {
+ // get all attributes
+ resultAttributeNames.add("*")
+ // get only the closest result
+ maxResults = 1
+ }
+ }
+
+ // locator task to provide geocoding services
+ private val locatorTask: LocatorTask by lazy {
+ LocatorTask(File(provisionPath, getString(R.string.san_diego_loc)).path)
+ }
+
+ // create a graphics overlay
+ private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // load the tile cache from local storage
+ val tileCache = TileCache("$provisionPath/streetmap_SD.tpkx")
+ // create a tiled layer and add it to as the base map
+ val tiledLayer = ArcGISTiledLayer(tileCache)
+ mapView.apply {
+ map = ArcGISMap(Basemap(tiledLayer))
+ // set map initial viewpoint
+ map?.initialViewpoint = Viewpoint(32.72, -117.155, 120000.0)
+ // add a graphics overlay to the map view
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // load geocode locator task
+ lifecycleScope.launch {
+ locatorTask.load().onSuccess {
+ mapView.onSingleTapConfirmed.collect { event ->
+ // find address with reverse geocode using the tapped location
+ event.mapPoint?.let { mapPoint -> findAddressReverseGeocode(mapPoint) }
+ }
+ }.onFailure {
+ showError(it.message.toString())
+ }
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu, menu)
+ val search = menu.findItem(R.id.appSearchBar)
+ // set up address search view and listeners
+ setupAddressSearchView(search.actionView as SearchView)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ /**
+ * Sets up the address SearchView and uses MatrixCursor to
+ * show suggestions to the user as text is entered.
+ */
+ private fun setupAddressSearchView(addressSearchView: SearchView) {
+ // disable threshold to show results from single character
+ val autoCompleteTextViewID = resources.getIdentifier("search_src_text", "id", packageName)
+ addressSearchView.findViewById(autoCompleteTextViewID).threshold = 0
+
+ // get the list of pre-made suggestions
+ val suggestions = resources.getStringArray(R.array.suggestion_items)
+ // set up parameters for searching with MatrixCursor
+ val columnNames = arrayOf(BaseColumns._ID, "address")
+ val suggestionsCursor = MatrixCursor(columnNames)
+ // add each address suggestion to a new row
+ suggestions.forEachIndexed { i, s -> suggestionsCursor.addRow(arrayOf(i, s)) }
+
+ // column names for the adapter to look at when mapping data
+ val cols = arrayOf("address")
+ // ids that show where data should be assigned in the layout
+ val to = intArrayOf(R.id.suggestionAddress)
+ // define SimpleCursorAdapter
+ val suggestionsAdapter = SimpleCursorAdapter(
+ this@MainActivity,
+ R.layout.suggestion_address, suggestionsCursor, cols, to, 0
+ )
+
+ addressSearchView.suggestionsAdapter = suggestionsAdapter
+ // handle an address suggestion being chosen
+ addressSearchView.setOnSuggestionListener(object : SearchView.OnSuggestionListener {
+ override fun onSuggestionSelect(position: Int): Boolean {
+ return false
+ }
+
+ override fun onSuggestionClick(position: Int): Boolean {
+ // geocode the typed address
+ addressSearchView.setQuery(suggestions[position], true)
+ return true
+ }
+ })
+
+ // geocode the searched address on submit
+ addressSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(address: String): Boolean {
+ geocodeAddress(address)
+ addressSearchView.clearFocus()
+ return true
+ }
+
+ override fun onQueryTextChange(newText: String?) = true
+ })
+ }
+
+ /**
+ * Use the locator task to geocode the given address.
+ *
+ * @param address as a string to geocode
+ */
+ private fun geocodeAddress(address: String) = lifecycleScope.launch {
+ // clear graphics on map before displaying search results
+ graphicsOverlay.graphics.clear()
+ // load the locator task
+ locatorTask.load().getOrThrow()
+ // run the locatorTask geocode task, passing in the address
+ val geocodeResults = locatorTask.geocode(address, geocodeParameters).getOrThrow()
+ geocodeResults.ifEmpty {
+ // no address found in geocode so return
+ showError("No address found for $address")
+ return@launch
+ }
+ // display address found in geocode
+ displaySearchResultOnMap(geocodeResults)
+ }
+
+ /**
+ * Get the reverse geocode result from the [mapPoint]
+ */
+ private suspend fun findAddressReverseGeocode(mapPoint: Point) {
+ // normalize the geometry - needed if the user crosses the international date line.
+ val normalizedPoint = GeometryEngine.normalizeCentralMeridian(mapPoint) as Point
+ locatorTask.reverseGeocode(normalizedPoint).onSuccess { geocodeResults ->
+ // no address found in geocode so return
+ if (geocodeResults.isEmpty()) {
+ showError("Could not find address at tapped point")
+ return@onSuccess
+ }
+ displaySearchResultOnMap(geocodeResults)
+ }.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ /**
+ * Turn the first address from [geocodeResultList] into a point marker and adds it to the graphic overlay of the map.
+ */
+ private fun displaySearchResultOnMap(geocodeResultList: List) {
+ // clear graphics overlay of existing graphics
+ graphicsOverlay.graphics.clear()
+
+ // create graphic object
+ val resultLocationGraphic = Graphic(
+ geocodeResultList[0].displayLocation,
+ geocodeResultList[0].attributes, pinSymbol
+ )
+ graphicsOverlay.graphics.add(resultLocationGraphic)
+ descriptionTV.text = geocodeResultList[0].label
+
+ // get the envelop to set the viewpoint
+ val envelope = graphicsOverlay.extent ?: return showError("Geocode result extent is null")
+ // animate viewpoint to geocode result's extent
+ lifecycleScope.launch {
+ mapView.setViewpointGeometry(envelope, 25.0)
+ }
+ }
+
+ /**
+ * Creates a picture marker symbol from the pin icon.
+ */
+ private fun createPinSymbol(): PictureMarkerSymbol {
+ val pinDrawable = ContextCompat.getDrawable(this, R.drawable.pin) as BitmapDrawable
+ val pinSymbol = PictureMarkerSymbol.createWithImage(pinDrawable)
+ pinSymbol.apply {
+ // resize the dimensions of the symbol
+ width = 18f
+ height = 65f
+ }
+ return pinSymbol
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/geocode-offline/src/main/res/drawable/ic_baseline_search_24.xml b/samples/geocode-offline/src/main/res/drawable/ic_baseline_search_24.xml
similarity index 100%
rename from geocode-offline/src/main/res/drawable/ic_baseline_search_24.xml
rename to samples/geocode-offline/src/main/res/drawable/ic_baseline_search_24.xml
diff --git a/geocode-offline/src/main/res/drawable/pin.png b/samples/geocode-offline/src/main/res/drawable/pin.png
similarity index 100%
rename from geocode-offline/src/main/res/drawable/pin.png
rename to samples/geocode-offline/src/main/res/drawable/pin.png
diff --git a/geocode-offline/src/main/res/layout/activity_main.xml b/samples/geocode-offline/src/main/res/layout/geocode_offline_activity_main.xml
similarity index 100%
rename from geocode-offline/src/main/res/layout/activity_main.xml
rename to samples/geocode-offline/src/main/res/layout/geocode_offline_activity_main.xml
diff --git a/geocode-offline/src/main/res/layout/suggestion_address.xml b/samples/geocode-offline/src/main/res/layout/suggestion_address.xml
similarity index 100%
rename from geocode-offline/src/main/res/layout/suggestion_address.xml
rename to samples/geocode-offline/src/main/res/layout/suggestion_address.xml
diff --git a/samples/geocode-offline/src/main/res/menu/menu.xml b/samples/geocode-offline/src/main/res/menu/menu.xml
new file mode 100644
index 000000000..cfc8dcab0
--- /dev/null
+++ b/samples/geocode-offline/src/main/res/menu/menu.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/samples/geocode-offline/src/main/res/values/strings.xml b/samples/geocode-offline/src/main/res/values/strings.xml
new file mode 100644
index 000000000..eea49c094
--- /dev/null
+++ b/samples/geocode-offline/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+ Geocode offline
+ Search for an address or tap on the map
+ /SanDiego_StreetAddress.loc
+ Enter address
+
+ - 910 N Harbor Dr, San Diego, CA 92101
+ - 2920 Zoo Dr, San Diego, CA 92101
+ - 111 W Harbor Dr, San Diego, CA 92101
+ - 868 4th Ave, San Diego, CA 92101
+ - 750 A St, San Diego, CA 92101
+
+
diff --git a/identify-layer-features/README.md b/samples/identify-layer-features/README.md
similarity index 100%
rename from identify-layer-features/README.md
rename to samples/identify-layer-features/README.md
diff --git a/identify-layer-features/README.metadata.json b/samples/identify-layer-features/README.metadata.json
similarity index 100%
rename from identify-layer-features/README.metadata.json
rename to samples/identify-layer-features/README.metadata.json
diff --git a/samples/identify-layer-features/build.gradle.kts b/samples/identify-layer-features/build.gradle.kts
new file mode 100644
index 000000000..bfec8fe0b
--- /dev/null
+++ b/samples/identify-layer-features/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.identifylayerfeatures"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/identify-layer-features/identify-layer-features.png b/samples/identify-layer-features/identify-layer-features.png
similarity index 100%
rename from identify-layer-features/identify-layer-features.png
rename to samples/identify-layer-features/identify-layer-features.png
diff --git a/samples/identify-layer-features/src/main/AndroidManifest.xml b/samples/identify-layer-features/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/identify-layer-features/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt b/samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt
new file mode 100644
index 000000000..de49cc454
--- /dev/null
+++ b/samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.identifylayerfeatures
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.identifylayerfeatures.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ IdentifyLayerFeaturesApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun IdentifyLayerFeaturesApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.identify_layer_features_app_name)
+ )
+ }
+ }
+}
diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt b/samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt
similarity index 100%
rename from identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt
rename to samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt
diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt b/samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt
similarity index 100%
rename from identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt
rename to samples/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt
diff --git a/samples/identify-layer-features/src/main/res/values/strings.xml b/samples/identify-layer-features/src/main/res/values/strings.xml
new file mode 100644
index 000000000..4d515866c
--- /dev/null
+++ b/samples/identify-layer-features/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Identify layer features
+
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/FeatureServer/0
+
+
+ https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer
+
+
diff --git a/manage-operational-layers/README.md b/samples/manage-operational-layers/README.md
similarity index 100%
rename from manage-operational-layers/README.md
rename to samples/manage-operational-layers/README.md
diff --git a/manage-operational-layers/README.metadata.json b/samples/manage-operational-layers/README.metadata.json
similarity index 100%
rename from manage-operational-layers/README.metadata.json
rename to samples/manage-operational-layers/README.metadata.json
diff --git a/samples/manage-operational-layers/build.gradle.kts b/samples/manage-operational-layers/build.gradle.kts
new file mode 100644
index 000000000..f24c20a5b
--- /dev/null
+++ b/samples/manage-operational-layers/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.manageoperationallayers"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/manage-operational-layers/manage-operational-layers.png b/samples/manage-operational-layers/manage-operational-layers.png
similarity index 100%
rename from manage-operational-layers/manage-operational-layers.png
rename to samples/manage-operational-layers/manage-operational-layers.png
diff --git a/samples/manage-operational-layers/src/main/AndroidManifest.xml b/samples/manage-operational-layers/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/manage-operational-layers/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt
new file mode 100644
index 000000000..e6b6b20a5
--- /dev/null
+++ b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.manageoperationallayers
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.manageoperationallayers.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ ManageOperationalLayersApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ManageOperationalLayersApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.manage_operational_layers_app_name)
+ )
+ }
+ }
+}
diff --git a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/components/MapViewModel.kt b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/components/MapViewModel.kt
similarity index 100%
rename from manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/components/MapViewModel.kt
rename to samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/components/MapViewModel.kt
diff --git a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt
similarity index 97%
rename from manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt
rename to samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt
index 60554fbef..71296ab26 100644
--- a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt
+++ b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/LayersList.kt
@@ -17,7 +17,6 @@
package com.esri.arcgismaps.sample.manageoperationallayers.screens
import android.content.res.Configuration
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -42,7 +41,6 @@ import com.esri.arcgismaps.sample.manageoperationallayers.R
/**
* Layout to display a list of operational layers on the map using [activateLayerNames].
*/
-@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LayersList(
activateLayerNames: List,
@@ -60,7 +58,7 @@ fun LayersList(
LazyColumn(modifier = Modifier.padding(12.dp)) {
items(activateLayerNames.size, key = { activateLayerNames[it] }) { index ->
LayerRow(
- modifier = Modifier.fillMaxWidth().animateItemPlacement(),
+ modifier = Modifier.fillMaxWidth().animateItem(),
layerName = activateLayerNames[index],
onMoveLayerUp = onMoveLayerUp,
onMoveLayerDown = onMoveLayerDown,
@@ -75,7 +73,7 @@ fun LayersList(
LazyColumn(modifier = Modifier.padding(12.dp)) {
items(inactiveLayers.size, key = { inactiveLayers[it].name }) { index ->
InactiveLayerRow(
- modifier = Modifier.fillMaxWidth().animateItemPlacement(),
+ modifier = Modifier.fillMaxWidth().animateItem(),
layerName = inactiveLayers[index].name,
onAddLayer = onAddLayer
)
diff --git a/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/MainScreen.kt b/samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/MainScreen.kt
similarity index 100%
rename from manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/MainScreen.kt
rename to samples/manage-operational-layers/src/main/java/com/esri/arcgismaps/sample/manageoperationallayers/screens/MainScreen.kt
diff --git a/manage-operational-layers/src/main/res/drawable-anydpi/hide.xml b/samples/manage-operational-layers/src/main/res/drawable-anydpi/hide.xml
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-anydpi/hide.xml
rename to samples/manage-operational-layers/src/main/res/drawable-anydpi/hide.xml
diff --git a/manage-operational-layers/src/main/res/drawable-anydpi/ic_show.xml b/samples/manage-operational-layers/src/main/res/drawable-anydpi/ic_show.xml
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-anydpi/ic_show.xml
rename to samples/manage-operational-layers/src/main/res/drawable-anydpi/ic_show.xml
diff --git a/manage-operational-layers/src/main/res/drawable-hdpi/hide.png b/samples/manage-operational-layers/src/main/res/drawable-hdpi/hide.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-hdpi/hide.png
rename to samples/manage-operational-layers/src/main/res/drawable-hdpi/hide.png
diff --git a/manage-operational-layers/src/main/res/drawable-hdpi/ic_show.png b/samples/manage-operational-layers/src/main/res/drawable-hdpi/ic_show.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-hdpi/ic_show.png
rename to samples/manage-operational-layers/src/main/res/drawable-hdpi/ic_show.png
diff --git a/manage-operational-layers/src/main/res/drawable-mdpi/hide.png b/samples/manage-operational-layers/src/main/res/drawable-mdpi/hide.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-mdpi/hide.png
rename to samples/manage-operational-layers/src/main/res/drawable-mdpi/hide.png
diff --git a/manage-operational-layers/src/main/res/drawable-mdpi/ic_show.png b/samples/manage-operational-layers/src/main/res/drawable-mdpi/ic_show.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-mdpi/ic_show.png
rename to samples/manage-operational-layers/src/main/res/drawable-mdpi/ic_show.png
diff --git a/manage-operational-layers/src/main/res/drawable-xhdpi/hide.png b/samples/manage-operational-layers/src/main/res/drawable-xhdpi/hide.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-xhdpi/hide.png
rename to samples/manage-operational-layers/src/main/res/drawable-xhdpi/hide.png
diff --git a/manage-operational-layers/src/main/res/drawable-xhdpi/ic_show.png b/samples/manage-operational-layers/src/main/res/drawable-xhdpi/ic_show.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-xhdpi/ic_show.png
rename to samples/manage-operational-layers/src/main/res/drawable-xhdpi/ic_show.png
diff --git a/manage-operational-layers/src/main/res/drawable-xxhdpi/hide.png b/samples/manage-operational-layers/src/main/res/drawable-xxhdpi/hide.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-xxhdpi/hide.png
rename to samples/manage-operational-layers/src/main/res/drawable-xxhdpi/hide.png
diff --git a/manage-operational-layers/src/main/res/drawable-xxhdpi/ic_show.png b/samples/manage-operational-layers/src/main/res/drawable-xxhdpi/ic_show.png
similarity index 100%
rename from manage-operational-layers/src/main/res/drawable-xxhdpi/ic_show.png
rename to samples/manage-operational-layers/src/main/res/drawable-xxhdpi/ic_show.png
diff --git a/samples/manage-operational-layers/src/main/res/values/strings.xml b/samples/manage-operational-layers/src/main/res/values/strings.xml
new file mode 100644
index 000000000..1bb23ec40
--- /dev/null
+++ b/samples/manage-operational-layers/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Manage operational layers
+ https://sampleserver5.arcgisonline.com/arcgis/rest/services/Elevation/WorldElevations/MapServer
+ https://sampleserver5.arcgisonline.com/arcgis/rest/services/Census/MapServer
+ https://sampleserver5.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer
+
diff --git a/navigate-route/README.md b/samples/navigate-route/README.md
similarity index 100%
rename from navigate-route/README.md
rename to samples/navigate-route/README.md
diff --git a/navigate-route/README.metadata.json b/samples/navigate-route/README.metadata.json
similarity index 100%
rename from navigate-route/README.metadata.json
rename to samples/navigate-route/README.metadata.json
diff --git a/samples/navigate-route/build.gradle.kts b/samples/navigate-route/build.gradle.kts
new file mode 100644
index 000000000..d06a0addf
--- /dev/null
+++ b/samples/navigate-route/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.navigateroute"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/navigate-route/navigate-route.png b/samples/navigate-route/navigate-route.png
similarity index 100%
rename from navigate-route/navigate-route.png
rename to samples/navigate-route/navigate-route.png
diff --git a/samples/navigate-route/src/main/AndroidManifest.xml b/samples/navigate-route/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..96965ab82
--- /dev/null
+++ b/samples/navigate-route/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt b/samples/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt
new file mode 100644
index 000000000..258559af9
--- /dev/null
+++ b/samples/navigate-route/src/main/java/com/esri/arcgismaps/sample/navigateroute/MainActivity.kt
@@ -0,0 +1,401 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.navigateroute
+
+import android.os.Bundle
+import android.speech.tts.TextToSpeech
+import android.text.format.DateUtils
+import android.util.Log
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.location.RouteTrackerLocationDataSource
+import com.arcgismaps.location.SimulatedLocationDataSource
+import com.arcgismaps.location.SimulationParameters
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.navigation.DestinationStatus
+import com.arcgismaps.navigation.RouteTracker
+import com.arcgismaps.navigation.TrackingStatus
+import com.arcgismaps.tasks.networkanalysis.RouteResult
+import com.arcgismaps.tasks.networkanalysis.RouteTask
+import com.arcgismaps.tasks.networkanalysis.Stop
+import com.esri.arcgismaps.sample.navigateroute.databinding.NavigateRouteActivityMainBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+import java.time.Instant
+import java.util.concurrent.atomic.AtomicBoolean
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: NavigateRouteActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.navigate_route_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val resetNavigationButton: MaterialButton by lazy {
+ activityMainBinding.resetNavigationButton
+ }
+
+ private val recenterButton: MaterialButton by lazy {
+ activityMainBinding.recenterButton
+ }
+
+ private val distanceRemainingTextView: TextView by lazy {
+ activityMainBinding.distanceRemainingTextView
+ }
+
+ private val timeRemainingTextView: TextView by lazy {
+ activityMainBinding.timeRemainingTextView
+ }
+
+ private val nextDirectionTextView: TextView by lazy {
+ activityMainBinding.nextDirectionTextView
+ }
+
+ private val nextStopTextView: TextView by lazy {
+ activityMainBinding.nextStopTextView
+ }
+
+ /**
+ * Destination list of stops for the RouteParameters
+ */
+ private val routeStops by lazy {
+ listOf(
+ // San Diego Convention Center
+ Stop(Point(-117.160386, 32.706608, SpatialReference.wgs84())),
+ // USS San Diego Memorial
+ Stop(Point(-117.173034, 32.712327, SpatialReference.wgs84())),
+ // RH Fleet Aerospace Museum
+ Stop(Point(-117.147230, 32.730467, SpatialReference.wgs84()))
+ )
+ }
+
+ // instance of the route ahead polyline
+ private var routeAheadGraphic: Graphic = Graphic()
+
+ // instance of the route traveled polyline
+ private var routeTraveledGraphic: Graphic = Graphic()
+
+ // instance of the MapView's graphic overlay
+ private val graphicsOverlay = GraphicsOverlay()
+
+ // boolean to check if Android text-speech is initialized
+ private var isTextToSpeechInitialized = AtomicBoolean(false)
+
+ // instance of Android text-speech
+ private var textToSpeech: TextToSpeech? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // some parts of the API require an Android Context to properly interact with Android system
+ // features, such as LocationProvider and application resources
+ ArcGISEnvironment.applicationContext = applicationContext
+
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a streets basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISStreets)
+ mapView.map = map
+
+ // create a graphics overlay to hold our route graphics
+ mapView.graphicsOverlays.add(graphicsOverlay)
+
+ // create text-to-speech to replay navigation voice guidance
+ textToSpeech = TextToSpeech(this) { status ->
+ if (status != TextToSpeech.ERROR) {
+ textToSpeech?.language = resources.configuration.locales[0]
+ isTextToSpeechInitialized.set(true)
+ }
+ }
+
+ // generate a route with directions and stops for navigation
+ val routeTask = RouteTask(getString(R.string.routing_service_url))
+
+ // create the default parameters and solve route
+ lifecycleScope.launch {
+ // load and set the route parameters
+ val routeParameters = routeTask.createDefaultParameters().getOrElse {
+ return@launch showError("Error creating default parameters:${it.message}")
+ }.apply {
+ setStops(routeStops)
+ returnDirections = true
+ returnStops = true
+ returnRoutes = true
+ }
+
+ // get the solved route result
+ val routeResult = routeTask.solveRoute(routeParameters).getOrElse {
+ return@launch showError("Error solving route:${it.message}")
+ }
+
+ // get the route geometry from the route result
+ val routeGeometry = routeResult.routes[0].routeGeometry
+
+ // set the map view view point to show the whole route
+ if (routeGeometry?.extent != null) {
+ mapView.setViewpoint(Viewpoint(routeGeometry.extent))
+ } else {
+ return@launch showError("Route geometry extent is null.")
+ }
+
+ // start navigating on app launch
+ startNavigation(routeResult)
+ }
+
+ // wire up recenter button
+ recenterButton.setOnClickListener {
+ mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
+ recenterButton.isEnabled = false
+ }
+ }
+
+ /**
+ * Start the navigation along the provided route using the [routeResult] and
+ * collects updates in the location using the MapView's location display.
+ * */
+ private fun startNavigation(routeResult: RouteResult) {
+ // get the route's geometry from the route result
+ val routeGeometry: Polyline = routeResult.routes[0].routeGeometry
+ ?: return showError("Route is missing geometry")
+
+ // create the graphics using the route's geometry
+ createRouteGraphics(routeGeometry)
+
+ // set up a simulated location data source which simulates movement along the route
+ val simulationParameters = SimulationParameters(
+ Instant.now(),
+ velocity = 35.0,
+ horizontalAccuracy = 5.0,
+ verticalAccuracy = 5.0
+ )
+
+ // create the simulated data source using the geometry and parameters
+ val simulatedLocationDataSource = SimulatedLocationDataSource(
+ routeGeometry, simulationParameters
+ )
+
+ // set up a RouteTracker for navigation along the calculated route
+ val routeTracker = RouteTracker(
+ routeResult,
+ routeIndex = 0,
+ skipCoincidentStops = true
+ ).apply {
+ setSpeechEngineReadyCallback {
+ isTextToSpeechInitialized.get() && textToSpeech?.isSpeaking == false
+ }
+ }
+ // plays the direction voice guidance
+ lifecycleScope.launch {
+ updateVoiceGuidance(routeTracker)
+ }
+
+ // create a route tracker location data source to snap the location display to the route
+ val routeTrackerLocationDataSource = RouteTrackerLocationDataSource(
+ routeTracker,
+ simulatedLocationDataSource
+ )
+
+ // get the map view's location display and set it up
+ val locationDisplay = mapView.locationDisplay.also {
+ // set the simulated location data source as the location data source for this app
+ it.dataSource = routeTrackerLocationDataSource
+ it.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
+ }
+
+ // start the LocationDisplay, which starts the
+ // RouteTrackerLocationDataSource and SimulatedLocationDataSource
+ lifecycleScope.launch {
+ locationDisplay.dataSource.start().getOrElse {
+ showError("Error starting LocationDataSource: ${it.message} ")
+ }
+ // set the text for first destination
+ nextStopTextView.text = resources.getStringArray(R.array.stop_message)[0]
+ }
+
+ // listen for changes in location
+ val locationDisplayJob = lifecycleScope.launch {
+ locationDisplay.location.collect {
+ // get the route's tracking status
+ val trackingStatus = routeTracker.trackingStatus.value ?: return@collect
+
+ // displays the remaining and traversed route
+ updateRouteGraphics(trackingStatus)
+
+ // display route status and directions info
+ displayRouteInfo(routeTracker, trackingStatus)
+ }
+ }
+
+ // listen if user navigates the map view away from the
+ // location display, activate the recenter button
+ val autoPanModeJob = lifecycleScope.launch {
+ locationDisplay.autoPanMode.filter { it == LocationDisplayAutoPanMode.Off }
+ .collect { recenterButton.isEnabled = true }
+ }
+
+ // reset the navigation if button is clicked
+ resetNavigationButton.setOnClickListener {
+ lifecycleScope.launch {
+ if (locationDisplayJob.isActive) {
+ // stop location data sources
+ locationDisplay.dataSource.stop()
+ // cancel the coroutine jobs
+ locationDisplayJob.cancelAndJoin()
+ autoPanModeJob.cancelAndJoin()
+ // start navigation again
+ startNavigation(routeResult)
+ }
+ }
+ }
+ }
+
+ /**
+ * Uses Android's [textToSpeech] to speak to say the latest
+ * voice guidance from the [routeTracker] out loud.
+ */
+ private suspend fun updateVoiceGuidance(routeTracker: RouteTracker) {
+ // listen for new voice guidance events
+ routeTracker.newVoiceGuidance.collect { voiceGuidance ->
+ // use Android's text to speech to speak the voice guidance
+ textToSpeech?.speak(voiceGuidance.text, TextToSpeech.QUEUE_FLUSH, null, null)
+ // set next direction text
+ nextDirectionTextView.text = getString(R.string.next_direction, voiceGuidance.text)
+ }
+ }
+
+ /**
+ * Displays the route distance and time information using [trackingStatus], and
+ * switches destinations using [routeTracker]. When final destination is reached,
+ * the location data source is stopped.
+ */
+ private suspend fun displayRouteInfo(
+ routeTracker: RouteTracker,
+ trackingStatus: TrackingStatus
+ ) {
+ // get remaining distance information
+ val remainingDistance = trackingStatus.destinationProgress.remainingDistance
+ // convert remaining minutes to hours:minutes:seconds
+ val remainingTimeString =
+ DateUtils.formatElapsedTime((trackingStatus.destinationProgress.remainingTime * 60).toLong())
+
+ // update text views
+ distanceRemainingTextView.text = getString(
+ R.string.distance_remaining,
+ remainingDistance.displayText,
+ remainingDistance.displayTextUnits.abbreviation
+ )
+ timeRemainingTextView.text = getString(R.string.time_remaining, remainingTimeString)
+
+ // if a destination has been reached
+ if (trackingStatus.destinationStatus == DestinationStatus.Reached) {
+ // if there are more destinations to visit. Greater than 1 because the start point is considered a "stop"
+ if (trackingStatus.remainingDestinationCount > 1) {
+ // switch to the next destination
+ routeTracker.switchToNextDestination().getOrElse {
+ return showError("Error retrieving next destination: ${it.message}")
+ }
+ // set second stop message
+ nextStopTextView.text = resources.getStringArray(R.array.stop_message)[1]
+ } else {
+ // the final destination has been reached,
+ // stop the location data source
+ mapView.locationDisplay.dataSource.stop()
+ // set last stop message
+ nextStopTextView.text = resources.getStringArray(R.array.stop_message)[2]
+ }
+ }
+ }
+
+ /**
+ * Update the remaining and traveled route graphics using [trackingStatus]
+ */
+ private fun updateRouteGraphics(trackingStatus: TrackingStatus) {
+ trackingStatus.routeProgress.let {
+ // set geometries for the route ahead and the remaining route
+ routeAheadGraphic.geometry = it.remainingGeometry
+ routeTraveledGraphic.geometry = it.traversedGeometry
+ }
+ }
+
+ /**
+ * Initialize and add route travel graphics to the map using [routeGeometry]
+ */
+ private fun createRouteGraphics(routeGeometry: Polyline) {
+ // clear any graphics from the current graphics overlay
+ graphicsOverlay.graphics.clear()
+
+ // create a graphic (with a dashed line symbol) to represent the route
+ routeAheadGraphic = Graphic(
+ routeGeometry,
+ SimpleLineSymbol(
+ SimpleLineSymbolStyle.Dash,
+ Color(getColor(com.esri.arcgismaps.sample.sampleslib.R.color.colorPrimary)),
+ 5f
+ )
+ )
+
+ // create a graphic (solid) to represent the route that's been traveled (initially empty)
+ routeTraveledGraphic = Graphic(
+ routeGeometry,
+ SimpleLineSymbol(
+ SimpleLineSymbolStyle.Solid,
+ Color.red,
+ 5f
+ )
+ )
+
+ // add the graphics to the mapView's graphics overlays
+ mapView.graphicsOverlays[0].graphics.addAll(
+ listOf(
+ routeAheadGraphic,
+ routeTraveledGraphic
+ )
+ )
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
diff --git a/navigate-route/src/main/res/layout/activity_main.xml b/samples/navigate-route/src/main/res/layout/navigate_route_activity_main.xml
similarity index 100%
rename from navigate-route/src/main/res/layout/activity_main.xml
rename to samples/navigate-route/src/main/res/layout/navigate_route_activity_main.xml
diff --git a/samples/navigate-route/src/main/res/values/strings.xml b/samples/navigate-route/src/main/res/values/strings.xml
new file mode 100644
index 000000000..65d2f0335
--- /dev/null
+++ b/samples/navigate-route/src/main/res/values/strings.xml
@@ -0,0 +1,14 @@
+
+ Navigate route
+ https://sampleserver7.arcgisonline.com/server/rest/services/NetworkAnalysis/SanDiego/NAServer/Route
+ Distance remaining: %s %s
+ Time remaining: %s
+ Next direction: %s
+ Reset Navigation
+ Recenter
+
+ - Navigating to the first stop, the USS San Diego Memorial.
+ - Navigating to the second stop, the Fleet Science Center.
+ - Arrived at the final destination.
+
+
diff --git a/play-kml-tour/README.md b/samples/play-kml-tour/README.md
similarity index 100%
rename from play-kml-tour/README.md
rename to samples/play-kml-tour/README.md
diff --git a/play-kml-tour/README.metadata.json b/samples/play-kml-tour/README.metadata.json
similarity index 100%
rename from play-kml-tour/README.metadata.json
rename to samples/play-kml-tour/README.metadata.json
diff --git a/samples/play-kml-tour/build.gradle.kts b/samples/play-kml-tour/build.gradle.kts
new file mode 100644
index 000000000..3179424cd
--- /dev/null
+++ b/samples/play-kml-tour/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.playkmltour"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/play-kml-tour/play-kml-tour.png b/samples/play-kml-tour/play-kml-tour.png
similarity index 100%
rename from play-kml-tour/play-kml-tour.png
rename to samples/play-kml-tour/play-kml-tour.png
diff --git a/samples/play-kml-tour/src/main/AndroidManifest.xml b/samples/play-kml-tour/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..38e49884c
--- /dev/null
+++ b/samples/play-kml-tour/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt b/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt
new file mode 100644
index 000000000..80b3c0501
--- /dev/null
+++ b/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/DownloadActivity.kt
@@ -0,0 +1,21 @@
+package com.esri.arcgismaps.sample.playkmltour
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.play_kml_tour_app_name),
+ listOf(
+ // ArcGIS Portal item containing the .kmz markup tour file
+ // of Esri HQ and some of the global offices
+ "https://arcgisruntime.maps.arcgis.com/home/item.html?id=f10b1d37fdd645c9bc9b189fb546307c"
+ )
+ )
+ }
+}
diff --git a/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt b/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt
new file mode 100644
index 000000000..d035b92ac
--- /dev/null
+++ b/samples/play-kml-tour/src/main/java/com/esri/arcgismaps/sample/playkmltour/MainActivity.kt
@@ -0,0 +1,249 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.playkmltour
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.ArcGISTiledElevationSource
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Surface
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.ViewpointType
+import com.arcgismaps.mapping.kml.KmlContainer
+import com.arcgismaps.mapping.kml.KmlDataset
+import com.arcgismaps.mapping.kml.KmlNode
+import com.arcgismaps.mapping.kml.KmlTour
+import com.arcgismaps.mapping.kml.KmlTourController
+import com.arcgismaps.mapping.kml.KmlTourStatus
+import com.arcgismaps.mapping.layers.KmlLayer
+import com.esri.arcgismaps.sample.playkmltour.databinding.PlayKmlTourActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.math.roundToInt
+
+
+class MainActivity : AppCompatActivity() {
+
+ private val provisionPath: String by lazy {
+ getExternalFilesDir(null)?.path.toString() + File.separator + getString(R.string.play_kml_tour_app_name)
+ }
+
+ // set up data binding for the activity
+ private val activityMainBinding: PlayKmlTourActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.play_kml_tour_activity_main)
+ }
+
+ private val sceneView by lazy {
+ activityMainBinding.sceneView
+ }
+
+ private val playPauseButton by lazy {
+ activityMainBinding.playPauseButton
+ }
+
+ private val resetTourButton by lazy {
+ activityMainBinding.resetTourButton
+ }
+
+ private val tourStatusTV by lazy {
+ activityMainBinding.tourStatusTV
+ }
+
+ private val tourProgressBar by lazy {
+ activityMainBinding.tourProgressBar
+ }
+
+ private var initialViewpoint: Viewpoint? = null
+
+ private val kmlTourController = KmlTourController()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(sceneView)
+
+
+ // add elevation data
+ val surface = Surface().apply {
+ elevationSources.add(ArcGISTiledElevationSource(getString(R.string.world_terrain_service)))
+ }
+
+ // create a scene and set the surface
+ sceneView.scene = ArcGISScene(BasemapStyle.ArcGISImagery).apply {
+ baseSurface = surface
+ }
+
+ // add a KML layer from a KML dataset with a KML tour
+ val kmlDataset = KmlDataset(provisionPath + getString(R.string.kml_tour_path))
+ val kmlLayer = KmlLayer(kmlDataset)
+
+ // add the layer to the scene view's operational layers
+ sceneView.scene?.operationalLayers?.add(kmlLayer)
+
+ // load the KML layer
+ lifecycleScope.launch {
+ kmlLayer.load().onFailure {
+ showError(it.message.toString())
+ }.onSuccess {
+ // get the first loaded KML tour
+ val kmlTour = findFirstKMLTour(kmlDataset.rootNodes)
+ if (kmlTour == null) {
+ showError("Cannot find KML tour in dataset")
+ return@onSuccess
+ }
+
+ // collect changes in KML tour status
+ collectKmlTourStatus(kmlTour)
+
+ // set the KML tour to the controller
+ kmlTourController.tour = kmlTour
+ }
+ }
+
+ resetTourButton.setOnClickListener {
+ // set tour to the initial viewpoint
+ initialViewpoint?.let { sceneView.setViewpoint(it) }
+ // reset tour controller
+ kmlTourController.reset()
+ }
+
+ playPauseButton.setOnClickListener {
+ // button was clicked when tour was playing
+ if (kmlTourController.tour?.status?.value == KmlTourStatus.Playing)
+ // pause KML tour
+ kmlTourController.pause()
+ else
+ // play KML tour
+ kmlTourController.play()
+ }
+ }
+
+ /**
+ * Recursively searches for the first KML tour in a list of [kmlNodes].
+ * Returns the first [KmlTour], or null if there are no tours.
+ */
+ private fun findFirstKMLTour(kmlNodes: List): KmlTour? {
+ kmlNodes.forEach { node ->
+ if (node is KmlTour)
+ return node
+ else if (node is KmlContainer)
+ return findFirstKMLTour(node.childNodes)
+ }
+ return null
+ }
+
+ /**
+ * Collects KmlTourStatus events from the [kmlTour] and then calls
+ * showKmlTourStatus()
+ */
+ private fun collectKmlTourStatus(kmlTour: KmlTour) = lifecycleScope.launch {
+ kmlTour.status.collect { kmlTourStatus ->
+ when (kmlTourStatus) {
+ KmlTourStatus.Completed -> {
+ showKmlTourStatus("Completed", isResetEnabled = false, isPlayingTour = false)
+ }
+ KmlTourStatus.Initialized -> {
+ showKmlTourStatus("Initialized", isResetEnabled = false, isPlayingTour = false)
+ }
+ KmlTourStatus.Paused -> {
+ showKmlTourStatus("Paused", isResetEnabled = true, isPlayingTour = false)
+ }
+ KmlTourStatus.Playing -> {
+ showKmlTourStatus("Playing", isResetEnabled = true, isPlayingTour = true)
+ // set the tour's initial viewpoint
+ if (initialViewpoint == null) {
+ initialViewpoint = sceneView.getCurrentViewpoint(
+ ViewpointType.BoundingGeometry
+ )
+ }
+ }
+ else -> {}
+ }
+ }
+ }
+
+ /**
+ * Displays the KML tour status using the [kmlTourStatus], display [resetTourButton]
+ * if [isResetEnabled] and set [playPauseButton] based on [isPlayingTour].
+ */
+ private fun showKmlTourStatus(
+ kmlTourStatus: String,
+ isResetEnabled: Boolean,
+ isPlayingTour: Boolean
+ ) {
+ // set the KML tour status
+ tourStatusTV.text = String.format("Tour status: %s", kmlTourStatus)
+
+ // enable the buttons
+ resetTourButton.isEnabled = isResetEnabled
+ playPauseButton.isEnabled = true
+
+ // show pause button if true
+ if (isPlayingTour) {
+ playPauseButton.apply {
+ // set button icon
+ icon = AppCompatResources.getDrawable(
+ this@MainActivity,
+ R.drawable.ic_round_pause_24
+ )
+ // set button text
+ text = getText(R.string.pause)
+ }
+ } else { // show play button if false
+ playPauseButton.apply {
+ // set button icon
+ icon = AppCompatResources.getDrawable(
+ this@MainActivity,
+ R.drawable.ic_round_play_arrow_24
+ )
+ // set button text
+ text = getString(R.string.play)
+ }
+ }
+
+ // get progress of tour every second
+ lifecycleScope.launch {
+ // run as long as KML tour status is "Playing"
+ while (kmlTourStatus == "Playing") {
+ // get percentage of current position over total duration
+ val tourProgressInt = ((kmlTourController.currentPosition.value * 100.0)
+ / (kmlTourController.totalDuration.value)).roundToInt()
+ tourProgressBar.progress = tourProgressInt
+ // set a second delay
+ delay(1000)
+ }
+ }
+
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(sceneView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/play-kml-tour/src/main/res/drawable/ic_baseline_reset_24.xml b/samples/play-kml-tour/src/main/res/drawable/ic_baseline_reset_24.xml
similarity index 100%
rename from play-kml-tour/src/main/res/drawable/ic_baseline_reset_24.xml
rename to samples/play-kml-tour/src/main/res/drawable/ic_baseline_reset_24.xml
diff --git a/play-kml-tour/src/main/res/drawable/ic_round_pause_24.xml b/samples/play-kml-tour/src/main/res/drawable/ic_round_pause_24.xml
similarity index 100%
rename from play-kml-tour/src/main/res/drawable/ic_round_pause_24.xml
rename to samples/play-kml-tour/src/main/res/drawable/ic_round_pause_24.xml
diff --git a/play-kml-tour/src/main/res/drawable/ic_round_play_arrow_24.xml b/samples/play-kml-tour/src/main/res/drawable/ic_round_play_arrow_24.xml
similarity index 100%
rename from play-kml-tour/src/main/res/drawable/ic_round_play_arrow_24.xml
rename to samples/play-kml-tour/src/main/res/drawable/ic_round_play_arrow_24.xml
diff --git a/play-kml-tour/src/main/res/layout/activity_main.xml b/samples/play-kml-tour/src/main/res/layout/play_kml_tour_activity_main.xml
similarity index 100%
rename from play-kml-tour/src/main/res/layout/activity_main.xml
rename to samples/play-kml-tour/src/main/res/layout/play_kml_tour_activity_main.xml
diff --git a/samples/play-kml-tour/src/main/res/values/strings.xml b/samples/play-kml-tour/src/main/res/values/strings.xml
new file mode 100644
index 000000000..acc0b82db
--- /dev/null
+++ b/samples/play-kml-tour/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Play KML tour
+ https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
+ /Esri_tour.kmz
+ Tour status:
+ Reset tour
+ Play tour
+ Pause tour
+
diff --git a/project-geometry/README.md b/samples/project-geometry/README.md
similarity index 100%
rename from project-geometry/README.md
rename to samples/project-geometry/README.md
diff --git a/project-geometry/README.metadata.json b/samples/project-geometry/README.metadata.json
similarity index 100%
rename from project-geometry/README.metadata.json
rename to samples/project-geometry/README.metadata.json
diff --git a/samples/project-geometry/build.gradle.kts b/samples/project-geometry/build.gradle.kts
new file mode 100644
index 000000000..d1d554250
--- /dev/null
+++ b/samples/project-geometry/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.projectgeometry"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/project-geometry/project-geometry.png b/samples/project-geometry/project-geometry.png
similarity index 100%
rename from project-geometry/project-geometry.png
rename to samples/project-geometry/project-geometry.png
diff --git a/samples/project-geometry/src/main/AndroidManifest.xml b/samples/project-geometry/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..42ec35d58
--- /dev/null
+++ b/samples/project-geometry/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt b/samples/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt
new file mode 100644
index 000000000..c9f8bbfb6
--- /dev/null
+++ b/samples/project-geometry/src/main/java/com/esri/arcgismaps/sample/projectgeometry/MainActivity.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.projectgeometry
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.*
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.MapView
+import com.esri.arcgismaps.sample.projectgeometry.databinding.ProjectGeometryActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.util.Locale
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ProjectGeometryActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.project_geometry_activity_main)
+ }
+
+ // setup the data binding for the MapView
+ private val mapView: MapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // shows the projection information as a TextView
+ private val infoTextView: TextView by lazy {
+ activityMainBinding.infoTextView
+ }
+
+ // setup the red pin marker image as bitmap drawable
+ private val markerDrawable: BitmapDrawable by lazy {
+ // load the bitmap from resources and create a drawable
+ val bitmap = BitmapFactory.decodeResource(resources, R.drawable.pin_symbol)
+ BitmapDrawable(resources, bitmap)
+ }
+
+ // setup the red pin marker as a Graphic
+ private val markerGraphic: Graphic by lazy {
+ // creates a symbol from the marker drawable
+ val markerSymbol = PictureMarkerSymbol.createWithImage(markerDrawable).apply {
+ // resize the symbol into a smaller size
+ width = 30f
+ height = 30f
+ // offset in +y axis so the marker spawned
+ // is right on the touch point
+ offsetY = 25f
+ }
+ // create the graphic from the symbol
+ Graphic(symbol = markerSymbol)
+ }
+
+ // creates a graphic overlay
+ private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ // configure mapView assignments
+ mapView.apply {
+ this.map = map
+ // add our marker overlay to the graphics overlay
+ graphicsOverlay.graphics.add(markerGraphic)
+ // add the graphics overlay to display marker graphics
+ graphicsOverlays.add(graphicsOverlay)
+ // set the default viewpoint to Redlands,CA
+ setViewpoint(Viewpoint(34.058, -117.195, 5e4))
+ }
+
+ lifecycleScope.launch {
+ // check if the map has loaded successfully
+ map.load().onSuccess {
+ // capture and collect when the user taps on the screen
+ mapView.onSingleTapConfirmed.collect { event ->
+ event.mapPoint?.let { point -> onGeoViewTapped(point) }
+ }
+ }.onFailure {
+ // if map load failed, show the error
+ showError("Error Loading Map", mapView)
+ }
+ }
+ }
+
+ /**
+ * Handles the SingleTapEvent by drawing a marker, re-centering the mapView to the marker
+ * and performs a Spatial reference transformation of the tapped Location
+ * using GeometryEngine and displays the result.
+ */
+ private suspend fun onGeoViewTapped(point: Point) {
+ // update the marker location to where the user tapped on the map
+ markerGraphic.geometry = point
+ // set mapview to recenter to the tapped location
+ mapView.setViewpointGeometry(point.extent)
+ // project the web mercator location into a WGS84
+ val projectedPoint = GeometryEngine.projectOrNull(point, SpatialReference.wgs84())
+ // build and display the projection result as a string
+ infoTextView.text = getString(
+ R.string.projection_info_text,
+ point.toDisplayFormat(),
+ projectedPoint?.toDisplayFormat()
+ )
+ }
+
+ /**
+ * Displays an error onscreen
+ */
+ private fun showError(message: String, view: View) {
+ Log.e(localClassName, message)
+ Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+/**
+ * Extension function for the Point type that returns
+ * a float-precision formatted string suitable for display
+ */
+private fun Point.toDisplayFormat() =
+ "${String.format(Locale.getDefault(),"%.5f", x)}, ${String.format(Locale.getDefault(),"%.5f", y)}"
diff --git a/project-geometry/src/main/res/drawable/pin_symbol.png b/samples/project-geometry/src/main/res/drawable/pin_symbol.png
similarity index 100%
rename from project-geometry/src/main/res/drawable/pin_symbol.png
rename to samples/project-geometry/src/main/res/drawable/pin_symbol.png
diff --git a/project-geometry/src/main/res/layout/activity_main.xml b/samples/project-geometry/src/main/res/layout/project_geometry_activity_main.xml
similarity index 100%
rename from project-geometry/src/main/res/layout/activity_main.xml
rename to samples/project-geometry/src/main/res/layout/project_geometry_activity_main.xml
diff --git a/samples/project-geometry/src/main/res/values/strings.xml b/samples/project-geometry/src/main/res/values/strings.xml
new file mode 100644
index 000000000..600833548
--- /dev/null
+++ b/samples/project-geometry/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Project geometry
+ Coordinates
+ Tap to begin
+ Original: %1s\nProjected: %2s
+
diff --git a/query-feature-table/README.md b/samples/query-feature-table/README.md
similarity index 100%
rename from query-feature-table/README.md
rename to samples/query-feature-table/README.md
diff --git a/query-feature-table/README.metadata.json b/samples/query-feature-table/README.metadata.json
similarity index 100%
rename from query-feature-table/README.metadata.json
rename to samples/query-feature-table/README.metadata.json
diff --git a/samples/query-feature-table/build.gradle.kts b/samples/query-feature-table/build.gradle.kts
new file mode 100644
index 000000000..2a316627d
--- /dev/null
+++ b/samples/query-feature-table/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.queryfeaturetable"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/query-feature-table/query-feature-table.png b/samples/query-feature-table/query-feature-table.png
similarity index 100%
rename from query-feature-table/query-feature-table.png
rename to samples/query-feature-table/query-feature-table.png
diff --git a/samples/query-feature-table/src/main/AndroidManifest.xml b/samples/query-feature-table/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/query-feature-table/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt b/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt
new file mode 100644
index 000000000..9c9c1140d
--- /dev/null
+++ b/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.queryfeaturetable
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.queryfeaturetable.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ QueryFeatureTableApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun QueryFeatureTableApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.query_feature_table_app_name)
+ )
+ }
+ }
+}
diff --git a/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/components/MapViewModel.kt b/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/components/MapViewModel.kt
similarity index 100%
rename from query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/components/MapViewModel.kt
rename to samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/components/MapViewModel.kt
diff --git a/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/MainScreen.kt b/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/MainScreen.kt
similarity index 100%
rename from query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/MainScreen.kt
rename to samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/MainScreen.kt
diff --git a/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/SearchBar.kt b/samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/SearchBar.kt
similarity index 100%
rename from query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/SearchBar.kt
rename to samples/query-feature-table/src/main/java/com/esri/arcgismaps/sample/queryfeaturetable/screens/SearchBar.kt
diff --git a/samples/query-feature-table/src/main/res/values/strings.xml b/samples/query-feature-table/src/main/res/values/strings.xml
new file mode 100644
index 000000000..05cc0027b
--- /dev/null
+++ b/samples/query-feature-table/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Query feature table
+ https://services.arcgis.com/jIL9msH9OI208GCb/arcgis/rest/services/USA_Daytime_Population_2016/FeatureServer/0
+
diff --git a/query-features-with-arcade-expression/README.md b/samples/query-features-with-arcade-expression/README.md
similarity index 100%
rename from query-features-with-arcade-expression/README.md
rename to samples/query-features-with-arcade-expression/README.md
diff --git a/query-features-with-arcade-expression/README.metadata.json b/samples/query-features-with-arcade-expression/README.metadata.json
similarity index 100%
rename from query-features-with-arcade-expression/README.metadata.json
rename to samples/query-features-with-arcade-expression/README.metadata.json
diff --git a/samples/query-features-with-arcade-expression/build.gradle.kts b/samples/query-features-with-arcade-expression/build.gradle.kts
new file mode 100644
index 000000000..f56e6c5e4
--- /dev/null
+++ b/samples/query-features-with-arcade-expression/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/query-features-with-arcade-expression/query-features-with-arcade-expression.png b/samples/query-features-with-arcade-expression/query-features-with-arcade-expression.png
similarity index 100%
rename from query-features-with-arcade-expression/query-features-with-arcade-expression.png
rename to samples/query-features-with-arcade-expression/query-features-with-arcade-expression.png
diff --git a/samples/query-features-with-arcade-expression/src/main/AndroidManifest.xml b/samples/query-features-with-arcade-expression/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..de1c23fc5
--- /dev/null
+++ b/samples/query-features-with-arcade-expression/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt b/samples/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt
new file mode 100644
index 000000000..49ae10fe4
--- /dev/null
+++ b/samples/query-features-with-arcade-expression/src/main/java/com/esri/arcgismaps/sample/queryfeatureswitharcadeexpression/MainActivity.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.arcade.ArcadeEvaluator
+import com.arcgismaps.arcade.ArcadeExpression
+import com.arcgismaps.arcade.ArcadeProfile
+import com.arcgismaps.data.ArcGISFeature
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.layers.Layer
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.mapping.PortalItem
+import com.esri.arcgismaps.sample.queryfeatureswitharcadeexpression.databinding.QueryFeaturesWithArcadeExpressionActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: QueryFeaturesWithArcadeExpressionActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.query_features_with_arcade_expression_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val infoTextView by lazy {
+ activityMainBinding.infoTextView
+ }
+
+ // progress indicator
+ private val progressBar by lazy {
+ activityMainBinding.progressBar
+ }
+
+ // setup the red pin marker image as a bitmap drawable
+ private val markerDrawable: BitmapDrawable by lazy {
+ // load the bitmap from resources and create a drawable
+ val bitmap = BitmapFactory.decodeResource(resources, R.drawable.map_pin_symbol)
+ BitmapDrawable(resources, bitmap)
+ }
+
+ // setup the red pin marker as a Graphic
+ private val markerGraphic: Graphic by lazy {
+ // creates a symbol from the marker drawable
+ val markerSymbol = PictureMarkerSymbol.createWithImage(markerDrawable).apply {
+ // resize the symbol into a smaller size
+ width = 30f
+ height = 30f
+ // offset in +y axis so the marker spawned is right on the touch point
+ offsetY = 25f
+ }
+ // create the graphic from the symbol
+ Graphic(symbol = markerSymbol)
+ }
+
+ // create a graphic overlay
+ private val graphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a portal item with the itemId of the web map
+ val portal = Portal("https://www.arcgis.com/")
+ val portalItem = PortalItem(portal, "539d93de54c7422f88f69bfac2aebf7d")
+ // create and add a map with with portal item
+ val map = ArcGISMap(portalItem)
+ // add the marker graphic to the graphics overlay
+ graphicsOverlay.graphics.add(markerGraphic)
+ mapView.apply {
+ this.map = map
+ // add the graphics overlay to the MapView
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ lifecycleScope.launch {
+ // show an error and return if the map load failed
+ map.load().onFailure {
+ return@launch showError("Error loading map:${it.message}")
+ }
+
+ // get the RPD Beats layer from the map's operational layers
+ val policeBeatsLayer = map.operationalLayers.firstOrNull { layer ->
+ layer.id == "RPD_Reorg_9254"
+ } ?: return@launch showError("Error finding RPD Beats layer")
+
+ // capture and collect when the user taps on the screen
+ mapView.onSingleTapConfirmed.collect { event ->
+ // update the marker location to where the user tapped on the map
+ event.mapPoint?.let { point ->
+ markerGraphic.geometry = point
+ mapView.setViewpointCenter(point)
+ }
+ // evaluate an Arcade expression on the tapped screen coordinate
+ evaluateArcadeExpression(event.screenCoordinate, map, policeBeatsLayer)
+ }
+ }
+ }
+
+ /**
+ * Evaluates an Arcade expression that returns crime in the last 60 days at the tapped
+ * [screenCoordinate] on the [map] with the [policeBeatsLayer] and displays the result
+ * in a textview
+ */
+ private suspend fun evaluateArcadeExpression(
+ screenCoordinate: ScreenCoordinate,
+ map: ArcGISMap,
+ policeBeatsLayer: Layer
+ ) {
+ // show the progress indicator as the Arcade evaluation can take time to complete
+ progressBar.visibility = View.VISIBLE
+ // identify the layer and its elements based on the position tapped on the mapView and
+ // get the result
+ val result = mapView.identifyLayer(
+ layer = policeBeatsLayer,
+ screenCoordinate = screenCoordinate,
+ tolerance = 12.0,
+ returnPopupsOnly = false
+ )
+ // get the result as an IdentifyLayerResult
+ val identifyLayerResult = result.getOrElse { error ->
+ // if the identifyLayer operation failed show an error and return
+ showError("Error identifying layer:${error.message}")
+ // reset the text view to show its default text
+ infoTextView.text = getString(R.string.tap_to_begin)
+ // dismiss the progress indicator
+ progressBar.visibility = View.GONE
+ return
+ }
+ // if there are no geoElements identified
+ if (identifyLayerResult.geoElements.isEmpty()) {
+ // since the layer is a feature layer, display that no features were found
+ infoTextView.text = getString(R.string.no_features_found)
+ // dismiss the progress indicator
+ progressBar.visibility = View.GONE
+ return
+ }
+ // get the first identified GeoElement as an ArcGISFeature
+ val identifiedFeature = identifyLayerResult.geoElements.first() as ArcGISFeature
+ // create a string containing the Arcade expression
+ val expressionValue =
+ "var crimes = FeatureSetByName(\$map, 'Crime in the last 60 days');\n" +
+ "return Count(Intersects(\$feature, crimes));"
+ // create an ArcadeExpression using the string expression
+ val arcadeExpression = ArcadeExpression(expressionValue)
+ // create an ArcadeEvaluator with the ArcadeExpression and an ArcadeProfile
+ val arcadeEvaluator = ArcadeEvaluator(arcadeExpression, ArcadeProfile.FormCalculation)
+ // create a map of profile variables with the feature and map as key value pairs
+ val profileVariables = mapOf("\$feature" to identifiedFeature, "\$map" to map)
+ // evaluate using the previously set profile variables and get the result
+ val evaluationResult = arcadeEvaluator.evaluate(profileVariables)
+ // get the result as an ArcadeEvaluationResult
+ val arcadeEvaluationResult = evaluationResult.getOrElse { error ->
+ // if the evaluation failed show an error and return
+ showError("Error evaluating Arcade expression:${error.message}")
+ // reset the text view to show its default text
+ infoTextView.text = getString(R.string.tap_to_begin)
+ // dismiss the progress indicator
+ progressBar.visibility = View.GONE
+ return
+ }
+ // get the crimes count from the arcadeEvaluationResult as a numerical double value
+ val crimesCount = arcadeEvaluationResult.result as Double
+ // display this result in a textview
+ infoTextView.text = getString(R.string.crime_info_text, crimesCount.toInt())
+ // hide the progress indicator
+ progressBar.visibility = View.GONE
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/query-features-with-arcade-expression/src/main/res/drawable/map_pin_symbol.png b/samples/query-features-with-arcade-expression/src/main/res/drawable/map_pin_symbol.png
similarity index 100%
rename from query-features-with-arcade-expression/src/main/res/drawable/map_pin_symbol.png
rename to samples/query-features-with-arcade-expression/src/main/res/drawable/map_pin_symbol.png
diff --git a/query-features-with-arcade-expression/src/main/res/layout/activity_main.xml b/samples/query-features-with-arcade-expression/src/main/res/layout/query_features_with_arcade_expression_activity_main.xml
similarity index 100%
rename from query-features-with-arcade-expression/src/main/res/layout/activity_main.xml
rename to samples/query-features-with-arcade-expression/src/main/res/layout/query_features_with_arcade_expression_activity_main.xml
diff --git a/samples/query-features-with-arcade-expression/src/main/res/values/strings.xml b/samples/query-features-with-arcade-expression/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f2a6344d1
--- /dev/null
+++ b/samples/query-features-with-arcade-expression/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Query features with arcade expression
+ Tap to begin
+ Crime in the last 60 days: %1d
+ No features found
+
diff --git a/render-multilayer-symbols/README.md b/samples/render-multilayer-symbols/README.md
similarity index 100%
rename from render-multilayer-symbols/README.md
rename to samples/render-multilayer-symbols/README.md
diff --git a/render-multilayer-symbols/README.metadata.json b/samples/render-multilayer-symbols/README.metadata.json
similarity index 100%
rename from render-multilayer-symbols/README.metadata.json
rename to samples/render-multilayer-symbols/README.metadata.json
diff --git a/samples/render-multilayer-symbols/build.gradle.kts b/samples/render-multilayer-symbols/build.gradle.kts
new file mode 100644
index 000000000..56e343faf
--- /dev/null
+++ b/samples/render-multilayer-symbols/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.rendermultilayersymbols"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/render-multilayer-symbols/render-multilayer-symbols.png b/samples/render-multilayer-symbols/render-multilayer-symbols.png
similarity index 100%
rename from render-multilayer-symbols/render-multilayer-symbols.png
rename to samples/render-multilayer-symbols/render-multilayer-symbols.png
diff --git a/samples/render-multilayer-symbols/src/main/AndroidManifest.xml b/samples/render-multilayer-symbols/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..3e37b8a68
--- /dev/null
+++ b/samples/render-multilayer-symbols/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt b/samples/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt
new file mode 100644
index 000000000..fba188dd3
--- /dev/null
+++ b/samples/render-multilayer-symbols/src/main/java/com/esri/arcgismaps/sample/rendermultilayersymbols/MainActivity.kt
@@ -0,0 +1,513 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.rendermultilayersymbols
+
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.symbology.DashGeometricEffect
+import com.arcgismaps.mapping.symbology.HatchFillSymbolLayer
+import com.arcgismaps.mapping.symbology.HorizontalAlignment
+import com.arcgismaps.mapping.symbology.MultilayerPointSymbol
+import com.arcgismaps.mapping.symbology.MultilayerPolygonSymbol
+import com.arcgismaps.mapping.symbology.MultilayerPolylineSymbol
+import com.arcgismaps.mapping.symbology.MultilayerSymbol
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbolLayer
+import com.arcgismaps.mapping.symbology.SolidFillSymbolLayer
+import com.arcgismaps.mapping.symbology.SolidStrokeSymbolLayer
+import com.arcgismaps.mapping.symbology.StrokeSymbolLayerCapStyle
+import com.arcgismaps.mapping.symbology.SymbolAnchor
+import com.arcgismaps.mapping.symbology.SymbolAnchorPlacementMode
+import com.arcgismaps.mapping.symbology.SymbolLayer
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.symbology.VectorMarkerSymbolElement
+import com.arcgismaps.mapping.symbology.VectorMarkerSymbolLayer
+import com.arcgismaps.mapping.symbology.VerticalAlignment
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.rendermultilayersymbols.databinding.RenderMultilayerSymbolsActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+private val Color.Companion.magenta: Color
+ get() {
+ return fromRgba(255, 0, 255, 255)
+ }
+
+// define offset used to keep a consistent distance between symbols in the same column
+private const val OFFSET = 20.0
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: RenderMultilayerSymbolsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.render_multilayer_symbols_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // define the graphics overlay to add the multilayer symbols
+ private var graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ mapView.apply {
+ // set the map to be displayed in the layout's map view
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISLightGray)
+ // add the graphic overlay to the map view
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // create labels to go above each category of graphic
+ addTextGraphics()
+
+ // create picture marker symbols, from URI or local cache
+ addImageGraphics()
+
+ // add graphics with simple vector marker symbol elements (MultilayerPoint Simple Markers on app UI)
+ val solidFillSymbolLayer = SolidFillSymbolLayer(Color.red)
+ val multilayerPolygonSymbol = MultilayerPolygonSymbol(listOf(solidFillSymbolLayer))
+ val solidStrokeSymbolLayer =
+ SolidStrokeSymbolLayer(1.0, Color.red, listOf(DashGeometricEffect()))
+ val multilayerPolylineSymbol = MultilayerPolylineSymbol(listOf(solidStrokeSymbolLayer))
+
+ // define vector element for a diamond, triangle and cross
+ val diamondGeometry =
+ Geometry.fromJsonOrNull("{\"rings\":[[[0.0,2.5],[2.5,0.0],[0.0,-2.5],[-2.5,0.0],[0.0,2.5]]]}")
+ val triangleGeometry =
+ Geometry.fromJsonOrNull("{\"rings\":[[[0.0,5.0],[5,-5.0],[-5,-5.0],[0.0,5.0]]]}")
+ val crossGeometry =
+ Geometry.fromJsonOrNull("{\"paths\":[[[-1,1],[0,0],[1,-1]],[[1,1],[0,0],[-1,-1]]]}")
+
+ if (diamondGeometry == null || triangleGeometry == null || crossGeometry == null) {
+ showError("Error reading geometry from json")
+ return
+ }
+
+ // add red diamond graphic
+ addGraphicsWithVectorMarkerSymbolElements(
+ multilayerPolygonSymbol, diamondGeometry, 0.0
+ )
+ // add red triangle graphic
+ addGraphicsWithVectorMarkerSymbolElements(
+ multilayerPolygonSymbol, triangleGeometry, OFFSET
+ )
+ // add red cross graphic
+ addGraphicsWithVectorMarkerSymbolElements(
+ multilayerPolylineSymbol, crossGeometry, 2 * OFFSET
+ )
+
+ // create line marker symbols with short dash dot dot
+ addLineGraphicsWithMarkerSymbols(
+ listOf(4.0, 6.0, 0.5, 6.0, 0.5, 6.0), 0.0
+ )
+ // create line marker symbol with short dash
+ addLineGraphicsWithMarkerSymbols(
+ listOf(4.0, 6.0), OFFSET
+ )
+ // create line marker symbol with dash dot dot
+ addLineGraphicsWithMarkerSymbols(
+ listOf(7.0, 9.0, 0.5, 9.0), 2 * OFFSET
+ )
+
+ // create polygon marker symbols
+ // cross-hatched diagonal lines
+ addPolygonGraphicsWithMarkerSymbols(
+ listOf(-45.0, 45.0), 0.0
+ )
+ // hatched diagonal lines
+ addPolygonGraphicsWithMarkerSymbols(
+ listOf(-45.0), OFFSET
+ )
+ // hatched vertical lines
+ addPolygonGraphicsWithMarkerSymbols(
+ listOf(90.0), 2 * OFFSET
+ )
+
+ // define vector element for a hexagon which will be used as the basis of a complex point
+ val complexPointGeometry =
+ Geometry.fromJsonOrNull("{\"rings\":[[[-2.89,5.0],[2.89,5.0],[5.77,0.0],[2.89,-5.0],[-2.89,-5.0],[-5.77,0.0],[-2.89,5.0]]]}")
+
+ // create the more complex multilayer graphics: a point, polygon, and polyline
+ complexPointGeometry?.let { addComplexPoint(it) }
+ addComplexPolygon()
+ addComplexPolyline()
+ }
+
+ /**
+ * Creates the label graphics to be displayed above each category of symbol,
+ * and adds them to the graphics overlay.
+ */
+ private fun addTextGraphics() {
+ graphicsOverlay.graphics.addAll(
+ listOf(
+ Graphic(
+ Point(-150.0, 50.0, SpatialReference.wgs84()),
+ getTextSymbol("MultilayerPoint\nSimple Markers")
+ ), Graphic(
+ Point(-80.0, 50.0, SpatialReference.wgs84()),
+ getTextSymbol("MultilayerPoint\nPicture Markers")
+ ), Graphic(
+ Point(0.0, 50.0, SpatialReference.wgs84()),
+ getTextSymbol("Multilayer\nPolyline")
+ ), Graphic(
+ Point(65.0, 50.0, SpatialReference.wgs84()),
+ getTextSymbol("Multilayer\nPolygon")
+ ), Graphic(
+ Point(130.0, 50.0, SpatialReference.wgs84()),
+ getTextSymbol("Multilayer\nComplex Symbols")
+ )
+ )
+ )
+ }
+
+ /**
+ * @return the TextSymbol with the [text] to be displayed on the map.
+ */
+ private fun getTextSymbol(text: String): TextSymbol {
+ val textSymbol = TextSymbol(
+ text, Color.black, 10F, HorizontalAlignment.Center, VerticalAlignment.Middle
+ )
+ // give the text symbol a white background
+ textSymbol.backgroundColor = Color.white
+ return textSymbol
+ }
+
+ /**
+ * Create picture marker symbols from online URI and local cache.
+ */
+ private fun addImageGraphics() {
+ // URI of image to display
+ val blueTentImageURI = "https://static.arcgis.com/images/Symbols/OutdoorRecreation/Camping.png"
+ // load the PictureMarkerSymbolLayer using the image URI
+ val pictureMarkerFromUri = PictureMarkerSymbolLayer(blueTentImageURI)
+ // add loaded layer to the map
+ addGraphicFromPictureMarkerSymbolLayer(pictureMarkerFromUri, 0.0)
+ // load blue pin from as a bitmap
+ val bitmap = BitmapFactory.decodeResource(resources, R.drawable.blue_pin)
+ // load the PictureMarkerSymbolLayer using the bitmap drawable
+ val pictureMarkerFromCache = PictureMarkerSymbolLayer.createWithImage(BitmapDrawable(resources, bitmap))
+ // add loaded layer to the map
+ addGraphicFromPictureMarkerSymbolLayer(pictureMarkerFromCache, 40.0)
+ }
+
+ /**
+ * Loads a picture marker symbol layer and after it has loaded, creates a new multilayer point symbol from it.
+ * A graphic is created from the multilayer point symbol and added to the graphics overlay.
+ *
+ * The [pictureMarkerSymbolLayer] to be loaded.
+ * The [offset] value used to keep a consistent distance between symbols in the same column.
+ */
+ private fun addGraphicFromPictureMarkerSymbolLayer(
+ pictureMarkerSymbolLayer: PictureMarkerSymbolLayer, offset: Double
+ ) = lifecycleScope.launch {
+ // wait for the picture marker symbol layer to load and check it has loaded
+ pictureMarkerSymbolLayer.load().getOrElse {
+ showError("Picture marker symbol layer failed to load: ${it.message}")
+ }
+ // set the size of the layer and create a new multilayer point symbol from it
+ pictureMarkerSymbolLayer.size = 40.0
+ val multilayerPointSymbol = MultilayerPointSymbol(listOf(pictureMarkerSymbolLayer))
+ // create location for the symbol
+ val point = Point(-80.0, 20.0 - offset, SpatialReference.wgs84())
+
+ // create graphic with the location and symbol and add it to the graphics overlay
+ val graphic = Graphic(point, multilayerPointSymbol)
+ graphicsOverlay.graphics.add(graphic)
+ }
+
+ /**
+ * Adds new graphics constructed from multilayer point symbols.
+ *
+ * The [multilayerSymbol] to construct the vector marker symbol element with.
+ * The input [geometry] for the vector marker symbol element.
+ * [offset] the value used to keep a consistent distance between symbols in the same column.
+ */
+ private fun addGraphicsWithVectorMarkerSymbolElements(
+ multilayerSymbol: MultilayerSymbol, geometry: Geometry, offset: Double
+ ) {
+ // define a vector element and create a new multilayer point symbol from it
+ val vectorMarkerSymbolElement = VectorMarkerSymbolElement(geometry, multilayerSymbol)
+ val vectorMarkerSymbolLayer = VectorMarkerSymbolLayer(listOf(vectorMarkerSymbolElement))
+ val multilayerPointSymbol = MultilayerPointSymbol(listOf(vectorMarkerSymbolLayer))
+
+ // create point graphic using the symbol and add it to the graphics overlay
+ val graphic =
+ Graphic(Point(-150.0, 20 - offset, SpatialReference.wgs84()), multilayerPointSymbol)
+ graphicsOverlay.graphics.add(graphic)
+ }
+
+ /**
+ * Adds new graphics constructed from multilayer polyline symbols.
+ *
+ * The pattern of [dashSpacing] dots/dashes used by the line and
+ * [offset] the value used to keep a consistent distance between symbols in the same column.
+ */
+ private fun addLineGraphicsWithMarkerSymbols(dashSpacing: List, offset: Double) {
+ // create a dash effect from the provided values
+ val dashGeometricEffect = DashGeometricEffect(dashSpacing)
+ // create stroke used by line symbols
+ val solidStrokeSymbolLayer = SolidStrokeSymbolLayer(
+ 3.0, Color.red, listOf(dashGeometricEffect)
+ ).apply {
+ capStyle = StrokeSymbolLayerCapStyle.Round
+ }
+ // create a polyline for the multilayer polyline symbol
+ val polylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
+ addPoint(Point(-30.0, 20 - offset))
+ addPoint(Point(30.0, 20 - offset))
+ }
+ // create a multilayer polyline symbol from the solidStrokeSymbolLayer
+ val multilayerPolylineSymbol = MultilayerPolylineSymbol(listOf(solidStrokeSymbolLayer))
+ // create a polyline graphic with geometry using the symbol created above, and add it to the graphics overlay
+ graphicsOverlay.graphics.add(
+ Graphic(
+ polylineBuilder.toGeometry(), multilayerPolylineSymbol
+ )
+ )
+ }
+
+ /**
+ * Adds new graphics constructed from multilayer polygon symbols.
+ *
+ * Takes a list containing the [angles] at which to draw fill lines within the polygon and
+ * [offset] the value used to keep a consistent distance between symbols in the same column.
+ */
+ private fun addPolygonGraphicsWithMarkerSymbols(angles: List, offset: Double) {
+ val polygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
+ addPoint(Point(60.0, 25 - offset))
+ addPoint(Point(70.0, 25 - offset))
+ addPoint(Point(70.0, 20 - offset))
+ addPoint(Point(60.0, 20 - offset))
+ }
+
+ // create a stroke symbol layer to be used by patterns
+ val strokeForHatches = SolidStrokeSymbolLayer(2.0, Color.red, listOf(DashGeometricEffect()))
+
+ // create a stroke symbol layer to be used as an outline for aforementioned patterns
+ val strokeForOutline =
+ SolidStrokeSymbolLayer(1.0, Color.black, listOf(DashGeometricEffect()))
+
+ // create a list to hold all necessary symbol layers - at least one for patterns and one for an outline at the end
+ val symbolLayerList = mutableListOf()
+
+ // for each angle, create a symbol layer using the pattern stroke, with hatched lines at the given angle
+ for (i in angles.indices) {
+ val hatchFillSymbolLayer = HatchFillSymbolLayer(
+ MultilayerPolylineSymbol(listOf(strokeForHatches)), angles[i]
+ )
+ // define separation distance for lines and add them to the symbol layer list
+ hatchFillSymbolLayer.separation = 9.0
+ symbolLayerList.add(hatchFillSymbolLayer)
+ }
+
+ // assign the outline layer to the last element of the symbol layer list
+ symbolLayerList.add(strokeForOutline)
+ // create a multilayer polygon symbol from the symbol layer list
+ val multilayerPolygonSymbol =
+ MultilayerPolygonSymbol(symbolLayerList)
+ // create a polygon graphic with geometry using the symbol created above, and add it to the graphics overlay
+ val graphic = Graphic(polygonBuilder.toGeometry(), multilayerPolygonSymbol)
+ graphicsOverlay.graphics.add(graphic)
+ }
+
+ /**
+ * Creates a complex point from multiple symbol layers and a provided geometry.
+ * @param complexPointGeometry a base geometry upon which other symbol layers are drawn.
+ */
+ private fun addComplexPoint(complexPointGeometry: Geometry) {
+ // create marker layers for complex point
+ val orangeSquareVectorMarkerLayer: VectorMarkerSymbolLayer =
+ getLayerForComplexPoint(Color.cyan, Color.blue, 11.0)
+ val blackSquareVectorMarkerLayer: VectorMarkerSymbolLayer =
+ getLayerForComplexPoint(Color.black, Color.cyan, 6.0)
+ val purpleSquareVectorMarkerLayer: VectorMarkerSymbolLayer = getLayerForComplexPoint(
+ Color.transparent, Color.magenta, 14.0
+ )
+
+ // set anchors for marker layers
+ orangeSquareVectorMarkerLayer.anchor =
+ SymbolAnchor(-4.0, -6.0, SymbolAnchorPlacementMode.Absolute)
+ blackSquareVectorMarkerLayer.anchor =
+ SymbolAnchor(2.0, 1.0, SymbolAnchorPlacementMode.Absolute)
+ purpleSquareVectorMarkerLayer.anchor =
+ SymbolAnchor(4.0, 2.0, SymbolAnchorPlacementMode.Absolute)
+
+ // create a yellow hexagon with a black outline
+ val yellowFillLayer = SolidFillSymbolLayer(Color.yellow)
+ val blackOutline = SolidStrokeSymbolLayer(2.0, Color.black, listOf(DashGeometricEffect()))
+ val hexagonVectorElement = VectorMarkerSymbolElement(
+ complexPointGeometry, MultilayerPolylineSymbol(listOf(yellowFillLayer, blackOutline))
+ )
+ val hexagonVectorMarkerLayer = VectorMarkerSymbolLayer(listOf(hexagonVectorElement)).apply {
+ size = 35.0
+ }
+
+ // create the multilayer point symbol
+ val multilayerPointSymbol = MultilayerPointSymbol(
+ listOf(
+ hexagonVectorMarkerLayer,
+ orangeSquareVectorMarkerLayer,
+ blackSquareVectorMarkerLayer,
+ purpleSquareVectorMarkerLayer
+ )
+ )
+
+ // create the multilayer point graphic using the symbols created above
+ val complexPointGraphic =
+ Graphic(Point(130.0, 20.0, SpatialReference.wgs84()), multilayerPointSymbol)
+ graphicsOverlay.graphics.add(complexPointGraphic)
+ }
+
+ /**
+ * Creates a symbol layer for use in the composition of a complex point
+ * using [fillColor], [outlineColor] as colors and the [size] of the symbol
+ * Then return a [VectorMarkerSymbolLayer] of the created symbol.
+ */
+ private fun getLayerForComplexPoint(
+ fillColor: Color, outlineColor: Color, size: Double
+ ): VectorMarkerSymbolLayer {
+ // create the fill layer and outline
+ val fillLayer = SolidFillSymbolLayer(fillColor)
+ val outline = SolidStrokeSymbolLayer(
+ 2.0, outlineColor, listOf(DashGeometricEffect())
+ )
+ // create a geometry from an envelope
+ val geometry = Envelope(
+ Point(-0.5, -0.5, SpatialReference.wgs84()), Point(0.5, 0.5, SpatialReference.wgs84())
+ )
+ //create a symbol element using the geometry, fill layer, and outline
+ val vectorMarkerSymbolElement = VectorMarkerSymbolElement(
+ geometry, MultilayerPolygonSymbol(listOf(fillLayer, outline))
+ )
+ // create a symbol layer containing just the above symbol element, set its size, and return it
+ val vectorMarkerSymbolLayer =
+ VectorMarkerSymbolLayer(listOf(vectorMarkerSymbolElement)).apply {
+ this.size = size
+ }
+ return vectorMarkerSymbolLayer
+ }
+
+ /**
+ * Adds a complex polygon generated with multiple symbol layers.
+ */
+ private fun addComplexPolygon() {
+ // create the multilayer polygon symbol
+ val multilayerPolygonSymbol = MultilayerPolygonSymbol(getLayersForComplexPolys(true))
+ // create the polygon
+ val polygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
+ addPoint(Point(120.0, 0.0))
+ addPoint(Point(140.0, 0.0))
+ addPoint(Point(140.0, -10.0))
+ addPoint(Point(120.0, -10.0))
+ }
+ // create a multilayer polygon graphic with geometry using the symbols
+ // created above and add it to the graphics overlay
+ graphicsOverlay.graphics.add(
+ Graphic(
+ polygonBuilder.toGeometry(), multilayerPolygonSymbol
+ )
+ )
+ }
+
+ /**
+ * Adds a complex polyline generated with multiple symbol layers.
+ */
+ private fun addComplexPolyline() {
+ // create the multilayer polyline symbol
+ val multilayerPolylineSymbol = MultilayerPolylineSymbol(getLayersForComplexPolys(false))
+ val polylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
+ addPoint(Point(120.0, -25.0))
+ addPoint(Point(140.0, -25.0))
+ }
+ // create the multilayer polyline graphic with geometry using the symbols created above and add it to the graphics overlay
+ graphicsOverlay.graphics.add(
+ Graphic(
+ polylineBuilder.toGeometry(), multilayerPolylineSymbol
+ )
+ )
+ }
+
+ /**
+ * Generates and returns the symbol layers used by the addComplexPolygon and addComplexPolyline methods.
+ * [includeRedFill] indicates whether to include the red fill needed by the complex polygon.
+ * @return a list of symbol layers including the necessary effects.
+ */
+ private fun getLayersForComplexPolys(includeRedFill: Boolean): List {
+ // create a black dash effect
+ val blackDashes = SolidStrokeSymbolLayer(
+ 1.0, Color.black, listOf(DashGeometricEffect(listOf(5.0, 3.0)))
+ ).apply {
+ capStyle = StrokeSymbolLayerCapStyle.Square
+ }
+
+ // create a black outline
+ val blackOutline = SolidStrokeSymbolLayer(
+ 7.0, Color.black, listOf(DashGeometricEffect())
+ ).apply {
+ capStyle = StrokeSymbolLayerCapStyle.Round
+ }
+
+ // create a yellow stroke inside
+ val yellowStroke = SolidStrokeSymbolLayer(
+ 5.0, Color.yellow, listOf(DashGeometricEffect())
+ ).apply {
+ capStyle = StrokeSymbolLayerCapStyle.Round
+ }
+
+ return if (includeRedFill) {
+ // create a red filling for the polygon
+ val redFillLayer = SolidFillSymbolLayer(Color.red)
+ listOf(redFillLayer, blackOutline, yellowStroke, blackDashes)
+ } else {
+ listOf(blackOutline, yellowStroke, blackDashes)
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/render-multilayer-symbols/src/main/res/drawable/blue_pin.png b/samples/render-multilayer-symbols/src/main/res/drawable/blue_pin.png
similarity index 100%
rename from render-multilayer-symbols/src/main/res/drawable/blue_pin.png
rename to samples/render-multilayer-symbols/src/main/res/drawable/blue_pin.png
diff --git a/render-multilayer-symbols/src/main/res/layout/activity_main.xml b/samples/render-multilayer-symbols/src/main/res/layout/render_multilayer_symbols_activity_main.xml
similarity index 100%
rename from render-multilayer-symbols/src/main/res/layout/activity_main.xml
rename to samples/render-multilayer-symbols/src/main/res/layout/render_multilayer_symbols_activity_main.xml
diff --git a/samples/render-multilayer-symbols/src/main/res/values/strings.xml b/samples/render-multilayer-symbols/src/main/res/values/strings.xml
new file mode 100644
index 000000000..e5e22652d
--- /dev/null
+++ b/samples/render-multilayer-symbols/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Render multilayer symbols
+
diff --git a/search-with-geocode/README.md b/samples/search-with-geocode/README.md
similarity index 100%
rename from search-with-geocode/README.md
rename to samples/search-with-geocode/README.md
diff --git a/search-with-geocode/README.metadata.json b/samples/search-with-geocode/README.metadata.json
similarity index 100%
rename from search-with-geocode/README.metadata.json
rename to samples/search-with-geocode/README.metadata.json
diff --git a/samples/search-with-geocode/build.gradle.kts b/samples/search-with-geocode/build.gradle.kts
new file mode 100644
index 000000000..456e97f07
--- /dev/null
+++ b/samples/search-with-geocode/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.searchwithgeocode"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/search-with-geocode/search-with-geocode.png b/samples/search-with-geocode/search-with-geocode.png
similarity index 100%
rename from search-with-geocode/search-with-geocode.png
rename to samples/search-with-geocode/search-with-geocode.png
diff --git a/samples/search-with-geocode/src/main/AndroidManifest.xml b/samples/search-with-geocode/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..254658fd3
--- /dev/null
+++ b/samples/search-with-geocode/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt b/samples/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt
new file mode 100644
index 000000000..23bb4a8de
--- /dev/null
+++ b/samples/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt
@@ -0,0 +1,379 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.searchwithgeocode
+
+import android.content.Context
+import android.database.MatrixCursor
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.provider.BaseColumns
+import android.text.SpannableStringBuilder
+import android.util.Log
+import android.view.Menu
+import android.view.inputmethod.InputMethodManager
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.SearchView
+import androidx.core.content.ContextCompat
+import androidx.core.text.bold
+import androidx.cursoradapter.widget.SimpleCursorAdapter
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.ViewpointType
+import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.arcgismaps.tasks.geocode.GeocodeParameters
+import com.arcgismaps.tasks.geocode.GeocodeResult
+import com.arcgismaps.tasks.geocode.LocatorTask
+import com.esri.arcgismaps.sample.searchwithgeocode.databinding.SearchWithGeocodeActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.switchmaterial.SwitchMaterial
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: SearchWithGeocodeActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.search_with_geocode_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val addressTextView: TextView by lazy {
+ activityMainBinding.addressTextView
+ }
+
+ private val extentSwitch: SwitchMaterial by lazy {
+ activityMainBinding.extentSwitch
+ }
+
+ // create a locator task from an online service
+ private val locatorTask: LocatorTask = LocatorTask(
+ "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"
+ )
+
+ // geocode parameters used to perform a search
+ private val addressGeocodeParameters: GeocodeParameters = GeocodeParameters().apply {
+ // get all attributes names for the geocode results
+ resultAttributeNames.addAll(listOf("PlaceName", "Place_addr"))
+ }
+
+ // create a new Graphics Overlay
+ private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ // instance of the map pin symbol
+ private var pinSourceSymbol: PictureMarkerSymbol? = null
+
+ // will search in the map view's viewpoint extent if enabled
+ private var isSearchInExtent = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ mapView.apply {
+ // set the map to be displayed in the MapView
+ map = ArcGISMap(BasemapStyle.ArcGISStreets)
+
+ // set map initial viewpoint
+ map?.initialViewpoint = Viewpoint(40.0, -100.0, 100000000.0)
+
+ // define the graphics overlay and add it to the map view
+ graphicsOverlays.add(graphicsOverlay)
+
+ // set an on touch listener on the map view
+ lifecycleScope.launch {
+ onSingleTapConfirmed.collect { tapEvent ->
+ // identify the graphic at the tapped coordinate
+ val tappedGraphic = identifyGraphic(tapEvent.screenCoordinate)
+ if (tappedGraphic != null) {
+ // show the address of the identified graphic
+ showAddressForGraphic(tappedGraphic)
+ }
+ }
+ }
+ }
+
+ // once the map has loaded successfully, set up address finding UI
+ lifecycleScope.launch {
+ // load the map then set up UI
+ mapView.map?.load()?.onSuccess {
+ // create the pin symbol
+ pinSourceSymbol = createPinSymbol()
+ }?.onFailure {
+ showError(it.message.toString())
+ }
+ }
+
+ // set the switch to update the isSearchInExtent value
+ extentSwitch.setOnCheckedChangeListener { _, isChecked -> isSearchInExtent = isChecked }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu, menu)
+ val search = menu.findItem(R.id.appSearchBar)
+ // set up address search view and listeners
+ setupAddressSearchView(search.actionView as SearchView)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ /**
+ * Sets up the address SearchView and uses MatrixCursor to
+ * show suggestions to the user as text is entered.
+ */
+ private fun setupAddressSearchView(addressSearchView: SearchView) {
+ addressSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(address: String): Boolean {
+ // geocode the typed address, search within map's viewpoint as keyword was submitted
+ geocodeAddress(address, true)
+ addressSearchView.clearAndHideKeyboard()
+ return true
+ }
+
+ override fun onQueryTextChange(newText: String): Boolean {
+ // if the newText string isn't empty, get suggestions from the locator task
+ if (newText.isNotEmpty()) {
+ lifecycleScope.launch {
+ locatorTask.suggest(newText).onSuccess { suggestResults ->
+ // create a SimpleCursorAdapter and assign it to the suggestion adapter of the SearchView
+ val simpleCursorAdapter = createSimpleCursorAdapter()
+ addressSearchView.suggestionsAdapter = simpleCursorAdapter
+
+ // add each address suggestion to a new row
+ for ((key, result) in suggestResults.withIndex()) {
+ val suggestionCursor = simpleCursorAdapter.cursor as MatrixCursor
+ suggestionCursor.addRow(arrayOf(key, result.label))
+ }
+ // notify the adapter when the data updates, so the view can refresh itself
+ simpleCursorAdapter.notifyDataSetChanged()
+
+ // handle an address suggestion being chosen
+ addressSearchView.setOnSuggestionListener(object :
+ SearchView.OnSuggestionListener {
+ override fun onSuggestionSelect(position: Int): Boolean {
+ return false
+ }
+
+ override fun onSuggestionClick(position: Int): Boolean {
+ // get the selected row
+ (simpleCursorAdapter.getItem(position) as? MatrixCursor)?.let { selectedRow ->
+ // get the row's index
+ val selectedCursorIndex =
+ selectedRow.getColumnIndex("address")
+ // get the string from the row at index and set it to query
+ val selectedAddress =
+ selectedRow.getString(selectedCursorIndex)
+ addressSearchView.setQuery(selectedAddress, false)
+ // geocode the typed address
+ geocodeAddress(selectedAddress, false)
+ addressSearchView.clearAndHideKeyboard()
+ }
+ return true
+ }
+ })
+ }.onFailure {
+ showError("Geocode suggestion error: ${it.message.toString()}")
+ }
+ }
+ }
+ return true
+ }
+ })
+ }
+
+ /**
+ * Creates and returns a SimpleCursorAdapter.
+ */
+ private fun createSimpleCursorAdapter(): SimpleCursorAdapter {
+ // set up parameters for searching with MatrixCursor
+ val columnNames = arrayOf(BaseColumns._ID, "address")
+ val suggestionsCursor = MatrixCursor(columnNames)
+ // column names for the adapter to look at when mapping data
+ val cols = arrayOf("address")
+ // ids that show where data should be assigned in the layout
+ val to = intArrayOf(R.id.suggestion_address)
+ // define SimpleCursorAdapter
+ return SimpleCursorAdapter(
+ this@MainActivity,
+ R.layout.suggestion, suggestionsCursor, cols, to, 0
+ )
+ }
+
+ /**
+ * Geocode an [address] passed in by the user.
+ */
+ private fun geocodeAddress(address: String, multipleResults: Boolean) = lifecycleScope.launch {
+ // clear graphics on map before displaying search results
+ graphicsOverlay.graphics.clear()
+
+ // search the map view's extent if enabled
+ if (isSearchInExtent)
+ addressGeocodeParameters.searchArea =
+ mapView.getCurrentViewpoint(ViewpointType.BoundingGeometry)?.targetGeometry
+ else
+ addressGeocodeParameters.searchArea = null
+
+ // if locator task needs to find multiple results,
+ // set maxResults to default to `6`.
+ addressGeocodeParameters.maxResults = if (multipleResults) 6 else 1
+
+ // load the locator task
+ locatorTask.load().getOrThrow()
+
+ // run the locatorTask geocode task, passing in the address
+ val geocodeResults = locatorTask.geocode(address, addressGeocodeParameters).getOrThrow()
+ // no address found in geocode so return
+ when {
+ geocodeResults.isEmpty() && isSearchInExtent -> {
+ showError("Address not found in map's extent")
+ return@launch
+ }
+ geocodeResults.isEmpty() && !isSearchInExtent -> {
+ showError("No address found for $address")
+ return@launch
+ }
+ // address found in geocode
+ else -> displaySearchResultOnMap(geocodeResults)
+ }
+
+ }
+
+ /**
+ * Turns a list of [geocodeResultList] into a point markers and adds it to the graphic overlay of the map.
+ */
+ private fun displaySearchResultOnMap(geocodeResultList: List) {
+ // clear graphics overlay of existing graphics
+ graphicsOverlay.graphics.clear()
+
+ // create graphic object for each resulting location
+ geocodeResultList.forEach { geocodeResult ->
+ val resultLocationGraphic = Graphic(
+ geocodeResult.displayLocation,
+ geocodeResult.attributes, pinSourceSymbol
+ )
+ // add graphic to location layer
+ graphicsOverlay.graphics.add(resultLocationGraphic)
+ }
+
+ when (geocodeResultList.size) {
+ // if there is only one result, display location's address
+ 1 -> {
+ val addressAttributes = geocodeResultList[0].attributes
+ val addressString = SpannableStringBuilder()
+ .append("Selected address\n")
+ .bold { append("${addressAttributes["PlaceName"]} ${addressAttributes["Place_addr"]}") }
+ addressTextView.text = addressString
+ }
+ // if there are multiple results, display tap pin message
+ else -> addressTextView.text = getString(R.string.tap_on_pin_to_select_address)
+ }
+
+ // get the envelop to set the viewpoint
+ val envelope = graphicsOverlay.extent ?: return showError("Geocode result extent is null")
+ // animate viewpoint to geocode result's extent
+ lifecycleScope.launch {
+ mapView.setViewpointGeometry(envelope, 25.0)
+ }
+ }
+
+ /**
+ * Identifies the tapped graphic at the [screenCoordinate] and shows it's address.
+ */
+ private suspend fun identifyGraphic(screenCoordinate: ScreenCoordinate): Graphic? {
+ // from the graphics overlay, get the graphics near the tapped location
+ val identifyGraphicsOverlayResult = mapView.identifyGraphicsOverlay(
+ graphicsOverlay,
+ screenCoordinate,
+ 10.0,
+ false
+ ).getOrElse { throwable ->
+ showError("Error with identifyGraphicsOverlay: ${throwable.message.toString()}")
+ return null
+ }
+
+ // if not graphic selected, return
+ if (identifyGraphicsOverlayResult.graphics.isEmpty()) {
+ return null
+ }
+
+ // get the first graphic identified
+ return identifyGraphicsOverlayResult.graphics[0]
+ }
+
+ /**
+ * Creates a picture marker symbol from the pin icon, and sets it to half of its original size.
+ */
+ private suspend fun createPinSymbol(): PictureMarkerSymbol {
+ val pinDrawable = ContextCompat.getDrawable(this, R.drawable.pin) as BitmapDrawable
+ val pinSymbol = PictureMarkerSymbol.createWithImage(pinDrawable)
+ pinSymbol.load().getOrThrow()
+ pinSymbol.width = 19f
+ pinSymbol.height = 72f
+ return pinSymbol
+ }
+
+ /**
+ * Display the address for the tapped [identifiedGraphic] using the attribute values
+ */
+ private suspend fun showAddressForGraphic(identifiedGraphic: Graphic) {
+ // get the non null value of the geometry
+ val pinGeometry: Geometry = identifiedGraphic.geometry
+ ?: return showError("Error retrieving geometry for tapped graphic")
+
+ // set the viewpoint to the pin location
+ mapView.apply {
+ setViewpointGeometry(pinGeometry.extent)
+ setViewpointScale(10e3)
+ }
+
+ // set the address text
+ val addressAttributes = identifiedGraphic.attributes
+ val addressString = SpannableStringBuilder()
+ .append("Selected address\n")
+ .bold { append("${addressAttributes["PlaceName"]} ${addressAttributes["Place_addr"]}") }
+ addressTextView.text = addressString
+
+ }
+
+ fun SearchView.clearAndHideKeyboard() {
+ // clear the searched text from the view
+ this.clearFocus()
+ // close the keyboard once search is complete
+ val inputManager =
+ context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputManager.hideSoftInputFromWindow(windowToken, 0)
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/search-with-geocode/src/main/res/drawable-v24/pin.png b/samples/search-with-geocode/src/main/res/drawable-v24/pin.png
similarity index 100%
rename from search-with-geocode/src/main/res/drawable-v24/pin.png
rename to samples/search-with-geocode/src/main/res/drawable-v24/pin.png
diff --git a/search-with-geocode/src/main/res/drawable/ic_baseline_search_24.xml b/samples/search-with-geocode/src/main/res/drawable/ic_baseline_search_24.xml
similarity index 100%
rename from search-with-geocode/src/main/res/drawable/ic_baseline_search_24.xml
rename to samples/search-with-geocode/src/main/res/drawable/ic_baseline_search_24.xml
diff --git a/search-with-geocode/src/main/res/layout/activity_main.xml b/samples/search-with-geocode/src/main/res/layout/search_with_geocode_activity_main.xml
similarity index 100%
rename from search-with-geocode/src/main/res/layout/activity_main.xml
rename to samples/search-with-geocode/src/main/res/layout/search_with_geocode_activity_main.xml
diff --git a/search-with-geocode/src/main/res/layout/suggestion.xml b/samples/search-with-geocode/src/main/res/layout/suggestion.xml
similarity index 100%
rename from search-with-geocode/src/main/res/layout/suggestion.xml
rename to samples/search-with-geocode/src/main/res/layout/suggestion.xml
diff --git a/samples/search-with-geocode/src/main/res/menu/menu.xml b/samples/search-with-geocode/src/main/res/menu/menu.xml
new file mode 100644
index 000000000..53abf2c5f
--- /dev/null
+++ b/samples/search-with-geocode/src/main/res/menu/menu.xml
@@ -0,0 +1,10 @@
+
+
diff --git a/samples/search-with-geocode/src/main/res/values/strings.xml b/samples/search-with-geocode/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d2c71e417
--- /dev/null
+++ b/samples/search-with-geocode/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Search with geocode
+ Enter address
+ Search for an address or a place
+ Tap on pin to select address
+ Search within map\'s extent
+
diff --git a/select-features-in-feature-layer/README.md b/samples/select-features-in-feature-layer/README.md
similarity index 100%
rename from select-features-in-feature-layer/README.md
rename to samples/select-features-in-feature-layer/README.md
diff --git a/select-features-in-feature-layer/README.metadata.json b/samples/select-features-in-feature-layer/README.metadata.json
similarity index 100%
rename from select-features-in-feature-layer/README.metadata.json
rename to samples/select-features-in-feature-layer/README.metadata.json
diff --git a/samples/select-features-in-feature-layer/build.gradle.kts b/samples/select-features-in-feature-layer/build.gradle.kts
new file mode 100644
index 000000000..c7e1d6c2f
--- /dev/null
+++ b/samples/select-features-in-feature-layer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/select-features-in-feature-layer/select-features-in-feature-layer.png b/samples/select-features-in-feature-layer/select-features-in-feature-layer.png
similarity index 100%
rename from select-features-in-feature-layer/select-features-in-feature-layer.png
rename to samples/select-features-in-feature-layer/select-features-in-feature-layer.png
diff --git a/samples/select-features-in-feature-layer/src/main/AndroidManifest.xml b/samples/select-features-in-feature-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a35daa642
--- /dev/null
+++ b/samples/select-features-in-feature-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt b/samples/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt
new file mode 100644
index 000000000..b47229483
--- /dev/null
+++ b/samples/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt
@@ -0,0 +1,125 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.data.Feature
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer.databinding.SelectFeaturesInFeatureLayerActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: SelectFeaturesInFeatureLayerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.select_features_in_feature_layer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val gdbPerCapitalURL =
+ "https://services1.arcgis.com/4yjifSiIG17X0gW4/arcgis/rest/services/GDP_per_capita_1960_2016/FeatureServer/0"
+
+ // create service feature table and a feature layer from it
+ private val serviceFeatureTable = ServiceFeatureTable(gdbPerCapitalURL)
+ private val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a map with the streets base map type
+ val streetsMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
+ // add the feature layer to the map's operational layers
+ operationalLayers.add(featureLayer)
+ }
+
+ mapView.apply {
+ // set the map to be displayed in the layout's map view
+ map = streetsMap
+ // set an initial view point
+ setViewpoint(
+ Viewpoint(
+ Envelope(
+ -1131596.019761,
+ 3893114.069099,
+ 3926705.982140,
+ 7977912.461790
+ )
+ )
+ )
+ // give any item selected on the map view a red selection halo
+ selectionProperties.color = Color.red
+ // set an on touch listener on the map view
+ lifecycleScope.launch {
+ onSingleTapConfirmed.collect { tapEvent ->
+ // get the tapped coordinate
+ val screenCoordinate = tapEvent.screenCoordinate
+ getSelectedFeatureLayer(screenCoordinate)
+ }
+ }
+ }
+ }
+
+ /**
+ * Displays the number of features selected on the given [screenCoordinate]
+ */
+ private suspend fun getSelectedFeatureLayer(screenCoordinate: ScreenCoordinate) {
+ // clear the previous selection
+ featureLayer.clearSelection()
+ // set a tolerance for accuracy of returned selections from point tapped
+ val tolerance = 25.0
+ // create a IdentifyLayerResult using the screen coordinate
+ val identifyLayerResult =
+ mapView.identifyLayer(featureLayer, screenCoordinate, tolerance, false, -1)
+ // handle the result's onSuccess and onFailure
+ identifyLayerResult.apply {
+ onSuccess { identifyLayerResult ->
+ // get the elements in the selection that are features
+ val features = identifyLayerResult.geoElements.filterIsInstance()
+ // add the features to the current feature layer selection
+ featureLayer.selectFeatures(features)
+ Snackbar.make(mapView, "${features.size} features selected", Snackbar.LENGTH_SHORT).show()
+ }
+ onFailure {
+ val errorMessage = "Select feature failed: " + it.message
+ Log.e(localClassName, errorMessage)
+ Snackbar.make(mapView, errorMessage, Snackbar.LENGTH_SHORT).show()
+ }
+ }
+ }
+}
diff --git a/select-features-in-feature-layer/src/main/res/layout/activity_main.xml b/samples/select-features-in-feature-layer/src/main/res/layout/select_features_in_feature_layer_activity_main.xml
similarity index 100%
rename from select-features-in-feature-layer/src/main/res/layout/activity_main.xml
rename to samples/select-features-in-feature-layer/src/main/res/layout/select_features_in_feature_layer_activity_main.xml
diff --git a/samples/select-features-in-feature-layer/src/main/res/values/strings.xml b/samples/select-features-in-feature-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5f00d29c3
--- /dev/null
+++ b/samples/select-features-in-feature-layer/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Select features in feature layer
+
diff --git a/set-max-extent/README.md b/samples/set-max-extent/README.md
similarity index 100%
rename from set-max-extent/README.md
rename to samples/set-max-extent/README.md
diff --git a/set-max-extent/README.metadata.json b/samples/set-max-extent/README.metadata.json
similarity index 100%
rename from set-max-extent/README.metadata.json
rename to samples/set-max-extent/README.metadata.json
diff --git a/samples/set-max-extent/build.gradle.kts b/samples/set-max-extent/build.gradle.kts
new file mode 100644
index 000000000..d2eed6267
--- /dev/null
+++ b/samples/set-max-extent/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.setmaxextent"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/set-max-extent/set-max-extent.png b/samples/set-max-extent/set-max-extent.png
similarity index 100%
rename from set-max-extent/set-max-extent.png
rename to samples/set-max-extent/set-max-extent.png
diff --git a/samples/set-max-extent/src/main/AndroidManifest.xml b/samples/set-max-extent/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..7030f03c9
--- /dev/null
+++ b/samples/set-max-extent/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt b/samples/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt
new file mode 100644
index 000000000..8c3dfeec5
--- /dev/null
+++ b/samples/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt
@@ -0,0 +1,103 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.setmaxextent
+
+import android.os.Bundle
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.SwitchCompat
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Envelope
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleRenderer
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.setmaxextent.databinding.SetMaxExtentActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: SetMaxExtentActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.set_max_extent_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val extentSwitch: SwitchCompat by lazy {
+ activityMainBinding.extentSwitch
+ }
+
+ private val extentTextView: TextView by lazy {
+ activityMainBinding.extentText
+ }
+
+ private val extentEnvelope = Envelope(
+ Point(-12139393.2109, 5012444.0468),
+ Point(-11359277.5124, 4438148.7816)
+ )
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a map with the BasemapStyle streets focused on Colorado
+ val coloradoMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
+ // set the map's max extent to an envelope of Colorado's northwest and southeast corners
+ maxExtent = extentEnvelope
+ }
+
+ // create a graphics overlay of the map's max extent
+ val coloradoGraphicsOverlay = GraphicsOverlay().apply {
+ // set the graphic's geometry to the max extent of the map
+ graphics.add(Graphic(coloradoMap.maxExtent))
+ // create a simple red dashed line renderer
+ renderer = SimpleRenderer(SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 5f))
+ }
+
+ extentSwitch.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ // set max extent to the state of Colorado
+ coloradoMap.maxExtent = extentEnvelope
+ extentTextView.text = getString(R.string.extentEnable)
+ } else {
+ // disable the max extent of the map, map is free to pan around
+ coloradoMap.maxExtent = null
+ extentTextView.text = getString(R.string.extentDisable)
+ }
+ }
+
+ mapView.apply {
+ // set the map to the map view
+ map = coloradoMap
+ // set the graphics overlay to the map view
+ graphicsOverlays.add(coloradoGraphicsOverlay)
+ }
+ }
+}
diff --git a/set-max-extent/src/main/res/layout/activity_main.xml b/samples/set-max-extent/src/main/res/layout/set_max_extent_activity_main.xml
similarity index 100%
rename from set-max-extent/src/main/res/layout/activity_main.xml
rename to samples/set-max-extent/src/main/res/layout/set_max_extent_activity_main.xml
diff --git a/samples/set-max-extent/src/main/res/values/strings.xml b/samples/set-max-extent/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5c01e3d04
--- /dev/null
+++ b/samples/set-max-extent/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Set max extent
+ Maximum extent enabled
+ Maximum extent disabled
+
diff --git a/set-up-location-driven-geotriggers/README.md b/samples/set-up-location-driven-geotriggers/README.md
similarity index 100%
rename from set-up-location-driven-geotriggers/README.md
rename to samples/set-up-location-driven-geotriggers/README.md
diff --git a/set-up-location-driven-geotriggers/README.metadata.json b/samples/set-up-location-driven-geotriggers/README.metadata.json
similarity index 100%
rename from set-up-location-driven-geotriggers/README.metadata.json
rename to samples/set-up-location-driven-geotriggers/README.metadata.json
diff --git a/samples/set-up-location-driven-geotriggers/build.gradle.kts b/samples/set-up-location-driven-geotriggers/build.gradle.kts
new file mode 100644
index 000000000..e83a940c3
--- /dev/null
+++ b/samples/set-up-location-driven-geotriggers/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.setuplocationdrivengeotriggers"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/set-up-location-driven-geotriggers/set-up-location-driven-geotriggers.png b/samples/set-up-location-driven-geotriggers/set-up-location-driven-geotriggers.png
similarity index 100%
rename from set-up-location-driven-geotriggers/set-up-location-driven-geotriggers.png
rename to samples/set-up-location-driven-geotriggers/set-up-location-driven-geotriggers.png
diff --git a/samples/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml b/samples/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0430f2f13
--- /dev/null
+++ b/samples/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureListAdapter.kt b/samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureListAdapter.kt
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureListAdapter.kt
rename to samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureListAdapter.kt
diff --git a/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureViewFragment.kt b/samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureViewFragment.kt
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureViewFragment.kt
rename to samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/FeatureViewFragment.kt
diff --git a/samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt b/samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt
new file mode 100644
index 000000000..f60ac5211
--- /dev/null
+++ b/samples/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.setuplocationdrivengeotriggers
+
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.arcade.ArcadeExpression
+import com.arcgismaps.data.ArcGISFeature
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geotriggers.*
+import com.arcgismaps.location.LocationDataSourceStatus
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.location.SimulatedLocationDataSource
+import com.arcgismaps.location.SimulationParameters
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.mapping.PortalItem
+import com.esri.arcgismaps.sample.setuplocationdrivengeotriggers.databinding.SetUpLocationDrivenGeotriggersActivityMainBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.time.Instant
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: SetUpLocationDrivenGeotriggersActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.set_up_location_driven_geotriggers_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val playPauseFAB: FloatingActionButton by lazy {
+ activityMainBinding.playPauseFAB
+ }
+
+ private val sectionButton: MaterialButton by lazy {
+ activityMainBinding.sectionButton
+ }
+
+ // recycler list view to show the the points of interest
+ private val poiListView by lazy {
+ activityMainBinding.poiListView
+ }
+
+ // custom list adapter for the points of interest
+ private val poiListAdapter by lazy {
+ // create a new feature list adapter from the poiList
+ FeatureListAdapter(poiList) { feature ->
+ // set the item callback to show the feature view fragment
+ showFeatureViewFragment(feature)
+ }
+ }
+
+ private val simulatedLocationDataSource: SimulatedLocationDataSource by lazy {
+ // create SimulationParameters starting at the current time,
+ // a velocity of 10 m/s, and a horizontal and vertical accuracy of 0.0
+ val simulationParameters = SimulationParameters(
+ Instant.now(),
+ velocity = 3.0,
+ horizontalAccuracy = 0.0,
+ verticalAccuracy = 0.0
+ )
+ // create a SimulatedLocationDataSource using the polyline from
+ // ArcGIS Online GeoJSON to define the path. retrieved from
+ // https://arcgisruntime.maps.arcgis.com/home/item.html?id=2a346cf1668d4564b8413382ae98a956
+ SimulatedLocationDataSource(
+ Geometry.fromJsonOrNull(getString(R.string.polyline_json)) as Polyline,
+ simulationParameters
+ )
+ }
+
+ // feature list to store the points of interest of a geotrigger
+ private val poiList = mutableListOf()
+
+ // geotrigger names for the geotrigger monitors
+ private val sectionGeotrigger = "Section Geotrigger"
+ private val poiGeotrigger = "POI Geotrigger"
+
+ // make monitors properties to prevent garbage collection
+ private lateinit var sectionGeotriggerMonitor: GeotriggerMonitor
+ private lateinit var poiGeotriggerMonitor: GeotriggerMonitor
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ val portal = Portal("https://www.arcgis.com")
+ // this sample uses a web map with a predefined tile basemap, feature styles, and labels
+ val map = ArcGISMap(PortalItem(portal, "6ab0e91dc39e478cae4f408e1a36a308"))
+ // set the mapview's map
+ mapView.map = map
+ // set the map to display the location of the simulatedLocationDataSource
+ mapView.locationDisplay.apply {
+ dataSource = simulatedLocationDataSource
+ setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ initialZoomScale = 1000.0
+ }
+
+ // instantiate the service feature tables to later create GeotriggerMonitors for
+ val gardenSections =
+ ServiceFeatureTable(PortalItem(portal, "1ba816341ea04243832136379b8951d9"), 0)
+ val gardenPOIs =
+ ServiceFeatureTable(PortalItem(portal, "7c6280c290c34ae8aeb6b5c4ec841167"), 0)
+ // create geotriggers for each of the service feature tables
+ sectionGeotriggerMonitor =
+ createGeotriggerMonitor(gardenSections, 0.0, sectionGeotrigger)
+ poiGeotriggerMonitor =
+ createGeotriggerMonitor(gardenPOIs, 10.0, poiGeotrigger)
+
+ // play or pause the simulation data source when the FAB is clicked
+ playPauseFAB.setOnClickListener {
+ when (simulatedLocationDataSource.status.value) {
+ LocationDataSourceStatus.Started -> {
+ stopSimulatedDataSource(true)
+ }
+ LocationDataSourceStatus.Stopped -> {
+ startSimulatedDataSource(true)
+ }
+ else -> {
+ // show an error if the status is anything else
+ showError(
+ "Error modifying location data source state: " +
+ "${simulatedLocationDataSource.status.value}"
+ )
+ }
+ }
+ }
+
+ // set the recycler view layout to a vertical linear layout
+ poiListView.layoutManager = LinearLayoutManager(this)
+ // assign its adapter
+ poiListView.adapter = poiListAdapter
+
+ lifecycleScope.launch {
+ // wait for the map load
+ map.load().onFailure {
+ // if the map load fails, show the error and return
+ showError("Error loading map: ${it.message}")
+ return@launch
+ }
+ // start the section geotrigger monitor
+ sectionGeotriggerMonitor.start().onFailure {
+ // if the monitor start fails, show the error and return
+ showError("Section Geotrigger Monitor failed to start: ${it.message}")
+ return@launch
+ }
+ // start the points of interest geotrigger monitor
+ poiGeotriggerMonitor.start().onFailure {
+ // if the monitor start fails, show the error and return
+ showError("POI Geotrigger Monitor failed to start: ${it.message}")
+ return@launch
+ }
+ // finally, start the simulated location data source
+ simulatedLocationDataSource.start().onFailure {
+ // if it fails, show the error and return
+ showError("Simulated Location DataSource failed to start: ${it.message}")
+ }
+ }
+ }
+
+ /**
+ * Creates and returns a geotrigger monitor with the [geotriggerName] name,
+ * using the [serviceFeatureTable] and [bufferSize] to initialize
+ * FeatureFenceParameters for the geotrigger
+ */
+ private fun createGeotriggerMonitor(
+ serviceFeatureTable: ServiceFeatureTable,
+ bufferSize: Double,
+ geotriggerName: String
+ ): GeotriggerMonitor {
+ // create a LocationGeotriggerFeed that uses the SimulatedLocationDataSource
+ val geotriggerFeed = LocationGeotriggerFeed(simulatedLocationDataSource)
+ // initialize FeatureFenceParameters to display the section the user has entered
+ val featureFenceParameters = FeatureFenceParameters(serviceFeatureTable, bufferSize)
+ // create a fence geotrigger
+ val fenceGeotrigger = FenceGeotrigger(
+ geotriggerFeed,
+ // triggers on enter/exit
+ FenceRuleType.EnterOrExit,
+ featureFenceParameters,
+ // arcade expression to get the feature name
+ ArcadeExpression("\$fenceFeature.name"),
+ geotriggerName
+ )
+
+ // initialize a geotrigger monitor with the fence geotrigger
+ val geotriggerMonitor = GeotriggerMonitor(fenceGeotrigger)
+ lifecycleScope.launch {
+ // capture and handle geotrigger notification based on the FenceRuleType
+ // hence, triggers on fence enter/exit.
+ geotriggerMonitor.notifications.collect { geotriggerNotificationInfo ->
+ handleGeotriggerNotification(geotriggerNotificationInfo)
+ }
+ }
+ return geotriggerMonitor
+ }
+
+ /**
+ * Handles the [geotriggerNotificationInfo] based on its geotrigger type
+ * and FenceNotificationType
+ */
+ private fun handleGeotriggerNotification(geotriggerNotificationInfo: GeotriggerNotificationInfo) {
+ // cast it to FenceGeotriggerNotificationInfo which provides
+ // access to the feature that triggered the notification
+ val fenceGeotriggerNotificationInfo =
+ geotriggerNotificationInfo as FenceGeotriggerNotificationInfo
+ // name of the fence feature, returned from the set arcade expression
+ val fenceFeatureName = fenceGeotriggerNotificationInfo.message
+ // get the specific geotrigger name we set during initialization
+ val geotriggerType = fenceGeotriggerNotificationInfo.geotriggerMonitor.geotrigger.name
+ // check for the type of notification
+ when (fenceGeotriggerNotificationInfo.fenceNotificationType) {
+ FenceNotificationType.Entered -> {
+ // if the user location entered the geofence, add the feature information to the UI
+ addFeatureInformation(
+ fenceFeatureName,
+ geotriggerType,
+ fenceGeotriggerNotificationInfo.fenceGeoElement as ArcGISFeature
+ )
+ }
+ FenceNotificationType.Exited -> {
+ // if the user exits a given geofence, remove the feature's information from the UI
+ removeFeatureInformation(fenceFeatureName, geotriggerType)
+ }
+ }
+ }
+
+ /**
+ * Adds the [fenceFeature] ArcGISFeature with the [fenceFeatureName] and [geotriggerType] to the current UI state
+ * and refreshes the UI
+ */
+ private fun addFeatureInformation(
+ fenceFeatureName: String,
+ geotriggerType: String,
+ fenceFeature: ArcGISFeature
+ ) {
+ // recenter the mapview
+ mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+
+ when (geotriggerType) {
+ // if it's a section geo trigger type
+ sectionGeotrigger -> {
+ // update the section button's onClickListener
+ // to show a new FeatureViewFragment
+ sectionButton.setOnClickListener { showFeatureViewFragment(fenceFeature) }
+ // update the section button text to the feature name
+ sectionButton.text = fenceFeatureName
+ // enable the button
+ sectionButton.isEnabled = true
+ }
+ // or a point of interest geo trigger
+ poiGeotrigger -> {
+ // add it to the stored list
+ poiList.add(fenceFeature)
+ // notify the list adapter to refresh its recycler views
+ poiListAdapter.notifyItemInserted(poiList.lastIndex)
+ }
+ }
+ }
+
+ /**
+ * Removes the ArcGISFeature with the given [fenceFeatureName] and corresponding
+ * [geotriggerType] from the current UI state and refreshes the UI.
+ */
+ private fun removeFeatureInformation(fenceFeatureName: String, geotriggerType: String) {
+ // check the type of geotrigger
+ when (geotriggerType) {
+ sectionGeotrigger -> {
+ // if it's a section geo trigger,
+ // remove the section information and disable the button
+ sectionButton.text = "N/A"
+ sectionButton.isEnabled = false
+ }
+ poiGeotrigger -> {
+ // if it's a point of interest geotrigger
+ // find its index from the stored list
+ val index = poiList.indexOfFirst { feature ->
+ feature.attributes["name"] == fenceFeatureName
+ }
+ if (index >= 0) {
+ // if the feature exists remove it
+ poiList.removeAt(index)
+ // notify the list adapter to refresh its recycler views
+ poiListAdapter.notifyItemRemoved(index)
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates and shows a new FeatureViewFragment using the given [feature]
+ */
+ private fun showFeatureViewFragment(feature: ArcGISFeature) {
+ // stop the simulated data source
+ stopSimulatedDataSource(false)
+ // create a new FeatureViewFragment
+ val featureViewFragment = FeatureViewFragment(feature) {
+ // set its onDismissedListener to
+ // resume the simulated data source
+ startSimulatedDataSource(false)
+ }
+ // show the fragment
+ featureViewFragment.show(supportFragmentManager, "FeatureViewFragment")
+ }
+
+ /**
+ * Starts the simulated data source and shows a status toast if [showAlert] is true.
+ * The data source is resumed from its previous location if stopped before.
+ */
+ private fun startSimulatedDataSource(showAlert: Boolean) = lifecycleScope.launch {
+ // start the simulated location data source
+ simulatedLocationDataSource.start()
+ // recenter the map view
+ mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ // show a toast if true
+ if (showAlert) {
+ Toast.makeText(this@MainActivity, "Resumed Simulation", Toast.LENGTH_SHORT)
+ .show()
+ }
+ // update the action button's drawable to a pause icon
+ playPauseFAB.setImageResource(R.drawable.ic_baseline_pause_24)
+ }
+
+ /**
+ * Stops the simulated data source and shows a status toast if [showAlert] is true.
+ */
+ private fun stopSimulatedDataSource(showAlert: Boolean) = lifecycleScope.launch {
+ // stop the simulated location data source
+ simulatedLocationDataSource.stop()
+ // show a toast if true
+ if (showAlert) {
+ Toast.makeText(this@MainActivity, "Stopped Simulation", Toast.LENGTH_SHORT)
+ .show()
+ }
+ // update the action button's drawable to a play icon
+ playPauseFAB.setImageResource(R.drawable.ic_baseline_play_arrow_24)
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_pause_24.xml b/samples/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_pause_24.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_pause_24.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_pause_24.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_play_arrow_24.xml b/samples/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_play_arrow_24.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_play_arrow_24.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/drawable/ic_baseline_play_arrow_24.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_primary.xml b/samples/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_primary.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_primary.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_primary.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_secondary.xml b/samples/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_secondary.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_secondary.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/drawable/rounded_button_secondary.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/layout/feature_list_item.xml b/samples/set-up-location-driven-geotriggers/src/main/res/layout/feature_list_item.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/layout/feature_list_item.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/layout/feature_list_item.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/layout/fragment_feature_view.xml b/samples/set-up-location-driven-geotriggers/src/main/res/layout/fragment_feature_view.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/layout/fragment_feature_view.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/layout/fragment_feature_view.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/layout/activity_main.xml b/samples/set-up-location-driven-geotriggers/src/main/res/layout/set_up_location_driven_geotriggers_activity_main.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/layout/activity_main.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/layout/set_up_location_driven_geotriggers_activity_main.xml
diff --git a/set-up-location-driven-geotriggers/src/main/res/values/colors.xml b/samples/set-up-location-driven-geotriggers/src/main/res/values/colors.xml
similarity index 100%
rename from set-up-location-driven-geotriggers/src/main/res/values/colors.xml
rename to samples/set-up-location-driven-geotriggers/src/main/res/values/colors.xml
diff --git a/samples/set-up-location-driven-geotriggers/src/main/res/values/strings.xml b/samples/set-up-location-driven-geotriggers/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f1f697758
--- /dev/null
+++ b/samples/set-up-location-driven-geotriggers/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ Set up location driven geotriggers
+ "{\"paths\":[[[-119.709881177746,34.4570041646846],[-119.709875813328,34.4570152227745],[-119.709869107805,34.4570240692453],[-119.709859720074,34.4570351273326],[-119.709853014551,34.4570539260775],[-119.709847650133,34.4570760422426],[-119.709848991238,34.4570926293626],[-119.70985569676,34.4571103222869],[-119.709873131119,34.4571202745552],[-119.709889224373,34.4571302268223],[-119.709902635418,34.4571357558591],[-119.709910682045,34.4571600836165],[-119.709910682045,34.4571744591062],[-119.709902635418,34.4571833055602],[-119.709889224373,34.4571910462067],[-119.70988251885,34.4571965752394],[-119.70988251885,34.4572032100782],[-119.709889224373,34.4572175855605],[-119.709898612104,34.4572264320099],[-119.709912023149,34.4572341726524],[-119.709901294313,34.4572419132941],[-119.709895929895,34.4572507597409],[-119.709897271,34.4572596061868],[-119.709902635418,34.4572728758539],[-119.709902635418,34.4572828281028],[-119.70990934094,34.457294991961],[-119.709912023149,34.4573038384022],[-119.709886542164,34.4573115790375],[-119.709861061178,34.4573248486963],[-119.709843626819,34.4573414357669],[-119.709836921297,34.4573668692686],[-119.709843626819,34.4573934085666],[-119.709827533565,34.4574055724087],[-119.709791323744,34.4574188420525],[-119.709749749504,34.4574332174977],[-119.709709516369,34.4574431697275],[-119.709734997354,34.4574807670294],[-119.709748062646,34.4575248306656],[-119.709757450378,34.4575635337324],[-119.709770861423,34.457600025179],[-119.709785613572,34.4576387282109],[-119.70980573014,34.4576730080242],[-119.709815117871,34.4577117110223],[-119.709821823394,34.4577504140025],[-119.709821823394,34.4577869053674],[-119.709821823394,34.4578256083127],[-119.70981780008,34.4578609938471],[-119.709819141185,34.457906331541],[-119.70981460448,34.4579890675855],[-119.709818627793,34.4580675790658],[-119.70982667442,34.4581118108532],[-119.709832038838,34.4581471962662],[-119.709834721047,34.4581947453913],[-119.709836062152,34.4582323423548],[-119.709834721047,34.4582787856393],[-119.709805216748,34.4583429215611],[-119.709759619195,34.4584026342716],[-119.709700610597,34.4584612411497],[-119.709645400048,34.4585103926263],[-119.709566274882,34.4585457778704],[-119.709493855239,34.4585944325566],[-119.709458986522,34.458622077252],[-119.709424117805,34.4586198656767],[-119.709386566878,34.4586110193749],[-119.70935438037,34.4586110193749],[-119.709339628221,34.4586231830396],[-119.709324876071,34.4586585682359],[-119.709306100608,34.4586862129101],[-119.709269890786,34.4587171749343],[-119.709244409801,34.4587238096523],[-119.709229657651,34.4587293385835],[-119.709212223293,34.4587459253751],[-119.70919076562,34.4587945799446],[-119.709174672366,34.4588503400161],[-119.709157238008,34.4589288506865],[-119.709153214694,34.4589951976744],[-119.709155896903,34.4590449578807],[-119.70916394353,34.4590958238387],[-119.709186742307,34.4591323146156],[-119.709218928815,34.4591621706939],[-119.709237704278,34.4591831805204],[-119.709241727592,34.4592252001575],[-119.709238133851,34.459258658624],[-119.709219358388,34.4592796684262],[-119.709207288447,34.4592962551085],[-119.709208629552,34.4593084186733],[-119.709271661463,34.4593791884701],[-119.709310553494,34.4594267368937],[-119.709330670062,34.4594510639836],[-119.709353468838,34.4595008239182],[-119.70936285657,34.4595362087426],[-119.709423221989,34.4595943612845],[-119.709455408497,34.4596297460692],[-119.709487595005,34.459665130839],[-119.709507711573,34.4596817174446],[-119.709523804827,34.4596861405389],[-119.709557332439,34.4596894578594],[-119.709586836739,34.4596894578594],[-119.709593542261,34.4596772943501],[-119.709590860052,34.4596496500041],[-119.709572084589,34.4595777746615],[-119.709566720171,34.4595313320996],[-119.709578790112,34.4595136396883],[-119.709590860052,34.4594992646013],[-119.709627069874,34.4594882068404],[-119.709675349636,34.4595567649343],[-119.709735699339,34.4596197941001],[-119.709775932474,34.4596795058974],[-119.709802754564,34.4597126790997],[-119.709832258863,34.4597359003334],[-119.70986712758,34.4597171021923],[-119.70986980979,34.4596839289918],[-119.709865786476,34.4596308518435],[-119.709876515312,34.4595788804365],[-119.70988187973,34.4595346494263],[-119.709879197521,34.4594926299449],[-119.709852375431,34.4594539277723],[-119.709806777878,34.4593953215911],[-119.709767885847,34.4593212344729],[-119.709720947189,34.4592195028005],[-119.709708720088,34.4591478732967],[-119.709710061193,34.4591058536206],[-119.709707378984,34.459079314867],[-119.709652393699,34.4590262373344],[-119.709617524982,34.4589499383221],[-119.709626771268,34.4588695962162],[-119.709683097658,34.4588032491285],[-119.709730036316,34.4587391135603],[-119.709759540615,34.4586993052518],[-119.7097850216,34.4586650258598],[-119.710059948024,34.4587744987075],[-119.710104204473,34.4587932970608],[-119.710128443889,34.4587649224307],[-119.71019415801,34.4587096331253],[-119.710273283176,34.4586731421637],[-119.71031619852,34.4586532379961],[-119.710367160491,34.4586023717685],[-119.710392641477,34.4585747270665],[-119.710432874612,34.4585083797445],[-119.710471766643,34.4584453497398],[-119.710505294256,34.4584165991955],[-119.710575487456,34.4583689119728],[-119.710705574593,34.4583136224052],[-119.710780676446,34.4582627559707],[-119.710839685044,34.4582030431601],[-119.710895995717,34.4581546042213],[-119.710948298793,34.458095997128],[-119.710988531928,34.4580263320391],[-119.711015354018,34.4579511379096],[-119.711011330705,34.4579102234284],[-119.710985849719,34.4578847900921],[-119.710946957688,34.4578715205223],[-119.710779319625,34.4578847900921],[-119.71073908649,34.4578847900921],[-119.7107122644,34.4578604625458],[-119.71069751225,34.4578295002039],[-119.710674713473,34.4578095958352],[-119.710642526965,34.4577996436491],[-119.710614363771,34.457830606002],[-119.71057547174,34.4578571451526],[-119.710539261918,34.4578737321174],[-119.71049500547,34.4578858958895],[-119.710452090125,34.4578836842947],[-119.710413198095,34.4578748379149],[-119.710375647168,34.4578527219614],[-119.710336755138,34.4578350291944],[-119.710299204211,34.4578107016336],[-119.71027506433,34.4577885856631],[-119.710269605616,34.4577886418169],[-119.710240101317,34.4577510446536],[-119.710198527077,34.4576758502763],[-119.710154270628,34.4575984442288],[-119.710112696389,34.4575265671206],[-119.710044300059,34.4574381029023],[-119.709978842634,34.4573543166616],[-119.709966772693,34.4573410470074],[-119.709972137111,34.4573200367174],[-119.70997481932,34.4573023438375],[-119.709973478216,34.4572846509538],[-119.709970796007,34.457259217427],[-119.709931903976,34.4572348896984],[-119.709914469617,34.4572337838924],[-119.709893011945,34.4572171968005],[-119.709884965318,34.4571972922858],[-119.709902232039,34.4571821133624],[-119.70991161977,34.457167737874],[-119.709907596457,34.4571389868898],[-119.709919666397,34.4571235055865],[-119.709922348606,34.4571047068572],[-119.709918325293,34.4570836965077],[-119.709919666397,34.4570648977695],[-119.70992637192,34.4570516280694],[-119.709933077442,34.4570339351326],[-119.709935759651,34.4570151363832],[-119.709927713024,34.4570062899114],[-119.70991161977,34.4570018666751],[-119.709883456576,34.4570040782933]]],\"spatialReference\":{\"wkid\":4326,\"latestWkid\":4326}}"
+ Current garden section:
+ Points of interest:
+ No features nearby
+ N/A
+ Play pause button
+ Garden section description
+
diff --git a/set-viewpoint-rotation/README.md b/samples/set-viewpoint-rotation/README.md
similarity index 100%
rename from set-viewpoint-rotation/README.md
rename to samples/set-viewpoint-rotation/README.md
diff --git a/set-viewpoint-rotation/README.metadata.json b/samples/set-viewpoint-rotation/README.metadata.json
similarity index 100%
rename from set-viewpoint-rotation/README.metadata.json
rename to samples/set-viewpoint-rotation/README.metadata.json
diff --git a/samples/set-viewpoint-rotation/build.gradle.kts b/samples/set-viewpoint-rotation/build.gradle.kts
new file mode 100644
index 000000000..93b5ebafd
--- /dev/null
+++ b/samples/set-viewpoint-rotation/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.setviewpointrotation"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/set-viewpoint-rotation/set-viewpoint-rotation.png b/samples/set-viewpoint-rotation/set-viewpoint-rotation.png
similarity index 100%
rename from set-viewpoint-rotation/set-viewpoint-rotation.png
rename to samples/set-viewpoint-rotation/set-viewpoint-rotation.png
diff --git a/samples/set-viewpoint-rotation/src/main/AndroidManifest.xml b/samples/set-viewpoint-rotation/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b9df0d639
--- /dev/null
+++ b/samples/set-viewpoint-rotation/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt b/samples/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt
new file mode 100644
index 000000000..54faa982a
--- /dev/null
+++ b/samples/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt
@@ -0,0 +1,63 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.setviewpointrotation
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.esri.arcgismaps.sample.setviewpointrotation.databinding.SetViewpointRotationActivityMainBinding
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: SetViewpointRotationActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.set_viewpoint_rotation_activity_main)
+ val mapView = activityMainBinding.mapView
+ val rotationSlider = activityMainBinding.rotationSlider
+ val rotationValueText = activityMainBinding.rotationValueText
+ lifecycle.addObserver(mapView)
+
+ // create a map with a topographic basemap and initial position
+ val map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ // set the map to be displayed in this view
+ mapView.map = map
+ mapView.setViewpoint(Viewpoint(34.056295, -117.195800, 10000.0))
+
+ rotationSlider.addOnChangeListener { _, angle, _ ->
+ // set the text to the value
+ rotationValueText.text = angle.toInt().toString()
+ // rotate map view to the progress angle
+ lifecycleScope.launch {
+ mapView.setViewpointRotation(angle.toDouble())
+ }
+ }
+ }
+}
diff --git a/set-viewpoint-rotation/src/main/res/layout/activity_main.xml b/samples/set-viewpoint-rotation/src/main/res/layout/set_viewpoint_rotation_activity_main.xml
similarity index 100%
rename from set-viewpoint-rotation/src/main/res/layout/activity_main.xml
rename to samples/set-viewpoint-rotation/src/main/res/layout/set_viewpoint_rotation_activity_main.xml
diff --git a/samples/set-viewpoint-rotation/src/main/res/values/strings.xml b/samples/set-viewpoint-rotation/src/main/res/values/strings.xml
new file mode 100644
index 000000000..c3e4da92d
--- /dev/null
+++ b/samples/set-viewpoint-rotation/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Set viewpoint rotation
+ 0
+
diff --git a/show-callout/README.md b/samples/show-callout/README.md
similarity index 100%
rename from show-callout/README.md
rename to samples/show-callout/README.md
diff --git a/show-callout/README.metadata.json b/samples/show-callout/README.metadata.json
similarity index 100%
rename from show-callout/README.metadata.json
rename to samples/show-callout/README.metadata.json
diff --git a/samples/show-callout/build.gradle.kts b/samples/show-callout/build.gradle.kts
new file mode 100644
index 000000000..bb5e20c2d
--- /dev/null
+++ b/samples/show-callout/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showcallout"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-callout/show-callout.png b/samples/show-callout/show-callout.png
similarity index 100%
rename from show-callout/show-callout.png
rename to samples/show-callout/show-callout.png
diff --git a/samples/show-callout/src/main/AndroidManifest.xml b/samples/show-callout/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/show-callout/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt b/samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt
new file mode 100644
index 000000000..43d04f9eb
--- /dev/null
+++ b/samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showcallout
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showcallout.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ ShowCalloutApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ShowCalloutApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.show_callout_app_name)
+ )
+ }
+ }
+}
diff --git a/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/components/MapViewModel.kt b/samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/components/MapViewModel.kt
similarity index 100%
rename from show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/components/MapViewModel.kt
rename to samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/components/MapViewModel.kt
diff --git a/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/screens/MainScreen.kt b/samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/screens/MainScreen.kt
similarity index 100%
rename from show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/screens/MainScreen.kt
rename to samples/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/screens/MainScreen.kt
diff --git a/samples/show-callout/src/main/res/values/strings.xml b/samples/show-callout/src/main/res/values/strings.xml
new file mode 100644
index 000000000..3fb6017ef
--- /dev/null
+++ b/samples/show-callout/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Show callout
+ "Lat: %.4f, Lon: %.4f"
+
diff --git a/show-coordinates-in-multiple-formats/README.md b/samples/show-coordinates-in-multiple-formats/README.md
similarity index 100%
rename from show-coordinates-in-multiple-formats/README.md
rename to samples/show-coordinates-in-multiple-formats/README.md
diff --git a/show-coordinates-in-multiple-formats/README.metadata.json b/samples/show-coordinates-in-multiple-formats/README.metadata.json
similarity index 100%
rename from show-coordinates-in-multiple-formats/README.metadata.json
rename to samples/show-coordinates-in-multiple-formats/README.metadata.json
diff --git a/samples/show-coordinates-in-multiple-formats/build.gradle.kts b/samples/show-coordinates-in-multiple-formats/build.gradle.kts
new file mode 100644
index 000000000..1db4b5ebe
--- /dev/null
+++ b/samples/show-coordinates-in-multiple-formats/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showcoordinatesinmultipleformats"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-coordinates-in-multiple-formats/show-coordinates-in-multiple-formats.png b/samples/show-coordinates-in-multiple-formats/show-coordinates-in-multiple-formats.png
similarity index 100%
rename from show-coordinates-in-multiple-formats/show-coordinates-in-multiple-formats.png
rename to samples/show-coordinates-in-multiple-formats/show-coordinates-in-multiple-formats.png
diff --git a/samples/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml b/samples/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt b/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt
new file mode 100644
index 000000000..15d865275
--- /dev/null
+++ b/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt
@@ -0,0 +1,54 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showcoordinatesinmultipleformats
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.core.view.WindowCompat
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // remove focus from text fields when keyboard closes
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ // set compose content
+ setContent {
+ SampleAppTheme {
+ ShowCoordinatesInMultipleFormatsApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ShowCoordinatesInMultipleFormatsApp() {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ MainScreen(sampleName = getString(R.string.show_coordinates_in_multiple_formats_app_name))
+ }
+ }
+}
diff --git a/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/MapViewModel.kt b/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/MapViewModel.kt
similarity index 100%
rename from show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/MapViewModel.kt
rename to samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/components/MapViewModel.kt
diff --git a/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/CoordinatesLayout.kt b/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/CoordinatesLayout.kt
similarity index 100%
rename from show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/CoordinatesLayout.kt
rename to samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/CoordinatesLayout.kt
diff --git a/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/MainScreen.kt b/samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/MainScreen.kt
similarity index 100%
rename from show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/MainScreen.kt
rename to samples/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/screens/MainScreen.kt
diff --git a/samples/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml b/samples/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml
new file mode 100644
index 000000000..8783233a0
--- /dev/null
+++ b/samples/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Show coordinates in multiple formats
+ https://wi.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer
+
diff --git a/show-device-location-using-indoor-positioning/README.md b/samples/show-device-location-using-indoor-positioning/README.md
similarity index 100%
rename from show-device-location-using-indoor-positioning/README.md
rename to samples/show-device-location-using-indoor-positioning/README.md
diff --git a/show-device-location-using-indoor-positioning/README.metadata.json b/samples/show-device-location-using-indoor-positioning/README.metadata.json
similarity index 100%
rename from show-device-location-using-indoor-positioning/README.metadata.json
rename to samples/show-device-location-using-indoor-positioning/README.metadata.json
diff --git a/samples/show-device-location-using-indoor-positioning/build.gradle.kts b/samples/show-device-location-using-indoor-positioning/build.gradle.kts
new file mode 100644
index 000000000..50c3ffba1
--- /dev/null
+++ b/samples/show-device-location-using-indoor-positioning/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-device-location-using-indoor-positioning/show-device-location-using-indoor-positioning.png b/samples/show-device-location-using-indoor-positioning/show-device-location-using-indoor-positioning.png
similarity index 100%
rename from show-device-location-using-indoor-positioning/show-device-location-using-indoor-positioning.png
rename to samples/show-device-location-using-indoor-positioning/show-device-location-using-indoor-positioning.png
diff --git a/samples/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml b/samples/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..3ce3f08b3
--- /dev/null
+++ b/samples/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt b/samples/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt
new file mode 100644
index 000000000..31d2e9f2a
--- /dev/null
+++ b/samples/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt
@@ -0,0 +1,359 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Guid
+import com.arcgismaps.data.ArcGISFeatureTable
+import com.arcgismaps.data.FeatureTable
+import com.arcgismaps.data.Field
+import com.arcgismaps.data.OrderBy
+import com.arcgismaps.data.QueryParameters
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.data.SortOrder
+import com.arcgismaps.location.IndoorsLocationDataSource
+import com.arcgismaps.location.Location
+import com.arcgismaps.location.LocationDataSourceStatus
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.portal.Portal
+import com.arcgismaps.mapping.PortalItem
+import com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning.databinding.ShowDeviceLocationUsingIndoorPositioningActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.text.DecimalFormat
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowDeviceLocationUsingIndoorPositioningActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_device_location_using_indoor_positioning_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val progressBar by lazy {
+ activityMainBinding.progressBar
+ }
+
+ private val textView by lazy {
+ activityMainBinding.textView
+ }
+
+ // keep track of the current floor in an indoor map, null if using GPS
+ private var currentFloor: Int? = null
+
+ // provides an indoor or outdoor position based on device sensor data (radio, GPS, motion sensors).
+ private var indoorsLocationDataSource: IndoorsLocationDataSource? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycle.addObserver(mapView)
+ // some parts of the API require an Android Context to properly interact with Android system
+ // features, such as LocationProvider and application resources
+ ArcGISEnvironment.applicationContext = applicationContext
+ // check for location permissions
+ // if permissions is allowed, the device's current location is shown
+ checkPermissions()
+ }
+
+ /**
+ * Check for location permissions, if not received then request for one
+ */
+ private fun checkPermissions() {
+ val requestCode = 1
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
+ ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
+ (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
+ ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)) {
+ val requestPermissions = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
+ // Android 12 required permission
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ requestPermissions.add(Manifest.permission.BLUETOOTH_SCAN)
+ }
+ ActivityCompat.requestPermissions(this, requestPermissions.toTypedArray(), requestCode)
+ } else {
+ // permission already given, so no need to request
+ setUpMap()
+ }
+ }
+
+ /**
+ * Set up the [mapView] to load a floor-aware web map.
+ */
+ private fun setUpMap() {
+ // load the portal and create a map from the portal item
+ val portalItem = PortalItem(
+ Portal("https://www.arcgis.com/"),
+ "8fa941613b4b4b2b8a34ad4cdc3e4bba"
+ )
+ val map = ArcGISMap(portalItem)
+ mapView.map = map
+ lifecycleScope.launch {
+ map.load().onSuccess {
+ val featureTables = map.tables
+ // check if the portalItem contains featureTables
+ if (featureTables.isNotEmpty()) {
+ setUpLoadTables(featureTables)
+ } else {
+ showError("Map does not contain feature tables")
+ }
+ }.onFailure {
+ // if map load failed, show the error
+ showError("Error Loading Map: {it.message}")
+ }
+ }
+ }
+
+ /**
+ * Load each feature table and setup the IndoorsLocationDataSource for each loaded table
+ */
+ private suspend fun setUpLoadTables(featureTables: MutableList) {
+ featureTables.forEach { featureTable ->
+ // load each FeatureTable
+ featureTable.load().onFailure {
+ showError("Error loading FeatureTable: ${it.message}")
+ }
+ }
+ // retrieve the loaded feature tables
+ featureTables.forEach{ featureTable ->
+ // ips_positioning table needs to be present
+ if (featureTable.tableName == "ips_positioning") {
+ return setupIndoorsLocationDataSource(featureTable)
+ }
+ }
+ // ips_positioning table not found
+ showError("Positioning Table not found in FeatureTables")
+ }
+
+ /**
+ * Sets up the [indoorsLocationDataSource] using the IPS_Positioning [featureTable]
+ */
+ private fun setupIndoorsLocationDataSource(featureTable: FeatureTable) {
+ // cast the featureTable to a ServiceFeatureTable to get the globalIdField which identifies a row in the positioning table
+ // and used as a parameter to setup IndoorsLocationDataSource
+ val positioningFeatureTable = featureTable as ServiceFeatureTable
+ // when multiple entries are available, IndoorsLocationDataSource constructor function
+ // looks up the entry with the most recent date and takes this positioning data
+ // set up queryParameters to grab one result.
+ val dateCreatedFieldName = getDateCreatedFieldName(positioningFeatureTable.fields)
+ ?: return showError("The service table does not contain \"DateCreated\" fields.")
+ val queryParameters = QueryParameters().apply {
+ // set a limit of 1 on the number of returned features per request
+ maxFeatures = 1
+ // 1=1 is a true where clause which will result in all matching records being returned
+ whereClause = "1 = 1"
+ // find and sort out the orderByFields by most recent first
+ orderByFields.add(
+ OrderBy(
+ dateCreatedFieldName,
+ sortOrder = SortOrder.Descending
+ )
+ )
+ }
+ lifecycleScope.launch {
+ positioningFeatureTable.queryFeatures(queryParameters)
+ .onSuccess { queryResults ->
+ val featureResult = queryResults.first()
+ // perform search query using the queryParameters
+ // check if serviceFeatureTable contains positioning data
+ // The ID that identifies a row in the positioning table.
+ val globalID =
+ featureResult.attributes[positioningFeatureTable.globalIdField].toString()
+ val positioningId = Guid(globalID)
+ // Setting up IndoorsLocationDataSource with positioning, pathways tables and positioning ID.
+ // positioningTable - the "ips_positioning" feature table from an IPS-enabled map.
+ // pathwaysTable - An ArcGISFeatureTable that contains pathways as per the ArcGIS Indoors Information Model.
+ // Setting this property enables path snapping of locations provided by the IndoorsLocationDataSource.
+ // levelsTable - An ArcGISFeatureTable that contains floor levels in accordance with the ArcGIS Indoors Information Model.
+ // Providing this table enables the retrieval of a location's floor level ID.
+ // positioningID - an ID which identifies a specific row in the positioningTable that should be used for setting up IPS.
+ indoorsLocationDataSource = IndoorsLocationDataSource(
+ positioningFeatureTable,
+ getFeatureTable("Pathways"),
+ getFeatureTable("Levels"),
+ positioningId
+ )
+ // start the location display (blue dot)
+ startLocationDisplay()
+ }.onFailure {
+ showError("The positioning table contain no data")
+ }
+ }
+ }
+
+ /**
+ * Find the exact formatting of the name "DateCreated" in the list of ServiceFeatureTable [fields].
+ */
+ private fun getDateCreatedFieldName(fields: List): String? {
+ val field = fields.find {
+ it.name.equals(
+ "DateCreated",
+ ignoreCase = true
+ ) || it.name.equals("Date_Created", ignoreCase = true)
+ }
+ return field?.name
+ }
+
+ /**
+ * Retrieves the "Pathways" or the "levels" table depending on the string passed.
+ */
+ private fun getFeatureTable(name: String): ArcGISFeatureTable? {
+ return mapView.map?.operationalLayers?.firstOrNull { operationalLayer ->
+ operationalLayer.name == name
+ }?.let { layer ->
+ if (layer is FeatureLayer) {
+ layer.featureTable as ArcGISFeatureTable
+ } else {
+ null
+ }
+ } ?: run {
+ showError("$name table not found")
+ null
+ }
+ }
+
+ /**
+ * Sets up the location listeners, the navigation mode, displays the devices location as a blue dot,
+ * collect data source location changes and handles its status changes
+ */
+ private fun startLocationDisplay() {
+ val locationDisplay = mapView.locationDisplay.apply {
+ setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
+ dataSource = indoorsLocationDataSource
+ ?: return showError("Error setting the IndoorsLocationDataSource value.")
+ }
+
+ // coroutine scope to start the location display, which will in-turn start IndoorsLocationDataSource to start receiving IPS updates.
+ lifecycleScope.launch {
+ locationDisplay.dataSource.start()
+ }
+
+ // coroutine scope to collect data source location changes like currentFloor, positionSource, transmitterCount, networkCount and horizontalAccuracy
+ lifecycleScope.launch {
+ locationDisplay.dataSource.locationChanged.collect { location ->
+ // get the location properties of the LocationDataSource
+ val locationProperties = location.additionalSourceProperties
+ // retrieve information about the location of the device
+ val floor = locationProperties["floor"]?.toString() ?: ""
+ val positionSource = locationProperties["positionSource"]?.toString() ?: ""
+ val transmitterCount = locationProperties["transmitterCount"]?.toString() ?: ""
+ val satelliteCount = locationProperties["satelliteCount"]?.toString() ?: ""
+
+ // check if current floor hasn't been set or if the floor has changed
+ if (floor.isNotEmpty()) {
+ val newFloor = floor.toInt()
+ if (currentFloor == null || currentFloor != newFloor) {
+ currentFloor = newFloor
+ // update layer's definition express with the current floor
+ mapView.map?.operationalLayers?.forEach { layer ->
+ val name = layer.name
+ if (layer is FeatureLayer && name in listOf(
+ "Details",
+ "Units",
+ "Levels"
+ )
+ ) {
+ layer.definitionExpression = "VERTICAL_ORDER = $currentFloor"
+ }
+ }
+ }
+ } else {
+ showError("Floors is empty.")
+ }
+ // set up the message with floor properties to be displayed to the textView
+ val sb = StringBuilder()
+ sb.append("Floor: $floor, ")
+ sb.append("Position-source: $positionSource, ")
+ val accuracy = DecimalFormat(".##").format(
+ location.horizontalAccuracy
+ )
+ sb.append("Horizontal-accuracy: ${accuracy}m, ")
+ sb.append(when (positionSource) {
+ Location.SourceProperties.Values.POSITION_SOURCE_GNSS -> "Satellite-count: $satelliteCount"
+ "BLE" -> "Transmitter-count: $transmitterCount"
+ else -> ""
+ }
+ )
+ textView.text = sb.toString()
+ }
+ }
+
+ lifecycleScope.launch {
+ // Handle status changes of IndoorsLocationDataSource
+ locationDisplay.dataSource.status.collect { status ->
+ when (status) {
+ LocationDataSourceStatus.Starting -> progressBar.visibility = View.VISIBLE
+ LocationDataSourceStatus.Started -> progressBar.visibility = View.GONE
+ LocationDataSourceStatus.FailedToStart -> {
+ progressBar.visibility = View.GONE
+ showError("Failed to start IndoorsLocationDataSource")
+ }
+ LocationDataSourceStatus.Stopped -> {
+ progressBar.visibility = View.GONE
+ showError("IndoorsLocationDataSource stopped due to an internal error")
+ }
+ else -> {}
+ }
+ }
+ }
+ }
+
+ /**
+ * Result of the user from location permissions request
+ */
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // if location permissions accepted, start setting up IndoorsLocationDataSource
+ setUpMap()
+ } else {
+ val message = "Location permission is not granted"
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
+ Log.e(localClassName, message)
+ progressBar.visibility = View.GONE
+ }
+ }
+
+ /**
+ * Displays an error onscreen
+ */
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
diff --git a/show-device-location-using-indoor-positioning/src/main/res/layout/activity_main.xml b/samples/show-device-location-using-indoor-positioning/src/main/res/layout/show_device_location_using_indoor_positioning_activity_main.xml
similarity index 100%
rename from show-device-location-using-indoor-positioning/src/main/res/layout/activity_main.xml
rename to samples/show-device-location-using-indoor-positioning/src/main/res/layout/show_device_location_using_indoor_positioning_activity_main.xml
diff --git a/samples/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml b/samples/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml
new file mode 100644
index 000000000..9ac752c7b
--- /dev/null
+++ b/samples/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Show device location using indoor positioning
+
diff --git a/show-device-location/README.md b/samples/show-device-location/README.md
similarity index 100%
rename from show-device-location/README.md
rename to samples/show-device-location/README.md
diff --git a/show-device-location/README.metadata.json b/samples/show-device-location/README.metadata.json
similarity index 100%
rename from show-device-location/README.metadata.json
rename to samples/show-device-location/README.metadata.json
diff --git a/samples/show-device-location/build.gradle.kts b/samples/show-device-location/build.gradle.kts
new file mode 100644
index 000000000..927a9e301
--- /dev/null
+++ b/samples/show-device-location/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showdevicelocation"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-device-location/show-device-location.png b/samples/show-device-location/show-device-location.png
similarity index 100%
rename from show-device-location/show-device-location.png
rename to samples/show-device-location/show-device-location.png
diff --git a/samples/show-device-location/src/main/AndroidManifest.xml b/samples/show-device-location/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e2213e572
--- /dev/null
+++ b/samples/show-device-location/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt b/samples/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt
new file mode 100644
index 000000000..aff23a669
--- /dev/null
+++ b/samples/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt
@@ -0,0 +1,183 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showdevicelocation
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.view.View
+import android.widget.AdapterView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.esri.arcgismaps.sample.showdevicelocation.databinding.ShowDeviceLocationActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowDeviceLocationActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_device_location_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // some parts of the API require an Android Context to properly interact with Android system
+ // features, such as LocationProvider and application resources
+ ArcGISEnvironment.applicationContext = applicationContext
+
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ mapView.map = map
+
+ val locationDisplay = mapView.locationDisplay
+
+ lifecycleScope.launch {
+ // listen to changes in the status of the location data source
+ locationDisplay.dataSource.start()
+ .onSuccess {
+ // permission already granted, so start the location display
+ activityMainBinding.spinner.setSelection(1, true)
+ }.onFailure {
+ // check permissions to see if failure may be due to lack of permissions
+ requestPermissions()
+ }
+ }
+
+ // populate the list for the location display options for the spinner's adapter
+ val panModeSpinnerElements = arrayListOf(
+ ItemData("Stop", R.drawable.locationdisplaydisabled),
+ ItemData("On", R.drawable.locationdisplayon),
+ ItemData("Re-center", R.drawable.locationdisplayrecenter),
+ ItemData("Navigation", R.drawable.locationdisplaynavigation),
+ ItemData("Compass", R.drawable.locationdisplayheading)
+ )
+
+ activityMainBinding.spinner.apply {
+ adapter = SpinnerAdapter(this@MainActivity, R.id.locationTextView, panModeSpinnerElements)
+ onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(
+ parent: AdapterView<*>?,
+ view: View,
+ position: Int,
+ id: Long
+ ) {
+ when (panModeSpinnerElements[position].text) {
+ "Stop" -> // stop location display
+ lifecycleScope.launch {
+ locationDisplay.dataSource.stop()
+ }
+ "On" -> // start location display
+ lifecycleScope.launch {
+ locationDisplay.dataSource.start()
+ }
+ "Re-center" -> {
+ // re-center MapView on location
+ locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ }
+ "Navigation" -> {
+ // start navigation mode
+ locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
+ }
+ "Compass" -> {
+ // start compass navigation mode
+ locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.CompassNavigation)
+ }
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {}
+ }
+ }
+ }
+
+ /**
+ * Request fine and coarse location permissions for API level 23+.
+ */
+ private fun requestPermissions() {
+ // coarse location permission
+ val permissionCheckCoarseLocation =
+ ContextCompat.checkSelfPermission(this@MainActivity, ACCESS_COARSE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED
+ // fine location permission
+ val permissionCheckFineLocation =
+ ContextCompat.checkSelfPermission(this@MainActivity, ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED
+
+ // if permissions are not already granted, request permission from the user
+ if (!(permissionCheckCoarseLocation && permissionCheckFineLocation)) {
+ ActivityCompat.requestPermissions(
+ this@MainActivity,
+ arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION),
+ 2
+ )
+ } else {
+ // permission already granted, so start the location display
+ lifecycleScope.launch {
+ mapView.locationDisplay.dataSource.start().onSuccess {
+ activityMainBinding.spinner.setSelection(1, true)
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle the permissions request response.
+ */
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ lifecycleScope.launch {
+ mapView.locationDisplay.dataSource.start().onSuccess {
+ activityMainBinding.spinner.setSelection(1, true)
+ }
+ }
+ } else {
+ Snackbar.make(
+ mapView,
+ "Location permissions required to run this sample!",
+ Snackbar.LENGTH_LONG
+ ).show()
+ // update UI to reflect that the location display did not actually start
+ activityMainBinding.spinner.setSelection(0, true)
+ }
+ }
+}
diff --git a/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/SpinnerAdapter.kt b/samples/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/SpinnerAdapter.kt
similarity index 100%
rename from show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/SpinnerAdapter.kt
rename to samples/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/SpinnerAdapter.kt
diff --git a/show-device-location/src/main/res/drawable-hdpi/locationdisplaydisabled.9.png b/samples/show-device-location/src/main/res/drawable-hdpi/locationdisplaydisabled.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-hdpi/locationdisplaydisabled.9.png
rename to samples/show-device-location/src/main/res/drawable-hdpi/locationdisplaydisabled.9.png
diff --git a/show-device-location/src/main/res/drawable-hdpi/locationdisplayheading.9.png b/samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayheading.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-hdpi/locationdisplayheading.9.png
rename to samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayheading.9.png
diff --git a/show-device-location/src/main/res/drawable-hdpi/locationdisplaynavigation.9.png b/samples/show-device-location/src/main/res/drawable-hdpi/locationdisplaynavigation.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-hdpi/locationdisplaynavigation.9.png
rename to samples/show-device-location/src/main/res/drawable-hdpi/locationdisplaynavigation.9.png
diff --git a/show-device-location/src/main/res/drawable-hdpi/locationdisplayon.9.png b/samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayon.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-hdpi/locationdisplayon.9.png
rename to samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayon.9.png
diff --git a/show-device-location/src/main/res/drawable-hdpi/locationdisplayrecenter.9.png b/samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayrecenter.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-hdpi/locationdisplayrecenter.9.png
rename to samples/show-device-location/src/main/res/drawable-hdpi/locationdisplayrecenter.9.png
diff --git a/show-device-location/src/main/res/drawable-mdpi/locationdisplaydisabled.9.png b/samples/show-device-location/src/main/res/drawable-mdpi/locationdisplaydisabled.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-mdpi/locationdisplaydisabled.9.png
rename to samples/show-device-location/src/main/res/drawable-mdpi/locationdisplaydisabled.9.png
diff --git a/show-device-location/src/main/res/drawable-mdpi/locationdisplayheading.9.png b/samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayheading.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-mdpi/locationdisplayheading.9.png
rename to samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayheading.9.png
diff --git a/show-device-location/src/main/res/drawable-mdpi/locationdisplaynavigation.9.png b/samples/show-device-location/src/main/res/drawable-mdpi/locationdisplaynavigation.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-mdpi/locationdisplaynavigation.9.png
rename to samples/show-device-location/src/main/res/drawable-mdpi/locationdisplaynavigation.9.png
diff --git a/show-device-location/src/main/res/drawable-mdpi/locationdisplayon.9.png b/samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayon.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-mdpi/locationdisplayon.9.png
rename to samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayon.9.png
diff --git a/show-device-location/src/main/res/drawable-mdpi/locationdisplayrecenter.9.png b/samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayrecenter.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-mdpi/locationdisplayrecenter.9.png
rename to samples/show-device-location/src/main/res/drawable-mdpi/locationdisplayrecenter.9.png
diff --git a/show-device-location/src/main/res/drawable-xhdpi/locationdisplaydisabled.9.png b/samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplaydisabled.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xhdpi/locationdisplaydisabled.9.png
rename to samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplaydisabled.9.png
diff --git a/show-device-location/src/main/res/drawable-xhdpi/locationdisplayheading.9.png b/samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayheading.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xhdpi/locationdisplayheading.9.png
rename to samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayheading.9.png
diff --git a/show-device-location/src/main/res/drawable-xhdpi/locationdisplaynavigation.9.png b/samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplaynavigation.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xhdpi/locationdisplaynavigation.9.png
rename to samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplaynavigation.9.png
diff --git a/show-device-location/src/main/res/drawable-xhdpi/locationdisplayon.9.png b/samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayon.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xhdpi/locationdisplayon.9.png
rename to samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayon.9.png
diff --git a/show-device-location/src/main/res/drawable-xhdpi/locationdisplayrecenter.9.png b/samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayrecenter.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xhdpi/locationdisplayrecenter.9.png
rename to samples/show-device-location/src/main/res/drawable-xhdpi/locationdisplayrecenter.9.png
diff --git a/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaydisabled.9.png b/samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaydisabled.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxhdpi/locationdisplaydisabled.9.png
rename to samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaydisabled.9.png
diff --git a/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayheading.9.png b/samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayheading.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxhdpi/locationdisplayheading.9.png
rename to samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayheading.9.png
diff --git a/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaynavigation.9.png b/samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaynavigation.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxhdpi/locationdisplaynavigation.9.png
rename to samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplaynavigation.9.png
diff --git a/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayon.9.png b/samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayon.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxhdpi/locationdisplayon.9.png
rename to samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayon.9.png
diff --git a/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayrecenter.9.png b/samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayrecenter.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxhdpi/locationdisplayrecenter.9.png
rename to samples/show-device-location/src/main/res/drawable-xxhdpi/locationdisplayrecenter.9.png
diff --git a/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaydisabled.9.png b/samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaydisabled.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaydisabled.9.png
rename to samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaydisabled.9.png
diff --git a/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayheading.9.png b/samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayheading.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayheading.9.png
rename to samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayheading.9.png
diff --git a/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaynavigation.9.png b/samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaynavigation.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaynavigation.9.png
rename to samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplaynavigation.9.png
diff --git a/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayon.9.png b/samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayon.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayon.9.png
rename to samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayon.9.png
diff --git a/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayrecenter.9.png b/samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayrecenter.9.png
similarity index 100%
rename from show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayrecenter.9.png
rename to samples/show-device-location/src/main/res/drawable-xxxhdpi/locationdisplayrecenter.9.png
diff --git a/show-device-location/src/main/res/layout/activity_main.xml b/samples/show-device-location/src/main/res/layout/show_device_location_activity_main.xml
similarity index 100%
rename from show-device-location/src/main/res/layout/activity_main.xml
rename to samples/show-device-location/src/main/res/layout/show_device_location_activity_main.xml
diff --git a/show-device-location/src/main/res/layout/spinner_layout.xml b/samples/show-device-location/src/main/res/layout/spinner_layout.xml
similarity index 100%
rename from show-device-location/src/main/res/layout/spinner_layout.xml
rename to samples/show-device-location/src/main/res/layout/spinner_layout.xml
diff --git a/samples/show-device-location/src/main/res/values/strings.xml b/samples/show-device-location/src/main/res/values/strings.xml
new file mode 100644
index 000000000..86272d0a9
--- /dev/null
+++ b/samples/show-device-location/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Show device location
+ Location point image
+
diff --git a/show-geodesic-path-between-two-points/README.md b/samples/show-geodesic-path-between-two-points/README.md
similarity index 100%
rename from show-geodesic-path-between-two-points/README.md
rename to samples/show-geodesic-path-between-two-points/README.md
diff --git a/show-geodesic-path-between-two-points/README.metadata.json b/samples/show-geodesic-path-between-two-points/README.metadata.json
similarity index 100%
rename from show-geodesic-path-between-two-points/README.metadata.json
rename to samples/show-geodesic-path-between-two-points/README.metadata.json
diff --git a/samples/show-geodesic-path-between-two-points/build.gradle.kts b/samples/show-geodesic-path-between-two-points/build.gradle.kts
new file mode 100644
index 000000000..2fbf9805c
--- /dev/null
+++ b/samples/show-geodesic-path-between-two-points/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-geodesic-path-between-two-points/show-geodesic-path-between-two-points.png b/samples/show-geodesic-path-between-two-points/show-geodesic-path-between-two-points.png
similarity index 100%
rename from show-geodesic-path-between-two-points/show-geodesic-path-between-two-points.png
rename to samples/show-geodesic-path-between-two-points/show-geodesic-path-between-two-points.png
diff --git a/samples/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml b/samples/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..cf988def1
--- /dev/null
+++ b/samples/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt b/samples/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt
new file mode 100644
index 000000000..38dc681da
--- /dev/null
+++ b/samples/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.LinearUnit
+import com.arcgismaps.geometry.LinearUnitId
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.GeodeticCurveType
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints.databinding.ShowGeodesicPathBetweenTwoPointsActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import kotlin.math.roundToInt
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowGeodesicPathBetweenTwoPointsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_geodesic_path_between_two_points_activity_main)
+ }
+
+ // set up data binding for the mapView
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // shows the distance information as a textview
+ private val infoTextView by lazy {
+ activityMainBinding.infoTextView
+ }
+
+ // a red marker symbol for the location points
+ private val locationMarkerSymbol by lazy {
+ SimpleMarkerSymbol(
+ SimpleMarkerSymbolStyle.Circle,
+ Color.red,
+ 10f
+ )
+ }
+
+ // the marker graphic for the starting location
+ private val startingLocationMarkerGraphic by lazy {
+ Graphic(startingPoint, locationMarkerSymbol)
+ }
+
+ // marker graphic for the destination
+ private val endLocationMarkerGraphic by lazy {
+ Graphic(symbol = locationMarkerSymbol)
+ }
+
+ // the geodesic path represented as line graphic
+ private val geodesicPathGraphic by lazy {
+ val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 5f)
+ Graphic(symbol = lineSymbol)
+ }
+
+ // the unit of distance measurement in kilometers
+ private val unitsOfMeasurement = LinearUnit(LinearUnitId.Kilometers)
+
+
+ // starting location for the distance calculation
+ private val startingPoint = Point(-73.7781, 40.6413, SpatialReference.wgs84())
+
+
+ // creates a graphic overlay to draw all graphics
+ private val graphicsOverlay = GraphicsOverlay()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ val map = ArcGISMap(BasemapStyle.ArcGISImageryStandard).apply {
+ initialViewpoint = Viewpoint(Point(34.77, -10.24), 20e7)
+ }
+ // configure mapView assignments
+ mapView.apply {
+ this.map = map
+ // add the graphics overlay to the mapview
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // add all our marker graphics to the graphics overlay
+ graphicsOverlay.graphics.addAll(
+ listOf(startingLocationMarkerGraphic, endLocationMarkerGraphic, geodesicPathGraphic)
+ )
+
+ lifecycleScope.launch {
+ // check if the map has loaded successfully
+ map.load().onSuccess {
+ // capture and collect when the user taps on the screen
+ mapView.onSingleTapConfirmed.collect { event ->
+ event.mapPoint?.let { point -> displayGeodesicPath(point) }
+ }
+ }.onFailure {
+ // if map load failed, show the error
+ showError("Error Loading Map")
+ }
+ }
+ }
+
+ /**
+ * Displays the destination location marker at the tapped location
+ * and draws a geodesic path curve using GeometryEngine.densifyGeodetic
+ * and computes the distance using GeometryEngine.lengthGeodetic
+ */
+ private fun displayGeodesicPath(point: Point) {
+ // project the tapped point into the same spatial reference as source point
+ val destinationPoint = GeometryEngine.projectOrNull(point, SpatialReference.wgs84())
+ ?: return showError("Error converting point projection")
+
+ // check if the destination point is within the map bounds
+ // isEmpty returns true if out of bounds
+ if (!destinationPoint.isEmpty) {
+ // update the end location marker location on map
+ endLocationMarkerGraphic.geometry = destinationPoint
+ // create a polyline between source and destination points
+ val polyline = Polyline(listOf(startingPoint, destinationPoint))
+ // generate a geodesic curve using the polyline
+ val pathGeometry = GeometryEngine.densifyGeodeticOrNull(
+ geometry = polyline,
+ maxSegmentLength = 1.0,
+ lengthUnit = unitsOfMeasurement,
+ curveType = GeodeticCurveType.Geodesic
+ // only compute the distance if the returned curved path geometry is not null
+ ) ?: return showError("Error creating a densified geometry")
+ // update the path graphic
+ geodesicPathGraphic.geometry = pathGeometry
+ // compute the path distance in kilometers
+ val distance = GeometryEngine.lengthGeodetic(
+ geometry = pathGeometry,
+ lengthUnit = unitsOfMeasurement,
+ curveType = GeodeticCurveType.Geodesic
+ )
+ // display the distance result
+ infoTextView.text = getString(R.string.distance_info_text, distance.roundToInt())
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/show-geodesic-path-between-two-points/src/main/res/layout/activity_main.xml b/samples/show-geodesic-path-between-two-points/src/main/res/layout/show_geodesic_path_between_two_points_activity_main.xml
similarity index 100%
rename from show-geodesic-path-between-two-points/src/main/res/layout/activity_main.xml
rename to samples/show-geodesic-path-between-two-points/src/main/res/layout/show_geodesic_path_between_two_points_activity_main.xml
diff --git a/samples/show-geodesic-path-between-two-points/src/main/res/values/strings.xml b/samples/show-geodesic-path-between-two-points/src/main/res/values/strings.xml
new file mode 100644
index 000000000..282c0ccad
--- /dev/null
+++ b/samples/show-geodesic-path-between-two-points/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Show geodesic path between two points
+ Tap to begin
+ Distance: %1d kilometers
+
diff --git a/show-grid/README.md b/samples/show-grid/README.md
similarity index 100%
rename from show-grid/README.md
rename to samples/show-grid/README.md
diff --git a/show-grid/README.metadata.json b/samples/show-grid/README.metadata.json
similarity index 100%
rename from show-grid/README.metadata.json
rename to samples/show-grid/README.metadata.json
diff --git a/samples/show-grid/build.gradle.kts b/samples/show-grid/build.gradle.kts
new file mode 100644
index 000000000..139299457
--- /dev/null
+++ b/samples/show-grid/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showgrid"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-grid/show-grid.png b/samples/show-grid/show-grid.png
similarity index 100%
rename from show-grid/show-grid.png
rename to samples/show-grid/show-grid.png
diff --git a/samples/show-grid/src/main/AndroidManifest.xml b/samples/show-grid/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..6c59c6a2c
--- /dev/null
+++ b/samples/show-grid/src/main/AndroidManifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt b/samples/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt
new file mode 100644
index 000000000..3720469c5
--- /dev/null
+++ b/samples/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt
@@ -0,0 +1,329 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showgrid
+
+import android.os.Bundle
+import android.util.Log
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.HorizontalAlignment
+import com.arcgismaps.mapping.symbology.LineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.symbology.VerticalAlignment
+import com.arcgismaps.mapping.view.GridLabelPosition
+import com.arcgismaps.mapping.view.LatitudeLongitudeGrid
+import com.arcgismaps.mapping.view.LatitudeLongitudeGridLabelFormat
+import com.arcgismaps.mapping.view.MgrsGrid
+import com.arcgismaps.mapping.view.UsngGrid
+import com.arcgismaps.mapping.view.UtmGrid
+import com.esri.arcgismaps.sample.showgrid.databinding.ShowGridActivityMainBinding
+import com.esri.arcgismaps.sample.showgrid.databinding.PopupDialogBinding
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.snackbar.Snackbar
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowGridActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_grid_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val menuButton: MaterialButton by lazy {
+ activityMainBinding.menuButton
+ }
+
+ // create a point to focus the map on in Quebec province
+ private val center: Point = Point(
+ -7702852.905619, 6217972.345771, SpatialReference(3857)
+ )
+
+ // the selected line color of the grid
+ private var lineColor: Color = Color.white
+
+ // the selected label color of the grid
+ private var labelColor: Color = Color.white
+
+ // the selected label position of the grid
+ private var labelPosition: GridLabelPosition = GridLabelPosition.AllSides
+
+ // boolean set if the layer is visible
+ private var isLabelVisible = true
+
+ // create a popup dialog to manage grid settings
+ private val popUpDialogBinding by lazy {
+ PopupDialogBinding.inflate(layoutInflater)
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ mapView.apply {
+ // create a map with imagery basemap
+ map = ArcGISMap(BasemapStyle.ArcGISImagery)
+ // set the initial viewpoint of the map
+ setViewpoint(Viewpoint(center, 23227.0))
+ // set defaults on grid
+ grid = LatitudeLongitudeGrid()
+ }
+
+ val dialog = MaterialAlertDialogBuilder(this@MainActivity).apply {
+ setView(popUpDialogBinding.root)
+ setTitle(getString(R.string.change_grid_button))
+ }.create()
+
+ // set up options in popup menu
+ // create drop-down list of different layer types
+ setupLayerDropdown(popUpDialogBinding)
+
+ // create drop-down list of different line colors
+ setupLineColorDropdown(popUpDialogBinding)
+
+ // create drop-down list of different label colors
+ setupLabelColorDropdown(popUpDialogBinding)
+
+ // create drop-down list of different label positions
+ setupLabelPositionDropdown(popUpDialogBinding)
+
+ // setup the checkbox to change the visibility of the labels
+ setupLabelsCheckbox(popUpDialogBinding)
+
+ // display pop-up box when button is clicked
+ menuButton.setOnClickListener {
+ dialog.show()
+ }
+ }
+
+ /**
+ * Sets up the [popupDialogBinding] for selecting a grid type
+ * and handles behavior for when a new grid type is selected.
+ */
+ private fun setupLayerDropdown(popupDialogBinding: PopupDialogBinding) {
+ popupDialogBinding.gridTypeDropdown.apply {
+ // set the grid type adapter
+ setAdapter(ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.layers_array))
+ )
+
+ // set the grid type click listener
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ when (position) {
+ 0 -> {
+ // LatitudeLongitudeGrid can have a label format of DecimalDegrees or DegreesMinutesSeconds
+ mapView.grid = LatitudeLongitudeGrid().apply {
+ labelFormat = LatitudeLongitudeGridLabelFormat.DecimalDegrees
+ }
+ mapView.setViewpoint(Viewpoint(center, 23227.0))
+ }
+ 1 -> {
+ mapView.grid = MgrsGrid()
+ mapView.setViewpoint(Viewpoint(center, 23227.0))
+ }
+ 2 -> {
+ mapView.grid = UtmGrid()
+ mapView.setViewpoint(Viewpoint(center, 10000000.0))
+ }
+ 3 -> {
+ mapView.grid = UsngGrid()
+ mapView.setViewpoint(Viewpoint(center, 23227.0))
+ }
+ else -> return@OnItemClickListener showError("Unsupported option")
+ }
+
+ // make sure settings persist on grid type change
+ setLabelVisibility(isLabelVisible)
+ changeGridColor(lineColor)
+ changeLabelColor(labelColor)
+ }
+ }
+ }
+
+ /**
+ * Sets up the [popupDialogBinding] for selecting a grid color and
+ * handles behavior for when a new line color is selected.
+ */
+ private fun setupLineColorDropdown(popupDialogBinding: PopupDialogBinding) {
+ popupDialogBinding.gridColorDropdown.apply {
+ // set the grid color adapter
+ setAdapter(ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.colors_array))
+ )
+
+ // set the grid color click listener
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ lineColor = when (position) {
+ 0 -> Color.red
+ 1 -> Color.white
+ 2 -> Color.blue
+ else -> return@OnItemClickListener showError("Unsupported option")
+ }
+ changeGridColor(lineColor)
+ }
+ }
+ }
+
+ /**
+ * Sets up the [popupDialogBinding] for selecting a label color
+ * and handles behavior for when a new label color is selected.
+ */
+ private fun setupLabelColorDropdown(popupDialogBinding: PopupDialogBinding) {
+ popupDialogBinding.labelColorDropdown.apply {
+ setAdapter(ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.colors_array))
+ )
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ // change grid labels color
+ labelColor = when (position) {
+ 0 -> Color.red
+ 1 -> Color.white
+ 2 -> Color.blue
+ else -> return@OnItemClickListener showError("Unsupported option")
+ }
+ changeLabelColor(labelColor)
+ }
+ }
+ }
+
+ /**
+ * Sets up the [popupDialogBinding] for selecting a label position relative to the grid
+ * and handles behavior for when a label position is selected.
+ */
+ private fun setupLabelPositionDropdown(popupDialogBinding: PopupDialogBinding) {
+ popupDialogBinding.labelPositionDropdown.apply {
+ setAdapter(ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.positions_array))
+ )
+
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ // set the label position
+ labelPosition = when (position) {
+ 0 -> GridLabelPosition.AllSides
+ 1 -> GridLabelPosition.BottomLeft
+ 2 -> GridLabelPosition.BottomRight
+ 3 -> GridLabelPosition.Center
+ 4 -> GridLabelPosition.Geographic
+ 5 -> GridLabelPosition.TopLeft
+ 6 -> GridLabelPosition.TopRight
+ else -> return@OnItemClickListener showError("Unsupported option")
+ }
+ changeLabelPosition(labelPosition)
+ }
+ }
+ }
+
+ /**
+ * Sets up the [popupDialogBinding] for the checkbox making labels visible or invisible.
+ */
+ private fun setupLabelsCheckbox(popupDialogBinding: PopupDialogBinding) {
+ popupDialogBinding.labelsCheckBox.apply {
+ isChecked = true
+ // hide and show label visibility when the checkbox is clicked
+ setOnClickListener {
+ isLabelVisible = isChecked
+ setLabelVisibility(isLabelVisible)
+ }
+ }
+ }
+
+ /**
+ * Sets the labels visibility based on [isVisible].
+ */
+ private fun setLabelVisibility(isVisible: Boolean) {
+ val grid = mapView.grid ?: return
+ grid.labelVisibility = isVisible
+ }
+
+ /**
+ * Sets the [color] of the grid lines.
+ */
+ private fun changeGridColor(color: Color) {
+ val grid = mapView.grid ?: return
+ val gridLevels = grid.levelCount
+ for (gridLevel in 0 until gridLevels) {
+ val lineSymbol: LineSymbol =
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, color, (gridLevel + 1).toFloat())
+ grid.setLineSymbol(gridLevel, lineSymbol)
+ }
+ }
+
+ /**
+ * Sets the [labelColor] of the labels on the grid.
+ */
+ private fun changeLabelColor(labelColor: Color) {
+ val grid = mapView.grid ?: return
+ val gridLevels = grid.levelCount
+ for (gridLevel in 0 until gridLevels) {
+ val textSymbol = TextSymbol().apply {
+ color = labelColor
+ size = 14f
+ horizontalAlignment = HorizontalAlignment.Left
+ verticalAlignment = VerticalAlignment.Bottom
+ haloColor = Color.black
+ haloWidth = 5f
+ }
+ grid.setTextSymbol(gridLevel, textSymbol)
+ }
+ }
+
+ /**
+ * Sets the [labelPosition] of the labels on the grid.
+ */
+ private fun changeLabelPosition(labelPosition: GridLabelPosition) {
+ val grid = mapView.grid ?: return
+ grid.labelPosition = labelPosition
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
diff --git a/show-grid/src/main/res/layout/popup_dialog.xml b/samples/show-grid/src/main/res/layout/popup_dialog.xml
similarity index 100%
rename from show-grid/src/main/res/layout/popup_dialog.xml
rename to samples/show-grid/src/main/res/layout/popup_dialog.xml
diff --git a/show-grid/src/main/res/layout/activity_main.xml b/samples/show-grid/src/main/res/layout/show_grid_activity_main.xml
similarity index 100%
rename from show-grid/src/main/res/layout/activity_main.xml
rename to samples/show-grid/src/main/res/layout/show_grid_activity_main.xml
diff --git a/samples/show-grid/src/main/res/values/strings.xml b/samples/show-grid/src/main/res/values/strings.xml
new file mode 100644
index 000000000..3da44993c
--- /dev/null
+++ b/samples/show-grid/src/main/res/values/strings.xml
@@ -0,0 +1,31 @@
+
+ Show grid
+ Labels Visible
+ Grid Color
+ Label Color
+ Grid Type
+ Grid Options
+ Label Position
+
+ - Lat and Long
+ - MGRS Grid
+ - UTM Grid
+ - USNG Grid
+
+
+ - Red
+ - White
+ - Blue
+
+
+
+
+ - AllSides
+ - BottomLeft
+ - BottomRight
+ - Center
+ - Geographic
+ - TopLeft
+ - TopRight
+
+
diff --git a/show-labels-on-layer/README.md b/samples/show-labels-on-layer/README.md
similarity index 100%
rename from show-labels-on-layer/README.md
rename to samples/show-labels-on-layer/README.md
diff --git a/show-labels-on-layer/README.metadata.json b/samples/show-labels-on-layer/README.metadata.json
similarity index 100%
rename from show-labels-on-layer/README.metadata.json
rename to samples/show-labels-on-layer/README.metadata.json
diff --git a/samples/show-labels-on-layer/build.gradle.kts b/samples/show-labels-on-layer/build.gradle.kts
new file mode 100644
index 000000000..9a18b235c
--- /dev/null
+++ b/samples/show-labels-on-layer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showlabelsonlayer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-labels-on-layer/show-labels-on-layer.png b/samples/show-labels-on-layer/show-labels-on-layer.png
similarity index 100%
rename from show-labels-on-layer/show-labels-on-layer.png
rename to samples/show-labels-on-layer/show-labels-on-layer.png
diff --git a/samples/show-labels-on-layer/src/main/AndroidManifest.xml b/samples/show-labels-on-layer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..74e45ad79
--- /dev/null
+++ b/samples/show-labels-on-layer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt b/samples/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt
new file mode 100644
index 000000000..d4e211500
--- /dev/null
+++ b/samples/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showlabelsonlayer
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.Color
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.arcgisservices.LabelingPlacement
+import com.arcgismaps.data.ServiceFeatureTable
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.labeling.ArcadeLabelExpression
+import com.arcgismaps.mapping.labeling.LabelDefinition
+import com.arcgismaps.mapping.layers.FeatureLayer
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.esri.arcgismaps.sample.showlabelsonlayer.databinding.ShowLabelsOnLayerActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowLabelsOnLayerActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_labels_on_layer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a map with a light gray map style
+ val map = ArcGISMap(BasemapStyle.ArcGISLightGray)
+ // create a service feature table from an online feature service of
+ // US Congressional Districts
+ val serviceFeatureTable =
+ ServiceFeatureTable(getString(R.string.congressional_districts_url))
+ // create the feature layer from the service feature table
+ val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
+ // add this feature layer to the map
+ map.operationalLayers.add(featureLayer)
+ // add the map to the mapview
+ mapView.map = map
+
+ lifecycleScope.launch {
+ // if the map load failed show an error and return
+ map.load().onFailure {
+ return@onFailure showError("Error loading map:${it.message}")
+ }
+ // if the feature layer load failed show an error and return
+ featureLayer.load().onFailure {
+ return@onFailure showError("Error loading feature layer:${it.message}")
+ }
+ // zoom to the layer when it's done loading
+ featureLayer.fullExtent?.let { extent ->
+ mapView.setViewpoint(Viewpoint(extent))
+ }
+ }
+
+ // create label definitions for the different party type attribute in the feature service
+ val republicanLabelDefinition = createLabelDefinition("Republican", Color.red)
+ val democraticLabelDefinition = createLabelDefinition("Democrat", Color.blue)
+
+ featureLayer.apply {
+ // add the label definitions to the feature layer
+ labelDefinitions.addAll(
+ listOf(
+ republicanLabelDefinition,
+ democraticLabelDefinition
+ )
+ )
+ // enable the labels
+ labelsEnabled = true
+ }
+ }
+
+ /**
+ * Creates and returns a [LabelDefinition] for the given party [name] attribute
+ * with the [labelColor]
+ */
+ private fun createLabelDefinition(
+ name: String,
+ labelColor: Color
+ ): LabelDefinition {
+ // create a text symbol for styling the label
+ val textSymbol = TextSymbol().apply {
+ color = labelColor
+ size = 12f
+ haloColor = Color.white
+ haloWidth = 2f
+ }
+ // create a arcade label expression for the label text
+ val arcadeLabelExpression =
+ ArcadeLabelExpression(
+ "\$feature.NAME + \" (\" + left(\$feature.PARTY,1) " +
+ "+ \")\\nDistrict \" + \$feature.CDFIPS"
+ )
+ // create and return a new label definition with the arcadeLabelExpression and textSymbol
+ return LabelDefinition(arcadeLabelExpression, textSymbol).apply {
+ // set the label placement
+ placement = LabelingPlacement.PolygonAlwaysHorizontal
+ // set the attribute name for which this label will be generated
+ whereClause = "PARTY = '$name'"
+ }
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
+
+/**
+ * Simple extension property that represents a blue color
+ */
+private val Color.Companion.blue get() = fromRgba(0, 0, 255)
diff --git a/show-labels-on-layer/src/main/res/layout/activity_main.xml b/samples/show-labels-on-layer/src/main/res/layout/show_labels_on_layer_activity_main.xml
similarity index 100%
rename from show-labels-on-layer/src/main/res/layout/activity_main.xml
rename to samples/show-labels-on-layer/src/main/res/layout/show_labels_on_layer_activity_main.xml
diff --git a/samples/show-labels-on-layer/src/main/res/values/strings.xml b/samples/show-labels-on-layer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..82c8eee15
--- /dev/null
+++ b/samples/show-labels-on-layer/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Show labels on layer
+ https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_116th_Congressional_Districts/FeatureServer/0
+
diff --git a/show-line-of-sight-between-geoelements/README.md b/samples/show-line-of-sight-between-geoelements/README.md
similarity index 100%
rename from show-line-of-sight-between-geoelements/README.md
rename to samples/show-line-of-sight-between-geoelements/README.md
diff --git a/show-line-of-sight-between-geoelements/README.metadata.json b/samples/show-line-of-sight-between-geoelements/README.metadata.json
similarity index 100%
rename from show-line-of-sight-between-geoelements/README.metadata.json
rename to samples/show-line-of-sight-between-geoelements/README.metadata.json
diff --git a/samples/show-line-of-sight-between-geoelements/build.gradle.kts b/samples/show-line-of-sight-between-geoelements/build.gradle.kts
new file mode 100644
index 000000000..cdc2cf502
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showlineofsightbetweengeoelements"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-line-of-sight-between-geoelements/show-line-of-sight-between-geoelements.png b/samples/show-line-of-sight-between-geoelements/show-line-of-sight-between-geoelements.png
similarity index 100%
rename from show-line-of-sight-between-geoelements/show-line-of-sight-between-geoelements.png
rename to samples/show-line-of-sight-between-geoelements/show-line-of-sight-between-geoelements.png
diff --git a/samples/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml b/samples/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0af9a23f4
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt
new file mode 100644
index 000000000..33334148f
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt
@@ -0,0 +1,36 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements
+
+import android.content.Intent
+import android.os.Bundle
+import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
+
+class DownloadActivity : DownloaderActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ downloadAndStartSample(
+ Intent(this, MainActivity::class.java),
+ // get the app name of the sample
+ getString(R.string.show_line_of_sight_between_geoelements_app_name),
+ listOf(
+ "https://www.arcgis.com/home/item.html?id=3af5cfec0fd24dac8d88aea679027cb9"
+ )
+
+ )
+ }
+}
diff --git a/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt
new file mode 100644
index 000000000..fcc4c1d97
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.show_line_of_sight_between_geoelements_app_name)
+ )
+ }
+ }
+}
diff --git a/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt
new file mode 100644
index 000000000..ff58c4d3b
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt
@@ -0,0 +1,278 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.components
+
+import android.app.Application
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.core.content.ContextCompat.getString
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import com.arcgismaps.Color
+import com.arcgismaps.analysis.GeoElementLineOfSight
+import com.arcgismaps.analysis.LineOfSightTargetVisibility
+import com.arcgismaps.geometry.AngularUnit
+import com.arcgismaps.geometry.GeodeticCurveType
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.LinearUnit
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.PointBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISScene
+import com.arcgismaps.mapping.ArcGISTiledElevationSource
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Surface
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.layers.ArcGISSceneLayer
+import com.arcgismaps.mapping.symbology.ModelSceneSymbol
+import com.arcgismaps.mapping.symbology.SceneSymbolAnchorPosition
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleRenderer
+import com.arcgismaps.mapping.view.AnalysisOverlay
+import com.arcgismaps.mapping.view.Camera
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.SurfacePlacement
+import com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.R
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import java.io.File
+import kotlin.concurrent.timer
+
+class SceneViewModel(private var application: Application) : AndroidViewModel(application) {
+
+ // Keep track of target visibility status string state.
+ var targetVisibilityString by mutableStateOf("")
+ private set
+
+ // Set visibility status string in the UI.
+ private fun updateTargetVisibilityString(targetVisibility: String) {
+ targetVisibilityString = targetVisibility
+ }
+
+ // Initialize z to 50 as starting point and emit its state changes
+ private val _observerHeight = MutableStateFlow(50.0)
+ val observerHeight: StateFlow = _observerHeight.asStateFlow()
+
+ // Keeps track of wayPoints
+ private var waypointsIndex = 0
+
+ // Create waypoints around a block for the taxi to drive to
+ private val wayPoints = listOf(
+ Point(-73.984513, 40.748469, SpatialReference.wgs84()),
+ Point(-73.985068, 40.747786, SpatialReference.wgs84()),
+ Point(-73.983452, 40.747091, SpatialReference.wgs84()),
+ Point(-73.982961, 40.747762, SpatialReference.wgs84()),
+ )
+
+ private val provisionPath: String by lazy {
+ application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
+ R.string.show_line_of_sight_between_geoelements_app_name
+ ) + File.separator
+ }
+
+ private val filePath = provisionPath + application.getString(R.string.dolmus_model)
+
+ // Create a symbol of a taxi using the model file
+ private val taxiSymbol = ModelSceneSymbol(
+ uri = filePath,
+ scale = 3.0F
+ ).apply {
+ anchorPosition = SceneSymbolAnchorPosition.Bottom
+ }
+
+ // Create a graphic of a taxi to be the target
+ private val taxiGraphic = Graphic(
+ geometry = wayPoints[0],
+ symbol = taxiSymbol
+ ).apply {
+ attributes["HEADING"] = 0.0
+ }
+
+ // Create a graphic near the Empire State Building to be the observer
+ private val observerGraphic = Graphic(
+ geometry = Point(
+ x = -73.9853,
+ y = 40.7484,
+ z = 50.0,
+ spatialReference = SpatialReference.wgs84()
+ ),
+ symbol = SimpleMarkerSymbol(
+ style = SimpleMarkerSymbolStyle.Circle,
+ color = Color.red,
+ size = 5f
+ )
+ )
+
+ // Zoom to show the observer
+ private val camera = Camera(
+ lookAtPoint = observerGraphic.geometry as Point,
+ distance = 700.0,
+ roll = 0.0,
+ pitch = 45.0,
+ heading = -30.0,
+ )
+
+
+ // Define base surface for elevation data
+ private val surface = Surface().apply {
+ elevationSources.add(
+ ArcGISTiledElevationSource(
+ uri = getString(
+ application,
+ R.string.elevation_service_url
+ )
+ )
+ )
+ }
+
+ // Define a scene layer for the New York buildings
+ private val buildings =
+ ArcGISSceneLayer(uri = application.getString(R.string.new_york_buildings_service_url))
+
+
+ // Create a scene and add a basemap to it.
+ // Set the surface and buildings in the scene, and define the viewpoint on launch
+ val scene = ArcGISScene(BasemapStyle.ArcGISTopographic).apply {
+ baseSurface = surface
+ operationalLayers.add(buildings)
+ initialViewpoint = Viewpoint(
+ boundingGeometry = observerGraphic.geometry as Point,
+ camera = camera
+ )
+ }
+
+
+ // Set up a heading expression to handle graphic rotation
+ private val renderer3D = SimpleRenderer().apply {
+ sceneProperties.headingExpression = ("[HEADING]")
+ }
+
+
+ // Create graphic overlay to hold graphics
+ // Set the surface placement, renderer, and add graphics,
+ val graphicsOverlay = GraphicsOverlay().apply {
+ sceneProperties.surfacePlacement = SurfacePlacement.RelativeToScene
+ renderer = renderer3D
+ graphics.addAll(listOf(observerGraphic, taxiGraphic))
+ }
+
+
+ // Create a line of sight between the two graphics and add it to the analysis overlay
+ private val lineOfSight = GeoElementLineOfSight(
+ observerGeoElement = observerGraphic,
+ targetGeoElement = taxiGraphic
+ ).apply {
+ // Observe the visibility status of the moving taxi
+ viewModelScope.launch(Dispatchers.Main) {
+
+ // Update target visibility status and select (highlight) the taxi when the line of sight target visibility changes to visible
+ targetVisibility.collect { targetVisibility ->
+ when(targetVisibility) {
+ is LineOfSightTargetVisibility.Visible -> {
+ updateTargetVisibilityString("Visible")
+ taxiGraphic.isSelected = true
+ }
+ is LineOfSightTargetVisibility.Obstructed -> {
+ updateTargetVisibilityString("Obstructed")
+ taxiGraphic.isSelected = false
+ }
+ is LineOfSightTargetVisibility.Unknown -> {
+ updateTargetVisibilityString("Unknown")
+ taxiGraphic.isSelected = false
+ }
+ }
+ }
+ }
+ }
+
+ // Create an analysis overlay to hold the line of sight
+ val analysisOverlay = AnalysisOverlay().apply {
+ analyses.add(lineOfSight)
+ }
+
+ init {
+
+ // Create a timer to animate the tank
+ timer(
+ initialDelay = 0,
+ period = 50,
+ action = {
+ animate()
+ }
+ )
+ }
+
+ /**
+ * Updates elevation of the observer graphic using the given [height]
+ */
+ fun updateHeight(height: Double) {
+ val pointBuilder = PointBuilder(observerGraphic.geometry as Point).apply {
+ z = height
+ }
+ observerGraphic.geometry = pointBuilder.toGeometry()
+ _observerHeight.value = height
+ }
+
+ /**
+ * Moves the taxi toward the current waypoint a short distance.
+ */
+ private fun animate() {
+
+ val meters = LinearUnit.meters
+ val degrees = AngularUnit.degrees
+ val waypoint = wayPoints[waypointsIndex]
+ val location = taxiGraphic.geometry as Point
+
+ // Calculate the geodetic distance between current taxi location and next waypoint
+ GeometryEngine.distanceGeodeticOrNull(
+ point1 = location,
+ point2 = waypoint,
+ distanceUnit = meters,
+ azimuthUnit = degrees,
+ curveType = GeodeticCurveType.Geodesic
+ )?.let { geodeticDistanceResult ->
+
+ taxiGraphic.apply {
+
+ // Move toward waypoint a short distance
+ geometry = GeometryEngine.tryMoveGeodetic(
+ pointCollection = listOf(location),
+ distance = 1.0,
+ distanceUnit = meters,
+ azimuth = geodeticDistanceResult.azimuth1,
+ azimuthUnit = degrees,
+ curveType = GeodeticCurveType.Geodesic
+ )[0]
+
+ // Rotate to the waypoint
+ attributes["HEADING"] = geodeticDistanceResult.azimuth1
+
+ // Reached waypoint, move to next waypoint
+ if (geodeticDistanceResult.distance <= 2) {
+ waypointsIndex = (waypointsIndex + 1) % wayPoints.size
+ }
+ }
+ }
+ }
+
+}
diff --git a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/screens/MainScreen.kt b/samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/screens/MainScreen.kt
similarity index 100%
rename from show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/screens/MainScreen.kt
rename to samples/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/screens/MainScreen.kt
diff --git a/samples/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml b/samples/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f50e5540f
--- /dev/null
+++ b/samples/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml
@@ -0,0 +1,10 @@
+
+ Show line of sight between geoelements
+ https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
+ https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/New_York_LoD2_3D_Buildings/SceneServer/layers/0
+ dolmus.3ds
+ dolmus_back.jpeg
+ dolmus_front.jpeg
+ dolmus_side.jpeg
+ tire_tread.jpeg
+
diff --git a/show-location-history/README.md b/samples/show-location-history/README.md
similarity index 100%
rename from show-location-history/README.md
rename to samples/show-location-history/README.md
diff --git a/show-location-history/README.metadata.json b/samples/show-location-history/README.metadata.json
similarity index 100%
rename from show-location-history/README.metadata.json
rename to samples/show-location-history/README.metadata.json
diff --git a/samples/show-location-history/build.gradle.kts b/samples/show-location-history/build.gradle.kts
new file mode 100644
index 000000000..bb3c50f51
--- /dev/null
+++ b/samples/show-location-history/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showlocationhistory"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-location-history/show-location-history.png b/samples/show-location-history/show-location-history.png
similarity index 100%
rename from show-location-history/show-location-history.png
rename to samples/show-location-history/show-location-history.png
diff --git a/samples/show-location-history/src/main/AndroidManifest.xml b/samples/show-location-history/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..8fb4139ac
--- /dev/null
+++ b/samples/show-location-history/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt b/samples/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt
new file mode 100644
index 000000000..647231dc7
--- /dev/null
+++ b/samples/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt
@@ -0,0 +1,148 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showlocationhistory
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.location.LocationDisplayAutoPanMode
+import com.arcgismaps.location.SimulatedLocationDataSource
+import com.arcgismaps.location.SimulationParameters
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleRenderer
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.showlocationhistory.databinding.ShowLocationHistoryActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+import java.time.Instant
+
+class MainActivity : AppCompatActivity() {
+
+ private var isTrackLocation: Boolean = false
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowLocationHistoryActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_location_history_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create a center point for the data in West Los Angeles, California
+ val center = Point(-13185535.98, 4037766.28, SpatialReference(102100))
+
+ // create a graphics overlay for the points and use a red circle for the symbols
+ val locationHistoryOverlay = GraphicsOverlay()
+ val locationSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10f)
+ locationHistoryOverlay.renderer = SimpleRenderer(locationSymbol)
+
+ // create a graphics overlay for the lines connecting the points and use a blue line for the symbol
+ val locationHistoryLineOverlay = GraphicsOverlay()
+ val locationLineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2.0f)
+ locationHistoryLineOverlay.renderer = SimpleRenderer(locationLineSymbol)
+
+ mapView.apply {
+ // create and add a map with a navigation night basemap style
+ map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
+ setViewpoint(Viewpoint(center, 7000.0))
+ graphicsOverlays.addAll(listOf(locationHistoryOverlay, locationHistoryLineOverlay))
+ }
+
+ // create a polyline builder to connect the location points
+ val polylineBuilder = PolylineBuilder(SpatialReference(102100))
+
+ // create a simulated location data source from json data with simulation parameters to set a consistent velocity
+ val simulatedLocationDataSource = SimulatedLocationDataSource(
+ Geometry.fromJsonOrNull(getString(R.string.polyline_data)) as Polyline,
+ SimulationParameters(Instant.now(), 30.0, 0.0, 0.0)
+ )
+
+ // coroutine scope to collect data source location changes
+ lifecycleScope.launch {
+ simulatedLocationDataSource.locationChanged.collect { location ->
+ // if location tracking is turned off, do not add to the polyline
+ if (!isTrackLocation) {
+ return@collect
+ }
+ // get the point from the location
+ val nextPoint = location.position
+ // add the new point to the polyline builder
+ polylineBuilder.addPoint(nextPoint)
+ // add the new point to the two graphics overlays and reset the line connecting the points
+ locationHistoryOverlay.graphics.add(Graphic(nextPoint))
+ locationHistoryLineOverlay.graphics.apply {
+ clear()
+ add((Graphic(polylineBuilder.toGeometry())))
+ }
+ }
+ }
+
+ // configure the map view's location display to follow the simulated location data source
+ mapView.locationDisplay.apply {
+ dataSource = simulatedLocationDataSource
+ setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ initialZoomScale = 7000.0
+ }
+
+ // coroutine scope to set a tap event on the map view
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect {
+ if (mapView.locationDisplay.autoPanMode.value == LocationDisplayAutoPanMode.Off) {
+ mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
+ }
+ if (isTrackLocation) {
+ isTrackLocation = false
+ Snackbar.make(mapView, "Tracking has stopped", Snackbar.LENGTH_INDEFINITE).show()
+ } else {
+ isTrackLocation = true
+ Snackbar.make(mapView, "Tracking has started", Snackbar.LENGTH_INDEFINITE ).show()
+ }
+ }
+ }
+
+ // coroutine scope to start the simulated location data source
+ lifecycleScope.launch {
+ simulatedLocationDataSource.start()
+ }
+ }
+}
+
diff --git a/show-location-history/src/main/res/drawable/ic_my_location_white_24dp.xml b/samples/show-location-history/src/main/res/drawable/ic_my_location_white_24dp.xml
similarity index 100%
rename from show-location-history/src/main/res/drawable/ic_my_location_white_24dp.xml
rename to samples/show-location-history/src/main/res/drawable/ic_my_location_white_24dp.xml
diff --git a/show-location-history/src/main/res/drawable/ic_navigation_white_24dp.xml b/samples/show-location-history/src/main/res/drawable/ic_navigation_white_24dp.xml
similarity index 100%
rename from show-location-history/src/main/res/drawable/ic_navigation_white_24dp.xml
rename to samples/show-location-history/src/main/res/drawable/ic_navigation_white_24dp.xml
diff --git a/show-location-history/src/main/res/layout/activity_main.xml b/samples/show-location-history/src/main/res/layout/show_location_history_activity_main.xml
similarity index 100%
rename from show-location-history/src/main/res/layout/activity_main.xml
rename to samples/show-location-history/src/main/res/layout/show_location_history_activity_main.xml
diff --git a/samples/show-location-history/src/main/res/values/strings.xml b/samples/show-location-history/src/main/res/values/strings.xml
new file mode 100644
index 000000000..b82572cd0
--- /dev/null
+++ b/samples/show-location-history/src/main/res/values/strings.xml
@@ -0,0 +1,50 @@
+
+ Show location history
+ {\"paths\":[[ [-13185646.046666779,4037971.5966668758],
+ [-13185586.780000051,4037827.6633333955],[-13185514.813333312,4037709.1299999417],
+ [-13185569.846666701,4037522.8633330846],[-13185591.01333339,4037378.9299996048],
+ [-13185629.113333428,4037283.6799995075],[-13185770.93000024,4037425.4966663187],
+ [-13185821.730000293,4037546.146666442],[-13185880.996667018,4037704.8966666036],
+ [-13185948.730000421,4037874.2300001099],[-13185974.130000448,4037946.1966668498],
+ [-13186120.180000596,4037958.896666863],[-13186264.113334076,4037984.296666889],
+ [-13186336.080000836,4038001.2300002342],[-13186314.91333415,4037757.8133333195],
+ [-13186272.580000773,4037560.9633331187],[-13186187.913334005,4037463.59666635],
+ [-13186431.33000092,4037404.3299996229],[-13186676.863334503,4037290.0299995062],
+ [-13187625.130002158,4038589.6633341513],[-13187333.030001862,4038756.8800009824],
+ [-13187091.730001617,4038617.1800008402],[-13186791.163334643,4038805.5633343654],
+ [-13186721.313334571,4038801.3300010278],[-13186833.49666802,4038195.9633337436],
+ [-13186977.677439401,4037699.8176972247],[-13186784.921301765,4037820.4541915278],
+ [-13186749.517113185,4038150.8932846226],[-13186649.860878762,4038288.5762400767],
+ [-13186556.760975549,4038323.9804286221],[-13186472.839936033,4038481.3323777127],
+ [-13186373.183701571,4038489.1999751539],[-13186344.335844241,4038242.6819215398],
+ [-13186126.665647998,4038308.245233661],[-13185814.584282301,4038358.0733508728],
+ [-13185651.987268206,4038116.8003622484],[-13185203.534213299,4038048.6145176427],
+ [-13184576.748949422,4038150.8932845518],[-13184251.55492135,4037833.5668537691],
+ [-13184146.653621957,4037524.1080205571],[-13183949.963685593,4037621.1417224966],
+ [-13183687.71043711,4037781.1162040718],[-13183480.530370807,4037875.5273735262],
+ [-13182307.629999243,4037859.7460188437],[-13181376.039484169,4037820.9297473822],
+ [-13180716.162869323,4038364.3575478429],[-13180182.439136729,4038810.7446696493],
+ [-13178474.523192419,4040237.2426458625],[-13178321.134040033,4039740.6894803117],
+ [-13177958.020228144,4039140.1550991111],[-13177073.512224896,4037459.5898928214],
+ [-13177757.842101147,4037589.9384406791],[-13178386.308314031,4037799.427178307],
+ [-13180095.012208173,4037811.6550856642],[-13180126.165447287,4036845.9046731163],
+ [-13179806.844746364,4036324.0879179495],[-13180928.361354485,4035887.9425703473],
+ [-13181598.155995468,4035428.432293402],[-13182984.47513606,4034447.105261297],
+ [-13182229.264383668,4033222.8051626245],[-13182058.735615831,4033339.8690072047],
+ [-13181939.035180708,4033691.2477038498],[-13182116.65518121,4033861.1450956347],
+ [-13181792.305615077,4034085.1007484416],[-13182027.845180977,4034467.3698799671],
+ [-13181877.254310986,4034644.9898804692],[-13181630.130832028,4034517.5668366305],
+ [-13181386.868657427,4034424.8955320208],[-13181228.555178719,4034652.7124891868],
+ [-13181379.14604871,4034942.3103160923],[-13181267.168222306,4035189.4337950516],
+ [-13181074.103004368,4035015.6750989081],[-13180807.673003616,4034934.5877073747],
+ [-13180618.469090037,4034814.8872722536],[-13180599.162568243,4035374.7764042714],
+ [-13181047.073873857,4035494.476839392],[-13181317.365178969,4035413.3894478586],
+ [-13180765.198655669,4035143.0981427468],[-13180328.871263131,4034892.1133594285],
+ [-13180270.951697765,4035258.9372735149],[-13180325.009958787,4035718.4324922049],
+ [-13180707.279090302,4035695.2646660525],[-13181413.897788007,4035536.9511873648],
+ [-13181618.54691902,4035807.2424924765],[-13181884.976919774,4036065.949884512],
+ [-13182159.129529245,4035861.3007534989],[-13182174.57474668,4035668.2355355616],
+ [-13182417.83692128,4035664.374231203],[-13182784.660835361,4035409.5281435261],
+ [-13182997.032575091,4035255.0759691764],[-13182618.624747934,4034679.7416197238]
+ ]],\"spatialReference\":{\"wkid\":102100,\"latestWkid\":3857}}
+
diff --git a/show-magnifier/README.md b/samples/show-magnifier/README.md
similarity index 100%
rename from show-magnifier/README.md
rename to samples/show-magnifier/README.md
diff --git a/show-magnifier/README.metadata.json b/samples/show-magnifier/README.metadata.json
similarity index 100%
rename from show-magnifier/README.metadata.json
rename to samples/show-magnifier/README.metadata.json
diff --git a/samples/show-magnifier/build.gradle.kts b/samples/show-magnifier/build.gradle.kts
new file mode 100644
index 000000000..b20161fcd
--- /dev/null
+++ b/samples/show-magnifier/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showmagnifier"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-magnifier/show-magnifier.png b/samples/show-magnifier/show-magnifier.png
similarity index 100%
rename from show-magnifier/show-magnifier.png
rename to samples/show-magnifier/show-magnifier.png
diff --git a/samples/show-magnifier/src/main/AndroidManifest.xml b/samples/show-magnifier/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/show-magnifier/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt b/samples/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt
new file mode 100644
index 000000000..d22c84da8
--- /dev/null
+++ b/samples/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showmagnifier
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showmagnifier.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ ShowMagnifierApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ShowMagnifierApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.show_magnifier_app_name)
+ )
+ }
+ }
+}
diff --git a/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/screens/MainScreen.kt b/samples/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/screens/MainScreen.kt
similarity index 100%
rename from show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/screens/MainScreen.kt
rename to samples/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/screens/MainScreen.kt
diff --git a/samples/show-magnifier/src/main/res/values/strings.xml b/samples/show-magnifier/src/main/res/values/strings.xml
new file mode 100644
index 000000000..a84de57b7
--- /dev/null
+++ b/samples/show-magnifier/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Show magnifier
+
diff --git a/show-portal-user-info/README.md b/samples/show-portal-user-info/README.md
similarity index 100%
rename from show-portal-user-info/README.md
rename to samples/show-portal-user-info/README.md
diff --git a/show-portal-user-info/README.metadata.json b/samples/show-portal-user-info/README.metadata.json
similarity index 100%
rename from show-portal-user-info/README.metadata.json
rename to samples/show-portal-user-info/README.metadata.json
diff --git a/samples/show-portal-user-info/build.gradle.kts b/samples/show-portal-user-info/build.gradle.kts
new file mode 100644
index 000000000..2e20aa361
--- /dev/null
+++ b/samples/show-portal-user-info/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showportaluserinfo"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+ implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
+}
diff --git a/show-portal-user-info/show-portal-user-info.png b/samples/show-portal-user-info/show-portal-user-info.png
similarity index 100%
rename from show-portal-user-info/show-portal-user-info.png
rename to samples/show-portal-user-info/show-portal-user-info.png
diff --git a/samples/show-portal-user-info/src/main/AndroidManifest.xml b/samples/show-portal-user-info/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a00ce7ae4
--- /dev/null
+++ b/samples/show-portal-user-info/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt b/samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt
new file mode 100644
index 000000000..78eaa0625
--- /dev/null
+++ b/samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt
@@ -0,0 +1,50 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showportaluserinfo
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showportaluserinfo.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ SampleAppTheme {
+ ShowPortalUserInfoApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ShowPortalUserInfoApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.show_portal_user_info_app_name),
+ application = application
+ )
+ }
+ }
+}
diff --git a/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/components/AppViewModel.kt b/samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/components/AppViewModel.kt
similarity index 100%
rename from show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/components/AppViewModel.kt
rename to samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/components/AppViewModel.kt
diff --git a/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/screens/MainScreen.kt b/samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/screens/MainScreen.kt
similarity index 100%
rename from show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/screens/MainScreen.kt
rename to samples/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/screens/MainScreen.kt
diff --git a/show-portal-user-info/src/main/res/drawable/user.png b/samples/show-portal-user-info/src/main/res/drawable/user.png
similarity index 100%
rename from show-portal-user-info/src/main/res/drawable/user.png
rename to samples/show-portal-user-info/src/main/res/drawable/user.png
diff --git a/samples/show-portal-user-info/src/main/res/values/strings.xml b/samples/show-portal-user-info/src/main/res/values/strings.xml
new file mode 100644
index 000000000..e113c3357
--- /dev/null
+++ b/samples/show-portal-user-info/src/main/res/values/strings.xml
@@ -0,0 +1,13 @@
+
+ Show portal user info
+ rR5K4TEyhkPDcTkf
+ my-ags-app://auth
+
+ Enter a valid portal url to load it and see the portal details displayed here.
+
+ Portal loaded successfully but no portal info was found.
+ The Portal is loaded successfully. Please check the Portal details below
+ Failed to load portal
+ https://www.arcgis.com
+
+
diff --git a/show-result-of-spatial-operations/README.md b/samples/show-result-of-spatial-operations/README.md
similarity index 100%
rename from show-result-of-spatial-operations/README.md
rename to samples/show-result-of-spatial-operations/README.md
diff --git a/show-result-of-spatial-operations/README.metadata.json b/samples/show-result-of-spatial-operations/README.metadata.json
similarity index 100%
rename from show-result-of-spatial-operations/README.metadata.json
rename to samples/show-result-of-spatial-operations/README.metadata.json
diff --git a/samples/show-result-of-spatial-operations/build.gradle.kts b/samples/show-result-of-spatial-operations/build.gradle.kts
new file mode 100644
index 000000000..6b8fb74e3
--- /dev/null
+++ b/samples/show-result-of-spatial-operations/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showresultofspatialoperations"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-result-of-spatial-operations/show-result-of-spatial-operations.png b/samples/show-result-of-spatial-operations/show-result-of-spatial-operations.png
similarity index 100%
rename from show-result-of-spatial-operations/show-result-of-spatial-operations.png
rename to samples/show-result-of-spatial-operations/show-result-of-spatial-operations.png
diff --git a/samples/show-result-of-spatial-operations/src/main/AndroidManifest.xml b/samples/show-result-of-spatial-operations/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e1d297df2
--- /dev/null
+++ b/samples/show-result-of-spatial-operations/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt b/samples/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt
new file mode 100644
index 000000000..1aa24b97b
--- /dev/null
+++ b/samples/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt
@@ -0,0 +1,207 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showresultofspatialoperations
+
+import android.os.Bundle
+import android.widget.ArrayAdapter
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.MutablePart
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.showresultofspatialoperations.databinding.ShowResultOfSpatialOperationsActivityMainBinding
+import kotlinx.coroutines.launch
+
+private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowResultOfSpatialOperationsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_result_of_spatial_operations_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // enum to keep track of the selected operation to display on the map
+ enum class SpatialOperation(val menuPosition: Int) {
+ NO_OPERATION(0),
+ INTERSECTION(1),
+ UNION(2),
+ DIFFERENCE(3),
+ SYMMETRIC_DIFFERENCE(4)
+ }
+
+ // create the graphic overlays
+ private val inputGeometryGraphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+ private val resultGeometryGraphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
+
+ // simple black line symbol for outlines
+ private val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 1f)
+
+ // red fill symbol for result
+ private val resultFillSymbol =
+ SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.red, lineSymbol)
+
+ // the two polygons for perform spatial operations
+ private lateinit var inputPolygon1: Polygon
+ private lateinit var inputPolygon2: Polygon
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // set up the adapter
+ val arrayAdapter = ArrayAdapter(
+ this,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.operation)
+ )
+ activityMainBinding.bottomListItems.apply {
+ setAdapter(arrayAdapter)
+ setOnItemClickListener { _, _, i, _ ->
+ updateGeometry(i)
+ }
+ }
+
+ // set up the MapView
+ mapView.apply {
+ // create an ArcGISMap with a light gray basemap
+ map = ArcGISMap(BasemapStyle.ArcGISLightGray)
+ // create graphics overlays to show the inputs and results of the spatial operation
+ graphicsOverlays.add(inputGeometryGraphicsOverlay)
+ graphicsOverlays.add(resultGeometryGraphicsOverlay)
+ }
+
+ // create input polygons and add graphics to display these polygons in an overlay
+ createPolygons()
+
+ // center the map view on the input geometries
+ val envelope = GeometryEngine.union(inputPolygon1, inputPolygon2).extent
+ lifecycleScope.launch {
+ mapView.setViewpointGeometry(envelope, 20.0)
+ }
+ }
+
+ private fun updateGeometry(position: Int) {
+ // clear previous operation result
+ resultGeometryGraphicsOverlay.graphics.clear()
+ // create a result geometry of the spatial operation
+ var resultGeometry: Geometry? = null
+ // get the selected operation
+ when (SpatialOperation.entries.find { it.menuPosition == position }) {
+ SpatialOperation.NO_OPERATION -> { /* No operation needed */
+ }
+ SpatialOperation.INTERSECTION -> {
+ resultGeometry = GeometryEngine.intersectionOrNull(inputPolygon1, inputPolygon2)
+ }
+ SpatialOperation.UNION -> {
+ resultGeometry = GeometryEngine.union(inputPolygon1, inputPolygon2)
+ }
+ SpatialOperation.DIFFERENCE -> {
+ resultGeometry = GeometryEngine.differenceOrNull(inputPolygon1, inputPolygon2)
+ }
+ SpatialOperation.SYMMETRIC_DIFFERENCE -> {
+ resultGeometry = GeometryEngine.symmetricDifferenceOrNull(inputPolygon1, inputPolygon2)
+ }
+ null -> {}
+ }
+
+ // add a graphic from the result geometry, showing result in red
+ if (resultGeometry != null) {
+ val resultGraphic = Graphic(resultGeometry, resultFillSymbol).also {
+ // select the result to highlight it
+ it.isSelected = true
+ }
+ // add the result graphic to the graphic overlay
+ resultGeometryGraphicsOverlay.graphics.add(resultGraphic)
+ }
+ }
+
+ private fun createPolygons() {
+ // create input polygon 1
+ val polygonBuilder1 = PolygonBuilder(SpatialReference.webMercator()) {
+ // add points to the point collection
+ addPoint(Point(-13160.0, 6710100.0))
+ addPoint(Point(-13300.0, 6710500.0))
+ addPoint(Point(-13760.0, 6710730.0))
+ addPoint(Point(-14660.0, 6710000.0))
+ addPoint(Point(-13960.0, 6709400.0))
+ }
+ inputPolygon1 = polygonBuilder1.toGeometry()
+
+ // create and add a blue graphic to show input polygon 1
+ val blueFill = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.blue, lineSymbol)
+ inputGeometryGraphicsOverlay.graphics.add(Graphic(inputPolygon1, blueFill))
+
+ // outer ring
+ val outerRing = MutablePart.createWithPoints(
+ listOf(
+ Point(-13060.0, 6711030.0),
+ Point(-12160.0, 6710730.0),
+ Point(-13160.0, 6709700.0),
+ Point(-14560.0, 6710730.0),
+ Point(-13060.0, 6711030.0),
+ ),
+ SpatialReference.webMercator()
+ )
+
+ // inner ring
+ val innerRing = MutablePart.createWithPoints(
+ listOf(
+ Point(-13060.0, 6710910.0),
+ Point(-12450.0, 6710660.0),
+ Point(-13160.0, 6709900.0),
+ Point(-14160.0, 6710630.0),
+ Point(-13060.0, 6710910.0)
+ ),
+ SpatialReference.webMercator()
+ )
+
+ // add both parts (rings) to a polygon and create a geometry from it
+ inputPolygon2 = Polygon(listOf(outerRing, innerRing))
+
+ // create and add a green graphic to show input polygon 2
+ val greenFill = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.green, lineSymbol)
+ inputGeometryGraphicsOverlay.graphics.add(Graphic(inputPolygon2, greenFill))
+ }
+}
diff --git a/show-result-of-spatial-operations/src/main/res/layout/activity_main.xml b/samples/show-result-of-spatial-operations/src/main/res/layout/show_result_of_spatial_operations_activity_main.xml
similarity index 100%
rename from show-result-of-spatial-operations/src/main/res/layout/activity_main.xml
rename to samples/show-result-of-spatial-operations/src/main/res/layout/show_result_of_spatial_operations_activity_main.xml
diff --git a/samples/show-result-of-spatial-operations/src/main/res/values/strings.xml b/samples/show-result-of-spatial-operations/src/main/res/values/strings.xml
new file mode 100644
index 000000000..d4bc9c049
--- /dev/null
+++ b/samples/show-result-of-spatial-operations/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+
+ Show result of spatial operations
+ Select spatial operation
+
+
+ - No operation
+ - Intersection
+ - Union
+ - Difference
+ - Symmetric difference
+
+
diff --git a/show-result-of-spatial-relationships/README.md b/samples/show-result-of-spatial-relationships/README.md
similarity index 100%
rename from show-result-of-spatial-relationships/README.md
rename to samples/show-result-of-spatial-relationships/README.md
diff --git a/show-result-of-spatial-relationships/README.metadata.json b/samples/show-result-of-spatial-relationships/README.metadata.json
similarity index 100%
rename from show-result-of-spatial-relationships/README.metadata.json
rename to samples/show-result-of-spatial-relationships/README.metadata.json
diff --git a/samples/show-result-of-spatial-relationships/build.gradle.kts b/samples/show-result-of-spatial-relationships/build.gradle.kts
new file mode 100644
index 000000000..4360788b5
--- /dev/null
+++ b/samples/show-result-of-spatial-relationships/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showresultofspatialrelationships"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-result-of-spatial-relationships/show-result-of-spatial-relationships.png b/samples/show-result-of-spatial-relationships/show-result-of-spatial-relationships.png
similarity index 100%
rename from show-result-of-spatial-relationships/show-result-of-spatial-relationships.png
rename to samples/show-result-of-spatial-relationships/show-result-of-spatial-relationships.png
diff --git a/samples/show-result-of-spatial-relationships/src/main/AndroidManifest.xml b/samples/show-result-of-spatial-relationships/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..5e546227e
--- /dev/null
+++ b/samples/show-result-of-spatial-relationships/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt b/samples/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt
new file mode 100644
index 000000000..4cb4615fe
--- /dev/null
+++ b/samples/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt
@@ -0,0 +1,268 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showresultofspatialrelationships
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.data.SpatialRelationship
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.ScreenCoordinate
+import com.esri.arcgismaps.sample.showresultofspatialrelationships.databinding.ShowResultOfSpatialRelationshipsActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: ShowResultOfSpatialRelationshipsActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.show_result_of_spatial_relationships_activity_main)
+ }
+
+ // create a MapView using binding
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ // create a graphics overlay
+ private val graphicsOverlay by lazy {
+ GraphicsOverlay()
+ }
+
+ // text view to display the selected graphic
+ private val selectedGraphicTV by lazy {
+ activityMainBinding.selectedGraphicTV
+ }
+
+ // create the polygon graphic
+ private val polygonGraphic by lazy {
+ // add polygon points to the polygon builder
+ val polygonBuilder = PolygonBuilder(SpatialReference.webMercator()) {
+ addPoint(Point(-5991501.677830, 5599295.131468))
+ addPoint(Point(-6928550.398185, 2087936.739807))
+ addPoint(Point(-3149463.800709, 1840803.011362))
+ addPoint(Point(-1563689.043184, 3714900.452072))
+ addPoint(Point(-3180355.516764, 5619889.608838))
+ }
+ // create a polygon from the polygon builder
+ val polygon = polygonBuilder.toGeometry()
+ val polygonSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.ForwardDiagonal, Color.green,
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
+ )
+ Graphic(polygon, polygonSymbol)
+ }
+
+
+ // create the polyline graphic
+ private val polylineGraphic by lazy {
+ // add polyline points to the polyline builder
+ val polylineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
+ addPoint(Point(-4354240.726880, -609939.795721))
+ addPoint(Point(-3427489.245210, 2139422.933233))
+ addPoint(Point(-2109442.693501, 4301843.057130))
+ addPoint(Point(-1810822.771630, 7205664.366363))
+ }
+ // create a polyline graphic
+ val polyline = polylineBuilder.toGeometry()
+ val polylineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 4f)
+ Graphic(polyline, polylineSymbol)
+ }
+
+
+ // create the point graphic
+ private val pointGraphic by lazy {
+ // create a point graphic
+ val point = Point(-4487263.495911, 3699176.480377, SpatialReference.webMercator())
+ val locationMarker = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.blue, 10f)
+ Graphic(point, locationMarker)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ // add MapView to the lifecycle
+ lifecycle.addObserver(mapView)
+ mapView.apply {
+ // add a map with a topographic basemap style
+ map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ // set selection color
+ selectionProperties.color = Color.red
+ // add graphics overlay
+ graphicsOverlays.add(graphicsOverlay)
+ //set viewpoint
+ setViewpoint(Viewpoint(33.183564, -42.428668, 90000000.0))
+ }
+ // add the graphics to the graphics overlay
+ graphicsOverlay.graphics.addAll(listOf(polygonGraphic, polylineGraphic, pointGraphic))
+ // set an on touch listener on the map view
+ lifecycleScope.launch {
+ mapView.onSingleTapConfirmed.collect { tapEvent ->
+ // get the tapped coordinate
+ val screenCoordinate = tapEvent.screenCoordinate
+ // identify the relationships of tapped graphic
+ identifyGraphicRelationships(screenCoordinate)
+ }
+ }
+ }
+
+ /**
+ * Identifies the selected graphic tapped at the [screenCoordinate]
+ * and finds the relations to other graphics on the map.
+ */
+ private suspend fun identifyGraphicRelationships(screenCoordinate: ScreenCoordinate) {
+ // get the graphic selected at the given ScreenCoordinate
+ val identifyGraphicsOverlayResult =
+ mapView.identifyGraphicsOverlay(graphicsOverlay, screenCoordinate, 1.0, false)
+ // get identified graphics overlay, else show an error
+ val identifyGraphicsOverlay = identifyGraphicsOverlayResult.getOrElse {
+ return showError(it.message.toString())
+ }
+ // get the identified selected graphics
+ val identifiedGraphics = identifyGraphicsOverlay.graphics
+ // if no graphic was selected
+ if (identifiedGraphics.isEmpty()) {
+ // display text and clear selected
+ selectedGraphicTV.text = getString(R.string.select_graphic)
+ graphicsOverlay.clearSelection()
+ return
+ }
+ // create HashMap that will hold relationships in between graphics
+ val relationships = mutableMapOf>()
+ // add the graphics as keys to the hashmap
+ relationships["Point"] = emptyList()
+ relationships["Polyline"] = emptyList()
+ relationships["Polygon"] = emptyList()
+ // select the identified graphic
+ graphicsOverlay.clearSelection()
+ // get the first graphic identified
+ val identifiedGraphic = identifiedGraphics[0]
+ // set the identified graphic to be selected
+ identifiedGraphic.isSelected = true
+ // tracks the type of geometry selected
+ var selectedGraphicName = ""
+ // find the geometry of the selected graphic
+ when (val selectedGeometry = identifiedGraphic.geometry) {
+ // if selected geometry is a point
+ is Point -> {
+ // get the point-polyline relationships
+ relationships["Polyline"] =
+ getSpatialRelationships(selectedGeometry, polylineGraphic.geometry)
+ // get the point-polygon relationships
+ relationships["Polygon"] =
+ getSpatialRelationships(selectedGeometry, polygonGraphic.geometry)
+ // update the name of the selected geometry
+ selectedGraphicName = "Point"
+ }
+ // if selected geometry is a polyline
+ is Polyline -> {
+ // get the polyline-polygon relationships
+ relationships["Polygon"] =
+ getSpatialRelationships(selectedGeometry, polygonGraphic.geometry)
+ // get the polyline-point relationships
+ relationships["Point"] =
+ getSpatialRelationships(selectedGeometry, pointGraphic.geometry)
+ // update the name of the selected geometry
+ selectedGraphicName = "Polyline"
+ }
+ // if selected geometry is a polygon
+ is Polygon -> {
+ // get the polygon-polyline relationships
+ relationships["Polyline"] =
+ getSpatialRelationships(selectedGeometry, polylineGraphic.geometry)
+ // get the polygon-point relationships
+ relationships["Point"] =
+ getSpatialRelationships(selectedGeometry, pointGraphic.geometry)
+ // update the name of the selected geometry
+ selectedGraphicName = "Polygon"
+ }
+ // no other graphic on map
+ else -> {}
+ }
+ // display selected graphic text
+ selectedGraphicTV.text = "$selectedGraphicName geometry is selected"
+ // create and display a dialog with the established graphics
+ RelationshipsDialog(layoutInflater, this, relationships, selectedGraphicName).createAndDisplayDialog()
+
+ }
+
+ /**
+ * Gets a list of spatial relationships that the [a] geometry has to the [b] geometry.
+ */
+ private fun getSpatialRelationships(
+ a: Geometry?,
+ b: Geometry?
+ ): List {
+ // check if either geometry is null
+ if (a == null || b == null) {
+ return emptyList()
+ }
+ val relationships: MutableList = mutableListOf()
+ if (GeometryEngine.crosses(a, b))
+ relationships.add(SpatialRelationship.Crosses)
+ if (GeometryEngine.contains(a, b))
+ relationships.add(SpatialRelationship.Contains)
+ if (GeometryEngine.disjoint(a, b))
+ relationships.add(SpatialRelationship.Disjoint)
+ if (GeometryEngine.intersects(a, b))
+ relationships.add(SpatialRelationship.Intersects)
+ if (GeometryEngine.overlaps(a, b))
+ relationships.add(SpatialRelationship.Overlaps)
+ if (GeometryEngine.touches(a, b))
+ relationships.add(SpatialRelationship.Touches)
+ if (GeometryEngine.within(a, b))
+ relationships.add(SpatialRelationship.Within)
+ return relationships
+ }
+
+ private fun showError(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+}
diff --git a/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/RelationshipsDialog.kt b/samples/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/RelationshipsDialog.kt
similarity index 100%
rename from show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/RelationshipsDialog.kt
rename to samples/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/RelationshipsDialog.kt
diff --git a/show-result-of-spatial-relationships/src/main/res/layout/dialog_layout.xml b/samples/show-result-of-spatial-relationships/src/main/res/layout/dialog_layout.xml
similarity index 100%
rename from show-result-of-spatial-relationships/src/main/res/layout/dialog_layout.xml
rename to samples/show-result-of-spatial-relationships/src/main/res/layout/dialog_layout.xml
diff --git a/show-result-of-spatial-relationships/src/main/res/layout/activity_main.xml b/samples/show-result-of-spatial-relationships/src/main/res/layout/show_result_of_spatial_relationships_activity_main.xml
similarity index 100%
rename from show-result-of-spatial-relationships/src/main/res/layout/activity_main.xml
rename to samples/show-result-of-spatial-relationships/src/main/res/layout/show_result_of_spatial_relationships_activity_main.xml
diff --git a/samples/show-result-of-spatial-relationships/src/main/res/values/strings.xml b/samples/show-result-of-spatial-relationships/src/main/res/values/strings.xml
new file mode 100644
index 000000000..774d9fbce
--- /dev/null
+++ b/samples/show-result-of-spatial-relationships/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Show result of spatial relationships
+ Tap on the map to select the graphic
+ with polyline:
+ with polygon:
+ with point:
+
diff --git a/show-viewshed-from-point-in-scene/README.md b/samples/show-viewshed-from-point-in-scene/README.md
similarity index 100%
rename from show-viewshed-from-point-in-scene/README.md
rename to samples/show-viewshed-from-point-in-scene/README.md
diff --git a/show-viewshed-from-point-in-scene/README.metadata.json b/samples/show-viewshed-from-point-in-scene/README.metadata.json
similarity index 100%
rename from show-viewshed-from-point-in-scene/README.metadata.json
rename to samples/show-viewshed-from-point-in-scene/README.metadata.json
diff --git a/samples/show-viewshed-from-point-in-scene/build.gradle.kts b/samples/show-viewshed-from-point-in-scene/build.gradle.kts
new file mode 100644
index 000000000..93380e23b
--- /dev/null
+++ b/samples/show-viewshed-from-point-in-scene/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.showviewshedfrompointinscene"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/show-viewshed-from-point-in-scene/show-viewshed-from-point-in-scene.png b/samples/show-viewshed-from-point-in-scene/show-viewshed-from-point-in-scene.png
similarity index 100%
rename from show-viewshed-from-point-in-scene/show-viewshed-from-point-in-scene.png
rename to samples/show-viewshed-from-point-in-scene/show-viewshed-from-point-in-scene.png
diff --git a/samples/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml b/samples/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt
new file mode 100644
index 000000000..5ada93490
--- /dev/null
+++ b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt
@@ -0,0 +1,51 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.showviewshedfrompointinscene
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.showviewshedfrompointinscene.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ ViewshedLocationApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun ViewshedLocationApp() {
+ Surface(color = MaterialTheme.colorScheme.background) {
+ MainScreen(sampleName = getString(R.string.show_viewshed_from_point_in_scene_app_name))
+ }
+ }
+}
diff --git a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/components/SceneViewModel.kt b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/components/SceneViewModel.kt
similarity index 100%
rename from show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/components/SceneViewModel.kt
rename to samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/components/SceneViewModel.kt
diff --git a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/MainScreen.kt b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/MainScreen.kt
similarity index 100%
rename from show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/MainScreen.kt
rename to samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/MainScreen.kt
diff --git a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedOptionsScreen.kt b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedOptionsScreen.kt
similarity index 100%
rename from show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedOptionsScreen.kt
rename to samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedOptionsScreen.kt
diff --git a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedSlider.kt b/samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedSlider.kt
similarity index 100%
rename from show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedSlider.kt
rename to samples/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/screens/ViewshedSlider.kt
diff --git a/samples/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml b/samples/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml
new file mode 100644
index 000000000..9272da39d
--- /dev/null
+++ b/samples/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ Show viewshed from point in scene
+ https://scene.arcgis.com/arcgis/rest/services/BREST_DTM_1M/ImageServer
+
+ https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer/layers/0
+
+
diff --git a/sketch-on-map/README.md b/samples/sketch-on-map/README.md
similarity index 100%
rename from sketch-on-map/README.md
rename to samples/sketch-on-map/README.md
diff --git a/sketch-on-map/README.metadata.json b/samples/sketch-on-map/README.metadata.json
similarity index 100%
rename from sketch-on-map/README.metadata.json
rename to samples/sketch-on-map/README.metadata.json
diff --git a/samples/sketch-on-map/build.gradle.kts b/samples/sketch-on-map/build.gradle.kts
new file mode 100644
index 000000000..1b1e057fc
--- /dev/null
+++ b/samples/sketch-on-map/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.sketchonmap"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/sketch-on-map/sketch-on-map.png b/samples/sketch-on-map/sketch-on-map.png
similarity index 100%
rename from sketch-on-map/sketch-on-map.png
rename to samples/sketch-on-map/sketch-on-map.png
diff --git a/samples/sketch-on-map/src/main/AndroidManifest.xml b/samples/sketch-on-map/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..c5e156902
--- /dev/null
+++ b/samples/sketch-on-map/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt b/samples/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt
new file mode 100644
index 000000000..5561de6f2
--- /dev/null
+++ b/samples/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt
@@ -0,0 +1,279 @@
+/* Copyright 2023 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.sketchonmap
+
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.lifecycleScope
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.GeometryBuilder
+import com.arcgismaps.geometry.GeometryType
+import com.arcgismaps.geometry.Multipoint
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.geometryeditor.FreehandTool
+import com.arcgismaps.mapping.view.geometryeditor.GeometryEditor
+import com.arcgismaps.mapping.view.geometryeditor.VertexTool
+import com.esri.arcgismaps.sample.sketchonmap.databinding.SketchOnMapActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+
+ private val activityMainBinding: SketchOnMapActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.sketch_on_map_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ private val selectedGeometryDropdown by lazy {
+ activityMainBinding.pointLinePolygonToolbar.selectGeometryDropdown
+ }
+
+ // create a symbol for the point graphic
+ private val pointSymbol: SimpleMarkerSymbol by lazy {
+ SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Square,
+ Color(getColor(R.color.point_symbol_color)),
+ 20f)
+ }
+
+ // create a symbol for a line graphic
+ private val lineSymbol: SimpleLineSymbol by lazy {
+ SimpleLineSymbol(
+ SimpleLineSymbolStyle.Solid,
+ Color(getColor(R.color.line_symbol_color)),
+ 4f
+ )
+ }
+
+ // create a symbol for the fill graphic
+ private val fillSymbol: SimpleFillSymbol by lazy {
+ SimpleFillSymbol(
+ SimpleFillSymbolStyle.Cross,
+ Color(getColor(R.color.fill_symbol_color)),
+ lineSymbol
+ )
+ }
+
+ // keep the instance graphic overlay to add graphics on the map
+ private var graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
+
+ // keep the instance of the freehand tool
+ private val freehandTool: FreehandTool = FreehandTool()
+
+ // keep the instance of the vertex tool
+ private val vertexTool: VertexTool = VertexTool()
+
+ // keep the instance to create new geometries, and change existing geometries
+ private var geometryEditor: GeometryEditor = GeometryEditor()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // create and add a map with a navigation night basemap style
+ mapView.apply {
+ map = ArcGISMap(BasemapStyle.ArcGISLightGray)
+ setViewpoint(Viewpoint(34.056295, -117.195800, 100000.0))
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // set MapView's geometry editor to sketch on map
+ mapView.geometryEditor = geometryEditor
+
+ // enable/disable the undo button if last event can be undone
+ lifecycleScope.launch {
+ geometryEditor.canUndo.collect { value ->
+ activityMainBinding.pointLinePolygonToolbar.undoButton.isEnabled = value
+ }
+ }
+
+ // enable/disable the redo button if the last event can be redone
+ lifecycleScope.launch {
+ geometryEditor.canRedo.collect { value ->
+ activityMainBinding.pointLinePolygonToolbar.redoButton.isEnabled = value
+ }
+ }
+
+ // set up the geometry list dropdown
+ selectedGeometryDropdown.apply {
+ // set the adapter to the list of geometries
+ setAdapter(
+ ArrayAdapter(
+ applicationContext,
+ com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
+ resources.getStringArray(R.array.geometry_list)
+ )
+ )
+
+ // set the dropdown click listener
+ onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
+ // set the GeometryEditorTool and then start the editing process
+ geometryEditor.apply {
+ when (position) {
+ 0 -> {
+ tool = freehandTool
+ start(GeometryType.Polygon)
+ }
+ 1 -> {
+ tool = freehandTool
+ start(GeometryType.Polyline)
+ }
+ 2 -> {
+ tool = vertexTool
+ start(GeometryType.Multipoint)
+ }
+ 3 -> {
+ tool = vertexTool
+ start(GeometryType.Point)
+ }
+ 4 -> {
+ tool = vertexTool
+ start(GeometryType.Polygon)
+ }
+ 5 -> {
+ tool = vertexTool
+ start(GeometryType.Polyline)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Undo the last event on the GeometryEditor.
+ */
+ fun undo(view: View) {
+ geometryEditor.undo()
+ }
+
+ /**
+ * Redo the last undone event on the GeometryEditor.
+ */
+ fun redo(view: View) {
+ geometryEditor.redo()
+ }
+
+ /**
+ * When the stop button is clicked, check that sketch is valid. If so, get the geometry from
+ * the sketch, set its symbol and add it to the graphics overlay.
+ */
+ fun stop(view: View) {
+ // get the geometry from sketch editor
+ val sketchGeometry = geometryEditor.geometry.value
+ ?: return showMessage("Error retrieving geometry")
+
+ if (!GeometryBuilder.builder(sketchGeometry).isSketchValid) {
+ return reportNotValid()
+ }
+
+ // stops the editing session
+ geometryEditor.stop()
+
+ // clear the UI selection
+ selectedGeometryDropdown.setText("")
+ selectedGeometryDropdown.clearFocus()
+
+ // create a graphic from the sketch editor geometry
+ val graphic = Graphic(sketchGeometry).apply {
+ // assign a symbol based on geometry type
+ symbol = when (sketchGeometry) {
+ is Polygon -> fillSymbol
+ is Polyline -> lineSymbol
+ is Point, is Multipoint -> pointSymbol
+ else -> null
+ }
+ }
+
+ // add the graphic to the graphics overlay
+ graphicsOverlay.graphics.add(graphic)
+ }
+
+ /**
+ * Clear the MapView of all the graphics and reset selections
+ */
+ fun clear(view: View) {
+ geometryEditor.clearGeometry()
+ geometryEditor.clearSelection()
+ geometryEditor.stop()
+ selectedGeometryDropdown.setText("")
+ selectedGeometryDropdown.clearFocus()
+ showMessage(getString(R.string.cleared_message))
+ }
+
+ /**
+ * Clear all editing and applied graphics on the map
+ */
+ fun restart(view: View) {
+ graphicsOverlay.graphics.clear()
+ geometryEditor.clearGeometry()
+ geometryEditor.clearSelection()
+ geometryEditor.stop()
+ selectedGeometryDropdown.setText("")
+ selectedGeometryDropdown.clearFocus()
+ showMessage(getString(R.string.restart_message))
+ }
+
+ /**
+ * Called if sketch is invalid. Reports to user why the sketch was invalid.
+ */
+ private fun reportNotValid() {
+ // get the geometry currently being added to map
+ val geometry = geometryEditor.geometry.value ?: return showMessage("Geometry not found")
+ // find the geometry type, and set the valid message
+ val validIfText: String = when (geometry) {
+ is Point -> getString(R.string.invalid_point_message)
+ is Multipoint -> getString(R.string.invalid_multipoint_message)
+ is Polyline -> getString(R.string.invalid_polyline_message)
+ is Polygon -> getString(R.string.invalid_polygon_message)
+ else -> getString(R.string.none_selected_message)
+ }
+ // set the invalid message to the TextView.
+ showMessage(validIfText)
+ }
+
+ private fun showMessage(message: String) {
+ Log.e(localClassName, message)
+ Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
+ }
+}
diff --git a/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_redo.png b/samples/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_redo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-hdpi/ic_menu_redo.png
rename to samples/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_redo.png
diff --git a/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_undo.png b/samples/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_undo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-hdpi/ic_menu_undo.png
rename to samples/sketch-on-map/src/main/res/drawable-hdpi/ic_menu_undo.png
diff --git a/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_redo.png b/samples/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_redo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-mdpi/ic_menu_redo.png
rename to samples/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_redo.png
diff --git a/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_undo.png b/samples/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_undo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-mdpi/ic_menu_undo.png
rename to samples/sketch-on-map/src/main/res/drawable-mdpi/ic_menu_undo.png
diff --git a/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_redo.png b/samples/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_redo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_redo.png
rename to samples/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_redo.png
diff --git a/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_undo.png b/samples/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_undo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_undo.png
rename to samples/sketch-on-map/src/main/res/drawable-xhdpi/ic_menu_undo.png
diff --git a/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_redo.png b/samples/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_redo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_redo.png
rename to samples/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_redo.png
diff --git a/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_undo.png b/samples/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_undo.png
similarity index 100%
rename from sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_undo.png
rename to samples/sketch-on-map/src/main/res/drawable-xxhdpi/ic_menu_undo.png
diff --git a/sketch-on-map/src/main/res/drawable/button_bar_background.xml b/samples/sketch-on-map/src/main/res/drawable/button_bar_background.xml
similarity index 100%
rename from sketch-on-map/src/main/res/drawable/button_bar_background.xml
rename to samples/sketch-on-map/src/main/res/drawable/button_bar_background.xml
diff --git a/sketch-on-map/src/main/res/drawable/button_pressed.xml b/samples/sketch-on-map/src/main/res/drawable/button_pressed.xml
similarity index 100%
rename from sketch-on-map/src/main/res/drawable/button_pressed.xml
rename to samples/sketch-on-map/src/main/res/drawable/button_pressed.xml
diff --git a/sketch-on-map/src/main/res/drawable/ic_round_commit_24.xml b/samples/sketch-on-map/src/main/res/drawable/ic_round_commit_24.xml
similarity index 100%
rename from sketch-on-map/src/main/res/drawable/ic_round_commit_24.xml
rename to samples/sketch-on-map/src/main/res/drawable/ic_round_commit_24.xml
diff --git a/sketch-on-map/src/main/res/drawable/ic_round_delete_outline_24.xml b/samples/sketch-on-map/src/main/res/drawable/ic_round_delete_outline_24.xml
similarity index 100%
rename from sketch-on-map/src/main/res/drawable/ic_round_delete_outline_24.xml
rename to samples/sketch-on-map/src/main/res/drawable/ic_round_delete_outline_24.xml
diff --git a/sketch-on-map/src/main/res/drawable/ic_round_refresh_24.xml b/samples/sketch-on-map/src/main/res/drawable/ic_round_refresh_24.xml
similarity index 100%
rename from sketch-on-map/src/main/res/drawable/ic_round_refresh_24.xml
rename to samples/sketch-on-map/src/main/res/drawable/ic_round_refresh_24.xml
diff --git a/sketch-on-map/src/main/res/layout/point_line_polygon_toolbar.xml b/samples/sketch-on-map/src/main/res/layout/point_line_polygon_toolbar.xml
similarity index 100%
rename from sketch-on-map/src/main/res/layout/point_line_polygon_toolbar.xml
rename to samples/sketch-on-map/src/main/res/layout/point_line_polygon_toolbar.xml
diff --git a/sketch-on-map/src/main/res/layout/activity_main.xml b/samples/sketch-on-map/src/main/res/layout/sketch_on_map_activity_main.xml
similarity index 100%
rename from sketch-on-map/src/main/res/layout/activity_main.xml
rename to samples/sketch-on-map/src/main/res/layout/sketch_on_map_activity_main.xml
diff --git a/sketch-on-map/src/main/res/values/colors.xml b/samples/sketch-on-map/src/main/res/values/colors.xml
similarity index 100%
rename from sketch-on-map/src/main/res/values/colors.xml
rename to samples/sketch-on-map/src/main/res/values/colors.xml
diff --git a/samples/sketch-on-map/src/main/res/values/strings.xml b/samples/sketch-on-map/src/main/res/values/strings.xml
new file mode 100644
index 000000000..322d9db4c
--- /dev/null
+++ b/samples/sketch-on-map/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+
+ Sketch on map
+ Undo
+ Redo
+ Clear
+ Commit edits
+ Point only valid if it contains an x & y coordinate.
+ Multipoint only valid if it contains at least one vertex.
+ Polyline only valid if it contains at least one part of 2 or more vertices.
+ Polygon only valid if it contains at least one part of 3 or more vertices which form a closed ring.
+ No sketch creation mode selected.
+ Restart
+ Cleared editing sketch from map
+ Cleared all graphics from map
+ Select geometry
+
+ - Freehand Polygon
+ - Freehand Polyline
+ - Multipoint
+ - Point
+ - Polygon
+ - Polyline
+
+
diff --git a/samples/snap-geometry-edits/README.md b/samples/snap-geometry-edits/README.md
new file mode 100644
index 000000000..d9ef3c6f6
--- /dev/null
+++ b/samples/snap-geometry-edits/README.md
@@ -0,0 +1,74 @@
+# Snap geometry edits
+
+Use the Geometry Editor to edit a geometry and align it to existing geometries on a map.
+
+![Image of Snap geometry edits](snap-geometry-edits.png)
+
+## Use case
+
+A field worker can create new features by editing and snapping the vertices of a geometry to existing features on a map. In a water distribution network, service line features can be represented with the polyline geometry type. By snapping the vertices of a proposed service line to existing features in the network, an exact footprint can be identified to show the path of the service line and what features in the network it connects to. The feature layer containing the service lines can then be accurately modified to include the proposed line.
+
+## How to use the sample
+
+To create a geometry, press the create button to choose the geometry type you want to create (i.e. points, multipoints, polyline, or polygon) and interactively tap and drag on the map view to create the geometry.
+
+Snap settings can be configured by enabling and disabling snapping, feature snapping, geometry guides, and snap sources.
+
+To interactively snap a vertex to a feature or graphic, ensure that snapping is enabled for the relevant snap source and move the map position of the reticle to nearby an existing feature or graphic. When the reticle is close to that existing geoelement, the edit position will be adjusted to coincide with (or snap to), edges and vertices of its geometry. Tap to place the vertex at the snapped location.
+
+To edit a geometry, tap the geometry to be edited in the map and then edit the geometry by tapping and moving its vertices and snapping them to nearby features or graphics.
+
+To edit a vertex using the reticle, tap when the reticle is located over the vertex, drag the map to move the position of the reticle, then tap a second time to place the vertex.
+
+To undo changes made to the geometry, press the undo button.
+
+To delete a vertex, tap when the reticle is located over the vertex and then press the delete button.
+
+To save your edits, press the save button.
+
+## How it works
+
+1. Create a `Map` from the `URL` and connect it to the `MapView`.
+2. Set the map's `LoadSettings.featureTilingMode` to `enabledWithFullResolutionWhenSupported`.
+3. Create a `GeometryEditor` and connect it to the map view.
+4. Create a `ReticleVertexTool` and set it into the `GeometryEditor.tool`.
+5. Call `syncSourceSettings` after the map's operational layers are loaded and the geometry editor connected to the map view.
+6. Set `SnapSettings.isEnabled` and `SnapSourceSettings.isEnabled` to true for the `SnapSource` of interest.
+7. Enable or disable geometry guides using `SnapSettings.isGeometryGuidesEnabled` and feature snapping using `SnapSettings.isFeatureSnappingEnabled`.
+8. Start the geometry editor with a `GeometryType`.
+
+## Relevant API
+
+* FeatureLayer
+* Geometry
+* GeometryEditor
+* GeometryEditorReticle
+* GeometryEditorStyle
+* GraphicsOverlay
+* MapView
+* ReticleVertexTool
+* SnapSettings
+* SnapSource
+* SnapSourceSettings
+
+## About the data
+
+The [Naperville water distribution network](https://www.arcgis.com/home/item.html?id=b95fe18073bc4f7788f0375af2bb445e) is based on ArcGIS Solutions for Water Utilities and provides a realistic depiction of a theoretical stormwater network.
+
+## Additional information
+
+Snapping is used to maintain data integrity between different sources of data when editing, so it is important that each `SnapSource` provides full resolution geometries to be valid for snapping. This means that some of the default optimizations used to improve the efficiency of data transfer and display of polygon and polyline layers based on feature services are not appropriate for use with snapping.
+
+To snap to polygon and polyline layers, the recommended approach is to set the `FeatureLayer`'s feature tiling mode to `FeatureTilingMode.enabledWithFullResolutionWhenSupported` and use the default `ServiceFeatureTable` feature request mode `FeatureRequestMode.onInteractionCache`. Local data sources, such as geodatabases, always provide full resolution geometries.
+
+Snapping can be used during interactive edits that move existing vertices using the `VertexTool` or `ReticleVertexTool`. It is also supported for adding new vertices for input devices with a hover event (such as a mouse move without a mouse button press). Using the `ReticleVertexTool` to add and move vertices allows users of touch screen devices to clearly see the visual cues for snapping.
+
+Geometry guides are enabled by default when snapping is enabled. These allow for snapping to a point coinciding with, parallel to, perpendicular to, or extending an existing geometry.
+
+On supported platforms haptic feedback on `SnapState.snappedToFeature` and `SnapState.snappedToGeometryGuide` is enabled by default when snapping is enabled. Custom haptic feedback can be configured by setting `SnapSettings.isHapticFeedbackEnabled` to false and listening to `GeometryEditor.snapChanged` events to provide specific feedback depending on the `SnapState`.
+
+This sample uses the GeoViewCompose Toolkit module to be able to implement a Composable MapView.
+
+## Tags
+
+edit, feature, geometryeditor, geoviewcompose, graphics, layers, magnify, map, reticle, snapping
diff --git a/snap-geometry-edits/README.metadata.json b/samples/snap-geometry-edits/README.metadata.json
similarity index 100%
rename from snap-geometry-edits/README.metadata.json
rename to samples/snap-geometry-edits/README.metadata.json
diff --git a/samples/snap-geometry-edits/build.gradle.kts b/samples/snap-geometry-edits/build.gradle.kts
new file mode 100644
index 000000000..688ce154e
--- /dev/null
+++ b/samples/snap-geometry-edits/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.android.library.compose)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.snapgeometryedits"
+ buildFeatures {
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/snap-geometry-edits/snap-geometry-edits.png b/samples/snap-geometry-edits/snap-geometry-edits.png
similarity index 100%
rename from snap-geometry-edits/snap-geometry-edits.png
rename to samples/snap-geometry-edits/snap-geometry-edits.png
diff --git a/samples/snap-geometry-edits/src/main/AndroidManifest.xml b/samples/snap-geometry-edits/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..1768fda7f
--- /dev/null
+++ b/samples/snap-geometry-edits/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt
new file mode 100644
index 000000000..04d84dfc9
--- /dev/null
+++ b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt
@@ -0,0 +1,55 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.snapgeometryedits
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
+import com.esri.arcgismaps.sample.snapgeometryedits.screens.MainScreen
+
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ setContent {
+ SampleAppTheme {
+ SampleApp()
+ }
+ }
+ }
+
+ @Composable
+ private fun SampleApp() {
+ Surface(
+ color = MaterialTheme.colorScheme.background
+ ) {
+ MainScreen(
+ sampleName = getString(R.string.snap_geometry_edits_app_name)
+ )
+ }
+ }
+}
diff --git a/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt
new file mode 100644
index 000000000..a692fe7b8
--- /dev/null
+++ b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt
@@ -0,0 +1,272 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.snapgeometryedits.components
+
+import android.app.Application
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.AndroidViewModel
+import com.arcgismaps.geometry.GeometryType
+import com.arcgismaps.geometry.Multipoint
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.layers.FeatureTilingMode
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.arcgismaps.mapping.view.SingleTapConfirmedEvent
+import com.arcgismaps.mapping.view.geometryeditor.GeometryEditor
+import com.arcgismaps.mapping.view.geometryeditor.GeometryEditorStyle
+import com.arcgismaps.mapping.view.geometryeditor.ReticleVertexTool
+import com.arcgismaps.mapping.view.geometryeditor.SnapSourceSettings
+import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
+import com.esri.arcgismaps.sample.snapgeometryedits.R
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class MapViewModel(
+ application: Application,
+ private val sampleCoroutineScope: CoroutineScope
+) : AndroidViewModel(application) {
+ // create a map using the URL of the web map
+ val map = ArcGISMap(application.getString(R.string.web_map))
+
+ // create a graphic, graphic overlay, and geometry editorenc
+ private var identifiedGraphic = Graphic()
+ val graphicsOverlay = GraphicsOverlay()
+ val geometryEditor = GeometryEditor()
+
+ // create a mapViewProxy that will be used to identify features in the MapView and set the viewpoint
+ val mapViewProxy = MapViewProxy()
+
+ // create a messageDialogViewModel to handle dialog interactions
+ val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
+
+ // create lists for displaying the snap sources in the bottom sheet
+ private val _snapSourceSettingsList = MutableStateFlow(listOf())
+ val snapSourceList: StateFlow> = _snapSourceSettingsList
+
+ // create boolean flags to track the state of UI components
+ val isCreateButtonEnabled = mutableStateOf(false)
+ val isSnapSettingsButtonEnabled = mutableStateOf(false)
+ val isBottomSheetVisible = mutableStateOf(false)
+ val snappingCheckedState = mutableStateOf(geometryEditor.snapSettings.isEnabled)
+ val geometryGuidesCheckedState = mutableStateOf(geometryEditor.snapSettings.isGeometryGuidesEnabled)
+ val featureSnappingCheckedState = mutableStateOf(geometryEditor.snapSettings.isFeatureSnappingEnabled)
+ val snapSourceCheckedState = mutableStateListOf()
+ val isUndoButtonEnabled = geometryEditor.canUndo
+ val isSaveButtonEnabled = geometryEditor.isStarted
+ val isDeleteButtonEnabled = geometryEditor.isStarted
+
+ /**
+ * Configure the map and enable the UI after the map's layers are loaded.
+ */
+ init {
+ // set the id for the graphics overlay
+ graphicsOverlay.id = "Editor Graphics Overlay"
+ // set the tool for the geometry editor to use a reticle
+ geometryEditor.tool = ReticleVertexTool()
+ // set the feature layer's tiling mode
+ map.loadSettings.featureTilingMode =
+ FeatureTilingMode.EnabledWithFullResolutionWhenSupported
+
+ isCreateButtonEnabled.value = true
+ isSnapSettingsButtonEnabled.value = true
+
+ sampleCoroutineScope.launch {
+ // load the map
+ map.load().onSuccess {
+ // load the map's operational layers
+ map.operationalLayers.forEach { layer ->
+ layer.load().onFailure { error ->
+ messageDialogVM.showMessageDialog(
+ error.message.toString(),
+ error.cause.toString()
+ )
+ }
+ }
+ }.onFailure { error ->
+ messageDialogVM.showMessageDialog(
+ error.message.toString(),
+ error.cause.toString()
+ )
+ }
+ }
+ }
+
+ /**
+ * Synchronises the snap source collection with the map's operational layers, sets the bottom
+ * sheet UI, and shows it to configure snapping.
+ */
+ fun showBottomSheet() {
+ if (geometryEditor.snapSettings.sourceSettings.isEmpty()) {
+ // sync the snap source collection
+ geometryEditor.snapSettings.syncSourceSettings()
+ // initialise the snap source lists used for the bottom sheet
+ geometryEditor.snapSettings.sourceSettings.forEach { snapSource ->
+ snapSourceCheckedState.add(snapSource.isEnabled)
+ }
+ _snapSourceSettingsList.value = geometryEditor.snapSettings.sourceSettings
+ }
+ isBottomSheetVisible.value = true
+ }
+
+ /**
+ * Toggles snapping overall (both geometry guides and feature snapping) using the
+ * [checkedValue] from the bottom sheet.
+ */
+ fun snappingEnabledStatus(checkedValue: Boolean) {
+ snappingCheckedState.value = checkedValue
+ geometryEditor.snapSettings.isEnabled = snappingCheckedState.value
+ }
+
+ /**
+ * Toggles geometry guides using the [checkedValue] from the bottom sheet.
+ * Note geometry guides will still be disabled unless snapping is also enabled overall.
+ */
+ fun geometryGuidesEnabledStatus(checkedValue: Boolean) {
+ geometryGuidesCheckedState.value = checkedValue
+ geometryEditor.snapSettings.isGeometryGuidesEnabled = geometryGuidesCheckedState.value
+ }
+
+ /**
+ * Toggles feature snapping using the [checkedValue] from the bottom sheet.
+ * Note feature snapping will still be disabled unless snapping is also enabled overall.
+ */
+ fun featureSnappingEnabledStatus(checkedValue: Boolean) {
+ featureSnappingCheckedState.value = checkedValue
+ geometryEditor.snapSettings.isFeatureSnappingEnabled = featureSnappingCheckedState.value
+ }
+
+ /**
+ * Toggles snapping for the snap source at [index] using the [checkedValue] from the
+ * BottomSheet.
+ */
+ fun sourceEnabledStatus(checkedValue: Boolean, index: Int) {
+ snapSourceCheckedState[index] = checkedValue
+ geometryEditor.snapSettings.sourceSettings[index].isEnabled = snapSourceCheckedState[index]
+ }
+
+ /**
+ * Hides the bottom sheet.
+ */
+ fun dismissBottomSheet() {
+ isBottomSheetVisible.value = false
+ }
+
+ /**
+ * Starts the GeometryEditor using the selected [GeometryType].
+ */
+ fun startEditor(selectedGeometry: GeometryType) {
+ if (!geometryEditor.isStarted.value) {
+ geometryEditor.start(selectedGeometry)
+ isCreateButtonEnabled.value = false
+ }
+ }
+
+ /**
+ * Stops the GeometryEditor and updates the identified graphic or calls [createGraphic].
+ */
+ fun stopEditor() {
+ if (identifiedGraphic.geometry != null) {
+ identifiedGraphic.geometry = geometryEditor.stop()
+ identifiedGraphic.isSelected = false
+ } else if (geometryEditor.isStarted.value) {
+ createGraphic()
+ }
+ isCreateButtonEnabled.value = true
+ }
+
+ /**
+ * Creates a graphic from the geometry and add it to the GraphicsOverlay.
+ */
+ private fun createGraphic() {
+ val geometry = geometryEditor.stop()
+ ?: return messageDialogVM.showMessageDialog(
+ "Error!",
+ "Error stopping editing session"
+ )
+ val graphic = Graphic(geometry)
+
+ when (geometry) {
+ is Point, is Multipoint -> graphic.symbol = GeometryEditorStyle().vertexSymbol
+ is Polyline -> graphic.symbol = GeometryEditorStyle().lineSymbol
+ is Polygon -> graphic.symbol = GeometryEditorStyle().fillSymbol
+ else -> {}
+ }
+ graphicsOverlay.graphics.add(graphic)
+ graphic.isSelected = false
+ }
+
+ /**
+ * Deletes the selected element and stops the geometry editor if there are no
+ * more elements in the geometry.
+ */
+ fun deleteSelection() {
+ if (geometryEditor.geometry.value?.isEmpty == true) {
+ geometryEditor.stop()
+ isCreateButtonEnabled.value = true
+ }
+
+ val selectedElement = geometryEditor.selectedElement.value
+ if (selectedElement?.canDelete == true) {
+ geometryEditor.deleteSelectedElement()
+ }
+ }
+
+ /**
+ * Reverts the last event on the geometry editor.
+ */
+ fun editorUndo() {
+ geometryEditor.undo()
+ }
+
+ /**
+ * Identifies the graphic at the tapped screen coordinate in the provided [singleTapConfirmedEvent]
+ * and starts the GeometryEditor using the identified graphic's geometry. Hide the BottomSheet on
+ * [singleTapConfirmedEvent].
+ */
+ fun identify(singleTapConfirmedEvent: SingleTapConfirmedEvent) {
+ sampleCoroutineScope.launch {
+ val graphicsResult = mapViewProxy.identifyGraphicsOverlays(
+ screenCoordinate = singleTapConfirmedEvent.screenCoordinate,
+ tolerance = 10.0.dp,
+ returnPopupsOnly = false
+ ).getOrNull()
+
+ if (!geometryEditor.isStarted.value) {
+ if (graphicsResult != null) {
+ if (graphicsResult.isNotEmpty()) {
+ identifiedGraphic = graphicsResult[0].graphics[0]
+ identifiedGraphic.isSelected = true
+ identifiedGraphic.geometry?.let {
+ geometryEditor.start(it)
+ isCreateButtonEnabled.value = false
+ }
+ }
+ }
+ identifiedGraphic.geometry = null
+ }
+ }
+ dismissBottomSheet()
+ }
+}
diff --git a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/ButtonMenu.kt b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/ButtonMenu.kt
similarity index 100%
rename from snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/ButtonMenu.kt
rename to samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/ButtonMenu.kt
diff --git a/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt
new file mode 100644
index 000000000..0cb2e83ce
--- /dev/null
+++ b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt
@@ -0,0 +1,95 @@
+/* Copyright 2024 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.snapgeometryedits.screens
+
+import android.app.Application
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import com.arcgismaps.toolkit.geoviewcompose.MapView
+import com.esri.arcgismaps.sample.sampleslib.components.BottomSheet
+import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
+import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
+import com.esri.arcgismaps.sample.snapgeometryedits.components.MapViewModel
+
+/**
+ * Main screen layout for the sample app
+ */
+@Composable
+fun MainScreen(sampleName: String) {
+ // coroutineScope that will be cancelled when this call leaves the composition
+ val sampleCoroutineScope = rememberCoroutineScope()
+ // get the application property that will be used to construct MapViewModel
+ val sampleApplication = LocalContext.current.applicationContext as Application
+ // create a ViewModel to handle MapView interactions
+ val mapViewModel = remember { MapViewModel(sampleApplication, sampleCoroutineScope) }
+ // the collection of graphics overlays used by the MapView
+ val graphicsOverlayCollection = listOf(mapViewModel.graphicsOverlay)
+
+ Scaffold(
+ content = {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it)
+ ) {
+ SampleTopAppBar(title = sampleName)
+ MapView(
+ modifier = Modifier
+ .fillMaxSize()
+ .weight(1f),
+ arcGISMap = mapViewModel.map,
+ geometryEditor = mapViewModel.geometryEditor,
+ graphicsOverlays = graphicsOverlayCollection,
+ mapViewProxy = mapViewModel.mapViewProxy,
+ onSingleTapConfirmed = mapViewModel::identify,
+ onPan = { mapViewModel.dismissBottomSheet() }
+ )
+ ButtonMenu(mapViewModel = mapViewModel)
+ mapViewModel.messageDialogVM.apply {
+ if (dialogStatus) {
+ MessageDialog(
+ title = messageTitle,
+ description = messageDescription,
+ onDismissRequest = ::dismissDialog
+ )
+ }
+ }
+ }
+ BottomSheet(isVisible = mapViewModel.isBottomSheetVisible.value) {
+ SnapSettings(
+ snapSourceList = mapViewModel.snapSourceList.collectAsState(),
+ onSnappingChanged = mapViewModel::snappingEnabledStatus,
+ onGeometryGuidesChanged = mapViewModel::geometryGuidesEnabledStatus,
+ onFeatureSnappingChanged = mapViewModel::featureSnappingEnabledStatus,
+ onSnapSourceChanged = mapViewModel::sourceEnabledStatus,
+ isSnappingEnabled = mapViewModel.snappingCheckedState.value,
+ isGeometryGuidesEnabled = mapViewModel.geometryGuidesCheckedState.value,
+ isFeatureSnappingEnabled = mapViewModel.featureSnappingCheckedState.value,
+ isSnapSourceEnabled = mapViewModel.snapSourceCheckedState
+ ) { mapViewModel.dismissBottomSheet() }
+ }
+ }
+ )
+}
diff --git a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt
similarity index 83%
rename from snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt
rename to samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt
index c14f1aab2..ab71e58d8 100644
--- a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt
+++ b/samples/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/SnapSettingsScreen.kt
@@ -49,8 +49,12 @@ import com.esri.arcgismaps.sample.sampleslib.theme.SampleTypography
fun SnapSettings(
snapSourceList: State>,
onSnappingChanged: (Boolean) -> Unit = { },
+ onGeometryGuidesChanged: (Boolean) -> Unit = { },
+ onFeatureSnappingChanged: (Boolean) -> Unit = { },
onSnapSourceChanged: (Boolean, Int) -> Unit = { _: Boolean, _: Int -> },
isSnappingEnabled: Boolean,
+ isGeometryGuidesEnabled: Boolean,
+ isFeatureSnappingEnabled: Boolean,
isSnapSourceEnabled: List,
onDismiss: () -> Unit = { }
) {
@@ -116,7 +120,7 @@ fun SnapSettings(
Text(
modifier = Modifier.padding(20.dp, 0.dp, 0.dp, 0.dp),
style = SampleTypography.bodyLarge,
- text = "Enabled",
+ text = "Snapping enabled",
)
Switch(
checked = isSnappingEnabled,
@@ -125,6 +129,40 @@ fun SnapSettings(
}
)
}
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ modifier = Modifier.padding(20.dp, 0.dp, 0.dp, 0.dp),
+ style = SampleTypography.bodyLarge,
+ text = "Geometry guides",
+ )
+ Switch(
+ checked = isGeometryGuidesEnabled,
+ onCheckedChange = {
+ onGeometryGuidesChanged(it)
+ }
+ )
+ }
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ modifier = Modifier.padding(20.dp, 0.dp, 0.dp, 0.dp),
+ style = SampleTypography.bodyLarge,
+ text = "Feature snapping",
+ )
+ Switch(
+ checked = isFeatureSnappingEnabled,
+ onCheckedChange = {
+ onFeatureSnappingChanged(it)
+ }
+ )
+ }
}
}
SnapSourceUI(snapSourceList, isSnapSourceEnabled, onSnapSourceChanged, GeometryType.Point)
diff --git a/snap-geometry-edits/src/main/res/drawable/save.xml b/samples/snap-geometry-edits/src/main/res/drawable/save.xml
similarity index 100%
rename from snap-geometry-edits/src/main/res/drawable/save.xml
rename to samples/snap-geometry-edits/src/main/res/drawable/save.xml
diff --git a/snap-geometry-edits/src/main/res/drawable/undo.xml b/samples/snap-geometry-edits/src/main/res/drawable/undo.xml
similarity index 100%
rename from snap-geometry-edits/src/main/res/drawable/undo.xml
rename to samples/snap-geometry-edits/src/main/res/drawable/undo.xml
diff --git a/samples/snap-geometry-edits/src/main/res/values/strings.xml b/samples/snap-geometry-edits/src/main/res/values/strings.xml
new file mode 100644
index 000000000..4a470622a
--- /dev/null
+++ b/samples/snap-geometry-edits/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Snap geometry edits ,
+ https://www.arcgis.com/home/item.html?id=b95fe18073bc4f7788f0375af2bb445e
+
diff --git a/style-graphics-with-renderer/README.md b/samples/style-graphics-with-renderer/README.md
similarity index 100%
rename from style-graphics-with-renderer/README.md
rename to samples/style-graphics-with-renderer/README.md
diff --git a/style-graphics-with-renderer/README.metadata.json b/samples/style-graphics-with-renderer/README.metadata.json
similarity index 100%
rename from style-graphics-with-renderer/README.metadata.json
rename to samples/style-graphics-with-renderer/README.metadata.json
diff --git a/samples/style-graphics-with-renderer/build.gradle.kts b/samples/style-graphics-with-renderer/build.gradle.kts
new file mode 100644
index 000000000..fba80910f
--- /dev/null
+++ b/samples/style-graphics-with-renderer/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.stylegraphicswithrenderer"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/style-graphics-with-renderer/src/main/AndroidManifest.xml b/samples/style-graphics-with-renderer/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a5c16bae6
--- /dev/null
+++ b/samples/style-graphics-with-renderer/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt b/samples/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt
new file mode 100644
index 000000000..4a63dc7ef
--- /dev/null
+++ b/samples/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt
@@ -0,0 +1,294 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.stylegraphicswithrenderer
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.AngularUnit
+import com.arcgismaps.geometry.CubicBezierSegment
+import com.arcgismaps.geometry.EllipticArcSegment
+import com.arcgismaps.geometry.GeodesicEllipseParameters
+import com.arcgismaps.geometry.Geometry
+import com.arcgismaps.geometry.GeometryEngine
+import com.arcgismaps.geometry.LinearUnit
+import com.arcgismaps.geometry.MutablePart
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleRenderer
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.stylegraphicswithrenderer.databinding.StyleGraphicsWithRendererActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ // set up data binding for the activity
+ private val activityMainBinding: StyleGraphicsWithRendererActivityMainBinding by lazy {
+ DataBindingUtil.setContentView(this, R.layout.style_graphics_with_renderer_activity_main)
+ }
+
+ private val mapView by lazy {
+ activityMainBinding.mapView
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+ lifecycle.addObserver(mapView)
+
+ // add a map with a topographic basemap style
+ mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
+ mapView.setViewpoint(Viewpoint(15.169193, 16.333479, 100_000_000.0))
+
+ // add graphics overlays
+ mapView.graphicsOverlays.addAll(
+ listOf(
+ makeRenderedPointGraphicsOverlay(),
+ makeRenderedLineGraphicsOverlay(),
+ makeRenderedPolygonGraphicsOverlay(),
+ makeRenderedCurvedPolygonGraphicsOverlay(),
+ makeRenderedEllipseGraphicsOverlay()
+ )
+ )
+ }
+
+ /**
+ * Make a point, its graphic, a graphics overlay for it, and add it to the map view.
+ */
+ private fun makeRenderedPointGraphicsOverlay(): GraphicsOverlay {
+ // create point
+ val pointGeometry = Point(40e5, 40e5, SpatialReference.webMercator())
+ // create graphic for point
+ val pointGraphic = Graphic(pointGeometry)
+ // red diamond point symbol
+ val pointSymbol =
+ SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Diamond, Color.red, 10f)
+ // create simple renderer
+ val pointRenderer = SimpleRenderer(pointSymbol)
+ // create a new graphics overlay with these settings and add it to the map view
+ return GraphicsOverlay().apply {
+ // add graphic to overlay
+ graphics.add(pointGraphic)
+ // set the renderer on the graphics overlay to the new renderer
+ renderer = pointRenderer
+ }
+ }
+
+ /**
+ * Create a polyline, its graphic, a graphics overlay for it, and add it to the map view.
+ */
+ private fun makeRenderedLineGraphicsOverlay(): GraphicsOverlay {
+ // create line
+ val lineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
+ addPoint(-10e5, 40e5)
+ addPoint(20e5, 50e5)
+ }
+ // create graphic for polyline
+ val lineGraphic = Graphic(lineBuilder.toGeometry())
+ // solid blue line symbol
+ val lineSymbol =
+ SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 5f)
+ // create simple renderer
+ val lineRenderer = SimpleRenderer(lineSymbol)
+
+ // create graphic overlay for polyline and add it to the map view
+ return GraphicsOverlay().apply {
+ // add graphic to overlay
+ graphics.add(lineGraphic)
+ // set the renderer on the graphics overlay to the new renderer
+ renderer = lineRenderer
+ }
+ }
+
+ /**
+ * Create a polygon, its graphic, a graphics overlay for it, and add it to the map view.
+ */
+ private fun makeRenderedPolygonGraphicsOverlay(): GraphicsOverlay {
+ // create polygon
+ val polygonBuilder = PolygonBuilder(SpatialReference.webMercator()) {
+ addPoint(-20e5, 20e5)
+ addPoint(20e5, 20e5)
+ addPoint(20e5, -20e5)
+ addPoint(-20e5, -20e5)
+ }
+ // create graphic for polygon
+ val polygonGraphic = Graphic(polygonBuilder.toGeometry())
+ // solid yellow polygon symbol
+ val polygonSymbol =
+ SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.yellow, null)
+ // create simple renderer
+ val polygonRenderer = SimpleRenderer(polygonSymbol)
+
+ // create graphic overlay for polygon and add it to the map view
+ return GraphicsOverlay().apply {
+ // add graphic to overlay
+ graphics.add(polygonGraphic)
+ // set the renderer on the graphics overlay to the new renderer
+ renderer = polygonRenderer
+ }
+ }
+
+ /**
+ * Create a curved polygon, its graphic, a graphics overlay for it, and add it to the map view.
+ */
+ private fun makeRenderedCurvedPolygonGraphicsOverlay(): GraphicsOverlay {
+ // create a point for the center of the geometry
+ val originPoint = Point(40e5, 5e5, SpatialReference.webMercator())
+ // create polygon
+ val curvedPolygonGeometry = makeHeartGeometry(originPoint, 10e5)
+ // create graphic for polygon
+ val polygonGraphic = Graphic(curvedPolygonGeometry)
+ // create a simple fill symbol with outline
+ val curvedLineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 1f)
+ val curvedFillSymbol =
+ SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.red, curvedLineSymbol)
+ // create simple renderer
+ val polygonRenderer = SimpleRenderer(curvedFillSymbol)
+
+ // create graphic overlay for polygon and add it to the map view
+ return GraphicsOverlay().apply {
+ // add graphic to overlay
+ graphics.add(polygonGraphic)
+ // set the renderer on the graphics overlay to the new renderer
+ renderer = polygonRenderer
+ }
+ }
+
+ /**
+ * Create a heart-shape geometry with Bezier and elliptic arc segments from a given [center]
+ * point and [sideLength].
+ */
+ private fun makeHeartGeometry(center: Point, sideLength: Double): Geometry {
+ val spatialReference = center.spatialReference
+ // the x and y coordinates to simplify the calculation
+ val minX = center.x - 0.5 * sideLength
+ val minY = center.y - 0.5 * sideLength
+ // the radius of the arcs
+ val arcRadius = sideLength * 0.25
+
+ // bottom left curve
+ val leftCurveStart = Point(center.x, minY, spatialReference)
+ val leftCurveEnd = Point(minX, minY + 0.75 * sideLength, spatialReference)
+ val leftControlPoint1 = Point(center.x, minY + 0.25 * sideLength, spatialReference)
+ val leftControlPoint2 = Point(minX, center.y, spatialReference)
+ val leftCurve = CubicBezierSegment(
+ leftCurveStart,
+ leftControlPoint1,
+ leftControlPoint2,
+ leftCurveEnd,
+ spatialReference
+ )
+
+ // top left arc
+ val leftArcCenter =
+ Point(minX + 0.25 * sideLength, minY + 0.75 * sideLength, spatialReference)
+ val leftArc = EllipticArcSegment.createCircularEllipticArc(
+ leftArcCenter,
+ arcRadius,
+ Math.PI,
+ -Math.PI,
+ spatialReference
+ )
+
+ // top right arc
+ val rightArcCenter =
+ Point(minX + 0.75 * sideLength, minY + 0.75 * sideLength, spatialReference)
+ val rightArc = EllipticArcSegment.createCircularEllipticArc(
+ rightArcCenter,
+ arcRadius,
+ Math.PI,
+ -Math.PI,
+ spatialReference
+ )
+
+ // bottom right curve
+ val rightCurveStart = Point(minX + sideLength, minY + 0.75 * sideLength, spatialReference)
+ val rightControlPoint1 = Point(minX + sideLength, center.y, spatialReference)
+ val rightCurve = CubicBezierSegment(
+ rightCurveStart,
+ rightControlPoint1,
+ leftControlPoint1,
+ leftCurveStart,
+ spatialReference
+ )
+
+ // create a mutable part list
+ val heartParts = MutablePart.createWithSegments(
+ listOf(leftCurve, leftArc, rightArc, rightCurve),
+ spatialReference
+ )
+ // return the heart
+ return Polygon(listOf(heartParts).asIterable())
+ }
+
+ /**
+ * Create an ellipse, its graphic, a graphics overlay for it, and add it to the map view.
+ */
+ private fun makeRenderedEllipseGraphicsOverlay(): GraphicsOverlay {
+ // create and set all the parameters so that the ellipse has a major axis of 400 kilometres,
+ // a minor axis of 200 kilometres and is rotated at an angle of -45 degrees
+ val parameters = GeodesicEllipseParameters.createForPolygon().apply {
+ axisDirection = -45.0
+ angularUnit = AngularUnit.degrees
+ center = Point(40e5, 23e5, SpatialReference.webMercator())
+ linearUnit = LinearUnit.kilometers
+ maxPointCount = 100L
+ maxSegmentLength = 20.0
+ semiAxis1Length = 200.0
+ semiAxis2Length = 400.0
+ }
+
+ // define the ellipse parameters to a polygon geometry
+ val polygon = GeometryEngine.ellipseGeodesicOrNull(parameters)
+ // set the ellipse fill color
+ val ellipseSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.magenta, null)
+ // return the purple ellipse
+ return GraphicsOverlay().apply {
+ // add the symbol to the renderer and add it to the graphic overlay
+ renderer = SimpleRenderer(ellipseSymbol)
+ graphics.add(Graphic(polygon))
+ }
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+ private val Color.Companion.magenta: Color
+ get() {
+ return fromRgba(255, 0, 255, 255)
+ }
+}
diff --git a/style-graphics-with-renderer/src/main/res/layout/activity_main.xml b/samples/style-graphics-with-renderer/src/main/res/layout/style_graphics_with_renderer_activity_main.xml
similarity index 100%
rename from style-graphics-with-renderer/src/main/res/layout/activity_main.xml
rename to samples/style-graphics-with-renderer/src/main/res/layout/style_graphics_with_renderer_activity_main.xml
diff --git a/samples/style-graphics-with-renderer/src/main/res/values/strings.xml b/samples/style-graphics-with-renderer/src/main/res/values/strings.xml
new file mode 100644
index 000000000..5abe3976e
--- /dev/null
+++ b/samples/style-graphics-with-renderer/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Style graphics with renderer
+
diff --git a/style-graphics-with-renderer/style-graphics-with-renderer.png b/samples/style-graphics-with-renderer/style-graphics-with-renderer.png
similarity index 100%
rename from style-graphics-with-renderer/style-graphics-with-renderer.png
rename to samples/style-graphics-with-renderer/style-graphics-with-renderer.png
diff --git a/style-graphics-with-symbols/README.md b/samples/style-graphics-with-symbols/README.md
similarity index 100%
rename from style-graphics-with-symbols/README.md
rename to samples/style-graphics-with-symbols/README.md
diff --git a/style-graphics-with-symbols/README.metadata.json b/samples/style-graphics-with-symbols/README.metadata.json
similarity index 100%
rename from style-graphics-with-symbols/README.metadata.json
rename to samples/style-graphics-with-symbols/README.metadata.json
diff --git a/samples/style-graphics-with-symbols/build.gradle.kts b/samples/style-graphics-with-symbols/build.gradle.kts
new file mode 100644
index 000000000..2062870c2
--- /dev/null
+++ b/samples/style-graphics-with-symbols/build.gradle.kts
@@ -0,0 +1,23 @@
+plugins {
+ alias(libs.plugins.arcgismaps.android.library)
+ alias(libs.plugins.arcgismaps.kotlin.sample)
+ alias(libs.plugins.gradle.secrets)
+}
+
+secrets {
+ // this file doesn't contain secrets, it just provides defaults which can be committed into git.
+ defaultPropertiesFileName = "secrets.defaults.properties"
+}
+
+android {
+ namespace = "com.esri.arcgismaps.sample.stylegraphicswithsymbols"
+ // For view based samples
+ buildFeatures {
+ dataBinding = true
+ buildConfig = true
+ }
+}
+
+dependencies {
+ // Only module specific dependencies needed here
+}
diff --git a/samples/style-graphics-with-symbols/src/main/AndroidManifest.xml b/samples/style-graphics-with-symbols/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..7bf4178d8
--- /dev/null
+++ b/samples/style-graphics-with-symbols/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt b/samples/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt
new file mode 100644
index 000000000..1f0a2bae7
--- /dev/null
+++ b/samples/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt
@@ -0,0 +1,306 @@
+/* Copyright 2022 Esri
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.esri.arcgismaps.sample.stylegraphicswithsymbols
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.databinding.DataBindingUtil
+import com.arcgismaps.ApiKey
+import com.arcgismaps.ArcGISEnvironment
+import com.arcgismaps.Color
+import com.arcgismaps.geometry.Point
+import com.arcgismaps.geometry.Polygon
+import com.arcgismaps.geometry.PolygonBuilder
+import com.arcgismaps.geometry.Polyline
+import com.arcgismaps.geometry.PolylineBuilder
+import com.arcgismaps.geometry.SpatialReference
+import com.arcgismaps.mapping.ArcGISMap
+import com.arcgismaps.mapping.BasemapStyle
+import com.arcgismaps.mapping.Viewpoint
+import com.arcgismaps.mapping.symbology.HorizontalAlignment
+import com.arcgismaps.mapping.symbology.SimpleFillSymbol
+import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleLineSymbol
+import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
+import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
+import com.arcgismaps.mapping.symbology.TextSymbol
+import com.arcgismaps.mapping.symbology.VerticalAlignment
+import com.arcgismaps.mapping.view.Graphic
+import com.arcgismaps.mapping.view.GraphicsOverlay
+import com.esri.arcgismaps.sample.stylegraphicswithsymbols.databinding.StyleGraphicsWithSymbolsActivityMainBinding
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // authentication with an API key or named user is
+ // required to access basemaps and other location services
+ ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN)
+
+ // set up data binding for the activity
+ val activityMainBinding: StyleGraphicsWithSymbolsActivityMainBinding =
+ DataBindingUtil.setContentView(this, R.layout.style_graphics_with_symbols_activity_main)
+ val mapView = activityMainBinding.mapView
+ lifecycle.addObserver(mapView)
+
+ // create the graphics overlay
+ val graphicsOverlay = GraphicsOverlay()
+
+ mapView.apply {
+ // create a map with the BasemapStyle Oceans and display it in this view
+ map = ArcGISMap(BasemapStyle.ArcGISOceans)
+ setViewpoint(Viewpoint(56.075844, -2.681572, 100000.0))
+ // add the overlay to the map view
+ graphicsOverlays.add(graphicsOverlay)
+ }
+
+ // add some buoy positions to the graphics overlay
+ val buoyPoints = createBuoyGraphics()
+ graphicsOverlay.graphics.addAll(buoyPoints)
+
+ // add boat trip polyline to graphics overlay
+ val tripRouteGraphic = createRoute()
+ graphicsOverlay.graphics.add(tripRouteGraphic)
+
+ // add nesting ground polygon to graphics overlay
+ val nestingGround = createNestingGround()
+ graphicsOverlay.graphics.add(nestingGround)
+
+ // add text symbols and points to graphics overlay
+ val textGraphics = createTextGraphics()
+ graphicsOverlay.graphics.addAll(textGraphics)
+ }
+
+ /**
+ * Create Graphics for some points.
+ *
+ * @return a new graphic
+ */
+ private fun createBuoyGraphics(): Array {
+ // define the buoy locations
+ val buoy1Loc = Point(-2.712642647560347, 56.06281256681154, SpatialReference.wgs84())
+ val buoy2Loc = Point(-2.690841695957230, 56.06444173689877, SpatialReference.wgs84())
+ val buoy3Loc = Point(-2.669727388499094, 56.06425007340287, SpatialReference.wgs84())
+ val buoy4Loc = Point(-2.639515046119973, 56.06127916736989, SpatialReference.wgs84())
+
+ // create a marker symbol
+ val buoyMarker =
+ SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10.0f)
+
+ // create graphics
+ return arrayOf(
+ Graphic(buoy1Loc, buoyMarker),
+ Graphic(buoy2Loc, buoyMarker),
+ Graphic(buoy3Loc, buoyMarker),
+ Graphic(buoy4Loc, buoyMarker)
+ )
+ }
+
+ /**
+ * Create graphics which display text at specific locations.
+ *
+ * @return a new graphic
+ */
+ private fun createTextGraphics(): Array {
+ // create a point geometry
+ val bassLocation =
+ Point(-2.640631, 56.078083, SpatialReference.wgs84())
+ val craigleithLocation =
+ Point(-2.720324, 56.073569, SpatialReference.wgs84())
+
+ // create text symbols
+ val bassRockSymbol = TextSymbol(
+ getString(R.string.bassrock),
+ Color.blue,
+ 10.0f,
+ HorizontalAlignment.Left, VerticalAlignment.Bottom
+ )
+ val craigleithSymbol = TextSymbol(
+ getString(R.string.craigleith),
+ Color.blue,
+ 10.0f,
+ HorizontalAlignment.Right, VerticalAlignment.Top
+ )
+
+ // define graphics from each geometry and symbol
+ val bassRockGraphic = Graphic(bassLocation, bassRockSymbol)
+ val craigleithGraphic = Graphic(craigleithLocation, craigleithSymbol)
+
+ return arrayOf(bassRockGraphic, craigleithGraphic)
+ }
+
+ /**
+ * Create a graphic which displays a polyline.
+ *
+ * @return a new graphic
+ */
+ private fun createRoute(): Graphic {
+ // define a polyline for the boat trip
+ val boatRoute: Polyline = getBoatTripGeometry()
+ // define a line symbol
+ val lineSymbol =
+ SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.magenta, 4.0f)
+
+ // create and return a new graphic
+ return Graphic(boatRoute, lineSymbol)
+ }
+
+ /**
+ * Create a graphic which displays a polygon.
+ *
+ * @return a new graphic
+ */
+ private fun createNestingGround(): Graphic {
+ // define the polygon for the nesting ground
+ val nestingGround = getNestingGroundGeometry()
+ // define the fill symbol and outline
+ val outlineSymbol =
+ SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.blue, 1.0f)
+ val fillSymbol = SimpleFillSymbol(
+ SimpleFillSymbolStyle.DiagonalCross, Color.green,
+ outlineSymbol
+ )
+
+ // create and return a new graphic
+ return Graphic(nestingGround, fillSymbol)
+ }
+
+ /**
+ * Create a polyline representing the route of the boat trip.
+ *
+ * @return a new polyline
+ */
+ private fun getBoatTripGeometry(): Polyline {
+ // a new point collection to make up the polyline
+ val boatPositionsPolylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
+ // add positions to the point collection
+ addPoint(Point(-2.718479122792677, 56.06147084563517))
+ addPoint(Point(-2.719680750046392, 56.06147084563517))
+ addPoint(Point(-2.722084004553823, 56.06214171205971))
+ addPoint(Point(-2.726375530459948, 56.06386674355254))
+ addPoint(Point(-2.726890513568683, 56.06607083814320))
+ addPoint(Point(-2.727062174604927, 56.06779569383808))
+ addPoint(Point(-2.725517225278723, 56.06875391365391))
+ addPoint(Point(-2.723113970771293, 56.06942465335233))
+ addPoint(Point(-2.719165766937657, 56.07028701581465))
+ addPoint(Point(-2.713672613777817, 56.07057446568132))
+ addPoint(Point(-2.709381087871692, 56.07095772883556))
+ addPoint(Point(-2.704402917820587, 56.07153261642126))
+ addPoint(Point(-2.698223120515766, 56.07239493172226))
+ addPoint(Point(-2.692386645283435, 56.07325722773041))
+ addPoint(Point(-2.686721831087350, 56.07335303720707))
+ addPoint(Point(-2.681228677927500, 56.07354465544585))
+ addPoint(Point(-2.676422168912640, 56.07421531177896))
+ addPoint(Point(-2.669899049535339, 56.07488595644139))
+ addPoint(Point(-2.664749218447989, 56.07574819671591))
+ addPoint(Point(-2.659427726324393, 56.07613140842321))
+ addPoint(Point(-2.654792878345778, 56.07622721075461))
+ addPoint(Point(-2.651359657620878, 56.07651461631978))
+ addPoint(Point(-2.647754775859732, 56.07708942101955))
+ addPoint(Point(-2.645008199279812, 56.07814320736718))
+ addPoint(Point(-2.643291588917362, 56.08025069360931))
+ addPoint(Point(-2.638656740938747, 56.08044227755186))
+ addPoint(Point(-2.636940130576297, 56.07881378367495))
+ addPoint(Point(-2.636425147467562, 56.07728102068079))
+ addPoint(Point(-2.637798435757522, 56.07661041769850))
+ addPoint(Point(-2.638656740938747, 56.07507756705851))
+ addPoint(Point(-2.641231656482422, 56.07479015077557))
+ addPoint(Point(-2.642776605808628, 56.07574819671591))
+ addPoint(Point(-2.645694843424792, 56.07546078543464))
+ addPoint(Point(-2.647239792750997, 56.07459853872940))
+ addPoint(Point(-2.649299725185938, 56.07268236586862))
+ addPoint(Point(-2.653076267983328, 56.07182005699860))
+ addPoint(Point(-2.655479522490758, 56.07086191340429))
+ addPoint(Point(-2.658741082179413, 56.07047864929729))
+ addPoint(Point(-2.663375930158029, 56.07028701581465))
+ addPoint(Point(-2.666637489846684, 56.07009538137926))
+ addPoint(Point(-2.670070710571584, 56.06990374599109))
+ addPoint(Point(-2.674190575441464, 56.06913719491074))
+ addPoint(Point(-2.678310440311345, 56.06808316228391))
+ addPoint(Point(-2.682086983108735, 56.06789151689155))
+ addPoint(Point(-2.686893492123596, 56.06760404701653))
+ addPoint(Point(-2.691185018029721, 56.06722075051504))
+ addPoint(Point(-2.695133221863356, 56.06702910083509))
+ addPoint(Point(-2.698223120515766, 56.06683745020233))
+ addPoint(Point(-2.701656341240667, 56.06645414607839))
+ addPoint(Point(-2.706119528183037, 56.06607083814320))
+ addPoint(Point(-2.710067732016672, 56.06559169786458))
+ addPoint(Point(-2.713329291705327, 56.06520838135397))
+ addPoint(Point(-2.716762512430227, 56.06453756828941))
+ addPoint(Point(-2.718307461756433, 56.06348340989081))
+ addPoint(Point(-2.719165766937657, 56.06281256681154))
+ addPoint(Point(-2.719852411082638, 56.06204587471371))
+ addPoint(Point(-2.719165766937657, 56.06166252294756))
+ addPoint(Point(-2.718307461756433, 56.06147084563517))
+ }
+
+ // create the polyline from the point collection
+ return boatPositionsPolylineBuilder.toGeometry()
+ }
+
+ /**
+ * Create a polygon from a point collection.
+ *
+ * @return a new polygon
+ */
+ private fun getNestingGroundGeometry(): Polygon {
+ // a new point collection to make up the polygon
+ val pointsPolygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
+ // add points to the point collection
+ addPoint(Point(-2.643077012566659, 56.07712534604447))
+ addPoint(Point(-2.642819521015944, 56.07717324600376))
+ addPoint(Point(-2.642540571836003, 56.07774804087097))
+ addPoint(Point(-2.642712232869812, 56.07792766250863))
+ addPoint(Point(-2.642454741319098, 56.07829887790651))
+ addPoint(Point(-2.641853927700763, 56.07852639525372))
+ addPoint(Point(-2.640974164902487, 56.07880180919243))
+ addPoint(Point(-2.639987113958079, 56.07881378366685))
+ addPoint(Point(-2.639407757968971, 56.07908919555142))
+ addPoint(Point(-2.638764029092183, 56.07917301616904))
+ addPoint(Point(-2.638485079912242, 56.07896945149566))
+ addPoint(Point(-2.638570910429147, 56.07820308072684))
+ addPoint(Point(-2.638785486721410, 56.07756841839600))
+ addPoint(Point(-2.639193181676709, 56.07719719596109))
+ addPoint(Point(-2.639944198699627, 56.07675411934114))
+ addPoint(Point(-2.640652300464093, 56.07673016910844))
+ addPoint(Point(-2.640673758093319, 56.07632301287509))
+ addPoint(Point(-2.640180232621116, 56.07599967986049))
+ addPoint(Point(-2.640244605508794, 56.07584400003405))
+ addPoint(Point(-2.640416266542604, 56.07578412301025))
+ addPoint(Point(-2.640888334385582, 56.07580807383093))
+ addPoint(Point(-2.641768097183858, 56.07623918605773))
+ addPoint(Point(-2.642197249768383, 56.07625116132851))
+ addPoint(Point(-2.642840978645171, 56.07661041772168))
+ addPoint(Point(-2.643077012566659, 56.07712534604447))
+ }
+
+ // create a polygon from the point collection
+ return pointsPolygonBuilder.toGeometry()
+ }
+
+ private val Color.Companion.blue: Color
+ get() {
+ return fromRgba(0, 0, 255, 255)
+ }
+
+ private val Color.Companion.magenta: Color
+ get() {
+ return fromRgba(255, 0, 255, 255)
+ }
+}
diff --git a/style-graphics-with-symbols/src/main/res/layout/activity_main.xml b/samples/style-graphics-with-symbols/src/main/res/layout/style_graphics_with_symbols_activity_main.xml
similarity index 100%
rename from style-graphics-with-symbols/src/main/res/layout/activity_main.xml
rename to samples/style-graphics-with-symbols/src/main/res/layout/style_graphics_with_symbols_activity_main.xml
diff --git a/samples/style-graphics-with-symbols/src/main/res/values/strings.xml b/samples/style-graphics-with-symbols/src/main/res/values/strings.xml
new file mode 100644
index 000000000..32d3476a1
--- /dev/null
+++ b/samples/style-graphics-with-symbols/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Style graphics with symbols
+ Bass Rock
+ Craigleith
+
diff --git a/style-graphics-with-symbols/style-graphics-with-symbols.png b/samples/style-graphics-with-symbols/style-graphics-with-symbols.png
similarity index 100%
rename from style-graphics-with-symbols/style-graphics-with-symbols.png
rename to samples/style-graphics-with-symbols/style-graphics-with-symbols.png
diff --git a/search-with-geocode/.gitignore b/search-with-geocode/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/search-with-geocode/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/search-with-geocode/build.gradle.kts b/search-with-geocode/build.gradle.kts
deleted file mode 100644
index a5b5979bb..000000000
--- a/search-with-geocode/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.searchwithgeocode"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.searchwithgeocode"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/search-with-geocode/proguard-rules.pro b/search-with-geocode/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/search-with-geocode/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/search-with-geocode/src/main/AndroidManifest.xml b/search-with-geocode/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/search-with-geocode/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt b/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt
deleted file mode 100644
index a9e837a7f..000000000
--- a/search-with-geocode/src/main/java/com/esri/arcgismaps/sample/searchwithgeocode/MainActivity.kt
+++ /dev/null
@@ -1,379 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.searchwithgeocode
-
-import android.content.Context
-import android.database.MatrixCursor
-import android.graphics.drawable.BitmapDrawable
-import android.os.Bundle
-import android.provider.BaseColumns
-import android.text.SpannableStringBuilder
-import android.util.Log
-import android.view.Menu
-import android.view.inputmethod.InputMethodManager
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.SearchView
-import androidx.core.content.ContextCompat
-import androidx.core.text.bold
-import androidx.cursoradapter.widget.SimpleCursorAdapter
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.ViewpointType
-import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.arcgismaps.tasks.geocode.GeocodeParameters
-import com.arcgismaps.tasks.geocode.GeocodeResult
-import com.arcgismaps.tasks.geocode.LocatorTask
-import com.esri.arcgismaps.sample.searchwithgeocode.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import com.google.android.material.switchmaterial.SwitchMaterial
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val addressTextView: TextView by lazy {
- activityMainBinding.addressTextView
- }
-
- private val extentSwitch: SwitchMaterial by lazy {
- activityMainBinding.extentSwitch
- }
-
- // create a locator task from an online service
- private val locatorTask: LocatorTask = LocatorTask(
- "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"
- )
-
- // geocode parameters used to perform a search
- private val addressGeocodeParameters: GeocodeParameters = GeocodeParameters().apply {
- // get all attributes names for the geocode results
- resultAttributeNames.addAll(listOf("PlaceName", "Place_addr"))
- }
-
- // create a new Graphics Overlay
- private val graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- // instance of the map pin symbol
- private var pinSourceSymbol: PictureMarkerSymbol? = null
-
- // will search in the map view's viewpoint extent if enabled
- private var isSearchInExtent = false
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- mapView.apply {
- // set the map to be displayed in the MapView
- map = ArcGISMap(BasemapStyle.ArcGISStreets)
-
- // set map initial viewpoint
- map?.initialViewpoint = Viewpoint(40.0, -100.0, 100000000.0)
-
- // define the graphics overlay and add it to the map view
- graphicsOverlays.add(graphicsOverlay)
-
- // set an on touch listener on the map view
- lifecycleScope.launch {
- onSingleTapConfirmed.collect { tapEvent ->
- // identify the graphic at the tapped coordinate
- val tappedGraphic = identifyGraphic(tapEvent.screenCoordinate)
- if (tappedGraphic != null) {
- // show the address of the identified graphic
- showAddressForGraphic(tappedGraphic)
- }
- }
- }
- }
-
- // once the map has loaded successfully, set up address finding UI
- lifecycleScope.launch {
- // load the map then set up UI
- mapView.map?.load()?.onSuccess {
- // create the pin symbol
- pinSourceSymbol = createPinSymbol()
- }?.onFailure {
- showError(it.message.toString())
- }
- }
-
- // set the switch to update the isSearchInExtent value
- extentSwitch.setOnCheckedChangeListener { _, isChecked -> isSearchInExtent = isChecked }
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu, menu)
- val search = menu.findItem(R.id.appSearchBar)
- // set up address search view and listeners
- setupAddressSearchView(search.actionView as SearchView)
- return super.onCreateOptionsMenu(menu)
- }
-
- /**
- * Sets up the address SearchView and uses MatrixCursor to
- * show suggestions to the user as text is entered.
- */
- private fun setupAddressSearchView(addressSearchView: SearchView) {
- addressSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
- override fun onQueryTextSubmit(address: String): Boolean {
- // geocode the typed address, search within map's viewpoint as keyword was submitted
- geocodeAddress(address, true)
- addressSearchView.clearAndHideKeyboard()
- return true
- }
-
- override fun onQueryTextChange(newText: String): Boolean {
- // if the newText string isn't empty, get suggestions from the locator task
- if (newText.isNotEmpty()) {
- lifecycleScope.launch {
- locatorTask.suggest(newText).onSuccess { suggestResults ->
- // create a SimpleCursorAdapter and assign it to the suggestion adapter of the SearchView
- val simpleCursorAdapter = createSimpleCursorAdapter()
- addressSearchView.suggestionsAdapter = simpleCursorAdapter
-
- // add each address suggestion to a new row
- for ((key, result) in suggestResults.withIndex()) {
- val suggestionCursor = simpleCursorAdapter.cursor as MatrixCursor
- suggestionCursor.addRow(arrayOf(key, result.label))
- }
- // notify the adapter when the data updates, so the view can refresh itself
- simpleCursorAdapter.notifyDataSetChanged()
-
- // handle an address suggestion being chosen
- addressSearchView.setOnSuggestionListener(object :
- SearchView.OnSuggestionListener {
- override fun onSuggestionSelect(position: Int): Boolean {
- return false
- }
-
- override fun onSuggestionClick(position: Int): Boolean {
- // get the selected row
- (simpleCursorAdapter.getItem(position) as? MatrixCursor)?.let { selectedRow ->
- // get the row's index
- val selectedCursorIndex =
- selectedRow.getColumnIndex("address")
- // get the string from the row at index and set it to query
- val selectedAddress =
- selectedRow.getString(selectedCursorIndex)
- addressSearchView.setQuery(selectedAddress, false)
- // geocode the typed address
- geocodeAddress(selectedAddress, false)
- addressSearchView.clearAndHideKeyboard()
- }
- return true
- }
- })
- }.onFailure {
- showError("Geocode suggestion error: ${it.message.toString()}")
- }
- }
- }
- return true
- }
- })
- }
-
- /**
- * Creates and returns a SimpleCursorAdapter.
- */
- private fun createSimpleCursorAdapter(): SimpleCursorAdapter {
- // set up parameters for searching with MatrixCursor
- val columnNames = arrayOf(BaseColumns._ID, "address")
- val suggestionsCursor = MatrixCursor(columnNames)
- // column names for the adapter to look at when mapping data
- val cols = arrayOf("address")
- // ids that show where data should be assigned in the layout
- val to = intArrayOf(R.id.suggestion_address)
- // define SimpleCursorAdapter
- return SimpleCursorAdapter(
- this@MainActivity,
- R.layout.suggestion, suggestionsCursor, cols, to, 0
- )
- }
-
- /**
- * Geocode an [address] passed in by the user.
- */
- private fun geocodeAddress(address: String, multipleResults: Boolean) = lifecycleScope.launch {
- // clear graphics on map before displaying search results
- graphicsOverlay.graphics.clear()
-
- // search the map view's extent if enabled
- if (isSearchInExtent)
- addressGeocodeParameters.searchArea =
- mapView.getCurrentViewpoint(ViewpointType.BoundingGeometry)?.targetGeometry
- else
- addressGeocodeParameters.searchArea = null
-
- // if locator task needs to find multiple results,
- // set maxResults to default to `6`.
- addressGeocodeParameters.maxResults = if (multipleResults) 6 else 1
-
- // load the locator task
- locatorTask.load().getOrThrow()
-
- // run the locatorTask geocode task, passing in the address
- val geocodeResults = locatorTask.geocode(address, addressGeocodeParameters).getOrThrow()
- // no address found in geocode so return
- when {
- geocodeResults.isEmpty() && isSearchInExtent -> {
- showError("Address not found in map's extent")
- return@launch
- }
- geocodeResults.isEmpty() && !isSearchInExtent -> {
- showError("No address found for $address")
- return@launch
- }
- // address found in geocode
- else -> displaySearchResultOnMap(geocodeResults)
- }
-
- }
-
- /**
- * Turns a list of [geocodeResultList] into a point markers and adds it to the graphic overlay of the map.
- */
- private fun displaySearchResultOnMap(geocodeResultList: List) {
- // clear graphics overlay of existing graphics
- graphicsOverlay.graphics.clear()
-
- // create graphic object for each resulting location
- geocodeResultList.forEach { geocodeResult ->
- val resultLocationGraphic = Graphic(
- geocodeResult.displayLocation,
- geocodeResult.attributes, pinSourceSymbol
- )
- // add graphic to location layer
- graphicsOverlay.graphics.add(resultLocationGraphic)
- }
-
- when (geocodeResultList.size) {
- // if there is only one result, display location's address
- 1 -> {
- val addressAttributes = geocodeResultList[0].attributes
- val addressString = SpannableStringBuilder()
- .append("Selected address\n")
- .bold { append("${addressAttributes["PlaceName"]} ${addressAttributes["Place_addr"]}") }
- addressTextView.text = addressString
- }
- // if there are multiple results, display tap pin message
- else -> addressTextView.text = getString(R.string.tap_on_pin_to_select_address)
- }
-
- // get the envelop to set the viewpoint
- val envelope = graphicsOverlay.extent ?: return showError("Geocode result extent is null")
- // animate viewpoint to geocode result's extent
- lifecycleScope.launch {
- mapView.setViewpointGeometry(envelope, 25.0)
- }
- }
-
- /**
- * Identifies the tapped graphic at the [screenCoordinate] and shows it's address.
- */
- private suspend fun identifyGraphic(screenCoordinate: ScreenCoordinate): Graphic? {
- // from the graphics overlay, get the graphics near the tapped location
- val identifyGraphicsOverlayResult = mapView.identifyGraphicsOverlay(
- graphicsOverlay,
- screenCoordinate,
- 10.0,
- false
- ).getOrElse { throwable ->
- showError("Error with identifyGraphicsOverlay: ${throwable.message.toString()}")
- return null
- }
-
- // if not graphic selected, return
- if (identifyGraphicsOverlayResult.graphics.isEmpty()) {
- return null
- }
-
- // get the first graphic identified
- return identifyGraphicsOverlayResult.graphics[0]
- }
-
- /**
- * Creates a picture marker symbol from the pin icon, and sets it to half of its original size.
- */
- private suspend fun createPinSymbol(): PictureMarkerSymbol {
- val pinDrawable = ContextCompat.getDrawable(this, R.drawable.pin) as BitmapDrawable
- val pinSymbol = PictureMarkerSymbol.createWithImage(pinDrawable)
- pinSymbol.load().getOrThrow()
- pinSymbol.width = 19f
- pinSymbol.height = 72f
- return pinSymbol
- }
-
- /**
- * Display the address for the tapped [identifiedGraphic] using the attribute values
- */
- private suspend fun showAddressForGraphic(identifiedGraphic: Graphic) {
- // get the non null value of the geometry
- val pinGeometry: Geometry = identifiedGraphic.geometry
- ?: return showError("Error retrieving geometry for tapped graphic")
-
- // set the viewpoint to the pin location
- mapView.apply {
- setViewpointGeometry(pinGeometry.extent)
- setViewpointScale(10e3)
- }
-
- // set the address text
- val addressAttributes = identifiedGraphic.attributes
- val addressString = SpannableStringBuilder()
- .append("Selected address\n")
- .bold { append("${addressAttributes["PlaceName"]} ${addressAttributes["Place_addr"]}") }
- addressTextView.text = addressString
-
- }
-
- fun SearchView.clearAndHideKeyboard() {
- // clear the searched text from the view
- this.clearFocus()
- // close the keyboard once search is complete
- val inputManager =
- context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- inputManager.hideSoftInputFromWindow(windowToken, 0)
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/search-with-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml b/search-with-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/search-with-geocode/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/search-with-geocode/src/main/res/drawable/ic_launcher_background.xml b/search-with-geocode/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/search-with-geocode/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/search-with-geocode/src/main/res/menu/menu.xml b/search-with-geocode/src/main/res/menu/menu.xml
deleted file mode 100644
index 777703c57..000000000
--- a/search-with-geocode/src/main/res/menu/menu.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/search-with-geocode/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher.png b/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png b/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher.png b/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png b/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png b/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png b/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/search-with-geocode/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/search-with-geocode/src/main/res/values/strings.xml b/search-with-geocode/src/main/res/values/strings.xml
deleted file mode 100644
index 394a140ea..000000000
--- a/search-with-geocode/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Search with geocode
- Enter address
- Search for an address or a place
- Tap on pin to select address
- Search within map\'s extent
-
diff --git a/secrets.defaults.properties b/secrets.defaults.properties
new file mode 100644
index 000000000..690696b65
--- /dev/null
+++ b/secrets.defaults.properties
@@ -0,0 +1,22 @@
+#
+#
+# Copyright 2023 Esri
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# This properties file contains default values for runtime secrets.
+# Actual secrets should be placed in `local.properties` to avoid committing sensitive information.
+# This file is tracked by git; `local.properties` is not.
+# To obtain a new API key access token, visit: https://links.esri.com/create-an-api-key.
+ACCESS_TOKEN="DEFAULT_ACCESS_TOKEN"
diff --git a/select-features-in-feature-layer/.gitignore b/select-features-in-feature-layer/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/select-features-in-feature-layer/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/select-features-in-feature-layer/build.gradle.kts b/select-features-in-feature-layer/build.gradle.kts
deleted file mode 100644
index 67163138b..000000000
--- a/select-features-in-feature-layer/build.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/select-features-in-feature-layer/proguard-rules.pro b/select-features-in-feature-layer/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/select-features-in-feature-layer/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/select-features-in-feature-layer/src/main/AndroidManifest.xml b/select-features-in-feature-layer/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/select-features-in-feature-layer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt b/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt
deleted file mode 100644
index d09457dc3..000000000
--- a/select-features-in-feature-layer/src/main/java/com/esri/arcgismaps/sample/selectfeaturesinfeaturelayer/MainActivity.kt
+++ /dev/null
@@ -1,125 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.data.Feature
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.esri.arcgismaps.sample.selectfeaturesinfeaturelayer.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val gdbPerCapitalURL =
- "https://services1.arcgis.com/4yjifSiIG17X0gW4/arcgis/rest/services/GDP_per_capita_1960_2016/FeatureServer/0"
-
- // create service feature table and a feature layer from it
- private val serviceFeatureTable = ServiceFeatureTable(gdbPerCapitalURL)
- private val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a map with the streets base map type
- val streetsMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
- // add the feature layer to the map's operational layers
- operationalLayers.add(featureLayer)
- }
-
- mapView.apply {
- // set the map to be displayed in the layout's map view
- map = streetsMap
- // set an initial view point
- setViewpoint(
- Viewpoint(
- Envelope(
- -1131596.019761,
- 3893114.069099,
- 3926705.982140,
- 7977912.461790
- )
- )
- )
- // give any item selected on the map view a red selection halo
- selectionProperties.color = Color.red
- // set an on touch listener on the map view
- lifecycleScope.launch {
- onSingleTapConfirmed.collect { tapEvent ->
- // get the tapped coordinate
- val screenCoordinate = tapEvent.screenCoordinate
- getSelectedFeatureLayer(screenCoordinate)
- }
- }
- }
- }
-
- /**
- * Displays the number of features selected on the given [screenCoordinate]
- */
- private suspend fun getSelectedFeatureLayer(screenCoordinate: ScreenCoordinate) {
- // clear the previous selection
- featureLayer.clearSelection()
- // set a tolerance for accuracy of returned selections from point tapped
- val tolerance = 25.0
- // create a IdentifyLayerResult using the screen coordinate
- val identifyLayerResult =
- mapView.identifyLayer(featureLayer, screenCoordinate, tolerance, false, -1)
- // handle the result's onSuccess and onFailure
- identifyLayerResult.apply {
- onSuccess { identifyLayerResult ->
- // get the elements in the selection that are features
- val features = identifyLayerResult.geoElements.filterIsInstance()
- // add the features to the current feature layer selection
- featureLayer.selectFeatures(features)
- Snackbar.make(mapView, "${features.size} features selected", Snackbar.LENGTH_SHORT).show()
- }
- onFailure {
- val errorMessage = "Select feature failed: " + it.message
- Log.e(localClassName, errorMessage)
- Snackbar.make(mapView, errorMessage, Snackbar.LENGTH_SHORT).show()
- }
- }
- }
-}
diff --git a/select-features-in-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml b/select-features-in-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/select-features-in-feature-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/select-features-in-feature-layer/src/main/res/drawable/ic_launcher_background.xml b/select-features-in-feature-layer/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/select-features-in-feature-layer/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/select-features-in-feature-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png b/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png b/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png b/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png b/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png b/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png b/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/select-features-in-feature-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/select-features-in-feature-layer/src/main/res/values/strings.xml b/select-features-in-feature-layer/src/main/res/values/strings.xml
deleted file mode 100644
index 390ea27c1..000000000
--- a/select-features-in-feature-layer/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Select features in feature layer
-
diff --git a/set-max-extent/build.gradle.kts b/set-max-extent/build.gradle.kts
deleted file mode 100644
index 98a7e8632..000000000
--- a/set-max-extent/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.setmaxextent"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.setmaxextent"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/set-max-extent/proguard-rules.pro b/set-max-extent/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/set-max-extent/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/set-max-extent/src/main/AndroidManifest.xml b/set-max-extent/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/set-max-extent/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt b/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt
deleted file mode 100644
index b2e5a59fa..000000000
--- a/set-max-extent/src/main/java/com/esri/arcgismaps/sample/setmaxextent/MainActivity.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.setmaxextent
-
-import android.os.Bundle
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.SwitchCompat
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Envelope
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleRenderer
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.setmaxextent.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val extentSwitch: SwitchCompat by lazy {
- activityMainBinding.extentSwitch
- }
-
- private val extentTextView: TextView by lazy {
- activityMainBinding.extentText
- }
-
- private val extentEnvelope = Envelope(
- Point(-12139393.2109, 5012444.0468),
- Point(-11359277.5124, 4438148.7816)
- )
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a map with the BasemapStyle streets focused on Colorado
- val coloradoMap = ArcGISMap(BasemapStyle.ArcGISStreets).apply {
- // set the map's max extent to an envelope of Colorado's northwest and southeast corners
- maxExtent = extentEnvelope
- }
-
- // create a graphics overlay of the map's max extent
- val coloradoGraphicsOverlay = GraphicsOverlay().apply {
- // set the graphic's geometry to the max extent of the map
- graphics.add(Graphic(coloradoMap.maxExtent))
- // create a simple red dashed line renderer
- renderer = SimpleRenderer(SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 5f))
- }
-
- extentSwitch.setOnCheckedChangeListener { _, isChecked ->
- if (isChecked) {
- // set max extent to the state of Colorado
- coloradoMap.maxExtent = extentEnvelope
- extentTextView.text = getString(R.string.extentEnable)
- } else {
- // disable the max extent of the map, map is free to pan around
- coloradoMap.maxExtent = null
- extentTextView.text = getString(R.string.extentDisable)
- }
- }
-
- mapView.apply {
- // set the map to the map view
- map = coloradoMap
- // set the graphics overlay to the map view
- graphicsOverlays.add(coloradoGraphicsOverlay)
- }
- }
-}
diff --git a/set-max-extent/src/main/res/drawable-v24/ic_launcher_foreground.xml b/set-max-extent/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/set-max-extent/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-max-extent/src/main/res/drawable/ic_launcher_background.xml b/set-max-extent/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/set-max-extent/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-max-extent/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher.png b/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher_round.png b/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/set-max-extent/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher.png b/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher_round.png b/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/set-max-extent/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher.png b/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher.png b/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/set-max-extent/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-max-extent/src/main/res/values/strings.xml b/set-max-extent/src/main/res/values/strings.xml
deleted file mode 100644
index d78f1d27c..000000000
--- a/set-max-extent/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Set max extent
- Maximum extent enabled
- Maximum extent disabled
-
diff --git a/set-up-location-driven-geotriggers/.gitignore b/set-up-location-driven-geotriggers/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/set-up-location-driven-geotriggers/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/set-up-location-driven-geotriggers/build.gradle.kts b/set-up-location-driven-geotriggers/build.gradle.kts
deleted file mode 100644
index 7ddfc8d63..000000000
--- a/set-up-location-driven-geotriggers/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.setuplocationdrivengeotriggers"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.setuplocationdrivengeotriggers"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/set-up-location-driven-geotriggers/proguard-rules.pro b/set-up-location-driven-geotriggers/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/set-up-location-driven-geotriggers/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml b/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/set-up-location-driven-geotriggers/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt b/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt
deleted file mode 100644
index 0e6353239..000000000
--- a/set-up-location-driven-geotriggers/src/main/java/com/esri/arcgismaps/sample/setuplocationdrivengeotriggers/MainActivity.kt
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.setuplocationdrivengeotriggers
-
-import android.os.Bundle
-import android.util.Log
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.arcade.ArcadeExpression
-import com.arcgismaps.data.ArcGISFeature
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geotriggers.*
-import com.arcgismaps.location.LocationDataSourceStatus
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.location.SimulatedLocationDataSource
-import com.arcgismaps.location.SimulationParameters
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.portal.Portal
-import com.arcgismaps.mapping.PortalItem
-import com.esri.arcgismaps.sample.setuplocationdrivengeotriggers.databinding.ActivityMainBinding
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.time.Instant
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val playPauseFAB: FloatingActionButton by lazy {
- activityMainBinding.playPauseFAB
- }
-
- private val sectionButton: MaterialButton by lazy {
- activityMainBinding.sectionButton
- }
-
- // recycler list view to show the the points of interest
- private val poiListView by lazy {
- activityMainBinding.poiListView
- }
-
- // custom list adapter for the points of interest
- private val poiListAdapter by lazy {
- // create a new feature list adapter from the poiList
- FeatureListAdapter(poiList) { feature ->
- // set the item callback to show the feature view fragment
- showFeatureViewFragment(feature)
- }
- }
-
- private val simulatedLocationDataSource: SimulatedLocationDataSource by lazy {
- // create SimulationParameters starting at the current time,
- // a velocity of 10 m/s, and a horizontal and vertical accuracy of 0.0
- val simulationParameters = SimulationParameters(
- Instant.now(),
- velocity = 3.0,
- horizontalAccuracy = 0.0,
- verticalAccuracy = 0.0
- )
- // create a SimulatedLocationDataSource using the polyline from
- // ArcGIS Online GeoJSON to define the path. retrieved from
- // https://arcgisruntime.maps.arcgis.com/home/item.html?id=2a346cf1668d4564b8413382ae98a956
- SimulatedLocationDataSource(
- Geometry.fromJsonOrNull(getString(R.string.polyline_json)) as Polyline,
- simulationParameters
- )
- }
-
- // feature list to store the points of interest of a geotrigger
- private val poiList = mutableListOf()
-
- // geotrigger names for the geotrigger monitors
- private val sectionGeotrigger = "Section Geotrigger"
- private val poiGeotrigger = "POI Geotrigger"
-
- // make monitors properties to prevent garbage collection
- private lateinit var sectionGeotriggerMonitor: GeotriggerMonitor
- private lateinit var poiGeotriggerMonitor: GeotriggerMonitor
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- val portal = Portal("https://www.arcgis.com")
- // this sample uses a web map with a predefined tile basemap, feature styles, and labels
- val map = ArcGISMap(PortalItem(portal, "6ab0e91dc39e478cae4f408e1a36a308"))
- // set the mapview's map
- mapView.map = map
- // set the map to display the location of the simulatedLocationDataSource
- mapView.locationDisplay.apply {
- dataSource = simulatedLocationDataSource
- setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- initialZoomScale = 1000.0
- }
-
- // instantiate the service feature tables to later create GeotriggerMonitors for
- val gardenSections =
- ServiceFeatureTable(PortalItem(portal, "1ba816341ea04243832136379b8951d9"), 0)
- val gardenPOIs =
- ServiceFeatureTable(PortalItem(portal, "7c6280c290c34ae8aeb6b5c4ec841167"), 0)
- // create geotriggers for each of the service feature tables
- sectionGeotriggerMonitor =
- createGeotriggerMonitor(gardenSections, 0.0, sectionGeotrigger)
- poiGeotriggerMonitor =
- createGeotriggerMonitor(gardenPOIs, 10.0, poiGeotrigger)
-
- // play or pause the simulation data source when the FAB is clicked
- playPauseFAB.setOnClickListener {
- when (simulatedLocationDataSource.status.value) {
- LocationDataSourceStatus.Started -> {
- stopSimulatedDataSource(true)
- }
- LocationDataSourceStatus.Stopped -> {
- startSimulatedDataSource(true)
- }
- else -> {
- // show an error if the status is anything else
- showError(
- "Error modifying location data source state: " +
- "${simulatedLocationDataSource.status.value}"
- )
- }
- }
- }
-
- // set the recycler view layout to a vertical linear layout
- poiListView.layoutManager = LinearLayoutManager(this)
- // assign its adapter
- poiListView.adapter = poiListAdapter
-
- lifecycleScope.launch {
- // wait for the map load
- map.load().onFailure {
- // if the map load fails, show the error and return
- showError("Error loading map: ${it.message}")
- return@launch
- }
- // start the section geotrigger monitor
- sectionGeotriggerMonitor.start().onFailure {
- // if the monitor start fails, show the error and return
- showError("Section Geotrigger Monitor failed to start: ${it.message}")
- return@launch
- }
- // start the points of interest geotrigger monitor
- poiGeotriggerMonitor.start().onFailure {
- // if the monitor start fails, show the error and return
- showError("POI Geotrigger Monitor failed to start: ${it.message}")
- return@launch
- }
- // finally, start the simulated location data source
- simulatedLocationDataSource.start().onFailure {
- // if it fails, show the error and return
- showError("Simulated Location DataSource failed to start: ${it.message}")
- }
- }
- }
-
- /**
- * Creates and returns a geotrigger monitor with the [geotriggerName] name,
- * using the [serviceFeatureTable] and [bufferSize] to initialize
- * FeatureFenceParameters for the geotrigger
- */
- private fun createGeotriggerMonitor(
- serviceFeatureTable: ServiceFeatureTable,
- bufferSize: Double,
- geotriggerName: String
- ): GeotriggerMonitor {
- // create a LocationGeotriggerFeed that uses the SimulatedLocationDataSource
- val geotriggerFeed = LocationGeotriggerFeed(simulatedLocationDataSource)
- // initialize FeatureFenceParameters to display the section the user has entered
- val featureFenceParameters = FeatureFenceParameters(serviceFeatureTable, bufferSize)
- // create a fence geotrigger
- val fenceGeotrigger = FenceGeotrigger(
- geotriggerFeed,
- // triggers on enter/exit
- FenceRuleType.EnterOrExit,
- featureFenceParameters,
- // arcade expression to get the feature name
- ArcadeExpression("\$fenceFeature.name"),
- geotriggerName
- )
-
- // initialize a geotrigger monitor with the fence geotrigger
- val geotriggerMonitor = GeotriggerMonitor(fenceGeotrigger)
- lifecycleScope.launch {
- // capture and handle geotrigger notification based on the FenceRuleType
- // hence, triggers on fence enter/exit.
- geotriggerMonitor.notifications.collect { geotriggerNotificationInfo ->
- handleGeotriggerNotification(geotriggerNotificationInfo)
- }
- }
- return geotriggerMonitor
- }
-
- /**
- * Handles the [geotriggerNotificationInfo] based on its geotrigger type
- * and FenceNotificationType
- */
- private fun handleGeotriggerNotification(geotriggerNotificationInfo: GeotriggerNotificationInfo) {
- // cast it to FenceGeotriggerNotificationInfo which provides
- // access to the feature that triggered the notification
- val fenceGeotriggerNotificationInfo =
- geotriggerNotificationInfo as FenceGeotriggerNotificationInfo
- // name of the fence feature, returned from the set arcade expression
- val fenceFeatureName = fenceGeotriggerNotificationInfo.message
- // get the specific geotrigger name we set during initialization
- val geotriggerType = fenceGeotriggerNotificationInfo.geotriggerMonitor.geotrigger.name
- // check for the type of notification
- when (fenceGeotriggerNotificationInfo.fenceNotificationType) {
- FenceNotificationType.Entered -> {
- // if the user location entered the geofence, add the feature information to the UI
- addFeatureInformation(
- fenceFeatureName,
- geotriggerType,
- fenceGeotriggerNotificationInfo.fenceGeoElement as ArcGISFeature
- )
- }
- FenceNotificationType.Exited -> {
- // if the user exits a given geofence, remove the feature's information from the UI
- removeFeatureInformation(fenceFeatureName, geotriggerType)
- }
- }
- }
-
- /**
- * Adds the [fenceFeature] ArcGISFeature with the [fenceFeatureName] and [geotriggerType] to the current UI state
- * and refreshes the UI
- */
- private fun addFeatureInformation(
- fenceFeatureName: String,
- geotriggerType: String,
- fenceFeature: ArcGISFeature
- ) {
- // recenter the mapview
- mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
-
- when (geotriggerType) {
- // if it's a section geo trigger type
- sectionGeotrigger -> {
- // update the section button's onClickListener
- // to show a new FeatureViewFragment
- sectionButton.setOnClickListener { showFeatureViewFragment(fenceFeature) }
- // update the section button text to the feature name
- sectionButton.text = fenceFeatureName
- // enable the button
- sectionButton.isEnabled = true
- }
- // or a point of interest geo trigger
- poiGeotrigger -> {
- // add it to the stored list
- poiList.add(fenceFeature)
- // notify the list adapter to refresh its recycler views
- poiListAdapter.notifyItemInserted(poiList.lastIndex)
- }
- }
- }
-
- /**
- * Removes the ArcGISFeature with the given [fenceFeatureName] and corresponding
- * [geotriggerType] from the current UI state and refreshes the UI.
- */
- private fun removeFeatureInformation(fenceFeatureName: String, geotriggerType: String) {
- // check the type of geotrigger
- when (geotriggerType) {
- sectionGeotrigger -> {
- // if it's a section geo trigger,
- // remove the section information and disable the button
- sectionButton.text = "N/A"
- sectionButton.isEnabled = false
- }
- poiGeotrigger -> {
- // if it's a point of interest geotrigger
- // find its index from the stored list
- val index = poiList.indexOfFirst { feature ->
- feature.attributes["name"] == fenceFeatureName
- }
- if (index >= 0) {
- // if the feature exists remove it
- poiList.removeAt(index)
- // notify the list adapter to refresh its recycler views
- poiListAdapter.notifyItemRemoved(index)
- }
- }
- }
- }
-
- /**
- * Creates and shows a new FeatureViewFragment using the given [feature]
- */
- private fun showFeatureViewFragment(feature: ArcGISFeature) {
- // stop the simulated data source
- stopSimulatedDataSource(false)
- // create a new FeatureViewFragment
- val featureViewFragment = FeatureViewFragment(feature) {
- // set its onDismissedListener to
- // resume the simulated data source
- startSimulatedDataSource(false)
- }
- // show the fragment
- featureViewFragment.show(supportFragmentManager, "FeatureViewFragment")
- }
-
- /**
- * Starts the simulated data source and shows a status toast if [showAlert] is true.
- * The data source is resumed from its previous location if stopped before.
- */
- private fun startSimulatedDataSource(showAlert: Boolean) = lifecycleScope.launch {
- // start the simulated location data source
- simulatedLocationDataSource.start()
- // recenter the map view
- mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- // show a toast if true
- if (showAlert) {
- Toast.makeText(this@MainActivity, "Resumed Simulation", Toast.LENGTH_SHORT)
- .show()
- }
- // update the action button's drawable to a pause icon
- playPauseFAB.setImageResource(R.drawable.ic_baseline_pause_24)
- }
-
- /**
- * Stops the simulated data source and shows a status toast if [showAlert] is true.
- */
- private fun stopSimulatedDataSource(showAlert: Boolean) = lifecycleScope.launch {
- // stop the simulated location data source
- simulatedLocationDataSource.stop()
- // show a toast if true
- if (showAlert) {
- Toast.makeText(this@MainActivity, "Stopped Simulation", Toast.LENGTH_SHORT)
- .show()
- }
- // update the action button's drawable to a play icon
- playPauseFAB.setImageResource(R.drawable.ic_baseline_play_arrow_24)
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable-v24/ic_launcher_foreground.xml b/set-up-location-driven-geotriggers/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/set-up-location-driven-geotriggers/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-up-location-driven-geotriggers/src/main/res/drawable/ic_launcher_background.xml b/set-up-location-driven-geotriggers/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/set-up-location-driven-geotriggers/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-up-location-driven-geotriggers/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher_round.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher_round.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/set-up-location-driven-geotriggers/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-up-location-driven-geotriggers/src/main/res/values/strings.xml b/set-up-location-driven-geotriggers/src/main/res/values/strings.xml
deleted file mode 100644
index 91e4068eb..000000000
--- a/set-up-location-driven-geotriggers/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
- Set up location driven geotriggers
- "{\"paths\":[[[-119.709881177746,34.4570041646846],[-119.709875813328,34.4570152227745],[-119.709869107805,34.4570240692453],[-119.709859720074,34.4570351273326],[-119.709853014551,34.4570539260775],[-119.709847650133,34.4570760422426],[-119.709848991238,34.4570926293626],[-119.70985569676,34.4571103222869],[-119.709873131119,34.4571202745552],[-119.709889224373,34.4571302268223],[-119.709902635418,34.4571357558591],[-119.709910682045,34.4571600836165],[-119.709910682045,34.4571744591062],[-119.709902635418,34.4571833055602],[-119.709889224373,34.4571910462067],[-119.70988251885,34.4571965752394],[-119.70988251885,34.4572032100782],[-119.709889224373,34.4572175855605],[-119.709898612104,34.4572264320099],[-119.709912023149,34.4572341726524],[-119.709901294313,34.4572419132941],[-119.709895929895,34.4572507597409],[-119.709897271,34.4572596061868],[-119.709902635418,34.4572728758539],[-119.709902635418,34.4572828281028],[-119.70990934094,34.457294991961],[-119.709912023149,34.4573038384022],[-119.709886542164,34.4573115790375],[-119.709861061178,34.4573248486963],[-119.709843626819,34.4573414357669],[-119.709836921297,34.4573668692686],[-119.709843626819,34.4573934085666],[-119.709827533565,34.4574055724087],[-119.709791323744,34.4574188420525],[-119.709749749504,34.4574332174977],[-119.709709516369,34.4574431697275],[-119.709734997354,34.4574807670294],[-119.709748062646,34.4575248306656],[-119.709757450378,34.4575635337324],[-119.709770861423,34.457600025179],[-119.709785613572,34.4576387282109],[-119.70980573014,34.4576730080242],[-119.709815117871,34.4577117110223],[-119.709821823394,34.4577504140025],[-119.709821823394,34.4577869053674],[-119.709821823394,34.4578256083127],[-119.70981780008,34.4578609938471],[-119.709819141185,34.457906331541],[-119.70981460448,34.4579890675855],[-119.709818627793,34.4580675790658],[-119.70982667442,34.4581118108532],[-119.709832038838,34.4581471962662],[-119.709834721047,34.4581947453913],[-119.709836062152,34.4582323423548],[-119.709834721047,34.4582787856393],[-119.709805216748,34.4583429215611],[-119.709759619195,34.4584026342716],[-119.709700610597,34.4584612411497],[-119.709645400048,34.4585103926263],[-119.709566274882,34.4585457778704],[-119.709493855239,34.4585944325566],[-119.709458986522,34.458622077252],[-119.709424117805,34.4586198656767],[-119.709386566878,34.4586110193749],[-119.70935438037,34.4586110193749],[-119.709339628221,34.4586231830396],[-119.709324876071,34.4586585682359],[-119.709306100608,34.4586862129101],[-119.709269890786,34.4587171749343],[-119.709244409801,34.4587238096523],[-119.709229657651,34.4587293385835],[-119.709212223293,34.4587459253751],[-119.70919076562,34.4587945799446],[-119.709174672366,34.4588503400161],[-119.709157238008,34.4589288506865],[-119.709153214694,34.4589951976744],[-119.709155896903,34.4590449578807],[-119.70916394353,34.4590958238387],[-119.709186742307,34.4591323146156],[-119.709218928815,34.4591621706939],[-119.709237704278,34.4591831805204],[-119.709241727592,34.4592252001575],[-119.709238133851,34.459258658624],[-119.709219358388,34.4592796684262],[-119.709207288447,34.4592962551085],[-119.709208629552,34.4593084186733],[-119.709271661463,34.4593791884701],[-119.709310553494,34.4594267368937],[-119.709330670062,34.4594510639836],[-119.709353468838,34.4595008239182],[-119.70936285657,34.4595362087426],[-119.709423221989,34.4595943612845],[-119.709455408497,34.4596297460692],[-119.709487595005,34.459665130839],[-119.709507711573,34.4596817174446],[-119.709523804827,34.4596861405389],[-119.709557332439,34.4596894578594],[-119.709586836739,34.4596894578594],[-119.709593542261,34.4596772943501],[-119.709590860052,34.4596496500041],[-119.709572084589,34.4595777746615],[-119.709566720171,34.4595313320996],[-119.709578790112,34.4595136396883],[-119.709590860052,34.4594992646013],[-119.709627069874,34.4594882068404],[-119.709675349636,34.4595567649343],[-119.709735699339,34.4596197941001],[-119.709775932474,34.4596795058974],[-119.709802754564,34.4597126790997],[-119.709832258863,34.4597359003334],[-119.70986712758,34.4597171021923],[-119.70986980979,34.4596839289918],[-119.709865786476,34.4596308518435],[-119.709876515312,34.4595788804365],[-119.70988187973,34.4595346494263],[-119.709879197521,34.4594926299449],[-119.709852375431,34.4594539277723],[-119.709806777878,34.4593953215911],[-119.709767885847,34.4593212344729],[-119.709720947189,34.4592195028005],[-119.709708720088,34.4591478732967],[-119.709710061193,34.4591058536206],[-119.709707378984,34.459079314867],[-119.709652393699,34.4590262373344],[-119.709617524982,34.4589499383221],[-119.709626771268,34.4588695962162],[-119.709683097658,34.4588032491285],[-119.709730036316,34.4587391135603],[-119.709759540615,34.4586993052518],[-119.7097850216,34.4586650258598],[-119.710059948024,34.4587744987075],[-119.710104204473,34.4587932970608],[-119.710128443889,34.4587649224307],[-119.71019415801,34.4587096331253],[-119.710273283176,34.4586731421637],[-119.71031619852,34.4586532379961],[-119.710367160491,34.4586023717685],[-119.710392641477,34.4585747270665],[-119.710432874612,34.4585083797445],[-119.710471766643,34.4584453497398],[-119.710505294256,34.4584165991955],[-119.710575487456,34.4583689119728],[-119.710705574593,34.4583136224052],[-119.710780676446,34.4582627559707],[-119.710839685044,34.4582030431601],[-119.710895995717,34.4581546042213],[-119.710948298793,34.458095997128],[-119.710988531928,34.4580263320391],[-119.711015354018,34.4579511379096],[-119.711011330705,34.4579102234284],[-119.710985849719,34.4578847900921],[-119.710946957688,34.4578715205223],[-119.710779319625,34.4578847900921],[-119.71073908649,34.4578847900921],[-119.7107122644,34.4578604625458],[-119.71069751225,34.4578295002039],[-119.710674713473,34.4578095958352],[-119.710642526965,34.4577996436491],[-119.710614363771,34.457830606002],[-119.71057547174,34.4578571451526],[-119.710539261918,34.4578737321174],[-119.71049500547,34.4578858958895],[-119.710452090125,34.4578836842947],[-119.710413198095,34.4578748379149],[-119.710375647168,34.4578527219614],[-119.710336755138,34.4578350291944],[-119.710299204211,34.4578107016336],[-119.71027506433,34.4577885856631],[-119.710269605616,34.4577886418169],[-119.710240101317,34.4577510446536],[-119.710198527077,34.4576758502763],[-119.710154270628,34.4575984442288],[-119.710112696389,34.4575265671206],[-119.710044300059,34.4574381029023],[-119.709978842634,34.4573543166616],[-119.709966772693,34.4573410470074],[-119.709972137111,34.4573200367174],[-119.70997481932,34.4573023438375],[-119.709973478216,34.4572846509538],[-119.709970796007,34.457259217427],[-119.709931903976,34.4572348896984],[-119.709914469617,34.4572337838924],[-119.709893011945,34.4572171968005],[-119.709884965318,34.4571972922858],[-119.709902232039,34.4571821133624],[-119.70991161977,34.457167737874],[-119.709907596457,34.4571389868898],[-119.709919666397,34.4571235055865],[-119.709922348606,34.4571047068572],[-119.709918325293,34.4570836965077],[-119.709919666397,34.4570648977695],[-119.70992637192,34.4570516280694],[-119.709933077442,34.4570339351326],[-119.709935759651,34.4570151363832],[-119.709927713024,34.4570062899114],[-119.70991161977,34.4570018666751],[-119.709883456576,34.4570040782933]]],\"spatialReference\":{\"wkid\":4326,\"latestWkid\":4326}}"
- Current garden section:
- Points of interest:
- No features nearby
- N/A
- Play pause button
- Garden section description
-
diff --git a/set-viewpoint-rotation/.gitignore b/set-viewpoint-rotation/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/set-viewpoint-rotation/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/set-viewpoint-rotation/build.gradle.kts b/set-viewpoint-rotation/build.gradle.kts
deleted file mode 100644
index a3c62a8af..000000000
--- a/set-viewpoint-rotation/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.setviewpointrotation"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.setviewpointrotation"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/set-viewpoint-rotation/proguard-rules.pro b/set-viewpoint-rotation/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/set-viewpoint-rotation/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/set-viewpoint-rotation/src/main/AndroidManifest.xml b/set-viewpoint-rotation/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/set-viewpoint-rotation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt b/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt
deleted file mode 100644
index 1eaeffca6..000000000
--- a/set-viewpoint-rotation/src/main/java/com/esri/arcgismaps/sample/setviewpointrotation/MainActivity.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.setviewpointrotation
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.esri.arcgismaps.sample.setviewpointrotation.databinding.ActivityMainBinding
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- val mapView = activityMainBinding.mapView
- val rotationSlider = activityMainBinding.rotationSlider
- val rotationValueText = activityMainBinding.rotationValueText
- lifecycle.addObserver(mapView)
-
- // create a map with a topographic basemap and initial position
- val map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- // set the map to be displayed in this view
- mapView.map = map
- mapView.setViewpoint(Viewpoint(34.056295, -117.195800, 10000.0))
-
- rotationSlider.addOnChangeListener { _, angle, _ ->
- // set the text to the value
- rotationValueText.text = angle.toInt().toString()
- // rotate map view to the progress angle
- lifecycleScope.launch {
- mapView.setViewpointRotation(angle.toDouble())
- }
- }
- }
-}
diff --git a/set-viewpoint-rotation/src/main/res/drawable-v24/ic_launcher_foreground.xml b/set-viewpoint-rotation/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/set-viewpoint-rotation/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-viewpoint-rotation/src/main/res/drawable/ic_launcher_background.xml b/set-viewpoint-rotation/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/set-viewpoint-rotation/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/set-viewpoint-rotation/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher.png b/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher_round.png b/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher.png b/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher_round.png b/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher.png b/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher.png b/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/set-viewpoint-rotation/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/set-viewpoint-rotation/src/main/res/values/strings.xml b/set-viewpoint-rotation/src/main/res/values/strings.xml
deleted file mode 100644
index a1b4dafcb..000000000
--- a/set-viewpoint-rotation/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Set viewpoint rotation
- 0
-
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 929411bac..8075bc8bc 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,23 +1,37 @@
pluginManagement {
+ includeBuild("build-logic")
+ includeBuild("gradle-plugins")
repositories {
- gradlePluginPortal()
- google()
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
mavenCentral()
+ gradlePluginPortal()
}
}
dependencyResolutionManagement {
- @Suppress("UnstableApiUsage")
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- @Suppress("UnstableApiUsage")
repositories {
google()
mavenCentral()
maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") }
+ maven(url = "https://jitpack.io")
}
}
-rootDir.listFiles()?.forEach {
- if (it.isDirectory && File(it, "build.gradle.kts").exists()) {
- include(":${it.name}")
- }
+rootProject.name = "ArcGISMapsKotlinSamples"
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+
+include(":app")
+include(":samples-lib")
+
+// dynamically include all sample libraries
+File("samples/").listFiles()?.filter { !it.name.contains(".DS_Store") }?.forEach { sampleFolder ->
+ // include all of the samples, which have been changed into libs of the sample viewer app
+ include(":samples:" + sampleFolder.name)
+ project(":samples:" + sampleFolder.name).projectDir = file("samples/" + sampleFolder.name)
}
diff --git a/show-callout/.gitignore b/show-callout/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-callout/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-callout/build.gradle.kts b/show-callout/build.gradle.kts
deleted file mode 100644
index f14a9a959..000000000
--- a/show-callout/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showcallout"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showcallout"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/show-callout/proguard-rules.pro b/show-callout/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-callout/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-callout/src/main/AndroidManifest.xml b/show-callout/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-callout/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt b/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt
deleted file mode 100644
index cb76f7897..000000000
--- a/show-callout/src/main/java/com/esri/arcgismaps/sample/showcallout/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showcallout
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showcallout.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ShowCalloutApp()
- }
- }
- }
-
- @Composable
- private fun ShowCalloutApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/show-callout/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-callout/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-callout/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-callout/src/main/res/drawable/ic_launcher_background.xml b/show-callout/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-callout/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-callout/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-callout/src/main/res/mipmap-hdpi/ic_launcher.png b/show-callout/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-callout/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-callout/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-callout/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-mdpi/ic_launcher.png b/show-callout/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-callout/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-callout/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-callout/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-callout/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-callout/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-callout/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-callout/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-callout/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-callout/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-callout/src/main/res/values/strings.xml b/show-callout/src/main/res/values/strings.xml
deleted file mode 100644
index 9c8df94f8..000000000
--- a/show-callout/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Show callout
- "Lat: %.4f, Lon: %.4f"
-
diff --git a/show-coordinates-in-multiple-formats/.gitignore b/show-coordinates-in-multiple-formats/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-coordinates-in-multiple-formats/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-coordinates-in-multiple-formats/build.gradle.kts b/show-coordinates-in-multiple-formats/build.gradle.kts
deleted file mode 100644
index 3871724f4..000000000
--- a/show-coordinates-in-multiple-formats/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showcoordinatesinmultipleformats"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showcoordinatesinmultipleformats"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/show-coordinates-in-multiple-formats/proguard-rules.pro b/show-coordinates-in-multiple-formats/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-coordinates-in-multiple-formats/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml b/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-coordinates-in-multiple-formats/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt b/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt
deleted file mode 100644
index 0d598c874..000000000
--- a/show-coordinates-in-multiple-formats/src/main/java/com/esri/arcgismaps/sample/showcoordinatesinmultipleformats/MainActivity.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showcoordinatesinmultipleformats
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import androidx.core.view.WindowCompat
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showcoordinatesinmultipleformats.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // remove focus from text fields when keyboard closes
- WindowCompat.setDecorFitsSystemWindows(window, false)
- // set compose content
- setContent {
- SampleAppTheme {
- ShowCoordinatesInMultipleFormatsApp()
- }
- }
- }
-
- @Composable
- private fun ShowCoordinatesInMultipleFormatsApp() {
- Surface(color = MaterialTheme.colorScheme.background) {
- MainScreen(sampleName = getString(R.string.app_name))
- }
- }
-}
diff --git a/show-coordinates-in-multiple-formats/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-coordinates-in-multiple-formats/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-coordinates-in-multiple-formats/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-coordinates-in-multiple-formats/src/main/res/drawable/ic_launcher_background.xml b/show-coordinates-in-multiple-formats/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-coordinates-in-multiple-formats/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-coordinates-in-multiple-formats/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-coordinates-in-multiple-formats/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml b/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml
deleted file mode 100644
index ec5bb341e..000000000
--- a/show-coordinates-in-multiple-formats/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Show coordinates in multiple formats
- https://wi.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer
-
diff --git a/show-device-location-using-indoor-positioning/.gitignore b/show-device-location-using-indoor-positioning/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-device-location-using-indoor-positioning/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-device-location-using-indoor-positioning/build.gradle.kts b/show-device-location-using-indoor-positioning/build.gradle.kts
deleted file mode 100644
index e5612342b..000000000
--- a/show-device-location-using-indoor-positioning/build.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-device-location-using-indoor-positioning/proguard-rules.pro b/show-device-location-using-indoor-positioning/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-device-location-using-indoor-positioning/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml b/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml
deleted file mode 100644
index b159d12f2..000000000
--- a/show-device-location-using-indoor-positioning/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt b/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt
deleted file mode 100644
index df734f309..000000000
--- a/show-device-location-using-indoor-positioning/src/main/java/com/esri/arcgismaps/sample/showdevicelocationusingindoorpositioning/MainActivity.kt
+++ /dev/null
@@ -1,359 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning
-
-import android.Manifest
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Guid
-import com.arcgismaps.data.ArcGISFeatureTable
-import com.arcgismaps.data.FeatureTable
-import com.arcgismaps.data.Field
-import com.arcgismaps.data.OrderBy
-import com.arcgismaps.data.QueryParameters
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.data.SortOrder
-import com.arcgismaps.location.IndoorsLocationDataSource
-import com.arcgismaps.location.Location
-import com.arcgismaps.location.LocationDataSourceStatus
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.portal.Portal
-import com.arcgismaps.mapping.PortalItem
-import com.esri.arcgismaps.sample.showdevicelocationusingindoorpositioning.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.text.DecimalFormat
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val progressBar by lazy {
- activityMainBinding.progressBar
- }
-
- private val textView by lazy {
- activityMainBinding.textView
- }
-
- // keep track of the current floor in an indoor map, null if using GPS
- private var currentFloor: Int? = null
-
- // provides an indoor or outdoor position based on device sensor data (radio, GPS, motion sensors).
- private var indoorsLocationDataSource: IndoorsLocationDataSource? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- lifecycle.addObserver(mapView)
- // some parts of the API require an Android Context to properly interact with Android system
- // features, such as LocationProvider and application resources
- ArcGISEnvironment.applicationContext = applicationContext
- // check for location permissions
- // if permissions is allowed, the device's current location is shown
- checkPermissions()
- }
-
- /**
- * Check for location permissions, if not received then request for one
- */
- private fun checkPermissions() {
- val requestCode = 1
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
- ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
- (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
- ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)) {
- val requestPermissions = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
- // Android 12 required permission
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- requestPermissions.add(Manifest.permission.BLUETOOTH_SCAN)
- }
- ActivityCompat.requestPermissions(this, requestPermissions.toTypedArray(), requestCode)
- } else {
- // permission already given, so no need to request
- setUpMap()
- }
- }
-
- /**
- * Set up the [mapView] to load a floor-aware web map.
- */
- private fun setUpMap() {
- // load the portal and create a map from the portal item
- val portalItem = PortalItem(
- Portal("https://www.arcgis.com/"),
- "8fa941613b4b4b2b8a34ad4cdc3e4bba"
- )
- val map = ArcGISMap(portalItem)
- mapView.map = map
- lifecycleScope.launch {
- map.load().onSuccess {
- val featureTables = map.tables
- // check if the portalItem contains featureTables
- if (featureTables.isNotEmpty()) {
- setUpLoadTables(featureTables)
- } else {
- showError("Map does not contain feature tables")
- }
- }.onFailure {
- // if map load failed, show the error
- showError("Error Loading Map: {it.message}")
- }
- }
- }
-
- /**
- * Load each feature table and setup the IndoorsLocationDataSource for each loaded table
- */
- private suspend fun setUpLoadTables(featureTables: MutableList) {
- featureTables.forEach { featureTable ->
- // load each FeatureTable
- featureTable.load().onFailure {
- showError("Error loading FeatureTable: ${it.message}")
- }
- }
- // retrieve the loaded feature tables
- featureTables.forEach{ featureTable ->
- // ips_positioning table needs to be present
- if (featureTable.tableName == "ips_positioning") {
- return setupIndoorsLocationDataSource(featureTable)
- }
- }
- // ips_positioning table not found
- showError("Positioning Table not found in FeatureTables")
- }
-
- /**
- * Sets up the [indoorsLocationDataSource] using the IPS_Positioning [featureTable]
- */
- private fun setupIndoorsLocationDataSource(featureTable: FeatureTable) {
- // cast the featureTable to a ServiceFeatureTable to get the globalIdField which identifies a row in the positioning table
- // and used as a parameter to setup IndoorsLocationDataSource
- val positioningFeatureTable = featureTable as ServiceFeatureTable
- // when multiple entries are available, IndoorsLocationDataSource constructor function
- // looks up the entry with the most recent date and takes this positioning data
- // set up queryParameters to grab one result.
- val dateCreatedFieldName = getDateCreatedFieldName(positioningFeatureTable.fields)
- ?: return showError("The service table does not contain \"DateCreated\" fields.")
- val queryParameters = QueryParameters().apply {
- // set a limit of 1 on the number of returned features per request
- maxFeatures = 1
- // 1=1 is a true where clause which will result in all matching records being returned
- whereClause = "1 = 1"
- // find and sort out the orderByFields by most recent first
- orderByFields.add(
- OrderBy(
- dateCreatedFieldName,
- sortOrder = SortOrder.Descending
- )
- )
- }
- lifecycleScope.launch {
- positioningFeatureTable.queryFeatures(queryParameters)
- .onSuccess { queryResults ->
- val featureResult = queryResults.first()
- // perform search query using the queryParameters
- // check if serviceFeatureTable contains positioning data
- // The ID that identifies a row in the positioning table.
- val globalID =
- featureResult.attributes[positioningFeatureTable.globalIdField].toString()
- val positioningId = Guid(globalID)
- // Setting up IndoorsLocationDataSource with positioning, pathways tables and positioning ID.
- // positioningTable - the "ips_positioning" feature table from an IPS-enabled map.
- // pathwaysTable - An ArcGISFeatureTable that contains pathways as per the ArcGIS Indoors Information Model.
- // Setting this property enables path snapping of locations provided by the IndoorsLocationDataSource.
- // levelsTable - An ArcGISFeatureTable that contains floor levels in accordance with the ArcGIS Indoors Information Model.
- // Providing this table enables the retrieval of a location's floor level ID.
- // positioningID - an ID which identifies a specific row in the positioningTable that should be used for setting up IPS.
- indoorsLocationDataSource = IndoorsLocationDataSource(
- positioningFeatureTable,
- getFeatureTable("Pathways"),
- getFeatureTable("Levels"),
- positioningId
- )
- // start the location display (blue dot)
- startLocationDisplay()
- }.onFailure {
- showError("The positioning table contain no data")
- }
- }
- }
-
- /**
- * Find the exact formatting of the name "DateCreated" in the list of ServiceFeatureTable [fields].
- */
- private fun getDateCreatedFieldName(fields: List): String? {
- val field = fields.find {
- it.name.equals(
- "DateCreated",
- ignoreCase = true
- ) || it.name.equals("Date_Created", ignoreCase = true)
- }
- return field?.name
- }
-
- /**
- * Retrieves the "Pathways" or the "levels" table depending on the string passed.
- */
- private fun getFeatureTable(name: String): ArcGISFeatureTable? {
- return mapView.map?.operationalLayers?.firstOrNull { operationalLayer ->
- operationalLayer.name == name
- }?.let { layer ->
- if (layer is FeatureLayer) {
- layer.featureTable as ArcGISFeatureTable
- } else {
- null
- }
- } ?: run {
- showError("$name table not found")
- null
- }
- }
-
- /**
- * Sets up the location listeners, the navigation mode, displays the devices location as a blue dot,
- * collect data source location changes and handles its status changes
- */
- private fun startLocationDisplay() {
- val locationDisplay = mapView.locationDisplay.apply {
- setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
- dataSource = indoorsLocationDataSource
- ?: return showError("Error setting the IndoorsLocationDataSource value.")
- }
-
- // coroutine scope to start the location display, which will in-turn start IndoorsLocationDataSource to start receiving IPS updates.
- lifecycleScope.launch {
- locationDisplay.dataSource.start()
- }
-
- // coroutine scope to collect data source location changes like currentFloor, positionSource, transmitterCount, networkCount and horizontalAccuracy
- lifecycleScope.launch {
- locationDisplay.dataSource.locationChanged.collect { location ->
- // get the location properties of the LocationDataSource
- val locationProperties = location.additionalSourceProperties
- // retrieve information about the location of the device
- val floor = locationProperties["floor"]?.toString() ?: ""
- val positionSource = locationProperties["positionSource"]?.toString() ?: ""
- val transmitterCount = locationProperties["transmitterCount"]?.toString() ?: ""
- val satelliteCount = locationProperties["satelliteCount"]?.toString() ?: ""
-
- // check if current floor hasn't been set or if the floor has changed
- if (floor.isNotEmpty()) {
- val newFloor = floor.toInt()
- if (currentFloor == null || currentFloor != newFloor) {
- currentFloor = newFloor
- // update layer's definition express with the current floor
- mapView.map?.operationalLayers?.forEach { layer ->
- val name = layer.name
- if (layer is FeatureLayer && name in listOf(
- "Details",
- "Units",
- "Levels"
- )
- ) {
- layer.definitionExpression = "VERTICAL_ORDER = $currentFloor"
- }
- }
- }
- } else {
- showError("Floors is empty.")
- }
- // set up the message with floor properties to be displayed to the textView
- val sb = StringBuilder()
- sb.append("Floor: $floor, ")
- sb.append("Position-source: $positionSource, ")
- val accuracy = DecimalFormat(".##").format(
- location.horizontalAccuracy
- )
- sb.append("Horizontal-accuracy: ${accuracy}m, ")
- sb.append(when (positionSource) {
- Location.SourceProperties.Values.POSITION_SOURCE_GNSS -> "Satellite-count: $satelliteCount"
- "BLE" -> "Transmitter-count: $transmitterCount"
- else -> ""
- }
- )
- textView.text = sb.toString()
- }
- }
-
- lifecycleScope.launch {
- // Handle status changes of IndoorsLocationDataSource
- locationDisplay.dataSource.status.collect { status ->
- when (status) {
- LocationDataSourceStatus.Starting -> progressBar.visibility = View.VISIBLE
- LocationDataSourceStatus.Started -> progressBar.visibility = View.GONE
- LocationDataSourceStatus.FailedToStart -> {
- progressBar.visibility = View.GONE
- showError("Failed to start IndoorsLocationDataSource")
- }
- LocationDataSourceStatus.Stopped -> {
- progressBar.visibility = View.GONE
- showError("IndoorsLocationDataSource stopped due to an internal error")
- }
- else -> {}
- }
- }
- }
- }
-
- /**
- * Result of the user from location permissions request
- */
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // if location permissions accepted, start setting up IndoorsLocationDataSource
- setUpMap()
- } else {
- val message = "Location permission is not granted"
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
- Log.e(localClassName, message)
- progressBar.visibility = View.GONE
- }
- }
-
- /**
- * Displays an error onscreen
- */
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
diff --git a/show-device-location-using-indoor-positioning/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-device-location-using-indoor-positioning/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-device-location-using-indoor-positioning/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-device-location-using-indoor-positioning/src/main/res/drawable/ic_launcher_background.xml b/show-device-location-using-indoor-positioning/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-device-location-using-indoor-positioning/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-device-location-using-indoor-positioning/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-device-location-using-indoor-positioning/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml b/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml
deleted file mode 100644
index 4b3dc3d03..000000000
--- a/show-device-location-using-indoor-positioning/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Show device location using indoor positioning
-
diff --git a/show-device-location/.gitignore b/show-device-location/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-device-location/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-device-location/build.gradle.kts b/show-device-location/build.gradle.kts
deleted file mode 100644
index b4684c302..000000000
--- a/show-device-location/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showdevicelocation"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showdevicelocation"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-device-location/proguard-rules.pro b/show-device-location/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-device-location/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-device-location/src/main/AndroidManifest.xml b/show-device-location/src/main/AndroidManifest.xml
deleted file mode 100644
index 5065a7b5e..000000000
--- a/show-device-location/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt b/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt
deleted file mode 100644
index edb8b749a..000000000
--- a/show-device-location/src/main/java/com/esri/arcgismaps/sample/showdevicelocation/MainActivity.kt
+++ /dev/null
@@ -1,183 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showdevicelocation
-
-import android.Manifest.permission.ACCESS_COARSE_LOCATION
-import android.Manifest.permission.ACCESS_FINE_LOCATION
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.view.View
-import android.widget.AdapterView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.esri.arcgismaps.sample.showdevicelocation.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // some parts of the API require an Android Context to properly interact with Android system
- // features, such as LocationProvider and application resources
- ArcGISEnvironment.applicationContext = applicationContext
-
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- mapView.map = map
-
- val locationDisplay = mapView.locationDisplay
-
- lifecycleScope.launch {
- // listen to changes in the status of the location data source
- locationDisplay.dataSource.start()
- .onSuccess {
- // permission already granted, so start the location display
- activityMainBinding.spinner.setSelection(1, true)
- }.onFailure {
- // check permissions to see if failure may be due to lack of permissions
- requestPermissions()
- }
- }
-
- // populate the list for the location display options for the spinner's adapter
- val panModeSpinnerElements = arrayListOf(
- ItemData("Stop", R.drawable.locationdisplaydisabled),
- ItemData("On", R.drawable.locationdisplayon),
- ItemData("Re-center", R.drawable.locationdisplayrecenter),
- ItemData("Navigation", R.drawable.locationdisplaynavigation),
- ItemData("Compass", R.drawable.locationdisplayheading)
- )
-
- activityMainBinding.spinner.apply {
- adapter = SpinnerAdapter(this@MainActivity, R.id.locationTextView, panModeSpinnerElements)
- onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(
- parent: AdapterView<*>?,
- view: View,
- position: Int,
- id: Long
- ) {
- when (panModeSpinnerElements[position].text) {
- "Stop" -> // stop location display
- lifecycleScope.launch {
- locationDisplay.dataSource.stop()
- }
- "On" -> // start location display
- lifecycleScope.launch {
- locationDisplay.dataSource.start()
- }
- "Re-center" -> {
- // re-center MapView on location
- locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- }
- "Navigation" -> {
- // start navigation mode
- locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Navigation)
- }
- "Compass" -> {
- // start compass navigation mode
- locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.CompassNavigation)
- }
- }
- }
-
- override fun onNothingSelected(parent: AdapterView<*>?) {}
- }
- }
- }
-
- /**
- * Request fine and coarse location permissions for API level 23+.
- */
- private fun requestPermissions() {
- // coarse location permission
- val permissionCheckCoarseLocation =
- ContextCompat.checkSelfPermission(this@MainActivity, ACCESS_COARSE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED
- // fine location permission
- val permissionCheckFineLocation =
- ContextCompat.checkSelfPermission(this@MainActivity, ACCESS_FINE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED
-
- // if permissions are not already granted, request permission from the user
- if (!(permissionCheckCoarseLocation && permissionCheckFineLocation)) {
- ActivityCompat.requestPermissions(
- this@MainActivity,
- arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION),
- 2
- )
- } else {
- // permission already granted, so start the location display
- lifecycleScope.launch {
- mapView.locationDisplay.dataSource.start().onSuccess {
- activityMainBinding.spinner.setSelection(1, true)
- }
- }
- }
- }
-
- /**
- * Handle the permissions request response.
- */
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- lifecycleScope.launch {
- mapView.locationDisplay.dataSource.start().onSuccess {
- activityMainBinding.spinner.setSelection(1, true)
- }
- }
- } else {
- Snackbar.make(
- mapView,
- "Location permissions required to run this sample!",
- Snackbar.LENGTH_LONG
- ).show()
- // update UI to reflect that the location display did not actually start
- activityMainBinding.spinner.setSelection(0, true)
- }
- }
-}
diff --git a/show-device-location/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-device-location/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index 2b068d114..000000000
--- a/show-device-location/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/show-device-location/src/main/res/drawable/ic_launcher_background.xml b/show-device-location/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-device-location/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-device-location/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-device-location/src/main/res/mipmap-hdpi/ic_launcher.png b/show-device-location/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-device-location/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-device-location/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-device-location/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-mdpi/ic_launcher.png b/show-device-location/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-device-location/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-device-location/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-device-location/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-device-location/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-device-location/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-device-location/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-device-location/src/main/res/values/strings.xml b/show-device-location/src/main/res/values/strings.xml
deleted file mode 100644
index bbb999ba3..000000000
--- a/show-device-location/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Show device location
- Location point image
-
diff --git a/show-geodesic-path-between-two-points/.gitignore b/show-geodesic-path-between-two-points/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-geodesic-path-between-two-points/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-geodesic-path-between-two-points/build.gradle.kts b/show-geodesic-path-between-two-points/build.gradle.kts
deleted file mode 100644
index 7574f23f5..000000000
--- a/show-geodesic-path-between-two-points/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-geodesic-path-between-two-points/proguard-rules.pro b/show-geodesic-path-between-two-points/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-geodesic-path-between-two-points/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml b/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-geodesic-path-between-two-points/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt b/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt
deleted file mode 100644
index 5663c017d..000000000
--- a/show-geodesic-path-between-two-points/src/main/java/com/esri/arcgismaps/sample/showgeodesicpathbetweentwopoints/MainActivity.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.LinearUnit
-import com.arcgismaps.geometry.LinearUnitId
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.GeodeticCurveType
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.showgeodesicpathbetweentwopoints.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import kotlin.math.roundToInt
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // set up data binding for the mapView
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // shows the distance information as a textview
- private val infoTextView by lazy {
- activityMainBinding.infoTextView
- }
-
- // a red marker symbol for the location points
- private val locationMarkerSymbol by lazy {
- SimpleMarkerSymbol(
- SimpleMarkerSymbolStyle.Circle,
- Color.red,
- 10f
- )
- }
-
- // the marker graphic for the starting location
- private val startingLocationMarkerGraphic by lazy {
- Graphic(startingPoint, locationMarkerSymbol)
- }
-
- // marker graphic for the destination
- private val endLocationMarkerGraphic by lazy {
- Graphic(symbol = locationMarkerSymbol)
- }
-
- // the geodesic path represented as line graphic
- private val geodesicPathGraphic by lazy {
- val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 5f)
- Graphic(symbol = lineSymbol)
- }
-
- // the unit of distance measurement in kilometers
- private val unitsOfMeasurement = LinearUnit(LinearUnitId.Kilometers)
-
-
- // starting location for the distance calculation
- private val startingPoint = Point(-73.7781, 40.6413, SpatialReference.wgs84())
-
-
- // creates a graphic overlay to draw all graphics
- private val graphicsOverlay = GraphicsOverlay()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- val map = ArcGISMap(BasemapStyle.ArcGISImageryStandard).apply {
- initialViewpoint = Viewpoint(Point(34.77, -10.24), 20e7)
- }
- // configure mapView assignments
- mapView.apply {
- this.map = map
- // add the graphics overlay to the mapview
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // add all our marker graphics to the graphics overlay
- graphicsOverlay.graphics.addAll(
- listOf(startingLocationMarkerGraphic, endLocationMarkerGraphic, geodesicPathGraphic)
- )
-
- lifecycleScope.launch {
- // check if the map has loaded successfully
- map.load().onSuccess {
- // capture and collect when the user taps on the screen
- mapView.onSingleTapConfirmed.collect { event ->
- event.mapPoint?.let { point -> displayGeodesicPath(point) }
- }
- }.onFailure {
- // if map load failed, show the error
- showError("Error Loading Map")
- }
- }
- }
-
- /**
- * Displays the destination location marker at the tapped location
- * and draws a geodesic path curve using GeometryEngine.densifyGeodetic
- * and computes the distance using GeometryEngine.lengthGeodetic
- */
- private fun displayGeodesicPath(point: Point) {
- // project the tapped point into the same spatial reference as source point
- val destinationPoint = GeometryEngine.projectOrNull(point, SpatialReference.wgs84())
- ?: return showError("Error converting point projection")
-
- // check if the destination point is within the map bounds
- // isEmpty returns true if out of bounds
- if (!destinationPoint.isEmpty) {
- // update the end location marker location on map
- endLocationMarkerGraphic.geometry = destinationPoint
- // create a polyline between source and destination points
- val polyline = Polyline(listOf(startingPoint, destinationPoint))
- // generate a geodesic curve using the polyline
- val pathGeometry = GeometryEngine.densifyGeodeticOrNull(
- geometry = polyline,
- maxSegmentLength = 1.0,
- lengthUnit = unitsOfMeasurement,
- curveType = GeodeticCurveType.Geodesic
- // only compute the distance if the returned curved path geometry is not null
- ) ?: return showError("Error creating a densified geometry")
- // update the path graphic
- geodesicPathGraphic.geometry = pathGeometry
- // compute the path distance in kilometers
- val distance = GeometryEngine.lengthGeodetic(
- geometry = pathGeometry,
- lengthUnit = unitsOfMeasurement,
- curveType = GeodeticCurveType.Geodesic
- )
- // display the distance result
- infoTextView.text = getString(R.string.distance_info_text, distance.roundToInt())
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/show-geodesic-path-between-two-points/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-geodesic-path-between-two-points/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-geodesic-path-between-two-points/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-geodesic-path-between-two-points/src/main/res/drawable/ic_launcher_background.xml b/show-geodesic-path-between-two-points/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-geodesic-path-between-two-points/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-geodesic-path-between-two-points/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-geodesic-path-between-two-points/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-geodesic-path-between-two-points/src/main/res/values/strings.xml b/show-geodesic-path-between-two-points/src/main/res/values/strings.xml
deleted file mode 100644
index c10b00d7c..000000000
--- a/show-geodesic-path-between-two-points/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Show geodesic path between two points
- Tap to begin
- Distance: %1d kilometers
-
diff --git a/show-grid/.gitignore b/show-grid/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-grid/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-grid/build.gradle.kts b/show-grid/build.gradle.kts
deleted file mode 100644
index e6465a3d9..000000000
--- a/show-grid/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showgrid"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showgrid"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-grid/proguard-rules.pro b/show-grid/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-grid/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-grid/src/main/AndroidManifest.xml b/show-grid/src/main/AndroidManifest.xml
deleted file mode 100644
index 88d4071c1..000000000
--- a/show-grid/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt b/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt
deleted file mode 100644
index e216c1db1..000000000
--- a/show-grid/src/main/java/com/esri/arcgismaps/sample/showgrid/MainActivity.kt
+++ /dev/null
@@ -1,329 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showgrid
-
-import android.os.Bundle
-import android.util.Log
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.HorizontalAlignment
-import com.arcgismaps.mapping.symbology.LineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.arcgismaps.mapping.symbology.VerticalAlignment
-import com.arcgismaps.mapping.view.GridLabelPosition
-import com.arcgismaps.mapping.view.LatitudeLongitudeGrid
-import com.arcgismaps.mapping.view.LatitudeLongitudeGridLabelFormat
-import com.arcgismaps.mapping.view.MgrsGrid
-import com.arcgismaps.mapping.view.UsngGrid
-import com.arcgismaps.mapping.view.UtmGrid
-import com.esri.arcgismaps.sample.showgrid.databinding.ActivityMainBinding
-import com.esri.arcgismaps.sample.showgrid.databinding.PopupDialogBinding
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val menuButton: MaterialButton by lazy {
- activityMainBinding.menuButton
- }
-
- // create a point to focus the map on in Quebec province
- private val center: Point = Point(
- -7702852.905619, 6217972.345771, SpatialReference(3857)
- )
-
- // the selected line color of the grid
- private var lineColor: Color = Color.white
-
- // the selected label color of the grid
- private var labelColor: Color = Color.white
-
- // the selected label position of the grid
- private var labelPosition: GridLabelPosition = GridLabelPosition.AllSides
-
- // boolean set if the layer is visible
- private var isLabelVisible = true
-
- // create a popup dialog to manage grid settings
- private val popUpDialogBinding by lazy {
- PopupDialogBinding.inflate(layoutInflater)
- }
-
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- mapView.apply {
- // create a map with imagery basemap
- map = ArcGISMap(BasemapStyle.ArcGISImagery)
- // set the initial viewpoint of the map
- setViewpoint(Viewpoint(center, 23227.0))
- // set defaults on grid
- grid = LatitudeLongitudeGrid()
- }
-
- val dialog = MaterialAlertDialogBuilder(this@MainActivity).apply {
- setView(popUpDialogBinding.root)
- setTitle(getString(R.string.change_grid_button))
- }.create()
-
- // set up options in popup menu
- // create drop-down list of different layer types
- setupLayerDropdown(popUpDialogBinding)
-
- // create drop-down list of different line colors
- setupLineColorDropdown(popUpDialogBinding)
-
- // create drop-down list of different label colors
- setupLabelColorDropdown(popUpDialogBinding)
-
- // create drop-down list of different label positions
- setupLabelPositionDropdown(popUpDialogBinding)
-
- // setup the checkbox to change the visibility of the labels
- setupLabelsCheckbox(popUpDialogBinding)
-
- // display pop-up box when button is clicked
- menuButton.setOnClickListener {
- dialog.show()
- }
- }
-
- /**
- * Sets up the [popupDialogBinding] for selecting a grid type
- * and handles behavior for when a new grid type is selected.
- */
- private fun setupLayerDropdown(popupDialogBinding: PopupDialogBinding) {
- popupDialogBinding.gridTypeDropdown.apply {
- // set the grid type adapter
- setAdapter(ArrayAdapter(
- applicationContext,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.layers_array))
- )
-
- // set the grid type click listener
- onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
- when (position) {
- 0 -> {
- // LatitudeLongitudeGrid can have a label format of DecimalDegrees or DegreesMinutesSeconds
- mapView.grid = LatitudeLongitudeGrid().apply {
- labelFormat = LatitudeLongitudeGridLabelFormat.DecimalDegrees
- }
- mapView.setViewpoint(Viewpoint(center, 23227.0))
- }
- 1 -> {
- mapView.grid = MgrsGrid()
- mapView.setViewpoint(Viewpoint(center, 23227.0))
- }
- 2 -> {
- mapView.grid = UtmGrid()
- mapView.setViewpoint(Viewpoint(center, 10000000.0))
- }
- 3 -> {
- mapView.grid = UsngGrid()
- mapView.setViewpoint(Viewpoint(center, 23227.0))
- }
- else -> return@OnItemClickListener showError("Unsupported option")
- }
-
- // make sure settings persist on grid type change
- setLabelVisibility(isLabelVisible)
- changeGridColor(lineColor)
- changeLabelColor(labelColor)
- }
- }
- }
-
- /**
- * Sets up the [popupDialogBinding] for selecting a grid color and
- * handles behavior for when a new line color is selected.
- */
- private fun setupLineColorDropdown(popupDialogBinding: PopupDialogBinding) {
- popupDialogBinding.gridColorDropdown.apply {
- // set the grid color adapter
- setAdapter(ArrayAdapter(
- applicationContext,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.colors_array))
- )
-
- // set the grid color click listener
- onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
- lineColor = when (position) {
- 0 -> Color.red
- 1 -> Color.white
- 2 -> Color.blue
- else -> return@OnItemClickListener showError("Unsupported option")
- }
- changeGridColor(lineColor)
- }
- }
- }
-
- /**
- * Sets up the [popupDialogBinding] for selecting a label color
- * and handles behavior for when a new label color is selected.
- */
- private fun setupLabelColorDropdown(popupDialogBinding: PopupDialogBinding) {
- popupDialogBinding.labelColorDropdown.apply {
- setAdapter(ArrayAdapter(
- applicationContext,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.colors_array))
- )
- onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
- // change grid labels color
- labelColor = when (position) {
- 0 -> Color.red
- 1 -> Color.white
- 2 -> Color.blue
- else -> return@OnItemClickListener showError("Unsupported option")
- }
- changeLabelColor(labelColor)
- }
- }
- }
-
- /**
- * Sets up the [popupDialogBinding] for selecting a label position relative to the grid
- * and handles behavior for when a label position is selected.
- */
- private fun setupLabelPositionDropdown(popupDialogBinding: PopupDialogBinding) {
- popupDialogBinding.labelPositionDropdown.apply {
- setAdapter(ArrayAdapter(
- applicationContext,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.positions_array))
- )
-
- onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
- // set the label position
- labelPosition = when (position) {
- 0 -> GridLabelPosition.AllSides
- 1 -> GridLabelPosition.BottomLeft
- 2 -> GridLabelPosition.BottomRight
- 3 -> GridLabelPosition.Center
- 4 -> GridLabelPosition.Geographic
- 5 -> GridLabelPosition.TopLeft
- 6 -> GridLabelPosition.TopRight
- else -> return@OnItemClickListener showError("Unsupported option")
- }
- changeLabelPosition(labelPosition)
- }
- }
- }
-
- /**
- * Sets up the [popupDialogBinding] for the checkbox making labels visible or invisible.
- */
- private fun setupLabelsCheckbox(popupDialogBinding: PopupDialogBinding) {
- popupDialogBinding.labelsCheckBox.apply {
- isChecked = true
- // hide and show label visibility when the checkbox is clicked
- setOnClickListener {
- isLabelVisible = isChecked
- setLabelVisibility(isLabelVisible)
- }
- }
- }
-
- /**
- * Sets the labels visibility based on [isVisible].
- */
- private fun setLabelVisibility(isVisible: Boolean) {
- val grid = mapView.grid ?: return
- grid.labelVisibility = isVisible
- }
-
- /**
- * Sets the [color] of the grid lines.
- */
- private fun changeGridColor(color: Color) {
- val grid = mapView.grid ?: return
- val gridLevels = grid.levelCount
- for (gridLevel in 0 until gridLevels) {
- val lineSymbol: LineSymbol =
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, color, (gridLevel + 1).toFloat())
- grid.setLineSymbol(gridLevel, lineSymbol)
- }
- }
-
- /**
- * Sets the [labelColor] of the labels on the grid.
- */
- private fun changeLabelColor(labelColor: Color) {
- val grid = mapView.grid ?: return
- val gridLevels = grid.levelCount
- for (gridLevel in 0 until gridLevels) {
- val textSymbol = TextSymbol().apply {
- color = labelColor
- size = 14f
- horizontalAlignment = HorizontalAlignment.Left
- verticalAlignment = VerticalAlignment.Bottom
- haloColor = Color.black
- haloWidth = 5f
- }
- grid.setTextSymbol(gridLevel, textSymbol)
- }
- }
-
- /**
- * Sets the [labelPosition] of the labels on the grid.
- */
- private fun changeLabelPosition(labelPosition: GridLabelPosition) {
- val grid = mapView.grid ?: return
- grid.labelPosition = labelPosition
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
diff --git a/show-grid/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-grid/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-grid/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-grid/src/main/res/drawable/ic_launcher_background.xml b/show-grid/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-grid/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-grid/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-grid/src/main/res/mipmap-hdpi/ic_launcher.png b/show-grid/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-grid/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-grid/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-grid/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-mdpi/ic_launcher.png b/show-grid/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-grid/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-grid/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-grid/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-grid/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-grid/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-grid/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-grid/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-grid/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-grid/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-grid/src/main/res/values/strings.xml b/show-grid/src/main/res/values/strings.xml
deleted file mode 100644
index c0beb6ef2..000000000
--- a/show-grid/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
- Show grid
- Labels Visible
- Grid Color
- Label Color
- Grid Type
- Grid Options
- Label Position
-
- - Lat and Long
- - MGRS Grid
- - UTM Grid
- - USNG Grid
-
-
- - Red
- - White
- - Blue
-
-
-
-
- - AllSides
- - BottomLeft
- - BottomRight
- - Center
- - Geographic
- - TopLeft
- - TopRight
-
-
diff --git a/show-labels-on-layer/.gitignore b/show-labels-on-layer/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-labels-on-layer/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-labels-on-layer/build.gradle.kts b/show-labels-on-layer/build.gradle.kts
deleted file mode 100644
index 478620740..000000000
--- a/show-labels-on-layer/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showlabelsonlayer"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showlabelsonlayer"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-labels-on-layer/proguard-rules.pro b/show-labels-on-layer/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-labels-on-layer/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-labels-on-layer/src/main/AndroidManifest.xml b/show-labels-on-layer/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-labels-on-layer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt b/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt
deleted file mode 100644
index c94078a88..000000000
--- a/show-labels-on-layer/src/main/java/com/esri/arcgismaps/sample/showlabelsonlayer/MainActivity.kt
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showlabelsonlayer
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.Color
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.arcgisservices.LabelingPlacement
-import com.arcgismaps.data.ServiceFeatureTable
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.labeling.ArcadeLabelExpression
-import com.arcgismaps.mapping.labeling.LabelDefinition
-import com.arcgismaps.mapping.layers.FeatureLayer
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.esri.arcgismaps.sample.showlabelsonlayer.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a map with a light gray map style
- val map = ArcGISMap(BasemapStyle.ArcGISLightGray)
- // create a service feature table from an online feature service of
- // US Congressional Districts
- val serviceFeatureTable =
- ServiceFeatureTable(getString(R.string.congressional_districts_url))
- // create the feature layer from the service feature table
- val featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
- // add this feature layer to the map
- map.operationalLayers.add(featureLayer)
- // add the map to the mapview
- mapView.map = map
-
- lifecycleScope.launch {
- // if the map load failed show an error and return
- map.load().onFailure {
- return@onFailure showError("Error loading map:${it.message}")
- }
- // if the feature layer load failed show an error and return
- featureLayer.load().onFailure {
- return@onFailure showError("Error loading feature layer:${it.message}")
- }
- // zoom to the layer when it's done loading
- featureLayer.fullExtent?.let { extent ->
- mapView.setViewpoint(Viewpoint(extent))
- }
- }
-
- // create label definitions for the different party type attribute in the feature service
- val republicanLabelDefinition = createLabelDefinition("Republican", Color.red)
- val democraticLabelDefinition = createLabelDefinition("Democrat", Color.blue)
-
- featureLayer.apply {
- // add the label definitions to the feature layer
- labelDefinitions.addAll(
- listOf(
- republicanLabelDefinition,
- democraticLabelDefinition
- )
- )
- // enable the labels
- labelsEnabled = true
- }
- }
-
- /**
- * Creates and returns a [LabelDefinition] for the given party [name] attribute
- * with the [labelColor]
- */
- private fun createLabelDefinition(
- name: String,
- labelColor: Color
- ): LabelDefinition {
- // create a text symbol for styling the label
- val textSymbol = TextSymbol().apply {
- color = labelColor
- size = 12f
- haloColor = Color.white
- haloWidth = 2f
- }
- // create a arcade label expression for the label text
- val arcadeLabelExpression =
- ArcadeLabelExpression(
- "\$feature.NAME + \" (\" + left(\$feature.PARTY,1) " +
- "+ \")\\nDistrict \" + \$feature.CDFIPS"
- )
- // create and return a new label definition with the arcadeLabelExpression and textSymbol
- return LabelDefinition(arcadeLabelExpression, textSymbol).apply {
- // set the label placement
- placement = LabelingPlacement.PolygonAlwaysHorizontal
- // set the attribute name for which this label will be generated
- whereClause = "PARTY = '$name'"
- }
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
-
-/**
- * Simple extension property that represents a blue color
- */
-private val Color.Companion.blue get() = fromRgba(0, 0, 255)
diff --git a/show-labels-on-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-labels-on-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-labels-on-layer/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-labels-on-layer/src/main/res/drawable/ic_launcher_background.xml b/show-labels-on-layer/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-labels-on-layer/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-labels-on-layer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher.png b/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher.png b/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-labels-on-layer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-labels-on-layer/src/main/res/values/strings.xml b/show-labels-on-layer/src/main/res/values/strings.xml
deleted file mode 100644
index a894d57db..000000000
--- a/show-labels-on-layer/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Show labels on layer
- https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_116th_Congressional_Districts/FeatureServer/0
-
diff --git a/show-line-of-sight-between-geoelements/.gitignore b/show-line-of-sight-between-geoelements/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-line-of-sight-between-geoelements/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-line-of-sight-between-geoelements/build.gradle.kts b/show-line-of-sight-between-geoelements/build.gradle.kts
deleted file mode 100644
index 97c2260c6..000000000
--- a/show-line-of-sight-between-geoelements/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showlineofsightbetweengeoelements"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showlineofsightbetweengeoelements"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/show-line-of-sight-between-geoelements/proguard-rules.pro b/show-line-of-sight-between-geoelements/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-line-of-sight-between-geoelements/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml b/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml
deleted file mode 100644
index 65de6378b..000000000
--- a/show-line-of-sight-between-geoelements/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt b/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt
deleted file mode 100644
index 083ec1813..000000000
--- a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/DownloadActivity.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements
-
-import android.content.Intent
-import android.os.Bundle
-import com.esri.arcgismaps.sample.sampleslib.DownloaderActivity
-
-class DownloadActivity : DownloaderActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- downloadAndStartSample(
- Intent(this, MainActivity::class.java),
- // get the app name of the sample
- getString(R.string.app_name),
- listOf(
- "https://www.arcgis.com/home/item.html?id=3af5cfec0fd24dac8d88aea679027cb9"
- )
-
- )
- }
-}
diff --git a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt b/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt
deleted file mode 100644
index 0bb7f39b6..000000000
--- a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt b/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt
deleted file mode 100644
index 58abd3c9a..000000000
--- a/show-line-of-sight-between-geoelements/src/main/java/com/esri/arcgismaps/sample/showlineofsightbetweengeoelements/components/SceneViewModel.kt
+++ /dev/null
@@ -1,278 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.components
-
-import android.app.Application
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.core.content.ContextCompat.getString
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.arcgismaps.Color
-import com.arcgismaps.analysis.GeoElementLineOfSight
-import com.arcgismaps.analysis.LineOfSightTargetVisibility
-import com.arcgismaps.geometry.AngularUnit
-import com.arcgismaps.geometry.GeodeticCurveType
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.LinearUnit
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.PointBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISScene
-import com.arcgismaps.mapping.ArcGISTiledElevationSource
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Surface
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.layers.ArcGISSceneLayer
-import com.arcgismaps.mapping.symbology.ModelSceneSymbol
-import com.arcgismaps.mapping.symbology.SceneSymbolAnchorPosition
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleRenderer
-import com.arcgismaps.mapping.view.AnalysisOverlay
-import com.arcgismaps.mapping.view.Camera
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.SurfacePlacement
-import com.esri.arcgismaps.sample.showlineofsightbetweengeoelements.R
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
-import java.io.File
-import kotlin.concurrent.timer
-
-class SceneViewModel(private var application: Application) : AndroidViewModel(application) {
-
- // Keep track of target visibility status string state.
- var targetVisibilityString by mutableStateOf("")
- private set
-
- // Set visibility status string in the UI.
- private fun updateTargetVisibilityString(targetVisibility: String) {
- targetVisibilityString = targetVisibility
- }
-
- // Initialize z to 50 as starting point and emit its state changes
- private val _observerHeight = MutableStateFlow(50.0)
- val observerHeight: StateFlow = _observerHeight.asStateFlow()
-
- // Keeps track of wayPoints
- private var waypointsIndex = 0
-
- // Create waypoints around a block for the taxi to drive to
- private val wayPoints = listOf(
- Point(-73.984513, 40.748469, SpatialReference.wgs84()),
- Point(-73.985068, 40.747786, SpatialReference.wgs84()),
- Point(-73.983452, 40.747091, SpatialReference.wgs84()),
- Point(-73.982961, 40.747762, SpatialReference.wgs84()),
- )
-
- private val provisionPath: String by lazy {
- application.getExternalFilesDir(null)?.path.toString() + File.separator + application.getString(
- R.string.app_name
- ) + File.separator
- }
-
- private val filePath = provisionPath + application.getString(R.string.dolmus_model)
-
- // Create a symbol of a taxi using the model file
- private val taxiSymbol = ModelSceneSymbol(
- uri = filePath,
- scale = 3.0F
- ).apply {
- anchorPosition = SceneSymbolAnchorPosition.Bottom
- }
-
- // Create a graphic of a taxi to be the target
- private val taxiGraphic = Graphic(
- geometry = wayPoints[0],
- symbol = taxiSymbol
- ).apply {
- attributes["HEADING"] = 0.0
- }
-
- // Create a graphic near the Empire State Building to be the observer
- private val observerGraphic = Graphic(
- geometry = Point(
- x = -73.9853,
- y = 40.7484,
- z = 50.0,
- spatialReference = SpatialReference.wgs84()
- ),
- symbol = SimpleMarkerSymbol(
- style = SimpleMarkerSymbolStyle.Circle,
- color = Color.red,
- size = 5f
- )
- )
-
- // Zoom to show the observer
- private val camera = Camera(
- lookAtPoint = observerGraphic.geometry as Point,
- distance = 700.0,
- roll = 0.0,
- pitch = 45.0,
- heading = -30.0,
- )
-
-
- // Define base surface for elevation data
- private val surface = Surface().apply {
- elevationSources.add(
- ArcGISTiledElevationSource(
- uri = getString(
- application,
- R.string.elevation_service_url
- )
- )
- )
- }
-
- // Define a scene layer for the New York buildings
- private val buildings =
- ArcGISSceneLayer(uri = application.getString(R.string.new_york_buildings_service_url))
-
-
- // Create a scene and add a basemap to it.
- // Set the surface and buildings in the scene, and define the viewpoint on launch
- val scene = ArcGISScene(BasemapStyle.ArcGISTopographic).apply {
- baseSurface = surface
- operationalLayers.add(buildings)
- initialViewpoint = Viewpoint(
- boundingGeometry = observerGraphic.geometry as Point,
- camera = camera
- )
- }
-
-
- // Set up a heading expression to handle graphic rotation
- private val renderer3D = SimpleRenderer().apply {
- sceneProperties.headingExpression = ("[HEADING]")
- }
-
-
- // Create graphic overlay to hold graphics
- // Set the surface placement, renderer, and add graphics,
- val graphicsOverlay = GraphicsOverlay().apply {
- sceneProperties.surfacePlacement = SurfacePlacement.RelativeToScene
- renderer = renderer3D
- graphics.addAll(listOf(observerGraphic, taxiGraphic))
- }
-
-
- // Create a line of sight between the two graphics and add it to the analysis overlay
- private val lineOfSight = GeoElementLineOfSight(
- observerGeoElement = observerGraphic,
- targetGeoElement = taxiGraphic
- ).apply {
- // Observe the visibility status of the moving taxi
- viewModelScope.launch(Dispatchers.Main) {
-
- // Update target visibility status and select (highlight) the taxi when the line of sight target visibility changes to visible
- targetVisibility.collect { targetVisibility ->
- when(targetVisibility) {
- is LineOfSightTargetVisibility.Visible -> {
- updateTargetVisibilityString("Visible")
- taxiGraphic.isSelected = true
- }
- is LineOfSightTargetVisibility.Obstructed -> {
- updateTargetVisibilityString("Obstructed")
- taxiGraphic.isSelected = false
- }
- is LineOfSightTargetVisibility.Unknown -> {
- updateTargetVisibilityString("Unknown")
- taxiGraphic.isSelected = false
- }
- }
- }
- }
- }
-
- // Create an analysis overlay to hold the line of sight
- val analysisOverlay = AnalysisOverlay().apply {
- analyses.add(lineOfSight)
- }
-
- init {
-
- // Create a timer to animate the tank
- timer(
- initialDelay = 0,
- period = 50,
- action = {
- animate()
- }
- )
- }
-
- /**
- * Updates elevation of the observer graphic using the given [height]
- */
- fun updateHeight(height: Double) {
- val pointBuilder = PointBuilder(observerGraphic.geometry as Point).apply {
- z = height
- }
- observerGraphic.geometry = pointBuilder.toGeometry()
- _observerHeight.value = height
- }
-
- /**
- * Moves the taxi toward the current waypoint a short distance.
- */
- private fun animate() {
-
- val meters = LinearUnit.meters
- val degrees = AngularUnit.degrees
- val waypoint = wayPoints[waypointsIndex]
- val location = taxiGraphic.geometry as Point
-
- // Calculate the geodetic distance between current taxi location and next waypoint
- GeometryEngine.distanceGeodeticOrNull(
- point1 = location,
- point2 = waypoint,
- distanceUnit = meters,
- azimuthUnit = degrees,
- curveType = GeodeticCurveType.Geodesic
- )?.let { geodeticDistanceResult ->
-
- taxiGraphic.apply {
-
- // Move toward waypoint a short distance
- geometry = GeometryEngine.tryMoveGeodetic(
- pointCollection = listOf(location),
- distance = 1.0,
- distanceUnit = meters,
- azimuth = geodeticDistanceResult.azimuth1,
- azimuthUnit = degrees,
- curveType = GeodeticCurveType.Geodesic
- )[0]
-
- // Rotate to the waypoint
- attributes["HEADING"] = geodeticDistanceResult.azimuth1
-
- // Reached waypoint, move to next waypoint
- if (geodeticDistanceResult.distance <= 2) {
- waypointsIndex = (waypointsIndex + 1) % wayPoints.size
- }
- }
- }
- }
-
-}
diff --git a/show-line-of-sight-between-geoelements/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-line-of-sight-between-geoelements/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-line-of-sight-between-geoelements/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-line-of-sight-between-geoelements/src/main/res/drawable/ic_launcher_background.xml b/show-line-of-sight-between-geoelements/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-line-of-sight-between-geoelements/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-line-of-sight-between-geoelements/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-line-of-sight-between-geoelements/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml b/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml
deleted file mode 100644
index 3b09ed461..000000000
--- a/show-line-of-sight-between-geoelements/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
- Show Line of Sight Between Geoelements
- https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer
- https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/New_York_LoD2_3D_Buildings/SceneServer/layers/0
- dolmus.3ds
- dolmus_back.jpeg
- dolmus_front.jpeg
- dolmus_side.jpeg
- tire_tread.jpeg
-
diff --git a/show-location-history/build.gradle.kts b/show-location-history/build.gradle.kts
deleted file mode 100644
index c0fdf4a72..000000000
--- a/show-location-history/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showlocationhistory"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showlocationhistory"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-location-history/proguard-rules.pro b/show-location-history/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-location-history/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-location-history/src/main/AndroidManifest.xml b/show-location-history/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-location-history/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt b/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt
deleted file mode 100644
index a41a0b3e6..000000000
--- a/show-location-history/src/main/java/com/esri/arcgismaps/sample/showlocationhistory/MainActivity.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showlocationhistory
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.location.LocationDisplayAutoPanMode
-import com.arcgismaps.location.SimulatedLocationDataSource
-import com.arcgismaps.location.SimulationParameters
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleRenderer
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.showlocationhistory.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import java.time.Instant
-
-class MainActivity : AppCompatActivity() {
-
- private var isTrackLocation: Boolean = false
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create a center point for the data in West Los Angeles, California
- val center = Point(-13185535.98, 4037766.28, SpatialReference(102100))
-
- // create a graphics overlay for the points and use a red circle for the symbols
- val locationHistoryOverlay = GraphicsOverlay()
- val locationSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10f)
- locationHistoryOverlay.renderer = SimpleRenderer(locationSymbol)
-
- // create a graphics overlay for the lines connecting the points and use a blue line for the symbol
- val locationHistoryLineOverlay = GraphicsOverlay()
- val locationLineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2.0f)
- locationHistoryLineOverlay.renderer = SimpleRenderer(locationLineSymbol)
-
- mapView.apply {
- // create and add a map with a navigation night basemap style
- map = ArcGISMap(BasemapStyle.ArcGISNavigationNight)
- setViewpoint(Viewpoint(center, 7000.0))
- graphicsOverlays.addAll(listOf(locationHistoryOverlay, locationHistoryLineOverlay))
- }
-
- // create a polyline builder to connect the location points
- val polylineBuilder = PolylineBuilder(SpatialReference(102100))
-
- // create a simulated location data source from json data with simulation parameters to set a consistent velocity
- val simulatedLocationDataSource = SimulatedLocationDataSource(
- Geometry.fromJsonOrNull(getString(R.string.polyline_data)) as Polyline,
- SimulationParameters(Instant.now(), 30.0, 0.0, 0.0)
- )
-
- // coroutine scope to collect data source location changes
- lifecycleScope.launch {
- simulatedLocationDataSource.locationChanged.collect { location ->
- // if location tracking is turned off, do not add to the polyline
- if (!isTrackLocation) {
- return@collect
- }
- // get the point from the location
- val nextPoint = location.position
- // add the new point to the polyline builder
- polylineBuilder.addPoint(nextPoint)
- // add the new point to the two graphics overlays and reset the line connecting the points
- locationHistoryOverlay.graphics.add(Graphic(nextPoint))
- locationHistoryLineOverlay.graphics.apply {
- clear()
- add((Graphic(polylineBuilder.toGeometry())))
- }
- }
- }
-
- // configure the map view's location display to follow the simulated location data source
- mapView.locationDisplay.apply {
- dataSource = simulatedLocationDataSource
- setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- initialZoomScale = 7000.0
- }
-
- // coroutine scope to set a tap event on the map view
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect {
- if (mapView.locationDisplay.autoPanMode.value == LocationDisplayAutoPanMode.Off) {
- mapView.locationDisplay.setAutoPanMode(LocationDisplayAutoPanMode.Recenter)
- }
- if (isTrackLocation) {
- isTrackLocation = false
- Snackbar.make(mapView, "Tracking has stopped", Snackbar.LENGTH_INDEFINITE).show()
- } else {
- isTrackLocation = true
- Snackbar.make(mapView, "Tracking has started", Snackbar.LENGTH_INDEFINITE ).show()
- }
- }
- }
-
- // coroutine scope to start the simulated location data source
- lifecycleScope.launch {
- simulatedLocationDataSource.start()
- }
- }
-}
-
diff --git a/show-location-history/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-location-history/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-location-history/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-location-history/src/main/res/drawable/ic_launcher_background.xml b/show-location-history/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-location-history/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-location-history/src/main/res/drawable/ic_launcher_foreground.xml b/show-location-history/src/main/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 7706ab9e6..000000000
--- a/show-location-history/src/main/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-location-history/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-location-history/src/main/res/mipmap-hdpi/ic_launcher.png b/show-location-history/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-location-history/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-location-history/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-location-history/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-mdpi/ic_launcher.png b/show-location-history/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-location-history/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-location-history/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-location-history/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-location-history/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-location-history/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-location-history/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-location-history/src/main/res/values/strings.xml b/show-location-history/src/main/res/values/strings.xml
deleted file mode 100644
index 56d1531f0..000000000
--- a/show-location-history/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
- Show location history
- {\"paths\":[[ [-13185646.046666779,4037971.5966668758],
- [-13185586.780000051,4037827.6633333955],[-13185514.813333312,4037709.1299999417],
- [-13185569.846666701,4037522.8633330846],[-13185591.01333339,4037378.9299996048],
- [-13185629.113333428,4037283.6799995075],[-13185770.93000024,4037425.4966663187],
- [-13185821.730000293,4037546.146666442],[-13185880.996667018,4037704.8966666036],
- [-13185948.730000421,4037874.2300001099],[-13185974.130000448,4037946.1966668498],
- [-13186120.180000596,4037958.896666863],[-13186264.113334076,4037984.296666889],
- [-13186336.080000836,4038001.2300002342],[-13186314.91333415,4037757.8133333195],
- [-13186272.580000773,4037560.9633331187],[-13186187.913334005,4037463.59666635],
- [-13186431.33000092,4037404.3299996229],[-13186676.863334503,4037290.0299995062],
- [-13187625.130002158,4038589.6633341513],[-13187333.030001862,4038756.8800009824],
- [-13187091.730001617,4038617.1800008402],[-13186791.163334643,4038805.5633343654],
- [-13186721.313334571,4038801.3300010278],[-13186833.49666802,4038195.9633337436],
- [-13186977.677439401,4037699.8176972247],[-13186784.921301765,4037820.4541915278],
- [-13186749.517113185,4038150.8932846226],[-13186649.860878762,4038288.5762400767],
- [-13186556.760975549,4038323.9804286221],[-13186472.839936033,4038481.3323777127],
- [-13186373.183701571,4038489.1999751539],[-13186344.335844241,4038242.6819215398],
- [-13186126.665647998,4038308.245233661],[-13185814.584282301,4038358.0733508728],
- [-13185651.987268206,4038116.8003622484],[-13185203.534213299,4038048.6145176427],
- [-13184576.748949422,4038150.8932845518],[-13184251.55492135,4037833.5668537691],
- [-13184146.653621957,4037524.1080205571],[-13183949.963685593,4037621.1417224966],
- [-13183687.71043711,4037781.1162040718],[-13183480.530370807,4037875.5273735262],
- [-13182307.629999243,4037859.7460188437],[-13181376.039484169,4037820.9297473822],
- [-13180716.162869323,4038364.3575478429],[-13180182.439136729,4038810.7446696493],
- [-13178474.523192419,4040237.2426458625],[-13178321.134040033,4039740.6894803117],
- [-13177958.020228144,4039140.1550991111],[-13177073.512224896,4037459.5898928214],
- [-13177757.842101147,4037589.9384406791],[-13178386.308314031,4037799.427178307],
- [-13180095.012208173,4037811.6550856642],[-13180126.165447287,4036845.9046731163],
- [-13179806.844746364,4036324.0879179495],[-13180928.361354485,4035887.9425703473],
- [-13181598.155995468,4035428.432293402],[-13182984.47513606,4034447.105261297],
- [-13182229.264383668,4033222.8051626245],[-13182058.735615831,4033339.8690072047],
- [-13181939.035180708,4033691.2477038498],[-13182116.65518121,4033861.1450956347],
- [-13181792.305615077,4034085.1007484416],[-13182027.845180977,4034467.3698799671],
- [-13181877.254310986,4034644.9898804692],[-13181630.130832028,4034517.5668366305],
- [-13181386.868657427,4034424.8955320208],[-13181228.555178719,4034652.7124891868],
- [-13181379.14604871,4034942.3103160923],[-13181267.168222306,4035189.4337950516],
- [-13181074.103004368,4035015.6750989081],[-13180807.673003616,4034934.5877073747],
- [-13180618.469090037,4034814.8872722536],[-13180599.162568243,4035374.7764042714],
- [-13181047.073873857,4035494.476839392],[-13181317.365178969,4035413.3894478586],
- [-13180765.198655669,4035143.0981427468],[-13180328.871263131,4034892.1133594285],
- [-13180270.951697765,4035258.9372735149],[-13180325.009958787,4035718.4324922049],
- [-13180707.279090302,4035695.2646660525],[-13181413.897788007,4035536.9511873648],
- [-13181618.54691902,4035807.2424924765],[-13181884.976919774,4036065.949884512],
- [-13182159.129529245,4035861.3007534989],[-13182174.57474668,4035668.2355355616],
- [-13182417.83692128,4035664.374231203],[-13182784.660835361,4035409.5281435261],
- [-13182997.032575091,4035255.0759691764],[-13182618.624747934,4034679.7416197238]
- ]],\"spatialReference\":{\"wkid\":102100,\"latestWkid\":3857}}
-
diff --git a/show-magnifier/.gitignore b/show-magnifier/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-magnifier/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-magnifier/build.gradle.kts b/show-magnifier/build.gradle.kts
deleted file mode 100644
index bd2845d89..000000000
--- a/show-magnifier/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showmagnifier"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showmagnifier"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/show-magnifier/proguard-rules.pro b/show-magnifier/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-magnifier/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-magnifier/src/main/AndroidManifest.xml b/show-magnifier/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-magnifier/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt b/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt
deleted file mode 100644
index 2c8416c74..000000000
--- a/show-magnifier/src/main/java/com/esri/arcgismaps/sample/showmagnifier/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showmagnifier
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showmagnifier.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ShowMagnifierApp()
- }
- }
- }
-
- @Composable
- private fun ShowMagnifierApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/show-magnifier/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-magnifier/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-magnifier/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-magnifier/src/main/res/drawable/ic_launcher_background.xml b/show-magnifier/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-magnifier/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-magnifier/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher.png b/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-magnifier/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher.png b/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-magnifier/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-magnifier/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-magnifier/src/main/res/values/strings.xml b/show-magnifier/src/main/res/values/strings.xml
deleted file mode 100644
index 33cfcd0b3..000000000
--- a/show-magnifier/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Show Magnifier
-
diff --git a/show-portal-user-info/build.gradle.kts b/show-portal-user-info/build.gradle.kts
deleted file mode 100644
index 5a8653458..000000000
--- a/show-portal-user-info/build.gradle.kts
+++ /dev/null
@@ -1,55 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showportaluserinfo"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showportaluserinfo"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(libs.androidx.browser)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.authentication)
-}
diff --git a/show-portal-user-info/proguard-rules.pro b/show-portal-user-info/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-portal-user-info/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-portal-user-info/src/main/AndroidManifest.xml b/show-portal-user-info/src/main/AndroidManifest.xml
deleted file mode 100644
index 0c2c9df0b..000000000
--- a/show-portal-user-info/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt b/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt
deleted file mode 100644
index 10fecc3c7..000000000
--- a/show-portal-user-info/src/main/java/com/esri/arcgismaps/sample/showportaluserinfo/MainActivity.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showportaluserinfo
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showportaluserinfo.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ShowPortalUserInfoApp()
- }
- }
- }
-
- @Composable
- private fun ShowPortalUserInfoApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name),
- application = application
- )
- }
- }
-}
diff --git a/show-portal-user-info/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-portal-user-info/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-portal-user-info/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-portal-user-info/src/main/res/drawable/ic_launcher_background.xml b/show-portal-user-info/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-portal-user-info/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-portal-user-info/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher.png b/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher.png b/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-portal-user-info/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-portal-user-info/src/main/res/values/strings.xml b/show-portal-user-info/src/main/res/values/strings.xml
deleted file mode 100644
index ca6f55eb5..000000000
--- a/show-portal-user-info/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
- Show portal user info
- rR5K4TEyhkPDcTkf
- my-ags-app://auth
-
- Enter a valid portal url to load it and see the portal details displayed here.
-
- Portal loaded successfully but no portal info was found.
- The Portal is loaded successfully. Please check the Portal details below
- Failed to load portal
- https://www.arcgis.com
-
-
diff --git a/show-result-of-spatial-operations/.gitignore b/show-result-of-spatial-operations/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-result-of-spatial-operations/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-result-of-spatial-operations/build.gradle.kts b/show-result-of-spatial-operations/build.gradle.kts
deleted file mode 100644
index 23cace956..000000000
--- a/show-result-of-spatial-operations/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showresultofspatialoperations"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showresultofspatialoperations"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-result-of-spatial-operations/proguard-rules.pro b/show-result-of-spatial-operations/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-result-of-spatial-operations/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-result-of-spatial-operations/src/main/AndroidManifest.xml b/show-result-of-spatial-operations/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-result-of-spatial-operations/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt b/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt
deleted file mode 100644
index 49f8427c0..000000000
--- a/show-result-of-spatial-operations/src/main/java/com/esri/arcgismaps/sample/showresultofspatialoperations/MainActivity.kt
+++ /dev/null
@@ -1,207 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showresultofspatialoperations
-
-import android.os.Bundle
-import android.widget.ArrayAdapter
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.MutablePart
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.showresultofspatialoperations.databinding.ActivityMainBinding
-import kotlinx.coroutines.launch
-
-private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // enum to keep track of the selected operation to display on the map
- enum class SpatialOperation(val menuPosition: Int) {
- NO_OPERATION(0),
- INTERSECTION(1),
- UNION(2),
- DIFFERENCE(3),
- SYMMETRIC_DIFFERENCE(4)
- }
-
- // create the graphic overlays
- private val inputGeometryGraphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
- private val resultGeometryGraphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
-
- // simple black line symbol for outlines
- private val lineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 1f)
-
- // red fill symbol for result
- private val resultFillSymbol =
- SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.red, lineSymbol)
-
- // the two polygons for perform spatial operations
- private lateinit var inputPolygon1: Polygon
- private lateinit var inputPolygon2: Polygon
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // set up the adapter
- val arrayAdapter = ArrayAdapter(
- this,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.operation)
- )
- activityMainBinding.bottomListItems.apply {
- setAdapter(arrayAdapter)
- setOnItemClickListener { _, _, i, _ ->
- updateGeometry(i)
- }
- }
-
- // set up the MapView
- mapView.apply {
- // create an ArcGISMap with a light gray basemap
- map = ArcGISMap(BasemapStyle.ArcGISLightGray)
- // create graphics overlays to show the inputs and results of the spatial operation
- graphicsOverlays.add(inputGeometryGraphicsOverlay)
- graphicsOverlays.add(resultGeometryGraphicsOverlay)
- }
-
- // create input polygons and add graphics to display these polygons in an overlay
- createPolygons()
-
- // center the map view on the input geometries
- val envelope = GeometryEngine.union(inputPolygon1, inputPolygon2).extent
- lifecycleScope.launch {
- mapView.setViewpointGeometry(envelope, 20.0)
- }
- }
-
- private fun updateGeometry(position: Int) {
- // clear previous operation result
- resultGeometryGraphicsOverlay.graphics.clear()
- // create a result geometry of the spatial operation
- var resultGeometry: Geometry? = null
- // get the selected operation
- when (SpatialOperation.entries.find { it.menuPosition == position }) {
- SpatialOperation.NO_OPERATION -> { /* No operation needed */
- }
- SpatialOperation.INTERSECTION -> {
- resultGeometry = GeometryEngine.intersectionOrNull(inputPolygon1, inputPolygon2)
- }
- SpatialOperation.UNION -> {
- resultGeometry = GeometryEngine.union(inputPolygon1, inputPolygon2)
- }
- SpatialOperation.DIFFERENCE -> {
- resultGeometry = GeometryEngine.differenceOrNull(inputPolygon1, inputPolygon2)
- }
- SpatialOperation.SYMMETRIC_DIFFERENCE -> {
- resultGeometry = GeometryEngine.symmetricDifferenceOrNull(inputPolygon1, inputPolygon2)
- }
- null -> {}
- }
-
- // add a graphic from the result geometry, showing result in red
- if (resultGeometry != null) {
- val resultGraphic = Graphic(resultGeometry, resultFillSymbol).also {
- // select the result to highlight it
- it.isSelected = true
- }
- // add the result graphic to the graphic overlay
- resultGeometryGraphicsOverlay.graphics.add(resultGraphic)
- }
- }
-
- private fun createPolygons() {
- // create input polygon 1
- val polygonBuilder1 = PolygonBuilder(SpatialReference.webMercator()) {
- // add points to the point collection
- addPoint(Point(-13160.0, 6710100.0))
- addPoint(Point(-13300.0, 6710500.0))
- addPoint(Point(-13760.0, 6710730.0))
- addPoint(Point(-14660.0, 6710000.0))
- addPoint(Point(-13960.0, 6709400.0))
- }
- inputPolygon1 = polygonBuilder1.toGeometry()
-
- // create and add a blue graphic to show input polygon 1
- val blueFill = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.blue, lineSymbol)
- inputGeometryGraphicsOverlay.graphics.add(Graphic(inputPolygon1, blueFill))
-
- // outer ring
- val outerRing = MutablePart.createWithPoints(
- listOf(
- Point(-13060.0, 6711030.0),
- Point(-12160.0, 6710730.0),
- Point(-13160.0, 6709700.0),
- Point(-14560.0, 6710730.0),
- Point(-13060.0, 6711030.0),
- ),
- SpatialReference.webMercator()
- )
-
- // inner ring
- val innerRing = MutablePart.createWithPoints(
- listOf(
- Point(-13060.0, 6710910.0),
- Point(-12450.0, 6710660.0),
- Point(-13160.0, 6709900.0),
- Point(-14160.0, 6710630.0),
- Point(-13060.0, 6710910.0)
- ),
- SpatialReference.webMercator()
- )
-
- // add both parts (rings) to a polygon and create a geometry from it
- inputPolygon2 = Polygon(listOf(outerRing, innerRing))
-
- // create and add a green graphic to show input polygon 2
- val greenFill = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.green, lineSymbol)
- inputGeometryGraphicsOverlay.graphics.add(Graphic(inputPolygon2, greenFill))
- }
-}
diff --git a/show-result-of-spatial-operations/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-result-of-spatial-operations/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-result-of-spatial-operations/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-operations/src/main/res/drawable/ic_launcher_background.xml b/show-result-of-spatial-operations/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-result-of-spatial-operations/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-result-of-spatial-operations/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher.png b/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher.png b/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-result-of-spatial-operations/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-operations/src/main/res/values/strings.xml b/show-result-of-spatial-operations/src/main/res/values/strings.xml
deleted file mode 100644
index 5bc70aef3..000000000
--- a/show-result-of-spatial-operations/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
- Show result of spatial operations
- Select spatial operation
-
-
- - No operation
- - Intersection
- - Union
- - Difference
- - Symmetric difference
-
-
diff --git a/show-result-of-spatial-relationships/.gitignore b/show-result-of-spatial-relationships/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-result-of-spatial-relationships/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-result-of-spatial-relationships/build.gradle.kts b/show-result-of-spatial-relationships/build.gradle.kts
deleted file mode 100644
index efcf7eeac..000000000
--- a/show-result-of-spatial-relationships/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showresultofspatialrelationships"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.showresultofspatialrelationships"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/show-result-of-spatial-relationships/proguard-rules.pro b/show-result-of-spatial-relationships/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-result-of-spatial-relationships/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-result-of-spatial-relationships/src/main/AndroidManifest.xml b/show-result-of-spatial-relationships/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-result-of-spatial-relationships/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt b/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt
deleted file mode 100644
index 4ff3a477a..000000000
--- a/show-result-of-spatial-relationships/src/main/java/com/esri/arcgismaps/sample/showresultofspatialrelationships/MainActivity.kt
+++ /dev/null
@@ -1,268 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showresultofspatialrelationships
-
-import android.os.Bundle
-import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.data.SpatialRelationship
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.ScreenCoordinate
-import com.esri.arcgismaps.sample.showresultofspatialrelationships.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- // create a MapView using binding
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- // create a graphics overlay
- private val graphicsOverlay by lazy {
- GraphicsOverlay()
- }
-
- // text view to display the selected graphic
- private val selectedGraphicTV by lazy {
- activityMainBinding.selectedGraphicTV
- }
-
- // create the polygon graphic
- private val polygonGraphic by lazy {
- // add polygon points to the polygon builder
- val polygonBuilder = PolygonBuilder(SpatialReference.webMercator()) {
- addPoint(Point(-5991501.677830, 5599295.131468))
- addPoint(Point(-6928550.398185, 2087936.739807))
- addPoint(Point(-3149463.800709, 1840803.011362))
- addPoint(Point(-1563689.043184, 3714900.452072))
- addPoint(Point(-3180355.516764, 5619889.608838))
- }
- // create a polygon from the polygon builder
- val polygon = polygonBuilder.toGeometry()
- val polygonSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.ForwardDiagonal, Color.green,
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f)
- )
- Graphic(polygon, polygonSymbol)
- }
-
-
- // create the polyline graphic
- private val polylineGraphic by lazy {
- // add polyline points to the polyline builder
- val polylineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
- addPoint(Point(-4354240.726880, -609939.795721))
- addPoint(Point(-3427489.245210, 2139422.933233))
- addPoint(Point(-2109442.693501, 4301843.057130))
- addPoint(Point(-1810822.771630, 7205664.366363))
- }
- // create a polyline graphic
- val polyline = polylineBuilder.toGeometry()
- val polylineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.red, 4f)
- Graphic(polyline, polylineSymbol)
- }
-
-
- // create the point graphic
- private val pointGraphic by lazy {
- // create a point graphic
- val point = Point(-4487263.495911, 3699176.480377, SpatialReference.webMercator())
- val locationMarker = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.blue, 10f)
- Graphic(point, locationMarker)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- // add MapView to the lifecycle
- lifecycle.addObserver(mapView)
- mapView.apply {
- // add a map with a topographic basemap style
- map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- // set selection color
- selectionProperties.color = Color.red
- // add graphics overlay
- graphicsOverlays.add(graphicsOverlay)
- //set viewpoint
- setViewpoint(Viewpoint(33.183564, -42.428668, 90000000.0))
- }
- // add the graphics to the graphics overlay
- graphicsOverlay.graphics.addAll(listOf(polygonGraphic, polylineGraphic, pointGraphic))
- // set an on touch listener on the map view
- lifecycleScope.launch {
- mapView.onSingleTapConfirmed.collect { tapEvent ->
- // get the tapped coordinate
- val screenCoordinate = tapEvent.screenCoordinate
- // identify the relationships of tapped graphic
- identifyGraphicRelationships(screenCoordinate)
- }
- }
- }
-
- /**
- * Identifies the selected graphic tapped at the [screenCoordinate]
- * and finds the relations to other graphics on the map.
- */
- private suspend fun identifyGraphicRelationships(screenCoordinate: ScreenCoordinate) {
- // get the graphic selected at the given ScreenCoordinate
- val identifyGraphicsOverlayResult =
- mapView.identifyGraphicsOverlay(graphicsOverlay, screenCoordinate, 1.0, false)
- // get identified graphics overlay, else show an error
- val identifyGraphicsOverlay = identifyGraphicsOverlayResult.getOrElse {
- return showError(it.message.toString())
- }
- // get the identified selected graphics
- val identifiedGraphics = identifyGraphicsOverlay.graphics
- // if no graphic was selected
- if (identifiedGraphics.isEmpty()) {
- // display text and clear selected
- selectedGraphicTV.text = getString(R.string.select_graphic)
- graphicsOverlay.clearSelection()
- return
- }
- // create HashMap that will hold relationships in between graphics
- val relationships = mutableMapOf>()
- // add the graphics as keys to the hashmap
- relationships["Point"] = emptyList()
- relationships["Polyline"] = emptyList()
- relationships["Polygon"] = emptyList()
- // select the identified graphic
- graphicsOverlay.clearSelection()
- // get the first graphic identified
- val identifiedGraphic = identifiedGraphics[0]
- // set the identified graphic to be selected
- identifiedGraphic.isSelected = true
- // tracks the type of geometry selected
- var selectedGraphicName = ""
- // find the geometry of the selected graphic
- when (val selectedGeometry = identifiedGraphic.geometry) {
- // if selected geometry is a point
- is Point -> {
- // get the point-polyline relationships
- relationships["Polyline"] =
- getSpatialRelationships(selectedGeometry, polylineGraphic.geometry)
- // get the point-polygon relationships
- relationships["Polygon"] =
- getSpatialRelationships(selectedGeometry, polygonGraphic.geometry)
- // update the name of the selected geometry
- selectedGraphicName = "Point"
- }
- // if selected geometry is a polyline
- is Polyline -> {
- // get the polyline-polygon relationships
- relationships["Polygon"] =
- getSpatialRelationships(selectedGeometry, polygonGraphic.geometry)
- // get the polyline-point relationships
- relationships["Point"] =
- getSpatialRelationships(selectedGeometry, pointGraphic.geometry)
- // update the name of the selected geometry
- selectedGraphicName = "Polyline"
- }
- // if selected geometry is a polygon
- is Polygon -> {
- // get the polygon-polyline relationships
- relationships["Polyline"] =
- getSpatialRelationships(selectedGeometry, polylineGraphic.geometry)
- // get the polygon-point relationships
- relationships["Point"] =
- getSpatialRelationships(selectedGeometry, pointGraphic.geometry)
- // update the name of the selected geometry
- selectedGraphicName = "Polygon"
- }
- // no other graphic on map
- else -> {}
- }
- // display selected graphic text
- selectedGraphicTV.text = "$selectedGraphicName geometry is selected"
- // create and display a dialog with the established graphics
- RelationshipsDialog(layoutInflater, this, relationships, selectedGraphicName).createAndDisplayDialog()
-
- }
-
- /**
- * Gets a list of spatial relationships that the [a] geometry has to the [b] geometry.
- */
- private fun getSpatialRelationships(
- a: Geometry?,
- b: Geometry?
- ): List {
- // check if either geometry is null
- if (a == null || b == null) {
- return emptyList()
- }
- val relationships: MutableList = mutableListOf()
- if (GeometryEngine.crosses(a, b))
- relationships.add(SpatialRelationship.Crosses)
- if (GeometryEngine.contains(a, b))
- relationships.add(SpatialRelationship.Contains)
- if (GeometryEngine.disjoint(a, b))
- relationships.add(SpatialRelationship.Disjoint)
- if (GeometryEngine.intersects(a, b))
- relationships.add(SpatialRelationship.Intersects)
- if (GeometryEngine.overlaps(a, b))
- relationships.add(SpatialRelationship.Overlaps)
- if (GeometryEngine.touches(a, b))
- relationships.add(SpatialRelationship.Touches)
- if (GeometryEngine.within(a, b))
- relationships.add(SpatialRelationship.Within)
- return relationships
- }
-
- private fun showError(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-}
diff --git a/show-result-of-spatial-relationships/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-result-of-spatial-relationships/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-result-of-spatial-relationships/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-relationships/src/main/res/drawable/ic_launcher_background.xml b/show-result-of-spatial-relationships/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-result-of-spatial-relationships/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-result-of-spatial-relationships/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher.png b/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher.png b/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-result-of-spatial-relationships/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-result-of-spatial-relationships/src/main/res/values/strings.xml b/show-result-of-spatial-relationships/src/main/res/values/strings.xml
deleted file mode 100644
index b5f2aed29..000000000
--- a/show-result-of-spatial-relationships/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Show result of spatial relationships
- Tap on the map to select the graphic
- with polyline:
- with polygon:
- with point:
-
diff --git a/show-viewshed-from-point-in-scene/.gitignore b/show-viewshed-from-point-in-scene/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/show-viewshed-from-point-in-scene/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/show-viewshed-from-point-in-scene/build.gradle.kts b/show-viewshed-from-point-in-scene/build.gradle.kts
deleted file mode 100644
index f54df5bc0..000000000
--- a/show-viewshed-from-point-in-scene/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.showviewshedfrompointinscene"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.showviewshedfrompointinscene"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/show-viewshed-from-point-in-scene/proguard-rules.pro b/show-viewshed-from-point-in-scene/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/show-viewshed-from-point-in-scene/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml b/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/show-viewshed-from-point-in-scene/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt b/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt
deleted file mode 100644
index 751772870..000000000
--- a/show-viewshed-from-point-in-scene/src/main/java/com/esri/arcgismaps/sample/showviewshedfrompointinscene/MainActivity.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.showviewshedfrompointinscene
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.showviewshedfrompointinscene.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- ViewshedLocationApp()
- }
- }
- }
-
- @Composable
- private fun ViewshedLocationApp() {
- Surface(color = MaterialTheme.colorScheme.background) {
- MainScreen(sampleName = getString(R.string.app_name))
- }
- }
-}
diff --git a/show-viewshed-from-point-in-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml b/show-viewshed-from-point-in-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/show-viewshed-from-point-in-scene/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-viewshed-from-point-in-scene/src/main/res/drawable/ic_launcher_background.xml b/show-viewshed-from-point-in-scene/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/show-viewshed-from-point-in-scene/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/show-viewshed-from-point-in-scene/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/show-viewshed-from-point-in-scene/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml b/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml
deleted file mode 100644
index 1dae0a3dc..000000000
--- a/show-viewshed-from-point-in-scene/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
- Show viewshed from point in scene
- https://scene.arcgis.com/arcgis/rest/services/BREST_DTM_1M/ImageServer
-
- https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer/layers/0
-
-
diff --git a/sketch-on-map/.gitignore b/sketch-on-map/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/sketch-on-map/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/sketch-on-map/build.gradle.kts b/sketch-on-map/build.gradle.kts
deleted file mode 100644
index 7f82f5712..000000000
--- a/sketch-on-map/build.gradle.kts
+++ /dev/null
@@ -1,38 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.sketchonmap"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.sketchonmap"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(libs.android.material)
- implementation(project(":samples-lib"))
-}
diff --git a/sketch-on-map/proguard-rules.pro b/sketch-on-map/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/sketch-on-map/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/sketch-on-map/src/main/AndroidManifest.xml b/sketch-on-map/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/sketch-on-map/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt b/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt
deleted file mode 100644
index 0b61fe9cf..000000000
--- a/sketch-on-map/src/main/java/com/esri/arcgismaps/sample/sketchonmap/MainActivity.kt
+++ /dev/null
@@ -1,279 +0,0 @@
-/* Copyright 2023 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.sketchonmap
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.lifecycleScope
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.GeometryBuilder
-import com.arcgismaps.geometry.GeometryType
-import com.arcgismaps.geometry.Multipoint
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.geometryeditor.FreehandTool
-import com.arcgismaps.mapping.view.geometryeditor.GeometryEditor
-import com.arcgismaps.mapping.view.geometryeditor.VertexTool
-import com.esri.arcgismaps.sample.sketchonmap.databinding.ActivityMainBinding
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-
-class MainActivity : AppCompatActivity() {
-
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- private val selectedGeometryDropdown by lazy {
- activityMainBinding.pointLinePolygonToolbar.selectGeometryDropdown
- }
-
- // create a symbol for the point graphic
- private val pointSymbol: SimpleMarkerSymbol by lazy {
- SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Square,
- Color(getColor(R.color.point_symbol_color)),
- 20f)
- }
-
- // create a symbol for a line graphic
- private val lineSymbol: SimpleLineSymbol by lazy {
- SimpleLineSymbol(
- SimpleLineSymbolStyle.Solid,
- Color(getColor(R.color.line_symbol_color)),
- 4f
- )
- }
-
- // create a symbol for the fill graphic
- private val fillSymbol: SimpleFillSymbol by lazy {
- SimpleFillSymbol(
- SimpleFillSymbolStyle.Cross,
- Color(getColor(R.color.fill_symbol_color)),
- lineSymbol
- )
- }
-
- // keep the instance graphic overlay to add graphics on the map
- private var graphicsOverlay: GraphicsOverlay = GraphicsOverlay()
-
- // keep the instance of the freehand tool
- private val freehandTool: FreehandTool = FreehandTool()
-
- // keep the instance of the vertex tool
- private val vertexTool: VertexTool = VertexTool()
-
- // keep the instance to create new geometries, and change existing geometries
- private var geometryEditor: GeometryEditor = GeometryEditor()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // create and add a map with a navigation night basemap style
- mapView.apply {
- map = ArcGISMap(BasemapStyle.ArcGISLightGray)
- setViewpoint(Viewpoint(34.056295, -117.195800, 100000.0))
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // set MapView's geometry editor to sketch on map
- mapView.geometryEditor = geometryEditor
-
- // enable/disable the undo button if last event can be undone
- lifecycleScope.launch {
- geometryEditor.canUndo.collect { value ->
- activityMainBinding.pointLinePolygonToolbar.undoButton.isEnabled = value
- }
- }
-
- // enable/disable the redo button if the last event can be redone
- lifecycleScope.launch {
- geometryEditor.canRedo.collect { value ->
- activityMainBinding.pointLinePolygonToolbar.redoButton.isEnabled = value
- }
- }
-
- // set up the geometry list dropdown
- selectedGeometryDropdown.apply {
- // set the adapter to the list of geometries
- setAdapter(
- ArrayAdapter(
- applicationContext,
- com.esri.arcgismaps.sample.sampleslib.R.layout.custom_dropdown_item,
- resources.getStringArray(R.array.geometry_list)
- )
- )
-
- // set the dropdown click listener
- onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
- // set the GeometryEditorTool and then start the editing process
- geometryEditor.apply {
- when (position) {
- 0 -> {
- tool = freehandTool
- start(GeometryType.Polygon)
- }
- 1 -> {
- tool = freehandTool
- start(GeometryType.Polyline)
- }
- 2 -> {
- tool = vertexTool
- start(GeometryType.Multipoint)
- }
- 3 -> {
- tool = vertexTool
- start(GeometryType.Point)
- }
- 4 -> {
- tool = vertexTool
- start(GeometryType.Polygon)
- }
- 5 -> {
- tool = vertexTool
- start(GeometryType.Polyline)
- }
- }
- }
- }
- }
- }
-
- /**
- * Undo the last event on the GeometryEditor.
- */
- fun undo(view: View) {
- geometryEditor.undo()
- }
-
- /**
- * Redo the last undone event on the GeometryEditor.
- */
- fun redo(view: View) {
- geometryEditor.redo()
- }
-
- /**
- * When the stop button is clicked, check that sketch is valid. If so, get the geometry from
- * the sketch, set its symbol and add it to the graphics overlay.
- */
- fun stop(view: View) {
- // get the geometry from sketch editor
- val sketchGeometry = geometryEditor.geometry.value
- ?: return showMessage("Error retrieving geometry")
-
- if (!GeometryBuilder.builder(sketchGeometry).isSketchValid) {
- return reportNotValid()
- }
-
- // stops the editing session
- geometryEditor.stop()
-
- // clear the UI selection
- selectedGeometryDropdown.setText("")
- selectedGeometryDropdown.clearFocus()
-
- // create a graphic from the sketch editor geometry
- val graphic = Graphic(sketchGeometry).apply {
- // assign a symbol based on geometry type
- symbol = when (sketchGeometry) {
- is Polygon -> fillSymbol
- is Polyline -> lineSymbol
- is Point, is Multipoint -> pointSymbol
- else -> null
- }
- }
-
- // add the graphic to the graphics overlay
- graphicsOverlay.graphics.add(graphic)
- }
-
- /**
- * Clear the MapView of all the graphics and reset selections
- */
- fun clear(view: View) {
- geometryEditor.clearGeometry()
- geometryEditor.clearSelection()
- geometryEditor.stop()
- selectedGeometryDropdown.setText("")
- selectedGeometryDropdown.clearFocus()
- showMessage(getString(R.string.cleared_message))
- }
-
- /**
- * Clear all editing and applied graphics on the map
- */
- fun restart(view: View) {
- graphicsOverlay.graphics.clear()
- geometryEditor.clearGeometry()
- geometryEditor.clearSelection()
- geometryEditor.stop()
- selectedGeometryDropdown.setText("")
- selectedGeometryDropdown.clearFocus()
- showMessage(getString(R.string.restart_message))
- }
-
- /**
- * Called if sketch is invalid. Reports to user why the sketch was invalid.
- */
- private fun reportNotValid() {
- // get the geometry currently being added to map
- val geometry = geometryEditor.geometry.value ?: return showMessage("Geometry not found")
- // find the geometry type, and set the valid message
- val validIfText: String = when (geometry) {
- is Point -> getString(R.string.invalid_point_message)
- is Multipoint -> getString(R.string.invalid_multipoint_message)
- is Polyline -> getString(R.string.invalid_polyline_message)
- is Polygon -> getString(R.string.invalid_polygon_message)
- else -> getString(R.string.none_selected_message)
- }
- // set the invalid message to the TextView.
- showMessage(validIfText)
- }
-
- private fun showMessage(message: String) {
- Log.e(localClassName, message)
- Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
- }
-}
diff --git a/sketch-on-map/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sketch-on-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/sketch-on-map/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sketch-on-map/src/main/res/drawable/ic_launcher_background.xml b/sketch-on-map/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/sketch-on-map/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/sketch-on-map/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher.png b/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher.png b/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher.png b/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/sketch-on-map/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/sketch-on-map/src/main/res/values/strings.xml b/sketch-on-map/src/main/res/values/strings.xml
deleted file mode 100644
index 10fe0d282..000000000
--- a/sketch-on-map/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
- Sketch on map
- Undo
- Redo
- Clear
- Commit edits
- Point only valid if it contains an x & y coordinate.
- Multipoint only valid if it contains at least one vertex.
- Polyline only valid if it contains at least one part of 2 or more vertices.
- Polygon only valid if it contains at least one part of 3 or more vertices which form a closed ring.
- No sketch creation mode selected.
- Restart
- Cleared editing sketch from map
- Cleared all graphics from map
- Select geometry
-
- - Freehand Polygon
- - Freehand Polyline
- - Multipoint
- - Point
- - Polygon
- - Polyline
-
-
diff --git a/snap-geometry-edits/.gitignore b/snap-geometry-edits/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/snap-geometry-edits/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/snap-geometry-edits/README.md b/snap-geometry-edits/README.md
deleted file mode 100644
index c03348aaf..000000000
--- a/snap-geometry-edits/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# Snap geometry edits
-
-Use the Geometry Editor to edit a geometry and align it to existing geometries on a map.
-
-![Image of Snap geometry edits](snap-geometry-edits.png)
-
-## Use case
-
-A field worker can create new features by editing and snapping the vertices of a geometry to existing features on a map. In a water distribution network, service line features can be represented with the polyline geometry type. By snapping the vertices of a proposed service line to existing features in the network, an exact footprint can be identified to show the path of the service line and what features in the network it connects to. The feature layer containing the service lines can then be accurately modified to include the proposed line.
-
-## How to use the sample
-
-To create a geometry, press the create button to choose the geometry type you want to create (i.e. points, multipoints, polyline, or polygon) and interactively tap and drag on the map view to create the geometry.
-
-To configure snapping, press the snap settings button to enable or disable snapping and choose which snap sources to snap to.
-
-To interactively snap a vertex to a feature or graphic, ensure that snapping is enabled for the relevant snap source and move the map position of the reticle to nearby an existing feature or graphic. When the reticle is close to that existing geoelement, the edit position will be adjusted to coincide with (or snap to), edges and vertices of its geometry. Tap to place the vertex at the snapped location.
-
-To edit a geometry, tap the geometry to be edited in the map and then edit the geometry by tapping and moving its vertices and snapping them to nearby features or graphics.
-
-To edit a vertex using the reticle, tap when the reticle is located over the vertex, drag the map to move the position of the reticle, then tap a second time to place the vertex.
-
-To undo changes made to the geometry, press the undo button.
-
-To delete a vertex, tap when the reticle is located over the vertex and then press the delete button.
-
-To save your edits, press the save button.
-
-## How it works
-
-1. Create a `Map` from the `URL` and connect it to the `MapView`.
-2. Set the map's `LoadSettings.featureTilingMode` to `enabledWithFullResolutionWhenSupported`.
-3. Create a `GeometryEditor` and connect it to the map view.
-4. Create a `ReticleVertexTool` and set it into the `GeometryEditor.tool`.
-5. Call `syncSourceSettings` after the map's operational layers are loaded and the geometry editor connected to the map view.
-6. Set `SnapSettings.isEnabled` and `SnapSourceSettings.isEnabled` to true for the `SnapSource` of interest.
-7. Start the geometry editor with a `GeometryType`.
-
-## Relevant API
-
-* FeatureLayer
-* Geometry
-* GeometryEditor
-* GeometryEditorReticle
-* GeometryEditorStyle
-* GraphicsOverlay
-* MapView
-* ReticleVertexTool
-* SnapSettings
-* SnapSource
-* SnapSourceSettings
-
-## About the data
-
-The [Naperville water distribution network](https://www.arcgis.com/home/item.html?id=b95fe18073bc4f7788f0375af2bb445e) is based on ArcGIS Solutions for Water Utilities and provides a realistic depiction of a theoretical stormwater network.
-
-## Additional information
-
-Snapping is used to maintain data integrity between different sources of data when editing, so it is important that each `SnapSource` provides full resolution geometries to be valid for snapping. This means that some of the default optimizations used to improve the efficiency of data transfer and display of polygon and polyline layers based on feature services are not appropriate for use with snapping.
-
-To snap to polygon and polyline layers, the recommended approach is to set the `FeatureLayer`'s feature tiling mode to `FeatureTilingMode.enabledWithFullResolutionWhenSupported` and use the default `ServiceFeatureTable` feature request mode `FeatureRequestMode.onInteractionCache`. Local data sources, such as geodatabases, always provide full resolution geometries.
-
-Snapping can be used during interactive edits that move existing vertices using the `VertexTool` or `ReticleVertexTool`. It is also supported for adding new vertices for input devices with a hover event (such as a mouse move without a mouse button press). Using the `ReticleVertexTool` to add and move vertices allows users of touch screen devices to clearly see the visual cues for snapping.
-
-This sample uses the GeoViewCompose Toolkit module to be able to implement a Composable MapView.
-
-## Tags
-
-edit, feature, geometryeditor, geoviewcompose, graphics, layers, magnify, map, reticle, snapping
diff --git a/snap-geometry-edits/build.gradle.kts b/snap-geometry-edits/build.gradle.kts
deleted file mode 100644
index 115707059..000000000
--- a/snap-geometry-edits/build.gradle.kts
+++ /dev/null
@@ -1,54 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.snapgeometryedits"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- compose = true
- buildConfig = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = libs.versions.kotlinCompilerExt.get()
- }
-
- namespace = "com.esri.arcgismaps.sample.snapgeometryedits"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.lifecycle.runtime.ktx)
- implementation(libs.androidx.lifecycle.viewmodel.compose)
- implementation(libs.androidx.activity.compose)
- // Jetpack Compose Bill of Materials
- implementation(platform(libs.androidx.compose.bom))
- // Jetpack Compose dependencies
- implementation(libs.androidx.compose.ui)
- implementation(libs.androidx.compose.material3)
- implementation(libs.androidx.compose.ui.tooling)
- implementation(libs.androidx.compose.ui.tooling.preview)
- implementation(project(":samples-lib"))
- // Toolkit dependencies
- implementation(platform(libs.arcgis.maps.kotlin.toolkit.bom))
- implementation(libs.arcgis.maps.kotlin.toolkit.geoview.compose)
-}
diff --git a/snap-geometry-edits/proguard-rules.pro b/snap-geometry-edits/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/snap-geometry-edits/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/snap-geometry-edits/src/main/AndroidManifest.xml b/snap-geometry-edits/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/snap-geometry-edits/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt b/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt
deleted file mode 100644
index 7cfcf1213..000000000
--- a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/MainActivity.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.snapgeometryedits
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme
-import com.esri.arcgismaps.sample.snapgeometryedits.screens.MainScreen
-
-class MainActivity : ComponentActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- setContent {
- SampleAppTheme {
- SampleApp()
- }
- }
- }
-
- @Composable
- private fun SampleApp() {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- MainScreen(
- sampleName = getString(R.string.app_name)
- )
- }
- }
-}
diff --git a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt b/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt
deleted file mode 100644
index 3c1406a53..000000000
--- a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/components/MapViewModel.kt
+++ /dev/null
@@ -1,251 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.snapgeometryedits.components
-
-import android.app.Application
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.AndroidViewModel
-import com.arcgismaps.geometry.GeometryType
-import com.arcgismaps.geometry.Multipoint
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.layers.FeatureTilingMode
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.arcgismaps.mapping.view.SingleTapConfirmedEvent
-import com.arcgismaps.mapping.view.geometryeditor.GeometryEditor
-import com.arcgismaps.mapping.view.geometryeditor.GeometryEditorStyle
-import com.arcgismaps.mapping.view.geometryeditor.ReticleVertexTool
-import com.arcgismaps.mapping.view.geometryeditor.SnapSourceSettings
-import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
-import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
-import com.esri.arcgismaps.sample.snapgeometryedits.R
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class MapViewModel(
- application: Application,
- private val sampleCoroutineScope: CoroutineScope
-) : AndroidViewModel(application) {
- // create a map using the URL of the web map
- val map = ArcGISMap(application.getString(R.string.web_map))
-
- // create a graphic, graphic overlay, and geometry editor
- private var identifiedGraphic = Graphic()
- val graphicsOverlay = GraphicsOverlay()
- val geometryEditor = GeometryEditor()
-
- // create a mapViewProxy that will be used to identify features in the MapView and set the viewpoint
- val mapViewProxy = MapViewProxy()
-
- // create a messageDialogViewModel to handle dialog interactions
- val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel()
-
- // create lists for displaying the snap sources in the bottom sheet
- private val _snapSourceSettingsList = MutableStateFlow(listOf())
- val snapSourceList: StateFlow> = _snapSourceSettingsList
-
- // create boolean flags to track the state of UI components
- val isCreateButtonEnabled = mutableStateOf(false)
- val isSnapSettingsButtonEnabled = mutableStateOf(false)
- val isBottomSheetVisible = mutableStateOf(false)
- val snappingCheckedState = mutableStateOf(false)
- val snapSourceCheckedState = mutableStateListOf()
- val isUndoButtonEnabled = geometryEditor.canUndo
- val isSaveButtonEnabled = geometryEditor.isStarted
- val isDeleteButtonEnabled = geometryEditor.isStarted
-
- /**
- * Configure the map and enable the UI after the map's layers are loaded.
- */
- init {
- // set the id for the graphics overlay
- graphicsOverlay.id = "Editor Graphics Overlay"
- // set the tool for the geometry editor to use a reticle
- geometryEditor.tool = ReticleVertexTool()
- // set the feature layer's tiling mode
- map.loadSettings.featureTilingMode =
- FeatureTilingMode.EnabledWithFullResolutionWhenSupported
-
- isCreateButtonEnabled.value = true
- isSnapSettingsButtonEnabled.value = true
-
- sampleCoroutineScope.launch {
- // load the map
- map.load().onSuccess {
- // load the map's operational layers
- map.operationalLayers.forEach { layer ->
- layer.load().onFailure { error ->
- messageDialogVM.showMessageDialog(
- error.message.toString(),
- error.cause.toString()
- )
- }
- }
- }.onFailure { error ->
- messageDialogVM.showMessageDialog(
- error.message.toString(),
- error.cause.toString()
- )
- }
- }
- }
-
- /**
- * Synchronises the snap source collection with the map's operational layers, sets the bottom
- * sheet UI, and shows it to configure snapping.
- */
- fun showBottomSheet() {
- if (geometryEditor.snapSettings.sourceSettings.isEmpty()) {
- // sync the snap source collection
- geometryEditor.snapSettings.syncSourceSettings()
- // initialise the snap source lists used for the bottom sheet
- geometryEditor.snapSettings.sourceSettings.forEach { snapSource ->
- snapSourceCheckedState.add(snapSource.isEnabled)
- }
- _snapSourceSettingsList.value = geometryEditor.snapSettings.sourceSettings
- }
- isBottomSheetVisible.value = true
- }
-
- /**
- * Toggles snapping using the [checkedValue] from the bottom sheet.
- */
- fun snappingEnabledStatus(checkedValue: Boolean) {
- snappingCheckedState.value = checkedValue
- geometryEditor.snapSettings.isEnabled = snappingCheckedState.value
- }
-
- /**
- * Toggles snapping for the snap source at [index] using the [checkedValue] from the
- * BottomSheet.
- */
- fun sourceEnabledStatus(checkedValue: Boolean, index: Int) {
- snapSourceCheckedState[index] = checkedValue
- geometryEditor.snapSettings.sourceSettings[index].isEnabled = snapSourceCheckedState[index]
- }
-
- /**
- * Hides the bottom sheet.
- */
- fun dismissBottomSheet() {
- isBottomSheetVisible.value = false
- }
-
- /**
- * Starts the GeometryEditor using the selected [GeometryType].
- */
- fun startEditor(selectedGeometry: GeometryType) {
- if (!geometryEditor.isStarted.value) {
- geometryEditor.start(selectedGeometry)
- isCreateButtonEnabled.value = false
- }
- }
-
- /**
- * Stops the GeometryEditor and updates the identified graphic or calls [createGraphic].
- */
- fun stopEditor() {
- if (identifiedGraphic.geometry != null) {
- identifiedGraphic.geometry = geometryEditor.stop()
- identifiedGraphic.isSelected = false
- } else if (geometryEditor.isStarted.value) {
- createGraphic()
- }
- isCreateButtonEnabled.value = true
- }
-
- /**
- * Creates a graphic from the geometry and add it to the GraphicsOverlay.
- */
- private fun createGraphic() {
- val geometry = geometryEditor.stop()
- ?: return messageDialogVM.showMessageDialog(
- "Error!",
- "Error stopping editing session"
- )
- val graphic = Graphic(geometry)
-
- when (geometry) {
- is Point, is Multipoint -> graphic.symbol = GeometryEditorStyle().vertexSymbol
- is Polyline -> graphic.symbol = GeometryEditorStyle().lineSymbol
- is Polygon -> graphic.symbol = GeometryEditorStyle().fillSymbol
- else -> {}
- }
- graphicsOverlay.graphics.add(graphic)
- graphic.isSelected = false
- }
-
- /**
- * Deletes the selected element and stops the geometry editor if there are no
- * more elements in the geometry.
- */
- fun deleteSelection() {
- if (geometryEditor.geometry.value?.isEmpty == true) {
- geometryEditor.stop()
- isCreateButtonEnabled.value = true
- }
-
- val selectedElement = geometryEditor.selectedElement.value
- if (selectedElement?.canDelete == true) {
- geometryEditor.deleteSelectedElement()
- }
- }
-
- /**
- * Reverts the last event on the geometry editor.
- */
- fun editorUndo() {
- geometryEditor.undo()
- }
-
- /**
- * Identifies the graphic at the tapped screen coordinate in the provided [singleTapConfirmedEvent]
- * and starts the GeometryEditor using the identified graphic's geometry. Hide the BottomSheet on
- * [singleTapConfirmedEvent].
- */
- fun identify(singleTapConfirmedEvent: SingleTapConfirmedEvent) {
- sampleCoroutineScope.launch {
- val graphicsResult = mapViewProxy.identifyGraphicsOverlays(
- screenCoordinate = singleTapConfirmedEvent.screenCoordinate,
- tolerance = 10.0.dp,
- returnPopupsOnly = false
- ).getOrNull()
-
- if (!geometryEditor.isStarted.value) {
- if (graphicsResult != null) {
- if (graphicsResult.isNotEmpty()) {
- identifiedGraphic = graphicsResult[0].graphics[0]
- identifiedGraphic.isSelected = true
- identifiedGraphic.geometry?.let {
- geometryEditor.start(it)
- isCreateButtonEnabled.value = false
- }
- }
- }
- identifiedGraphic.geometry = null
- }
- }
- dismissBottomSheet()
- }
-}
diff --git a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt b/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt
deleted file mode 100644
index 866e200b1..000000000
--- a/snap-geometry-edits/src/main/java/com/esri/arcgismaps/sample/snapgeometryedits/screens/MainScreen.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2024 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.snapgeometryedits.screens
-
-import android.app.Application
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Scaffold
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import com.arcgismaps.toolkit.geoviewcompose.MapView
-import com.esri.arcgismaps.sample.sampleslib.components.BottomSheet
-import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
-import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
-import com.esri.arcgismaps.sample.snapgeometryedits.components.MapViewModel
-
-/**
- * Main screen layout for the sample app
- */
-@Composable
-fun MainScreen(sampleName: String) {
- // coroutineScope that will be cancelled when this call leaves the composition
- val sampleCoroutineScope = rememberCoroutineScope()
- // get the application property that will be used to construct MapViewModel
- val sampleApplication = LocalContext.current.applicationContext as Application
- // create a ViewModel to handle MapView interactions
- val mapViewModel = remember { MapViewModel(sampleApplication, sampleCoroutineScope) }
- // the collection of graphics overlays used by the MapView
- val graphicsOverlayCollection = listOf(mapViewModel.graphicsOverlay)
-
- Scaffold(
- content = {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(it)
- ) {
- SampleTopAppBar(title = sampleName)
- MapView(
- modifier = Modifier
- .fillMaxSize()
- .weight(1f),
- arcGISMap = mapViewModel.map,
- geometryEditor = mapViewModel.geometryEditor,
- graphicsOverlays = graphicsOverlayCollection,
- mapViewProxy = mapViewModel.mapViewProxy,
- onSingleTapConfirmed = mapViewModel::identify,
- onPan = { mapViewModel.dismissBottomSheet() }
- )
- ButtonMenu(mapViewModel = mapViewModel)
- mapViewModel.messageDialogVM.apply {
- if (dialogStatus) {
- MessageDialog(
- title = messageTitle,
- description = messageDescription,
- onDismissRequest = ::dismissDialog
- )
- }
- }
- }
- BottomSheet(isVisible = mapViewModel.isBottomSheetVisible.value) {
- SnapSettings(
- snapSourceList = mapViewModel.snapSourceList.collectAsState(),
- onSnappingChanged = mapViewModel::snappingEnabledStatus,
- onSnapSourceChanged = mapViewModel::sourceEnabledStatus,
- isSnappingEnabled = mapViewModel.snappingCheckedState.value,
- isSnapSourceEnabled = mapViewModel.snapSourceCheckedState
- ) { mapViewModel.dismissBottomSheet() }
- }
- }
- )
-}
diff --git a/snap-geometry-edits/src/main/res/drawable-v24/ic_launcher_foreground.xml b/snap-geometry-edits/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/snap-geometry-edits/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/snap-geometry-edits/src/main/res/drawable/ic_launcher_background.xml b/snap-geometry-edits/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/snap-geometry-edits/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/snap-geometry-edits/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher.png b/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher_round.png b/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher.png b/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher_round.png b/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher.png b/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher.png b/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/snap-geometry-edits/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/snap-geometry-edits/src/main/res/values/strings.xml b/snap-geometry-edits/src/main/res/values/strings.xml
deleted file mode 100644
index 123574f8a..000000000
--- a/snap-geometry-edits/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
- Snap Geometry Edits ,
- https://www.arcgis.com/home/item.html?id=b95fe18073bc4f7788f0375af2bb445e
-
diff --git a/style-graphics-with-renderer/.gitignore b/style-graphics-with-renderer/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/style-graphics-with-renderer/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/style-graphics-with-renderer/build.gradle.kts b/style-graphics-with-renderer/build.gradle.kts
deleted file mode 100644
index 7ade9ea90..000000000
--- a/style-graphics-with-renderer/build.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.stylegraphicswithrenderer"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.stylegraphicswithrenderer"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(project(":samples-lib"))
-}
diff --git a/style-graphics-with-renderer/proguard-rules.pro b/style-graphics-with-renderer/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/style-graphics-with-renderer/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/style-graphics-with-renderer/src/main/AndroidManifest.xml b/style-graphics-with-renderer/src/main/AndroidManifest.xml
deleted file mode 100644
index e08429722..000000000
--- a/style-graphics-with-renderer/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt b/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt
deleted file mode 100644
index 2372771da..000000000
--- a/style-graphics-with-renderer/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithrenderer/MainActivity.kt
+++ /dev/null
@@ -1,299 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.stylegraphicswithrenderer
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.AngularUnit
-import com.arcgismaps.geometry.CubicBezierSegment
-import com.arcgismaps.geometry.EllipticArcSegment
-import com.arcgismaps.geometry.GeodesicEllipseParameters
-import com.arcgismaps.geometry.Geometry
-import com.arcgismaps.geometry.GeometryEngine
-import com.arcgismaps.geometry.LinearUnit
-import com.arcgismaps.geometry.MutablePart
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleRenderer
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.stylegraphicswithrenderer.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- // set up data binding for the activity
- private val activityMainBinding: ActivityMainBinding by lazy {
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- }
-
- private val mapView by lazy {
- activityMainBinding.mapView
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
- lifecycle.addObserver(mapView)
-
- // add a map with a topographic basemap style
- mapView.map = ArcGISMap(BasemapStyle.ArcGISTopographic)
- mapView.setViewpoint(Viewpoint(15.169193, 16.333479, 100_000_000.0))
-
- // add graphics overlays
- mapView.graphicsOverlays.addAll(
- listOf(
- makeRenderedPointGraphicsOverlay(),
- makeRenderedLineGraphicsOverlay(),
- makeRenderedPolygonGraphicsOverlay(),
- makeRenderedCurvedPolygonGraphicsOverlay(),
- makeRenderedEllipseGraphicsOverlay()
- )
- )
- }
-
- /**
- * Make a point, its graphic, a graphics overlay for it, and add it to the map view.
- */
- private fun makeRenderedPointGraphicsOverlay(): GraphicsOverlay {
- // create point
- val pointGeometry = Point(40e5, 40e5, SpatialReference.webMercator())
- // create graphic for point
- val pointGraphic = Graphic(pointGeometry)
- // red diamond point symbol
- val pointSymbol =
- SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Diamond, Color.red, 10f)
- // create simple renderer
- val pointRenderer = SimpleRenderer(pointSymbol)
- // create a new graphics overlay with these settings and add it to the map view
- return GraphicsOverlay().apply {
- // add graphic to overlay
- graphics.add(pointGraphic)
- // set the renderer on the graphics overlay to the new renderer
- renderer = pointRenderer
- }
- }
-
- /**
- * Create a polyline, its graphic, a graphics overlay for it, and add it to the map view.
- */
- private fun makeRenderedLineGraphicsOverlay(): GraphicsOverlay {
- // create line
- val lineBuilder = PolylineBuilder(SpatialReference.webMercator()) {
- addPoint(-10e5, 40e5)
- addPoint(20e5, 50e5)
- }
- // create graphic for polyline
- val lineGraphic = Graphic(lineBuilder.toGeometry())
- // solid blue line symbol
- val lineSymbol =
- SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 5f)
- // create simple renderer
- val lineRenderer = SimpleRenderer(lineSymbol)
-
- // create graphic overlay for polyline and add it to the map view
- return GraphicsOverlay().apply {
- // add graphic to overlay
- graphics.add(lineGraphic)
- // set the renderer on the graphics overlay to the new renderer
- renderer = lineRenderer
- }
- }
-
- /**
- * Create a polygon, its graphic, a graphics overlay for it, and add it to the map view.
- */
- private fun makeRenderedPolygonGraphicsOverlay(): GraphicsOverlay {
- // create polygon
- val polygonBuilder = PolygonBuilder(SpatialReference.webMercator()) {
- addPoint(-20e5, 20e5)
- addPoint(20e5, 20e5)
- addPoint(20e5, -20e5)
- addPoint(-20e5, -20e5)
- }
- // create graphic for polygon
- val polygonGraphic = Graphic(polygonBuilder.toGeometry())
- // solid yellow polygon symbol
- val polygonSymbol =
- SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.yellow, null)
- // create simple renderer
- val polygonRenderer = SimpleRenderer(polygonSymbol)
-
- // create graphic overlay for polygon and add it to the map view
- return GraphicsOverlay().apply {
- // add graphic to overlay
- graphics.add(polygonGraphic)
- // set the renderer on the graphics overlay to the new renderer
- renderer = polygonRenderer
- }
- }
-
- /**
- * Create a curved polygon, its graphic, a graphics overlay for it, and add it to the map view.
- */
- private fun makeRenderedCurvedPolygonGraphicsOverlay(): GraphicsOverlay {
- // create a point for the center of the geometry
- val originPoint = Point(40e5, 5e5, SpatialReference.webMercator())
- // create polygon
- val curvedPolygonGeometry = makeHeartGeometry(originPoint, 10e5)
- // create graphic for polygon
- val polygonGraphic = Graphic(curvedPolygonGeometry)
- // create a simple fill symbol with outline
- val curvedLineSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.black, 1f)
- val curvedFillSymbol =
- SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.red, curvedLineSymbol)
- // create simple renderer
- val polygonRenderer = SimpleRenderer(curvedFillSymbol)
-
- // create graphic overlay for polygon and add it to the map view
- return GraphicsOverlay().apply {
- // add graphic to overlay
- graphics.add(polygonGraphic)
- // set the renderer on the graphics overlay to the new renderer
- renderer = polygonRenderer
- }
- }
-
- /**
- * Create a heart-shape geometry with Bezier and elliptic arc segments from a given [center]
- * point and [sideLength].
- */
- private fun makeHeartGeometry(center: Point, sideLength: Double): Geometry {
- val spatialReference = center.spatialReference
- // the x and y coordinates to simplify the calculation
- val minX = center.x - 0.5 * sideLength
- val minY = center.y - 0.5 * sideLength
- // the radius of the arcs
- val arcRadius = sideLength * 0.25
-
- // bottom left curve
- val leftCurveStart = Point(center.x, minY, spatialReference)
- val leftCurveEnd = Point(minX, minY + 0.75 * sideLength, spatialReference)
- val leftControlPoint1 = Point(center.x, minY + 0.25 * sideLength, spatialReference)
- val leftControlPoint2 = Point(minX, center.y, spatialReference)
- val leftCurve = CubicBezierSegment(
- leftCurveStart,
- leftControlPoint1,
- leftControlPoint2,
- leftCurveEnd,
- spatialReference
- )
-
- // top left arc
- val leftArcCenter =
- Point(minX + 0.25 * sideLength, minY + 0.75 * sideLength, spatialReference)
- val leftArc = EllipticArcSegment.createCircularEllipticArc(
- leftArcCenter,
- arcRadius,
- Math.PI,
- -Math.PI,
- spatialReference
- )
-
- // top right arc
- val rightArcCenter =
- Point(minX + 0.75 * sideLength, minY + 0.75 * sideLength, spatialReference)
- val rightArc = EllipticArcSegment.createCircularEllipticArc(
- rightArcCenter,
- arcRadius,
- Math.PI,
- -Math.PI,
- spatialReference
- )
-
- // bottom right curve
- val rightCurveStart = Point(minX + sideLength, minY + 0.75 * sideLength, spatialReference)
- val rightControlPoint1 = Point(minX + sideLength, center.y, spatialReference)
- val rightCurve = CubicBezierSegment(
- rightCurveStart,
- rightControlPoint1,
- leftControlPoint1,
- leftCurveStart,
- spatialReference
- )
-
- // create a mutable part list
- val heartParts = MutablePart.createWithSegments(
- listOf(leftCurve, leftArc, rightArc, rightCurve),
- spatialReference
- )
- // return the heart
- return Polygon(listOf(heartParts).asIterable())
- }
-
- /**
- * Create an ellipse, its graphic, a graphics overlay for it, and add it to the map view.
- */
- private fun makeRenderedEllipseGraphicsOverlay(): GraphicsOverlay {
- // create and set all the parameters so that the ellipse has a major axis of 400 kilometres,
- // a minor axis of 200 kilometres and is rotated at an angle of -45 degrees
- val parameters = GeodesicEllipseParameters.createForPolygon().apply {
- axisDirection = -45.0
- angularUnit = AngularUnit.degrees
- center = Point(40e5, 23e5, SpatialReference.webMercator())
- linearUnit = LinearUnit.kilometers
- maxPointCount = 100L
- maxSegmentLength = 20.0
- semiAxis1Length = 200.0
- semiAxis2Length = 400.0
- }
-
- // define the ellipse parameters to a polygon geometry
- val polygon = GeometryEngine.ellipseGeodesicOrNull(parameters)
- // set the ellipse fill color
- val ellipseSymbol = SimpleFillSymbol(SimpleFillSymbolStyle.Solid, Color.magenta, null)
- // return the purple ellipse
- return GraphicsOverlay().apply {
- // add the symbol to the renderer and add it to the graphic overlay
- renderer = SimpleRenderer(ellipseSymbol)
- graphics.add(Graphic(polygon))
- }
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
- private val Color.Companion.yellow: Color
- get() {
- return fromRgba(255, 255, 0, 255)
- }
-
- private val Color.Companion.magenta: Color
- get() {
- return fromRgba(255, 0, 255, 255)
- }
-}
diff --git a/style-graphics-with-renderer/src/main/res/drawable-v24/ic_launcher_foreground.xml b/style-graphics-with-renderer/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/style-graphics-with-renderer/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-renderer/src/main/res/drawable/ic_launcher_background.xml b/style-graphics-with-renderer/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/style-graphics-with-renderer/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/style-graphics-with-renderer/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher.png b/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher_round.png b/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher.png b/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher_round.png b/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher.png b/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher.png b/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/style-graphics-with-renderer/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-renderer/src/main/res/values/strings.xml b/style-graphics-with-renderer/src/main/res/values/strings.xml
deleted file mode 100644
index c3103616f..000000000
--- a/style-graphics-with-renderer/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Style graphics with renderer
-
diff --git a/style-graphics-with-symbols/.gitignore b/style-graphics-with-symbols/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/style-graphics-with-symbols/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/style-graphics-with-symbols/build.gradle.kts b/style-graphics-with-symbols/build.gradle.kts
deleted file mode 100644
index 15e84d2cd..000000000
--- a/style-graphics-with-symbols/build.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- id("com.android.application")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
-
- defaultConfig {
- applicationId = "com.esri.arcgismaps.sample.stylegraphicswithsymbols"
- minSdk = libs.versions.minSdk.get().toInt()
- targetSdk = libs.versions.targetSdk.get().toInt()
- versionCode = libs.versions.versionCode.get().toInt()
- versionName = libs.versions.versionName.get()
- buildConfigField("String", "API_KEY", project.properties["API_KEY"].toString())
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
- }
- }
-
- buildFeatures {
- dataBinding = true
- buildConfig = true
- }
-
- namespace = "com.esri.arcgismaps.sample.stylegraphicswithsymbols"
-}
-
-dependencies {
- // lib dependencies from rootProject build.gradle.kts
- implementation(libs.androidx.constraintlayout)
- implementation(project(":samples-lib"))
-}
diff --git a/style-graphics-with-symbols/proguard-rules.pro b/style-graphics-with-symbols/proguard-rules.pro
deleted file mode 100644
index 2f9dc5a47..000000000
--- a/style-graphics-with-symbols/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.kts.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/style-graphics-with-symbols/src/main/AndroidManifest.xml b/style-graphics-with-symbols/src/main/AndroidManifest.xml
deleted file mode 100644
index c6647b46d..000000000
--- a/style-graphics-with-symbols/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt b/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt
deleted file mode 100644
index cf0382054..000000000
--- a/style-graphics-with-symbols/src/main/java/com/esri/arcgismaps/sample/stylegraphicswithsymbols/MainActivity.kt
+++ /dev/null
@@ -1,306 +0,0 @@
-/* Copyright 2022 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.esri.arcgismaps.sample.stylegraphicswithsymbols
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.databinding.DataBindingUtil
-import com.arcgismaps.ApiKey
-import com.arcgismaps.ArcGISEnvironment
-import com.arcgismaps.Color
-import com.arcgismaps.geometry.Point
-import com.arcgismaps.geometry.Polygon
-import com.arcgismaps.geometry.PolygonBuilder
-import com.arcgismaps.geometry.Polyline
-import com.arcgismaps.geometry.PolylineBuilder
-import com.arcgismaps.geometry.SpatialReference
-import com.arcgismaps.mapping.ArcGISMap
-import com.arcgismaps.mapping.BasemapStyle
-import com.arcgismaps.mapping.Viewpoint
-import com.arcgismaps.mapping.symbology.HorizontalAlignment
-import com.arcgismaps.mapping.symbology.SimpleFillSymbol
-import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleLineSymbol
-import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
-import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
-import com.arcgismaps.mapping.symbology.TextSymbol
-import com.arcgismaps.mapping.symbology.VerticalAlignment
-import com.arcgismaps.mapping.view.Graphic
-import com.arcgismaps.mapping.view.GraphicsOverlay
-import com.esri.arcgismaps.sample.stylegraphicswithsymbols.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // authentication with an API key or named user is
- // required to access basemaps and other location services
- ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
-
- // set up data binding for the activity
- val activityMainBinding: ActivityMainBinding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
- val mapView = activityMainBinding.mapView
- lifecycle.addObserver(mapView)
-
- // create the graphics overlay
- val graphicsOverlay = GraphicsOverlay()
-
- mapView.apply {
- // create a map with the BasemapStyle Oceans and display it in this view
- map = ArcGISMap(BasemapStyle.ArcGISOceans)
- setViewpoint(Viewpoint(56.075844, -2.681572, 100000.0))
- // add the overlay to the map view
- graphicsOverlays.add(graphicsOverlay)
- }
-
- // add some buoy positions to the graphics overlay
- val buoyPoints = createBuoyGraphics()
- graphicsOverlay.graphics.addAll(buoyPoints)
-
- // add boat trip polyline to graphics overlay
- val tripRouteGraphic = createRoute()
- graphicsOverlay.graphics.add(tripRouteGraphic)
-
- // add nesting ground polygon to graphics overlay
- val nestingGround = createNestingGround()
- graphicsOverlay.graphics.add(nestingGround)
-
- // add text symbols and points to graphics overlay
- val textGraphics = createTextGraphics()
- graphicsOverlay.graphics.addAll(textGraphics)
- }
-
- /**
- * Create Graphics for some points.
- *
- * @return a new graphic
- */
- private fun createBuoyGraphics(): Array {
- // define the buoy locations
- val buoy1Loc = Point(-2.712642647560347, 56.06281256681154, SpatialReference.wgs84())
- val buoy2Loc = Point(-2.690841695957230, 56.06444173689877, SpatialReference.wgs84())
- val buoy3Loc = Point(-2.669727388499094, 56.06425007340287, SpatialReference.wgs84())
- val buoy4Loc = Point(-2.639515046119973, 56.06127916736989, SpatialReference.wgs84())
-
- // create a marker symbol
- val buoyMarker =
- SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10.0f)
-
- // create graphics
- return arrayOf(
- Graphic(buoy1Loc, buoyMarker),
- Graphic(buoy2Loc, buoyMarker),
- Graphic(buoy3Loc, buoyMarker),
- Graphic(buoy4Loc, buoyMarker)
- )
- }
-
- /**
- * Create graphics which display text at specific locations.
- *
- * @return a new graphic
- */
- private fun createTextGraphics(): Array {
- // create a point geometry
- val bassLocation =
- Point(-2.640631, 56.078083, SpatialReference.wgs84())
- val craigleithLocation =
- Point(-2.720324, 56.073569, SpatialReference.wgs84())
-
- // create text symbols
- val bassRockSymbol = TextSymbol(
- getString(R.string.bassrock),
- Color.blue,
- 10.0f,
- HorizontalAlignment.Left, VerticalAlignment.Bottom
- )
- val craigleithSymbol = TextSymbol(
- getString(R.string.craigleith),
- Color.blue,
- 10.0f,
- HorizontalAlignment.Right, VerticalAlignment.Top
- )
-
- // define graphics from each geometry and symbol
- val bassRockGraphic = Graphic(bassLocation, bassRockSymbol)
- val craigleithGraphic = Graphic(craigleithLocation, craigleithSymbol)
-
- return arrayOf(bassRockGraphic, craigleithGraphic)
- }
-
- /**
- * Create a graphic which displays a polyline.
- *
- * @return a new graphic
- */
- private fun createRoute(): Graphic {
- // define a polyline for the boat trip
- val boatRoute: Polyline = getBoatTripGeometry()
- // define a line symbol
- val lineSymbol =
- SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.magenta, 4.0f)
-
- // create and return a new graphic
- return Graphic(boatRoute, lineSymbol)
- }
-
- /**
- * Create a graphic which displays a polygon.
- *
- * @return a new graphic
- */
- private fun createNestingGround(): Graphic {
- // define the polygon for the nesting ground
- val nestingGround = getNestingGroundGeometry()
- // define the fill symbol and outline
- val outlineSymbol =
- SimpleLineSymbol(SimpleLineSymbolStyle.Dash, Color.blue, 1.0f)
- val fillSymbol = SimpleFillSymbol(
- SimpleFillSymbolStyle.DiagonalCross, Color.green,
- outlineSymbol
- )
-
- // create and return a new graphic
- return Graphic(nestingGround, fillSymbol)
- }
-
- /**
- * Create a polyline representing the route of the boat trip.
- *
- * @return a new polyline
- */
- private fun getBoatTripGeometry(): Polyline {
- // a new point collection to make up the polyline
- val boatPositionsPolylineBuilder = PolylineBuilder(SpatialReference.wgs84()) {
- // add positions to the point collection
- addPoint(Point(-2.718479122792677, 56.06147084563517))
- addPoint(Point(-2.719680750046392, 56.06147084563517))
- addPoint(Point(-2.722084004553823, 56.06214171205971))
- addPoint(Point(-2.726375530459948, 56.06386674355254))
- addPoint(Point(-2.726890513568683, 56.06607083814320))
- addPoint(Point(-2.727062174604927, 56.06779569383808))
- addPoint(Point(-2.725517225278723, 56.06875391365391))
- addPoint(Point(-2.723113970771293, 56.06942465335233))
- addPoint(Point(-2.719165766937657, 56.07028701581465))
- addPoint(Point(-2.713672613777817, 56.07057446568132))
- addPoint(Point(-2.709381087871692, 56.07095772883556))
- addPoint(Point(-2.704402917820587, 56.07153261642126))
- addPoint(Point(-2.698223120515766, 56.07239493172226))
- addPoint(Point(-2.692386645283435, 56.07325722773041))
- addPoint(Point(-2.686721831087350, 56.07335303720707))
- addPoint(Point(-2.681228677927500, 56.07354465544585))
- addPoint(Point(-2.676422168912640, 56.07421531177896))
- addPoint(Point(-2.669899049535339, 56.07488595644139))
- addPoint(Point(-2.664749218447989, 56.07574819671591))
- addPoint(Point(-2.659427726324393, 56.07613140842321))
- addPoint(Point(-2.654792878345778, 56.07622721075461))
- addPoint(Point(-2.651359657620878, 56.07651461631978))
- addPoint(Point(-2.647754775859732, 56.07708942101955))
- addPoint(Point(-2.645008199279812, 56.07814320736718))
- addPoint(Point(-2.643291588917362, 56.08025069360931))
- addPoint(Point(-2.638656740938747, 56.08044227755186))
- addPoint(Point(-2.636940130576297, 56.07881378367495))
- addPoint(Point(-2.636425147467562, 56.07728102068079))
- addPoint(Point(-2.637798435757522, 56.07661041769850))
- addPoint(Point(-2.638656740938747, 56.07507756705851))
- addPoint(Point(-2.641231656482422, 56.07479015077557))
- addPoint(Point(-2.642776605808628, 56.07574819671591))
- addPoint(Point(-2.645694843424792, 56.07546078543464))
- addPoint(Point(-2.647239792750997, 56.07459853872940))
- addPoint(Point(-2.649299725185938, 56.07268236586862))
- addPoint(Point(-2.653076267983328, 56.07182005699860))
- addPoint(Point(-2.655479522490758, 56.07086191340429))
- addPoint(Point(-2.658741082179413, 56.07047864929729))
- addPoint(Point(-2.663375930158029, 56.07028701581465))
- addPoint(Point(-2.666637489846684, 56.07009538137926))
- addPoint(Point(-2.670070710571584, 56.06990374599109))
- addPoint(Point(-2.674190575441464, 56.06913719491074))
- addPoint(Point(-2.678310440311345, 56.06808316228391))
- addPoint(Point(-2.682086983108735, 56.06789151689155))
- addPoint(Point(-2.686893492123596, 56.06760404701653))
- addPoint(Point(-2.691185018029721, 56.06722075051504))
- addPoint(Point(-2.695133221863356, 56.06702910083509))
- addPoint(Point(-2.698223120515766, 56.06683745020233))
- addPoint(Point(-2.701656341240667, 56.06645414607839))
- addPoint(Point(-2.706119528183037, 56.06607083814320))
- addPoint(Point(-2.710067732016672, 56.06559169786458))
- addPoint(Point(-2.713329291705327, 56.06520838135397))
- addPoint(Point(-2.716762512430227, 56.06453756828941))
- addPoint(Point(-2.718307461756433, 56.06348340989081))
- addPoint(Point(-2.719165766937657, 56.06281256681154))
- addPoint(Point(-2.719852411082638, 56.06204587471371))
- addPoint(Point(-2.719165766937657, 56.06166252294756))
- addPoint(Point(-2.718307461756433, 56.06147084563517))
- }
-
- // create the polyline from the point collection
- return boatPositionsPolylineBuilder.toGeometry()
- }
-
- /**
- * Create a polygon from a point collection.
- *
- * @return a new polygon
- */
- private fun getNestingGroundGeometry(): Polygon {
- // a new point collection to make up the polygon
- val pointsPolygonBuilder = PolygonBuilder(SpatialReference.wgs84()) {
- // add points to the point collection
- addPoint(Point(-2.643077012566659, 56.07712534604447))
- addPoint(Point(-2.642819521015944, 56.07717324600376))
- addPoint(Point(-2.642540571836003, 56.07774804087097))
- addPoint(Point(-2.642712232869812, 56.07792766250863))
- addPoint(Point(-2.642454741319098, 56.07829887790651))
- addPoint(Point(-2.641853927700763, 56.07852639525372))
- addPoint(Point(-2.640974164902487, 56.07880180919243))
- addPoint(Point(-2.639987113958079, 56.07881378366685))
- addPoint(Point(-2.639407757968971, 56.07908919555142))
- addPoint(Point(-2.638764029092183, 56.07917301616904))
- addPoint(Point(-2.638485079912242, 56.07896945149566))
- addPoint(Point(-2.638570910429147, 56.07820308072684))
- addPoint(Point(-2.638785486721410, 56.07756841839600))
- addPoint(Point(-2.639193181676709, 56.07719719596109))
- addPoint(Point(-2.639944198699627, 56.07675411934114))
- addPoint(Point(-2.640652300464093, 56.07673016910844))
- addPoint(Point(-2.640673758093319, 56.07632301287509))
- addPoint(Point(-2.640180232621116, 56.07599967986049))
- addPoint(Point(-2.640244605508794, 56.07584400003405))
- addPoint(Point(-2.640416266542604, 56.07578412301025))
- addPoint(Point(-2.640888334385582, 56.07580807383093))
- addPoint(Point(-2.641768097183858, 56.07623918605773))
- addPoint(Point(-2.642197249768383, 56.07625116132851))
- addPoint(Point(-2.642840978645171, 56.07661041772168))
- addPoint(Point(-2.643077012566659, 56.07712534604447))
- }
-
- // create a polygon from the point collection
- return pointsPolygonBuilder.toGeometry()
- }
-
- private val Color.Companion.blue: Color
- get() {
- return fromRgba(0, 0, 255, 255)
- }
-
- private val Color.Companion.magenta: Color
- get() {
- return fromRgba(255, 0, 255, 255)
- }
-}
diff --git a/style-graphics-with-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml b/style-graphics-with-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/style-graphics-with-symbols/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-symbols/src/main/res/drawable/ic_launcher_background.xml b/style-graphics-with-symbols/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 6d8cae103..000000000
--- a/style-graphics-with-symbols/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6b78462d6..000000000
--- a/style-graphics-with-symbols/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher.png b/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index a2f590828..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png b/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1b5239980..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher.png b/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ff10afd6e..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png b/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 115a4c768..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png b/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index dcd3cd808..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 459ca609d..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png b/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 8ca12fe02..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 8e19b410a..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index b824ebdd4..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 4c19a13c2..000000000
Binary files a/style-graphics-with-symbols/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/style-graphics-with-symbols/src/main/res/values/strings.xml b/style-graphics-with-symbols/src/main/res/values/strings.xml
deleted file mode 100644
index efeb39f35..000000000
--- a/style-graphics-with-symbols/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
- Style graphics with symbols
- Bass Rock
- Craigleith
-